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. 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>;