Skip to content

Commit 23f73f1

Browse files
aidangarskedgarske
authored andcommitted
Add wolfBoot port for STM32N6 (NUCLEO-N657X0-Q)
Add HAL, build system, test app, and documentation for the STM32N6 (Cortex-M55) targeting the NUCLEO-N657X0-Q board. wolfBoot runs from SRAM as FSBL and boots a signed application via XIP from external NOR flash on XSPI2.
1 parent 5222385 commit 23f73f1

File tree

13 files changed

+1678
-2
lines changed

13 files changed

+1678
-2
lines changed

.github/workflows/test-configs.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,12 @@ jobs:
473473
arch: arm
474474
config-file: ./config/examples/stm32h5-tz-dualbank-otp-lms.config
475475

476+
stm32n6_test:
477+
uses: ./.github/workflows/test-build.yml
478+
with:
479+
arch: arm
480+
config-file: ./config/examples/stm32n6.config
481+
476482
stm32h7_test:
477483
uses: ./.github/workflows/test-build.yml
478484
with:

Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,11 @@ ifeq ($(TARGET),sama5d3)
274274
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
275275
endif
276276

277+
ifeq ($(TARGET),stm32n6)
278+
# wolfBoot runs from SRAM, app from XIP on external NOR - no contiguous factory.bin
279+
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
280+
endif
281+
277282
ifeq ($(TARGET),rp2350)
278283
MAIN_TARGET:=include/target.h keytools wolfboot_signing_private_key.der pico-sdk-info
279284
endif
@@ -653,6 +658,12 @@ stack-usage: wolfboot.bin
653658
image-header-size: wolfboot.bin
654659
$(Q)echo $(IMAGE_HEADER_SIZE) > .image_header_size
655660

661+
## Target-specific flash targets
662+
ifeq ($(TARGET),stm32n6)
663+
flash: wolfboot.bin test-app/image_v1_signed.bin
664+
$(Q)tools/scripts/stm32n6_flash.sh --skip-build
665+
endif
666+
656667

657668
cppcheck:
658669
cppcheck -f --enable=warning --enable=portability \

arch.mk

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,16 @@ ifeq ($(ARCH),ARM)
270270

271271
endif
272272

273+
ifeq ($(TARGET),stm32n6)
274+
CORTEX_M55=1
275+
CFLAGS+=-Ihal
276+
ARCH_FLASH_OFFSET=0x70000000
277+
WOLFBOOT_ORIGIN=0x34000000
278+
EXT_FLASH=1
279+
PART_UPDATE_EXT=1
280+
PART_SWAP_EXT=1
281+
endif
282+
273283
ifeq ($(TARGET),rp2350)
274284
CORTEX_M33=1
275285
CFLAGS+=-Ihal
@@ -352,9 +362,17 @@ else
352362
CORTEXM_ARM_EXTRA_CFLAGS+=-DWOLFSSL_ARMASM -DWOLFSSL_ARMASM_NO_HW_CRYPTO \
353363
-DWOLFSSL_ARMASM_NO_NEON -DWOLFSSL_ARMASM_THUMB2
354364
endif
365+
ifeq ($(CORTEX_M55),1)
366+
CORTEX_M33=1
367+
CFLAGS+=-mcpu=cortex-m55
368+
LDFLAGS+=-mcpu=cortex-m55
369+
endif
355370
ifeq ($(CORTEX_M33),1)
356-
CFLAGS+=-mcpu=cortex-m33 -DCORTEX_M33
357-
LDFLAGS+=-mcpu=cortex-m33
371+
CFLAGS+=-DCORTEX_M33
372+
ifneq ($(CORTEX_M55),1)
373+
CFLAGS+=-mcpu=cortex-m33
374+
LDFLAGS+=-mcpu=cortex-m33
375+
endif
358376
ifeq ($(TZEN),1)
359377
ifneq (,$(findstring stm32,$(TARGET)))
360378
OBJS+=hal/stm32_tz.o

