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

Released under the MIT License.