Skip to content

Commit 2bb241d

Browse files
committed
Use AdaBoot which is MCUBoot + UF2
This will make it easier to load CircuitPython updates onto Zephyr supported boards.
1 parent f30c408 commit 2bb241d

118 files changed

Lines changed: 3775 additions & 194 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ports/zephyr-cp/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ if(CONFIG_BOARD_NATIVE_SIM)
1010
target_sources(app PRIVATE native_sim_i2c_emul_control.c)
1111
endif()
1212

13+
# External nRF70 firmware loader (reads FW from flash partition)
14+
if(CONFIG_NRF_WIFI_PATCHES_EXTERNAL)
15+
target_sources(app PRIVATE nrf70_fw_load_external.c)
16+
endif()
17+
1318
if(CONFIG_TRACING_PERFETTO)
1419
zephyr_include_directories(${ZEPHYR_BINARY_DIR}/subsys/tracing/perfetto/proto)
1520
endif()

ports/zephyr-cp/Kconfig.sysbuild

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Copyright 2023-2024 Nordic Semiconductor ASA
22
# SPDX-License-Identifier: Apache-2.0
33

4+
config ADABOOT
5+
bool "Use Adaboot (MCUboot) bootloader"
6+
default y
7+
48
source "share/sysbuild/Kconfig"
59

610
config NET_CORE_BOARD

ports/zephyr-cp/Makefile

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ ifeq ($(DEBUG),1)
2121
WEST_CMAKE_ARGS += -Dzephyr-cp_EXTRA_CONF_FILE=$(DEBUG_CONF_FILE)
2222
endif
2323

24-
.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash recover debug debug-jlink debugserver attach run run-sim clean menuconfig all clean-all test fetch-port-submodules
24+
.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash recover debug debug-jlink debugserver attach run run-sim clean menuconfig all clean-all sim clean-sim test fetch-port-submodules mcuboot
2525

2626
$(BUILD)/zephyr-cp/zephyr/zephyr.elf:
2727
python cptools/pre_zephyr_build_prep.py $(BOARD)
@@ -37,7 +37,7 @@ $(BUILD)/firmware.exe: $(BUILD)/zephyr-cp/zephyr/zephyr.elf
3737
cp $(BUILD)/zephyr-cp/zephyr/zephyr.exe $@
3838

3939
$(BUILD)/firmware.uf2: $(BUILD)/zephyr-cp/zephyr/zephyr.elf
40-
cp $(BUILD)/zephyr-cp/zephyr/zephyr.uf2 $@
40+
python zephyr/scripts/build/uf2conv.py -c -b $(shell grep CONFIG_FLASH_LOAD_OFFSET $(BUILD)/zephyr-cp/zephyr/.config | cut -d= -f2) -o $@ $(BUILD)/zephyr-cp/zephyr/zephyr.signed.bin
4141

4242
flash: $(BUILD)/zephyr-cp/zephyr/zephyr.elf
4343
west flash -d $(BUILD)
@@ -73,6 +73,9 @@ run-sim:
7373
menuconfig:
7474
west build $(WEST_SHIELD_ARGS) --sysbuild -d $(BUILD) -t menuconfig -- $(WEST_CMAKE_ARGS)
7575

76+
mcuboot:
77+
west build -d $(BUILD) --domain mcuboot
78+
7679
clean:
7780
rm -rf $(BUILD)
7881

@@ -87,6 +90,13 @@ all:
8790
clean-all:
8891
rm -rf build build-*
8992

93+
# Build all sim boards concurrently using the same jobserver as `make all`.
94+
sim:
95+
+python cptools/build_all_boards.py --vendor native --continue-on-error
96+
97+
clean-sim:
98+
rm -rf $(wildcard build-native_*)
99+
90100
test: build-native_native_sim/zephyr-cp/zephyr/zephyr.exe
91101
pytest cptools/tests
92102
pytest tests/ -v

