Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/test-configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,12 @@ jobs:
arch: arm
config-file: ./config/examples/stm32h5-tz-dualbank-otp-lms.config

stm32n6_test:
uses: ./.github/workflows/test-build.yml
with:
arch: arm
config-file: ./config/examples/stm32n6.config

stm32h7_test:
uses: ./.github/workflows/test-build.yml
with:
Expand Down
11 changes: 11 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,11 @@ ifeq ($(TARGET),sama5d3)
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
endif

ifeq ($(TARGET),stm32n6)
# wolfBoot runs from SRAM, app from XIP on external NOR - no contiguous factory.bin
MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin
endif

ifeq ($(TARGET),rp2350)
MAIN_TARGET:=include/target.h keytools wolfboot_signing_private_key.der pico-sdk-info
endif
Expand Down Expand Up @@ -647,6 +652,12 @@ stack-usage: wolfboot.bin
image-header-size: wolfboot.bin
$(Q)echo $(IMAGE_HEADER_SIZE) > .image_header_size

## Target-specific flash targets
ifeq ($(TARGET),stm32n6)
flash: wolfboot.bin test-app/image_v1_signed.bin
$(Q)tools/scripts/stm32n6_flash.sh --skip-build
endif


cppcheck:
cppcheck -f --enable=warning --enable=portability \
Expand Down
11 changes: 11 additions & 0 deletions arch.mk
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,17 @@ ifeq ($(ARCH),ARM)

endif

ifeq ($(TARGET),stm32n6)
CORTEX_M33=1
CFLAGS+=-Ihal -mcpu=cortex-m55
LDFLAGS+=-mcpu=cortex-m55
ARCH_FLASH_OFFSET=0x70000000
WOLFBOOT_ORIGIN=0x34000000
EXT_FLASH=1
PART_UPDATE_EXT=1
PART_SWAP_EXT=1
endif

ifeq ($(TARGET),rp2350)
CORTEX_M33=1
CFLAGS+=-Ihal
Expand Down
26 changes: 26 additions & 0 deletions config/examples/stm32n6.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
ARCH?=ARM
TARGET?=stm32n6
SIGN?=ECC256
HASH?=SHA256
DEBUG?=0
VTOR?=1
CORTEX_M0?=0
CORTEX_M33?=1
NO_ASM?=0
NO_MPU=1
EXT_FLASH?=1
PART_UPDATE_EXT?=1
PART_SWAP_EXT?=1
SPI_FLASH?=0
ALLOW_DOWNGRADE?=0
NVM_FLASH_WRITEONCE?=0
WOLFBOOT_VERSION?=1
V?=0
SPMATH?=1
RAM_CODE?=0
WOLFBOOT_SECTOR_SIZE?=0x1000
WOLFBOOT_PARTITION_SIZE?=0x100000
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x70020000
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x00120000
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x00010000
IMAGE_HEADER_SIZE?=1024
108 changes: 108 additions & 0 deletions config/openocd/openocd_stm32n6.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# OpenOCD config for NUCLEO-N657X0-Q with MX25UM51245G NOR on XSPI2

source [find interface/stlink.cfg]
transport select swd

set CHIPNAME stm32n6x
set WORKAREASIZE 0x10000

source [find target/stm32n6x.cfg]

# Work-area above wolfBoot SRAM region
$_TARGETNAME configure -work-area-phys 0x34020000 -work-area-size $WORKAREASIZE -work-area-backup 0

# XSPI2 NOR flash bank (memory-mapped at 0x70000000, regs at 0x5802A000)
set XSPI2_BANK_ID [llength [flash list]]
flash bank $CHIPNAME.xspi2 stmqspi 0x70000000 0 0 0 $CHIPNAME.cpu 0x5802A000

# Mark VDDIO supplies valid (required for XSPI2 GPIO)
proc pwr_enable_io_supply {} {
mmw 0x5602825C 0x00040000 0 ;# RCC_AHB4ENR: PWR clock
mmw 0x56024834 0x00000100 0 ;# SVMCR1: VDDIO4SV
mmw 0x56024838 0x00000100 0 ;# SVMCR2: VDDIO5SV
mmw 0x5602483C 0x00000300 0 ;# SVMCR3: VDDIO2SV + VDDIO3SV
}

