Skip to content

RP2040 ​

The RP2040 is a dual-core Cortex-M0+ microcontroller from Raspberry Pi with 264 KB SRAM and 2 MB flash (external). ferrite-sdk supports the RP2040 with the thumbv6m-none-eabi target.

Memory layout ​

The RP2040 has 264 KB of SRAM mapped at 0x20000000 - 0x20041FFF. The memory consists of six banks (four 64 KB "striped" banks and two 4 KB banks for the USB and XIP subsystems), but all are accessible as a contiguous address range.

Linker script ​

Reserve the last 256 bytes for the retained block:

ld
MEMORY
{
  FLASH : ORIGIN = 0x10000000, LENGTH = 2M
  RAM   : ORIGIN = 0x20000000, LENGTH = 263936   /* 264K - 256 bytes */
  RETAINED (rwx) : ORIGIN = 0x20041F00, LENGTH = 0x100
}

SECTIONS
{
  .uninit.ferrite (NOLOAD) : {
    . = ALIGN(4);
    _ferrite_retained_start = .;
    KEEP(*(.uninit.ferrite))
    _ferrite_retained_end = .;
    . = ALIGN(4);
  } > RETAINED
}

The pre-built fragment is at linker/rp2040-retained.x.

RAM regions ​

rust
ram_regions: &[RamRegion {
    start: 0x2000_0000,
    end: 0x2004_2000,  // 264 KB
}],

Cortex-M0+ considerations ​

The RP2040 uses Cortex-M0+, which has a simpler fault model than Cortex-M3/M4:

  • There is no separate MemFault, BusFault, or UsageFault -- all faults escalate to HardFault.
  • The CFSR, MMFAR, and BFAR registers do not exist. The SDK will read zeroes for these fields.
  • Only the base exception frame (R0-R3, R12, LR, PC, xPSR) is pushed by hardware.

The SDK's HardFault handler works on M0+ but the captured fault information is less detailed than on M3/M4.

Build ​

bash
rustup target add thumbv6m-none-eabi
cargo build -p ferrite-sdk --features cortex-m --target thumbv6m-none-eabi

Retained RAM on RP2040 ​

SRAM on the RP2040 is retained across software resets (watchdog and AIRCR.SYSRESETREQ). It is not retained across power-on reset or the RUN pin reset.

The RP2040 watchdog can be configured to reset only the processor (preserving SRAM) or to reset the entire chip. For retained RAM to work after a watchdog reset, use processor-only reset:

rust
// Ensure watchdog does NOT reset SRAM
let watchdog = &*pac::WATCHDOG::ptr();
watchdog.ctrl.modify(|_, w| w.enable().set_bit());

Reset reason ​

The RP2040 has a CHIP_RESET register in the VREG_AND_CHIP_RESET block. Read it to determine the reset cause:

rust
fn read_rp2040_reset_reason() -> ferrite_sdk::RebootReason {
    let vreg = unsafe { &*pac::VREG_AND_CHIP_RESET::ptr() };
    let chip_reset = vreg.chip_reset.read();

    if chip_reset.had_psm_restart().bit_is_set() {
        ferrite_sdk::RebootReason::SoftwareReset
    } else if chip_reset.had_run().bit_is_set() {
        ferrite_sdk::RebootReason::PinReset
    } else if chip_reset.had_por().bit_is_set() {
        ferrite_sdk::RebootReason::PowerOnReset
    } else {
        ferrite_sdk::RebootReason::Unknown
    }
}

Flashing ​

Use probe-rs or elf2uf2-rs for flashing:

bash
# Via probe-rs (SWD debug probe required)
cargo run --release

# Via UF2 (drag-and-drop, no debug probe needed)
cargo install elf2uf2-rs
cargo build --release
elf2uf2-rs target/thumbv6m-none-eabi/release/my-firmware
# Copy the .uf2 file to the RP2040 mass storage device