ports/zephyr-cp/boards/README.md

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
# Board Configuration
2+
3+
Each board requires several files to integrate with the CircuitPython Zephyr
4+
build system. This document describes the file layout, partitioning scheme, and
5+
MCUboot (Adaboot) bootloader requirements.
6+
7+
## File Layout
8+
9+
For a board named `<board>` with Zephyr qualifier `<board>_<soc>_<cpu>`:
10+
11+
```
12+
boards/
13+
├── <vendor>/<board>/
14+
│ ├── circuitpython.toml # Build config (extensions, USB IDs, flash overrides)
15+
│ └── autogen_board_info.toml # Auto-generated module info (committed, not edited)
16+
├── <board>_<soc>_<cpu>.conf # Kconfig settings (BLE, WiFi, etc.)
17+
├── <board>_<soc>_<cpu>.overlay # Device tree overlay (partitions, code-partition, USB)
18+
├── mcuboot_<board>.conf # Board-specific MCUboot config (optional)
19+
├── partitions/
20+
│ ├── <board>.dtsi # Hand-written partition overlay (see below)
21+
│ └── generated/
22+
│ └── <board>.dtsi # Auto-generated partition layout
23+
└── board_aliases.cmake # Maps friendly names to Zephyr board specs
24+
```
25+
26+
## Partition Structure
27+
28+
Boards use a two-layer partition scheme:
29+
30+
### Hand-written layer (`partitions/<board>.dtsi`)
31+
32+
This file is written once per board and handles board-specific setup:
33+
34+
1. **Delete Zephyr-default partitions** so they can be replaced:
35+
```dts
36+
&flash0 { /delete-node/ partitions; };
37+
```
38+
39+
2. **Enable external flash** if it's disabled by default:
40+
```dts
41+
&mx25r64 { status = "okay"; };
42+
```
43+
44+
3. **Set up retention memory** for MCUboot double-tap reset (if the SoC has
45+
`gpregret`):
46+
```dts
47+
&gpregret1 {
48+
status = "okay";
49+
boot_mode0: boot_mode@0 {
50+
compatible = "zephyr,retention";
51+
status = "okay";
52+
reg = <0x0 0x1>;
53+
};
54+
};
55+
/ {
56+
chosen { zephyr,boot-mode = &boot_mode0; };
57+
};
58+
```
59+
60+
4. **Define MCUboot button alias**:
61+
```dts
62+
/ { aliases { mcuboot-button0 = &button0; }; };
63+
```
64+
65+
5. **Define additional flash controllers** for multi-image boards (e.g.
66+
nRF5340 net core flash):
67+
```dts
68+
/ { soc {
69+
netcore_flash_controller: flash-controller@41080000 {
70+
compatible = "nordic,nrf53-flash-controller";
71+
/* ... flash1 with netcore_partition ... */
72+
};
73+
}; };
74+
```
75+
76+
6. **Include the generated layout**:
77+
```dts
78+
#include "generated/<board>.dtsi"
79+
```
80+
81+
### Generated layer (`partitions/generated/<board>.dtsi`)
82+
83+
Auto-generated by `cptools/bootloaderify.py --fix <board_id>` for most boards.
84+
For boards with multi-core partitioning (nRF54H20), the generated file is
85+
hand-written since the tool doesn't know about multi-domain layouts.
86+
87+
Contains the actual partition table with sizes and offsets aligned to erase
88+
block boundaries.
89+
90+
Typical layout with internal + external flash:
91+
92+
| Partition | Location | Purpose |
93+
|------------|----------|---------|
94+
| `mcuboot` | Internal | Adaboot bootloader (128 KB) |
95+
| `image-0` | Internal | Application slot (fills remaining internal flash) |
96+
| `storage` | Internal | Zephyr settings storage |
97+
| `nvm` | Internal or External | Non-volatile memory for CircuitPython |
98+
| `image-1` | External | OTA update slot (same size as image-0 + 1 erase page) |
99+
| `image-2` | Internal or flash1 | Net/radio core firmware (multi-core boards only) |
100+
| `circuitpy`| External | CircuitPython filesystem (remainder of external flash) |
101+
102+
For internal-only boards, all partitions share the single flash device.
103+
104+
## Board Overlay (`<board>_<soc>_<cpu>.overlay`)
105+
106+
The overlay ties partitions to the build and adds USB:
107+
108+
```dts
109+
#include "partitions/<board>.dtsi"
110+
111+
/ {
112+
chosen {
113+
zephyr,code-partition = &slot0_partition;
114+
};
115+
};
116+
117+
#include "../app.overlay" /* Only if the board has USB */
118+
```
119+
120+
## MCUboot (Adaboot) Integration
121+
122+
The `sysbuild/CMakeLists.txt` automatically enables MCUboot for any board that
123+
has a `boards/partitions/<board>.dtsi` file. It combines:
124+
125+
- `sysbuild/adaboot.conf` — enables MCUboot in single-app mode, no signatures
126+
- `sysbuild/mcuboot.conf` — UF2 drag-and-drop, GPIO button entrance, double-tap reset
127+
- `boards/mcuboot_<board>.conf` — optional board-specific overrides
128+
129+
### Board-specific MCUboot config
130+
131+
Create `boards/mcuboot_<board>.conf` when the board needs:
132+
133+
- **Retention subsystem** (`CONFIG_RETAINED_MEM=y`, `CONFIG_RETENTION=y`,
134+
`CONFIG_RETENTION_BOOT_MODE=y`) — required for double-tap reset on boards
135+
with `gpregret`
136+
- **USB driver quirks** (e.g. `CONFIG_MULTITHREADING=y` for DWC2 USB controllers)
137+
- **UF2 disabled** (`CONFIG_MCUBOOT_UF2=n`) — for boards without USB
138+
- **Double-tap disabled** (`CONFIG_MCUBOOT_UF2_ENTRANCE_DOUBLE_TAP=n`) — for
139+
boards without `gpregret` retention (e.g. nRF54H20)
140+
141+
## Multi-Image Updates
142+
143+
Boards with multiple cores can update all core firmware from a single UF2
144+
drag-and-drop operation. The UF2 handler in MCUboot (`uf2_disk.c`) supports
145+
multiple flash targets, routing UF2 blocks by `target_addr` to the correct
146+
flash region.
147+
148+
### nRF5340 (cpuapp + cpunet, separate flash per core)
149+
150+
The nRF5340 has a dedicated 256 KB flash for the network core (`flash1` at
151+
0x01000000), separate from the 1 MB app core flash (`flash0`).
152+
153+
The partition dtsi defines the net core flash controller
154+
(`nordic,nrf53-flash-controller` at 0x41080000) so MCUboot running on the app
155+
core can write to it. A `netcore_partition` covering the full 256 KB is
156+
registered as a secondary UF2 target.
157+
158+
UF2 blocks targeting 0x00000000-range go to flash0 (app core), blocks
159+
targeting 0x01000000-range go to flash1 (net core). The build system produces
160+
separate UF2 files per core, concatenated into one file for the user.
161+
162+
| Device | Partition | Size | Purpose |
163+
|--------|-----------|------|---------|
164+
| flash0 | mcuboot | 128 KB | Bootloader |
165+
| flash0 | image-0 | ~864 KB | App core firmware |
166+
| flash0 | storage | 32 KB | Settings |
167+
| flash1 | image-2 | 256 KB | Net core firmware (entire flash1) |
168+
| mx25r64 | nvm | 4 KB | Non-volatile memory |
169+
| mx25r64 | image-1 | ~868 KB | App core update slot |
170+
| mx25r64 | circuitpy | ~7 MB | CircuitPython filesystem |
171+
172+
Applies to: **nrf5340dk**, **nrf7002dk**
173+
174+
### nRF54H20 (cpuapp + cpurad, shared MRAM behind IronSide SE)
175+
176+
The nRF54H20 has a single 2 MB MRAM shared by all cores (cpuapp, cpurad,
177+
cpuppr, cpuflpr). Nordic's IronSide SE secure element occupies the first
178+
192 KB and acts as the first-stage bootloader — it validates and boots MCUboot
179+
(Adaboot) at offset 0x30000.
180+
181+
IronSide SE's update service is only for updating IronSide SE itself.
182+
Application firmware is managed entirely by MCUboot. The app core boots the
183+
radio core at runtime via `ironside_se_cpuconf()`.
184+
185+
The `netcore_partition` (labeled `image-2`) holds the cpurad firmware. UF2
186+
blocks are routed by address: cpuapp blocks target the `image-0` range,
187+
cpurad blocks target the `image-2` range — both within MRAM.
188+
189+
No `gpregret` is available on nRF54H20, so double-tap reset is disabled.
190+
UF2 entrance is via GPIO button only.
191+
192+
| Device | Partition | Size | Purpose |
193+
|--------|-----------|------|---------|
194+
| mram1x | *(IronSide SE)* | 192 KB | Secure boot (reserved, not touched) |
195+
| mram1x | mcuboot | 128 KB | Adaboot bootloader |
196+
| mram1x | image-0 | 1344 KB | App core firmware |
197+
| mram1x | image-2 | 352 KB | Radio core firmware (cpurad) |
198+
| mram1x | nvm | 16 KB | Non-volatile memory |
199+
| mram1x | storage | 16 KB | Settings |
200+
| mx25uw63 | image-1 | ~1348 KB | App core update slot |
201+
| mx25uw63 | circuitpy | ~6.7 MB | CircuitPython filesystem |
202+
203+
### nRF54LM20 / nRF54L15 (single core with external flash)
204+
205+
Standard single-core layout. Internal RRAM holds the bootloader, app, and
206+
settings. External SPI flash holds the update slot and CircuitPython
207+
filesystem. RRAM's `gpregret` provides double-tap reset support.
208+
209+
The nRF54L15 has no USB, so UF2 is disabled in its MCUboot config. Firmware
210+
is flashed via J-Link.
211+
212+
## Adding a New Board
213+
214+
1. Create `boards/<vendor>/<board>/circuitpython.toml` with build extensions
215+
and USB VID/PID. Add `[external_flash]` if the board has SPI/QSPI flash
216+
without `erase-block-size` in the device tree:
217+
```toml
218+
CIRCUITPY_BUILD_EXTENSIONS = ["elf"]
219+
220+
[external_flash]
221+
label = "mx25r64"
222+
erase_block_size = 4096
223+
```
224+
225+
2. Add a board alias to `board_aliases.cmake`:
226+
```cmake
227+
cp_board_alias(vendor_board board/soc/cpu)
228+
```
229+
230+
3. Generate partitions:
231+
```sh
232+
python3 cptools/bootloaderify.py --fix vendor_board
233+
```
234+
For multi-core boards where bootloaderify can't handle the layout, create
235+
`boards/partitions/generated/<board>.dtsi` manually.
236+
237+
4. Create `boards/partitions/<board>.dtsi` — delete default partitions,
238+
enable flash/retention, add mcuboot button alias, define additional flash
239+
controllers for multi-core, include generated dtsi.
240+
241+
5. Create `boards/<board>_<soc>_<cpu>.overlay` — include partition dtsi,
242+
set `zephyr,code-partition`, include `app.overlay` if USB is available.
243+
244+
6. Create `boards/mcuboot_<board>.conf` if needed for retention, USB quirks,
245+
or disabling features (UF2, double-tap).
246+
247+
7. Create `boards/<board>_<soc>_<cpu>.conf` for any Kconfig settings
248+
(BLE, WiFi, logging, etc.).
249+
250+
## Notes
251+
252+
- The `bootloaderify.py` tool detects predefined mcuboot partitions in the
253+
upstream Zephyr DTS. When external flash is available, it moves slot1 to
254+
external flash and grows slot0 to fill internal flash.
255+
- Boards without USB (e.g. nrf54l15dk) get partitions for circuitpy/nvm/storage
256+
but disable UF2 in their mcuboot config. Firmware is flashed via J-Link or
257+
serial recovery.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
adaboot = false
12
CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"]
23
USB_VID=0x239A
34
USB_PID=0x802A

