[Dmitry Grinberg] is an adept tinkerer who wanted a much larger RAM space on his Raspberry Pi 2040 (RP2040) than the measly 264kb on-board SRAM. The chip does support 16MB of off-flash memory via a QSPI bus, but this must be accessed explicitly rather than being memory mapped. With clever trickery involving XIP (Execute in Place), Dmitry mapped 8MB of external QSPI RAM into the address space.
XIP mode allows the chip to fetch data on-demand from an external chip and place it into RP2040 caches mapped at 0x10xxxxxx. The RP2040, although incredibly versatile, has a limitation – it can only perform read and execute operations in its XIP mode. The first step to solving this was to get data from persistent storage to RAM on boot. Armed with a dual-OR gate IC, an inverter, and two resistors, [Dmitry] can toggle the nCS pin that selects between flash and RAM. A first-stage bootloader copies the program from flash to RAM, then sets up XIP mode and launches into a second-stage loader.
Of course, glitches couldn’t resist making an appearance. The RP2040’s GPIO IP block doesn’t reset properly, so the nCS pin doesn’t go back to selecting flash after reset. For [Dmitry], it wasn’t a problem as he used an I2C IO expander and made the troublesome pin dance to his tune.
The next step was to write changes back to RAM when the cache was flushed. This is where the MPU (Memory Protection Unit) gets abused. Usually, a write to 0x10xxxxxx will flush the cache line. When a write happens, the MPU can trigger a hard fault by marking that region as read-only. The handler emulates the write instruction and flushes the cache line. From a high level, it seems pretty straightforward, but there are 127 different types of write instructions for ARMv6M. While most code is read-heavy rather than write-heavy, performance is still a concern. Luckily for him,
memcpy clocked in at 36 Mbit/s at stock clock rates, a commendable achievement given the inherent challenges.
[Dmitry’s] groundbreaking code is accessible for download on his website under a BSD-2 license, allowing others to explore the potential of vast amounts of RAM on the RP2040 microcontroller. This innovative approach opens doors to all sorts of creative possibilities. Despite being a partial ARMv6 emulator, this isn’t the first emulator we’ve seen on RP2040, as this RISC-V emulator attests.