config/examples/stm32n6.config

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
ARCH?=ARM
2+
TARGET?=stm32n6
3+
SIGN?=ECC256
4+
HASH?=SHA256
5+
DEBUG?=0
6+
VTOR?=1
7+
NO_ASM?=0
8+
NO_MPU=1
9+
SPI_FLASH?=0
10+
ALLOW_DOWNGRADE?=0
11+
NVM_FLASH_WRITEONCE?=0
12+
WOLFBOOT_VERSION?=1
13+
V?=0
14+
SPMATH?=1
15+
RAM_CODE?=0
16+
WOLFBOOT_SECTOR_SIZE?=0x1000
17+
WOLFBOOT_PARTITION_SIZE?=0x100000
18+
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x70020000
19+
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x00120000
20+
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x00010000
21+
IMAGE_HEADER_SIZE?=1024

config/openocd/openocd_stm32n6.cfg

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# OpenOCD config for NUCLEO-N657X0-Q with MX25UM51245G NOR on XSPI2
2+
3+
source [find interface/stlink.cfg]
4+
transport select swd
5+
6+
set CHIPNAME stm32n6x
7+
set WORKAREASIZE 0x10000
8+
9+
source [find target/stm32n6x.cfg]
10+
11+
# Work-area above wolfBoot SRAM region
12+
$_TARGETNAME configure -work-area-phys 0x34020000 -work-area-size $WORKAREASIZE -work-area-backup 0
13+
14+
# XSPI2 NOR flash bank (memory-mapped at 0x70000000, regs at 0x5802A000)
15+
set XSPI2_BANK_ID [llength [flash list]]
16+
flash bank $CHIPNAME.xspi2 stmqspi 0x70000000 0 0 0 $CHIPNAME.cpu 0x5802A000
17+
18+
# Mark VDDIO supplies valid (required for XSPI2 GPIO)
19+
proc pwr_enable_io_supply {} {
20+
mmw 0x5602825C 0x00040000 0 ;# RCC_AHB4ENR: PWR clock
21+
mmw 0x56024834 0x00000100 0 ;# SVMCR1: VDDIO4SV
22+
mmw 0x56024838 0x00000100 0 ;# SVMCR2: VDDIO5SV
23+
mmw 0x5602483C 0x00000300 0 ;# SVMCR3: VDDIO2SV + VDDIO3SV
24+
}
25+
26+
# Port N GPIO for XSPI2 (PN0-PN11, AF9, very high speed)
27+
proc xspi2_gpio_init {} {
28+
mmw 0x5602825C 0x00002000 0 ;# RCC_AHB4ENR: GPION clock
29+
sleep 1
30+
mmw 0x56023400 0x00AAAAAA 0x00555555 ;# MODER: AF mode
31+
mmw 0x56023408 0x00FFFFFF 0 ;# OSPEEDR: very high
32+
mmw 0x5602340C 0 0x00FFFFFF ;# PUPDR: no pull
33+
mww 0x56023420 0x99999999 ;# AFRL: AF9
34+
mww 0x56023424 0x00009999 ;# AFRH: AF9
35+
}
36+
37+
# XSPI2 init: single-SPI, /16 prescaler, NOR reset, enter mmap mode
38+
proc xspi2_init {} {
39+
mmw 0x56028260 0x00003000 0 ;# RCC_AHB5ENR: XSPI2 + XSPIM clocks
40+
mmw 0x56028248 0x00000008 0 ;# RCC_MISCENR: XSPI PHY comp clock
41+
sleep 1
42+
43+
mww 0x5802A000 0x00000000 ;# CR: disable
44+
sleep 1
45+
mww 0x5802A008 0x001A0308 ;# DCR1: DLYBYP, DEVSIZE=26, CSHT=3
46+
mww 0x5802A00C 0x0000000F ;# DCR2: prescaler /16
47+
sleep 1
48+
mww 0x5802A000 0x00000001 ;# CR: enable
49+
50+
# NOR flash software reset (0x66 + 0x99)
51+
mmw 0x5802A000 0x00000002 0 ;# abort
52+
sleep 1
53+
mww 0x5802A024 0x0000000B ;# FCR: clear flags
54+
mww 0x5802A100 0x00000001 ;# CCR: IMODE=single
55+
mww 0x5802A108 0x00000000 ;# TCR: no dummy
56+
mww 0x5802A110 0x00000066 ;# IR: Reset Enable
57+
sleep 1
58+
59+
mmw 0x5802A000 0x00000002 0 ;# abort
60+
sleep 1
61+
mww 0x5802A024 0x0000000B
62+
mww 0x5802A100 0x00000001
63+
mww 0x5802A108 0x00000000
64+
mww 0x5802A110 0x00000099 ;# IR: Reset Memory
65+
sleep 10
66+
67+
xspi2_mem_mapped
68+
}
69+
70+
# Memory-mapped fast-read mode (single-SPI, 4-byte addr, 8 dummy cycles)
71+
proc xspi2_mem_mapped {} {
72+
mmw 0x5802A000 0x00000002 0 ;# abort
73+
sleep 1
74+
mww 0x5802A000 0x30000001 ;# CR: mmap + enable
75+
mww 0x5802A100 0x01003101 ;# CCR: IMODE=1, ADMODE=1, ADSIZE=3, DMODE=1
76+
mww 0x5802A108 0x40000008 ;# TCR: DCYC=8, SSHIFT
77+
mww 0x5802A110 0x0000000C ;# IR: Fast Read 4B
78+
}
79+
80+
# Set NOR flash params manually (SFDP not readable in single-SPI mode)
81+
proc xspi2_flash_set {} {
82+
global XSPI2_BANK_ID
83+
stmqspi set $XSPI2_BANK_ID MX25UM51245G 0x4000000 0x100 0x13 0 0x12 0x60 0x1000 0x21
84+
}
85+
86+
# Full reinit for use when XSPI2 may already be configured
87+
proc xspi2_reinit {} {
88+
global XSPI2_BANK_ID
89+
pwr_enable_io_supply
90+
xspi2_gpio_init
91+
xspi2_init
92+
xspi2_flash_set
93+
flash probe $XSPI2_BANK_ID
94+
xspi2_flash_set
95+
}
96+
97+
$_TARGETNAME configure -event reset-init {
98+
global XSPI2_BANK_ID
99+
pwr_enable_io_supply
100+
xspi2_gpio_init
101+
xspi2_init
102+
xspi2_flash_set
103+
flash probe $XSPI2_BANK_ID
104+
# Re-set after probe (stmqspi driver quirk)
105+
xspi2_flash_set
106+
}
107+
108+
init