# Port N GPIO for XSPI2 (PN0-PN11, AF9, very high speed)
proc xspi2_gpio_init {} {
mmw 0x5602825C 0x00002000 0 ;# RCC_AHB4ENR: GPION clock
sleep 1
mmw 0x56023400 0x00AAAAAA 0x00555555 ;# MODER: AF mode
mmw 0x56023408 0x00FFFFFF 0 ;# OSPEEDR: very high
mmw 0x5602340C 0 0x00FFFFFF ;# PUPDR: no pull
mww 0x56023420 0x99999999 ;# AFRL: AF9
mww 0x56023424 0x00009999 ;# AFRH: AF9
}

# XSPI2 init: single-SPI, /16 prescaler, NOR reset, enter mmap mode
proc xspi2_init {} {
mmw 0x56028260 0x00003000 0 ;# RCC_AHB5ENR: XSPI2 + XSPIM clocks
mmw 0x56028248 0x00000008 0 ;# RCC_MISCENR: XSPI PHY comp clock
sleep 1

mww 0x5802A000 0x00000000 ;# CR: disable
sleep 1
mww 0x5802A008 0x001A0308 ;# DCR1: DLYBYP, DEVSIZE=26, CSHT=3
mww 0x5802A00C 0x0000000F ;# DCR2: prescaler /16
sleep 1
mww 0x5802A000 0x00000001 ;# CR: enable

# NOR flash software reset (0x66 + 0x99)
mmw 0x5802A000 0x00000002 0 ;# abort
sleep 1
mww 0x5802A024 0x0000000B ;# FCR: clear flags
mww 0x5802A100 0x00000001 ;# CCR: IMODE=single
mww 0x5802A108 0x00000000 ;# TCR: no dummy
mww 0x5802A110 0x00000066 ;# IR: Reset Enable
sleep 1

mmw 0x5802A000 0x00000002 0 ;# abort
sleep 1
mww 0x5802A024 0x0000000B
mww 0x5802A100 0x00000001
mww 0x5802A108 0x00000000
mww 0x5802A110 0x00000099 ;# IR: Reset Memory
sleep 10

xspi2_mem_mapped
}

# Memory-mapped fast-read mode (single-SPI, 4-byte addr, 8 dummy cycles)
proc xspi2_mem_mapped {} {
mmw 0x5802A000 0x00000002 0 ;# abort
sleep 1
mww 0x5802A000 0x30000001 ;# CR: mmap + enable
mww 0x5802A100 0x01003101 ;# CCR: IMODE=1, ADMODE=1, ADSIZE=3, DMODE=1
mww 0x5802A108 0x40000008 ;# TCR: DCYC=8, SSHIFT
mww 0x5802A110 0x0000000C ;# IR: Fast Read 4B
}

# Set NOR flash params manually (SFDP not readable in single-SPI mode)
proc xspi2_flash_set {} {
global XSPI2_BANK_ID
stmqspi set $XSPI2_BANK_ID MX25UM51245G 0x4000000 0x100 0x13 0 0x12 0x60 0x1000 0x21
}

# Full reinit for use when XSPI2 may already be configured
proc xspi2_reinit {} {
global XSPI2_BANK_ID
pwr_enable_io_supply
xspi2_gpio_init
xspi2_init
xspi2_flash_set
flash probe $XSPI2_BANK_ID
xspi2_flash_set
}

$_TARGETNAME configure -event reset-init {
global XSPI2_BANK_ID
pwr_enable_io_supply
xspi2_gpio_init
xspi2_init
xspi2_flash_set
flash probe $XSPI2_BANK_ID
# Re-set after probe (stmqspi driver quirk)
xspi2_flash_set
}