ports/zephyr-cp/boards/board_aliases.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ cp_board_alias(renesas_da14695_dk_usb da14695_dk_usb)
3434
cp_board_alias(native_native_sim native_sim/native)
3535
cp_board_alias(native_nrf5340bsim nrf5340bsim/nrf5340/cpuapp)
3636
cp_board_alias(nordic_nrf54l15dk nrf54l15dk/nrf54l15/cpuapp)
37+
cp_board_alias(nordic_nrf54lm20dk nrf54lm20dk/nrf54lm20a/cpuapp)
3738
cp_board_alias(nordic_nrf54h20dk nrf54h20dk/nrf54h20/cpuapp)
3839
cp_board_alias(nordic_nrf5340dk nrf5340dk/nrf5340/cpuapp)
3940
cp_board_alias(nordic_nrf7002dk nrf7002dk/nrf5340/cpuapp)
Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
&flash0 {
2-
partitions{
3-
circuitpy_partition: partition@118000 {
4-
label = "circuitpy";
5-
reg = <0x118000 (DT_SIZE_M(4) - DT_SIZE_K(1120))>;
6-
};
7-
};
1+
#include "partitions/da14695_dk_usb.dtsi"
2+
3+
/ {
4+
chosen {
5+
zephyr,code-partition = &slot0_partition;
6+
};
87
};
98

109
#include "../app.overlay"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Disable BIN output; Renesas RA OFS registers at high addresses create
2+
# ~16 MB binaries that break imgtool signing. HEX output (already the
3+
# SoC default) is sparse and works correctly with MCUboot.
4+
CONFIG_BUILD_OUTPUT_BIN=n
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#include "partitions/ek_ra6m5.dtsi"
2+
3+
/ {
4+
chosen {
5+
zephyr,code-partition = &slot0_partition;
6+
};
7+
};
8+
9+
/* Disable OFS (Option Function Select) nodes so their high-address
10+
* sections don't end up in the app HEX. MCUboot's own build still
11+
* has them, so the merged HEX programs OFS correctly.
12+
*/
13+
&option_setting_ofs0 { status = "disabled"; };
14+
&option_setting_dualsel { status = "disabled"; };
15+
&option_setting_ofs1_sec { status = "disabled"; };
16+
&option_setting_banksel_sec { status = "disabled"; };
17+
&option_setting_bps_sec { status = "disabled"; };
18+
&option_setting_pbps_sec { status = "disabled"; };
19+
&option_setting_ofs1_sel { status = "disabled"; };
20+
&option_setting_banksel_sel { status = "disabled"; };
21+
&option_setting_bps_sel { status = "disabled"; };
22+
23+
#include "../app.overlay"
Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1-
21
# Enable Zephyr display subsystem so DT chosen zephyr,display creates a device.
32
CONFIG_DISPLAY=y
3+
4+
# Disable BIN output; Renesas RA OFS registers at high addresses create
5+
# ~16 MB binaries that break imgtool signing. HEX output (already the
6+
# SoC default) is sparse and works correctly with MCUboot.
7+
CONFIG_BUILD_OUTPUT_BIN=n
8+
9+
# RA8 USB can generate bursts of UDC/USBD events (especially with HS + MSC).
10+
# Keep queues/buffers larger to avoid dropped events that can desynchronize
11+
# control-transfer state (e.g. "udc: Cannot determine the next stage").
12+
CONFIG_UDC_RENESAS_RA_MAX_QMESSAGES=32
13+
CONFIG_USBD_MAX_UDC_MSG=32
14+
CONFIG_USBD_MSG_SLAB_COUNT=32
15+
CONFIG_UDC_BUF_COUNT=32
16+
CONFIG_UDC_BUF_POOL_SIZE=2048

0 commit comments

Comments
 (0)