From a49dfc507ef17d88b0a9896c86803f206e115098 Mon Sep 17 00:00:00 2001 From: tyeth Date: Tue, 8 Sep 2026 19:27:14 +0100 Subject: [PATCH 1/2] zephyr-cp: restore "ranges" on RP2040 partitions, fixing unbootable images 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 --- .../zephyr-cp/boards/adafruit_feather_rp2040.overlay | 12 ++++++++++++ ports/zephyr-cp/boards/rpi_pico_rp2040.overlay | 12 ++++++++++++ ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay | 12 ++++++++++++ 3 files changed, 36 insertions(+) diff --git a/ports/zephyr-cp/boards/adafruit_feather_rp2040.overlay b/ports/zephyr-cp/boards/adafruit_feather_rp2040.overlay index a19e2a066db..550310fccdf 100644 --- a/ports/zephyr-cp/boards/adafruit_feather_rp2040.overlay +++ b/ports/zephyr-cp/boards/adafruit_feather_rp2040.overlay @@ -2,6 +2,18 @@ /delete-node/ partitions; partitions { + /* + * The RP2040 flash is memory mapped at 0x10000000 and flash0 carries + * "ranges" to express that. Re-creating "partitions" after + * /delete-node/ drops the "ranges" the stock rpi_pico-common.dtsi puts + * here, which breaks address translation for the + * "zephyr,mapped-partition" children: DT_REG_ADDR(code_partition) + * would then return the bare 0x100 offset instead of 0x10000100. That + * links the image at 0x100 (ROM_ADDR in cortex_m/scripts/linker.ld) + * and makes RP2_REQUIRES_SECOND_STAGE_BOOT default to n, so no .boot2 + * is emitted at 0x10000000 and the resulting UF2 does not boot. + */ + ranges; #address-cells = <1>; #size-cells = <1>; diff --git a/ports/zephyr-cp/boards/rpi_pico_rp2040.overlay b/ports/zephyr-cp/boards/rpi_pico_rp2040.overlay index 1cdd4ca2033..9a58c6126eb 100644 --- a/ports/zephyr-cp/boards/rpi_pico_rp2040.overlay +++ b/ports/zephyr-cp/boards/rpi_pico_rp2040.overlay @@ -2,6 +2,18 @@ /delete-node/ partitions; partitions { + /* + * The RP2040 flash is memory mapped at 0x10000000 and flash0 carries + * "ranges" to express that. Re-creating "partitions" after + * /delete-node/ drops the "ranges" the stock rpi_pico-common.dtsi puts + * here, which breaks address translation for the + * "zephyr,mapped-partition" children: DT_REG_ADDR(code_partition) + * would then return the bare 0x100 offset instead of 0x10000100. That + * links the image at 0x100 (ROM_ADDR in cortex_m/scripts/linker.ld) + * and makes RP2_REQUIRES_SECOND_STAGE_BOOT default to n, so no .boot2 + * is emitted at 0x10000000 and the resulting UF2 does not boot. + */ + ranges; #address-cells = <1>; #size-cells = <1>; diff --git a/ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay b/ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay index 1cdd4ca2033..9a58c6126eb 100644 --- a/ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay +++ b/ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay @@ -2,6 +2,18 @@ /delete-node/ partitions; partitions { + /* + * The RP2040 flash is memory mapped at 0x10000000 and flash0 carries + * "ranges" to express that. Re-creating "partitions" after + * /delete-node/ drops the "ranges" the stock rpi_pico-common.dtsi puts + * here, which breaks address translation for the + * "zephyr,mapped-partition" children: DT_REG_ADDR(code_partition) + * would then return the bare 0x100 offset instead of 0x10000100. That + * links the image at 0x100 (ROM_ADDR in cortex_m/scripts/linker.ld) + * and makes RP2_REQUIRES_SECOND_STAGE_BOOT default to n, so no .boot2 + * is emitted at 0x10000000 and the resulting UF2 does not boot. + */ + ranges; #address-cells = <1>; #size-cells = <1>; From 97496e4f2d93493d09fcf4845c3ac746991f9204 Mon Sep 17 00:00:00 2001 From: tyeth Date: Tue, 8 Sep 2026 22:10:16 +0100 Subject: [PATCH 2/2] zephyr-cp: document the overlay pitfalls behind the RP2040 boot bug 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 --- ports/zephyr-cp/README.md | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/ports/zephyr-cp/README.md b/ports/zephyr-cp/README.md index 162ed90bcc8..a003480ae5c 100644 --- a/ports/zephyr-cp/README.md +++ b/ports/zephyr-cp/README.md @@ -116,3 +116,47 @@ west build -b nrf52840dk/nrf52840 ``` This is already supported in `ports/nordic` as `pca10056`. + +## Board overlay pitfalls + +Two things about `boards/.overlay` that are easy to get wrong and fail in +ways that point away from the cause. + +### Keep `ranges;` when replacing the partitions node + +If an overlay does `/delete-node/ partitions;` and rebuilds the node, it must +re-add `ranges;`. Board DTS files put it there, and it is what lets devicetree +address translation walk from a partition up through the flash node's +`ranges = <0x0 0x10000000 ...>` into the SoC address space. Drop it and the code +partition resolves to a raw offset instead of an absolute address: + +``` +code_partition REG_IDX_0_VAL_ADDRESS = 256 /* 0x100, wrong */ + = 268435712 /* 0x10000100 */ +``` + +Prefer merging into the existing `partitions` node over deleting and recreating +it, which avoids the problem entirely. + +On a SoC whose flash base is not `0x0` this produces an unbootable image, and the +symptom is remote from the cause. With `CONFIG_FLASH_USES_MAPPED_PARTITION=y` the +linker takes `ROM_ADDR` straight from the partition address, so the whole image +is mis-linked. On RP2040 it also silently disables the second-stage bootloader: +`soc/raspberrypi/rpi_pico/rp2040/Kconfig` gates `RP2_REQUIRES_SECOND_STAGE_BOOT` +on the address being *exactly* `0x10000100`, so `.boot2` is omitted from the ELF +altogether and the chip drops back to BOOTSEL when flashed. + +Overlays whose flash base is `0x0` (the nRF boards, `native_sim`) are unaffected, +because the untranslated offset happens to equal the absolute address. + +### Size the settings partition for the flash geometry + +Any board enabling `CONFIG_BT_SETTINGS` (which Bluetooth bond keys need) must +give the `storage` partition at least **two erase sectors**, aligned to an erase +sector boundary. NVS needs two sectors minimum, and `flash_area_get_sectors()` +reports zero sectors for a partition too small or misaligned to hold one. + +`settings_subsys_init()` then fails and `bt_enable()` returns before it ever +opens the HCI driver, which surfaces to Python as a bare `OSError` from +`import _bleio` with nothing pointing at flash layout. On a part with 4K sectors +that means 8K aligned to 4K, not the 2K some boards started with.