init
118 changes: 118 additions & 0 deletions docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ This README describes configuration of supported targets.
* [STM32F7](#stm32f7)
* [STM32G0](#stm32g0)
* [STM32H5](#stm32h5)
* [STM32N6](#stm32n6)
* [STM32H7](#stm32h7)
* [STM32L0](#stm32l0)
* [STM32L4](#stm32l4)
Expand Down Expand Up @@ -1613,6 +1614,123 @@ c
```


## STM32N6

The STM32N6 (Cortex-M55) has no internal flash — all firmware resides on external
NOR flash (Macronix MX25UM51245G, 64MB) connected via XSPI2. The on-chip Boot ROM
copies the FSBL (First Stage Boot Loader) from external flash to internal SRAM and
jumps to it. wolfBoot serves as the FSBL, performing image verification and
chain-loading the application from external flash in XIP (Execute-In-Place) mode.

Tested on: **NUCLEO-N657X0-Q** (STM32N657X0H, MB1940)

### Memory Layout

```
XSPI2 NOR Flash (memory-mapped at 0x70000000):
0x70000000 FSBL header area (128KB, future autonomous boot)
0x70010000 Swap partition (64KB, device-relative: 0x00010000)
0x70020000 Boot partition (1MB, app runs from here via XIP)
0x70120000 Update partition (1MB, device-relative: 0x00120000)

AXISRAM (0x34000000):
0x34000000 wolfBoot (loaded to SRAM via SWD or Boot ROM FSBL copy)
0x34020000 Stack / work area
```

### Build and Flash

Use the example configuration and build:

```sh
cp config/examples/stm32n6.config .config
make
make flash
```

`make flash` uses OpenOCD with the stmqspi driver to:
1. Program the signed application to NOR flash at 0x70020000
2. Load wolfBoot to SRAM at 0x34000000
3. Start wolfBoot, which verifies and boots the application via XIP

Prerequisites:
- OpenOCD 0.12+ with stm32n6x target support (build from source if needed)
- ST-Link connected to the Nucleo board
- arm-none-eabi toolchain in PATH

### Build Options

```sh
make TARGET=stm32n6 SIGN=ECC256
```

The example config uses:
- `EXT_FLASH=1` with `PART_UPDATE_EXT=1` and `PART_SWAP_EXT=1`
- Boot partition at 0x70020000 (XIP, not marked EXT)
- Update/swap partitions use device-relative offsets
- 4KB sector size (`WOLFBOOT_SECTOR_SIZE=0x1000`)
- ECC256 + SHA256 for signature verification

### XIP Constraints

Since the application executes directly from NOR flash via XSPI2 memory-mapped
mode, the following constraints apply:

- The application must NOT call `hal_init()` — XSPI2 is already configured by
wolfBoot for memory-mapped mode. Reinitializing XSPI2 would disable XIP and
crash the CPU.
- Calling `wolfBoot_success()` requires all flash write functions to be placed
in RAM (RAMFUNCTION). The HAL flash functions in `hal/stm32n6.c` need the
RAMFUNCTION attribute for this to work from an XIP application.

### Flash Script Options

The flash script supports several modes:

```sh
./tools/scripts/stm32n6_flash.sh # Build and flash all
./tools/scripts/stm32n6_flash.sh --skip-build # Flash only (existing binaries)
./tools/scripts/stm32n6_flash.sh --app-only # Flash signed app only
./tools/scripts/stm32n6_flash.sh --test-update # Flash v1 boot + v2 update
./tools/scripts/stm32n6_flash.sh --halt # Leave OpenOCD running
```

### Debugging

OpenOCD:

```sh
openocd -f config/openocd/openocd_stm32n6.cfg
```

After OpenOCD starts, connect via telnet (port 4444). To manually load wolfBoot
and start it:

```sh
reset halt
load_image wolfboot.bin 0x34000000 bin
reg msplim_s 0x00000000
reg psplim_s 0x00000000
reg msp 0x34020000
mww 0xE000ED08 0x34000000
resume <entry_address>
```

The entry address can be found with:
```sh
arm-none-eabi-nm wolfboot.elf | grep isr_reset
```

GDB:

```sh
arm-none-eabi-gdb wolfboot.elf
target remote :3333
mon halt
add-symbol-file test-app/image.elf 0x70020400
```


## STM32H7

The STM32H7 flash geometry must be defined beforehand.
Expand Down
Loading
Loading