wokwi / rp2040js

A Raspberry Pi Pico Emulator in JavaScript
MIT License
410 stars 46 forks source link

Support for newer CircuitPython firmwares #139

Closed rgov closed 9 months ago

rgov commented 9 months ago

rp2040js is unable to boot newer CircuitPython firmware. The default version is 8.0.2, which indeed works. Since then, several releases have been made. I tested a few of them:

Version Works?
8.0.5
8.1.0 hangs
8.2.0 [RP2040] read from address 100a692d, which is not 32 bit aligned

Others including 8.2.9 and 9.0.0-beta.0 fail with similar unaligned read errors:

$  npm run start:circuitpython -- --image=adafruit-circuitpython-raspberry_pi_pico-en_US-9.0.0-beta.0.uf2
...
Error: [RP2040] read from address 2, which is not 32 bit aligned
    at ConsoleLogger.error (/private/tmp/rp2040js/src/utils/logging.ts:48:15)
    at RP2040.readUint32 (/private/tmp/rp2040js/src/rp2040.ts:202:19)
    at CortexM0Core.readUint32 (/private/tmp/rp2040js/src/cortex-m0-core.ts:194:24)
    at CortexM0Core.executeInstruction (/private/tmp/rp2040js/src/cortex-m0-core.ts:826:36)
    at RP2040.execute (/private/tmp/rp2040js/src/rp2040.ts:381:17)
    at Timeout._onTimeout (/private/tmp/rp2040js/src/rp2040.ts:384:49)
    at listOnTimeout (node:internal/timers:573:17)
    at process.processTimers (node:internal/timers:514:7)
rgov commented 9 months ago

Performed a bisection of commits between 8.0.5 and 8.1.0 and found this was the first that fails: https://github.com/adafruit/circuitpython/commit/cbfb2d0f55a59d025c186cb6bdfea8ce5a8afb6c (which modifies ports/raspberrypi/supervisor/port.c).

After reverting that commit, rp2040js can run all the way up to 9.0.0!

$ git apply -R <(git diff cbfb2d0'^':ports/raspberrypi/supervisor/port.c cbfb2d0:ports/raspberrypi/supervisor/port.c)
Details of the awful bisect script I had to remove the part of `demo/micropython-run.ts` that handles `process.stdin`. Note I'm on macOS so `gmake` is GNU `make`, `sed` is slightly different. ```sh #!/bin/bash -eu make fetch-submodules make -C mpy-cross cd ports/raspberrypi sed -i '' 's/-Werror //g' Makefile gmake -j8 BOARD=raspberry_pi_pico git checkout Makefile # Run the program with a timeout cd /tmp/rp2040js exec gtimeout 12 npm run start:circuitpython -- --image=circuitpython/ports/raspberrypi/build-raspberry_pi_pico/firmware.uf2 --expect-text "Press any key to enter the REPL" ```
urish commented 9 months ago

Good job! I believe this is the culprit:

https://github.com/adafruit/circuitpython/commit/cbfb2d0f55a59d025c186cb6bdfea8ce5a8afb6c#diff-a05d314adb461e8edad8e3a970d717cafb82f7a41fa4e9fe4220e003f7c947abR107-R111

    // Load from the XIP memory space that doesn't cache. That way we don't
    // evict anything else. The code we're loading is linked to the RAM address
    // anyway.
    size_t nocache = 0x03000000;
rgov commented 9 months ago

That would be the offset from XIP_BASE to XIP_NOCACHE_NOALLOC_BASE. The RP2040 datasheet, §2.6.3.1 "XIP Cache" explains this is a "mirror" of the base address which bypasses the cache.

urish commented 9 months ago

Thanks again for spotting the issue. One of the best bug reports I ever received :)