Skip to content

zephyr-cp: restore "ranges" on RP2040 partitions, fixing unbootable images - #13

Closed
tyeth wants to merge 2 commits into
mainfrom
rp2040-boot-ranges-fix
Closed

zephyr-cp: restore "ranges" on RP2040 partitions, fixing unbootable images#13
tyeth wants to merge 2 commits into
mainfrom
rp2040-boot-ranges-fix

Conversation

@tyeth

@tyeth tyeth commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Fixes #6.

Independent of the Bluetooth work — this is why no RP2040 board in ports/zephyr-cp has ever booted from its UF2. Based on main, not on the BLE stack.

The RP2040 overlays /delete-node/ partitions; and rebuild the node, dropping the ranges; the upstream board DTS provides. Address translation then can't reach flash0's ranges = <0x0 0x10000000 ...>, so the code partition resolves to 0x100 instead of 0x10000100. That both mis-links the image and, because soc/raspberrypi/rpi_pico/rp2040/Kconfig gates second-stage boot on the address being exactly 0x10000100, drops .boot2 from the ELF entirely.

The Pico 2 W overlay merges into the existing partitions node instead of deleting it, so it kept ranges; and booted. That was the whole difference.

Before / after

before after
rom_start VMA/LMA 0x00000100 0x10000100
.boot2 absent from ELF 0x10000000, 256 B
UF2 range 0x00000100..0x00107b00 0x10000000..0x10107b00
UF2 block at 0x10000000 no yes

All three affected boards verified by regenerating artifacts: raspberrypi_rpi_pico_zephyr, raspberrypi_rpi_pico_w_zephyr, adafruit_feather_rp2040_zephyr. UF2 family id stays 0xe48bff56.

Verified on hardware

A Pico W flashed with the fixed UF2 boots, enumerates CDC + CIRCUITPY, and runs code — where the same build before this change fell straight back to BOOTSEL (2e8a:0003 RP2 Boot).

Pre-flash static checks also held: the CRC32 the RP2040 bootrom validates over boot2 (MPEG-2 variant, bytes 0–251, stored LE at 252–255) matches at 0xd58f0b07 on all three boards' UF2s, and the vector table at 0x10000100 has a sane initial SP (0x2001f300) and reset vector (0x1000f889, z_arm_reset + Thumb bit).

Nothing under zephyr/ or modules/ is touched — upstream is correct, the port was supplying malformed devicetree.

Note the code partition is now 0x100..0x180000 rather than 0x0..0x180000, so the image gains 256 usable bytes; nvm/storage/circuitpy offsets are unchanged, so existing filesystem contents survive.

Related, not fixed here: #7 (same defect in the STM32WBA overlay, unverified).

🤖 Generated with Claude Code

…mages

Every RP2040 board overlay in this port does

    &flash0 {
        /delete-node/ partitions;
        partitions { #address-cells = <1>; #size-cells = <1>; ... };
    };

to replace the stock partition table. Re-creating the node dropped the
"ranges;" property that the upstream board DTS puts on "partitions"
(zephyr/boards/raspberrypi/rpi_pico/rpi_pico-common.dtsi and
zephyr/boards/adafruit/feather_rp2040/adafruit_feather_rp2040.dts).

"ranges" is what makes devicetree address translation walk from a
partition up through flash0's "ranges = <0x0 0x10000000 ...>" into the
SoC address space. Without it the translation stops at "partitions", so
for the "zephyr,mapped-partition" children DT_REG_ADDR() returns the
bare in-flash offset instead of the XIP address:

    before:  code_partition REG_IDX_0_VAL_ADDRESS = 0x100
    after:   code_partition REG_IDX_0_VAL_ADDRESS = 0x10000100

Two independent things then went wrong, and together they made the
image unbootable:

1. With CONFIG_FLASH_USES_MAPPED_PARTITION=y, ROM_ADDR in
   zephyr/include/zephyr/arch/arm/cortex_m/scripts/linker.ld is
   DT_REG_ADDR(DT_CHOSEN(zephyr_code_partition)), so FLASH ORIGIN
   became 0x100 and the whole image was linked below the XIP window.

2. soc/raspberrypi/rpi_pico/rp2040/Kconfig gates
   RP2_REQUIRES_SECOND_STAGE_BOOT on that address being exactly
   0x10000100. It defaulted to n, so neither
   modules/hal_rpi_pico/CMakeLists.txt (which compiles and checksums
   boot_stage2) nor soc/raspberrypi/rpi_pico/rp2040/linker.ld (which
   places .boot2 at 0x10000000) did anything, and the image shipped
   with no second-stage bootloader at all. It also flipped the UF2
   base from BUILD_OUTPUT_UF2_USE_FLASH_BASE to _USE_FLASH_OFFSET.

The resulting UF2 targeted 0x00000100..0x00107b00 and wrote nothing to
0x10000000, so the RP2040 bootrom found no boot2 and dropped straight
back into BOOTSEL.

Adding "ranges;" back restores both. Verified by rebuilding:

    rpi_pico/rp2040/w   .boot2 0x10000000 (256 B), rom_start 0x10000100
                        UF2 family 0xe48bff56, 0x10000000..0x10107c00
    rpi_pico/rp2040     .boot2 0x10000000 (256 B), rom_start 0x10000100
                        UF2 family 0xe48bff56, 0x10000000..0x10062b00

The RP2350 boards were never affected: their overlays merge into the
existing "partitions" node instead of deleting it, so they kept
"ranges" and already linked at 0x10000000.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two failure modes that cost real debugging time, written down so the next
board port does not rediscover them:

- Replacing the partitions node without re-adding "ranges;" mis-links the
  whole image on any SoC whose flash base is not 0x0, and on RP2040 also
  silently drops the second-stage bootloader.
- Undersizing or misaligning the settings partition makes NVS report zero
  sectors, so bt_enable() fails before opening the HCI driver and the
  application sees an unexplained OSError from "import _bleio".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tyeth

tyeth commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

Closing: this diff is now on main independently.

adafruit/circuitpython commit ffd62e1c59 ("zephyr-cp: add ranges; to three rebuilt partitions nodes", 2026-08-29) puts ranges; back on exactly the nodes this PR touched, and main (121489fe70 at time of writing) carries it — see ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/board.overlay line 5, and the same line in raspberrypi/rpi_pico_zephyr/board.overlay and adafruit/feather_rp2040_zephyr/board.overlay. Its commit message reaches the same root cause as #6 (code partition at 0x100 instead of 0x10000100, so RP2_REQUIRES_SECOND_STAGE_BOOT stops matching and boot2 is dropped), and also covers the STM32WBA board from #7.

The board files this PR edited (boards/rpi_pico*.overlay) no longer exist — upstream moved them to boards/<vendor>/<board>/board.overlay in d0f27bfc97 — which is why this showed mergeable=CONFLICTING and never got a Build CI run (GitHub does not run pull_request workflows when it cannot compute a merge commit; details on #16).

The second commit on this branch, the README "Board overlay pitfalls" section, is still worth having and is being reworked against the new layout in a separate PR. Branch left in place.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

zephyr-cp: RP2040 boards are unbootable — overlays drop "ranges" from the partitions node

1 participant