docs/Targets.md

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ This README describes configuration of supported targets.
4343
* [STM32F7](#stm32f7)
4444
* [STM32G0](#stm32g0)
4545
* [STM32H5](#stm32h5)
46+
* [STM32N6](#stm32n6)
4647
* [STM32H7](#stm32h7)
4748
* [STM32L0](#stm32l0)
4849
* [STM32L4](#stm32l4)
@@ -1819,6 +1820,123 @@ c
18191820
```
18201821

18211822

1823+
## STM32N6
1824+
1825+
The STM32N6 (Cortex-M55) has no internal flash — all firmware resides on external
1826+
NOR flash (Macronix MX25UM51245G, 64MB) connected via XSPI2. The on-chip Boot ROM
1827+
copies the FSBL (First Stage Boot Loader) from external flash to internal SRAM and
1828+
jumps to it. wolfBoot serves as the FSBL, performing image verification and
1829+
chain-loading the application from external flash in XIP (Execute-In-Place) mode.
1830+
1831+
Tested on: **NUCLEO-N657X0-Q** (STM32N657X0H, MB1940)
1832+
1833+
### Memory Layout
1834+
1835+
```
1836+
XSPI2 NOR Flash (memory-mapped at 0x70000000):
1837+
0x70000000 FSBL header area (128KB, future autonomous boot)
1838+
0x70010000 Swap partition (64KB, device-relative: 0x00010000)
1839+
0x70020000 Boot partition (1MB, app runs from here via XIP)
1840+
0x70120000 Update partition (1MB, device-relative: 0x00120000)
1841+
1842+
AXISRAM (0x34000000):
1843+
0x34000000 wolfBoot (loaded to SRAM via SWD or Boot ROM FSBL copy)
1844+
0x34020000 Stack / work area
1845+
```
1846+
1847+
### Build and Flash
1848+
1849+
Use the example configuration and build:
1850+
1851+
```sh
1852+
cp config/examples/stm32n6.config .config
1853+
make
1854+
make flash
1855+
```
1856+
1857+
`make flash` uses OpenOCD with the stmqspi driver to:
1858+
1. Program the signed application to NOR flash at 0x70020000
1859+
2. Load wolfBoot to SRAM at 0x34000000
1860+
3. Start wolfBoot, which verifies and boots the application via XIP
1861+
1862+
Prerequisites:
1863+
- OpenOCD 0.12+ with stm32n6x target support (build from source if needed)
1864+
- ST-Link connected to the Nucleo board
1865+
- arm-none-eabi toolchain in PATH
1866+
1867+
### Build Options
1868+
1869+
```sh
1870+
make TARGET=stm32n6 SIGN=ECC256
1871+
```
1872+
1873+
The example config uses:
1874+
- `EXT_FLASH=1` with `PART_UPDATE_EXT=1` and `PART_SWAP_EXT=1`
1875+
- Boot partition at 0x70020000 (XIP, not marked EXT)
1876+
- Update/swap partitions use device-relative offsets
1877+
- 4KB sector size (`WOLFBOOT_SECTOR_SIZE=0x1000`)
1878+
- ECC256 + SHA256 for signature verification
1879+
1880+
### XIP Constraints
1881+
1882+
Since the application executes directly from NOR flash via XSPI2 memory-mapped
1883+
mode, the following constraints apply:
1884+
1885+
- The application must NOT call `hal_init()` — XSPI2 is already configured by
1886+
wolfBoot for memory-mapped mode. Reinitializing XSPI2 would disable XIP and
1887+
crash the CPU.
1888+
- Calling `wolfBoot_success()` requires all flash write functions to be placed
1889+
in RAM (RAMFUNCTION). The HAL flash functions in `hal/stm32n6.c` need the
1890+
RAMFUNCTION attribute for this to work from an XIP application.
1891+
1892+
### Flash Script Options
1893+
1894+
The flash script supports several modes:
1895+
1896+
```sh
1897+
./tools/scripts/stm32n6_flash.sh # Build and flash all
1898+
./tools/scripts/stm32n6_flash.sh --skip-build # Flash only (existing binaries)
1899+
./tools/scripts/stm32n6_flash.sh --app-only # Flash signed app only
1900+
./tools/scripts/stm32n6_flash.sh --test-update # Flash v1 boot + v2 update
1901+
./tools/scripts/stm32n6_flash.sh --halt # Leave OpenOCD running
1902+
```
1903+
1904+
### Debugging
1905+
1906+
OpenOCD:
1907+
1908+
```sh
1909+
openocd -f config/openocd/openocd_stm32n6.cfg
1910+
```
1911+
1912+
After OpenOCD starts, connect via telnet (port 4444). To manually load wolfBoot
1913+
and start it:
1914+
1915+
```sh
1916+
reset halt
1917+
load_image wolfboot.bin 0x34000000 bin
1918+
reg msplim_s 0x00000000
1919+
reg psplim_s 0x00000000
1920+
reg msp 0x34020000
1921+
mww 0xE000ED08 0x34000000
1922+
resume <entry_address>
1923+
```
1924+
1925+
The entry address can be found with:
1926+
```sh
1927+
arm-none-eabi-nm wolfboot.elf | grep isr_reset
1928+
```
1929+
1930+
GDB:
1931+
1932+
```sh
1933+
arm-none-eabi-gdb wolfboot.elf
1934+
target remote :3333
1935+
mon halt
1936+
add-symbol-file test-app/image.elf 0x70020400
1937+
```
1938+
1939+
18221940
## STM32H7
18231941

18241942
The STM32H7 flash geometry must be defined beforehand.

0 commit comments

Comments
 (0)