From 23f73f13af6a67f487a16d3062c61a6affeeea90 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 10 Mar 2026 17:48:20 -0700 Subject: [PATCH 1/4] 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. --- .github/workflows/test-configs.yml | 6 + Makefile | 11 + arch.mk | 22 +- config/examples/stm32n6.config | 21 ++ config/openocd/openocd_stm32n6.cfg | 108 ++++++ docs/Targets.md | 118 ++++++ hal/stm32n6.c | 568 +++++++++++++++++++++++++++++ hal/stm32n6.h | 444 ++++++++++++++++++++++ hal/stm32n6.ld | 56 +++ test-app/ARM-stm32n6.ld | 57 +++ test-app/Makefile | 12 + test-app/app_stm32n6.c | 120 ++++++ tools/scripts/stm32n6_flash.sh | 137 +++++++ 13 files changed, 1678 insertions(+), 2 deletions(-) create mode 100644 config/examples/stm32n6.config create mode 100644 config/openocd/openocd_stm32n6.cfg create mode 100644 hal/stm32n6.c create mode 100644 hal/stm32n6.h create mode 100644 hal/stm32n6.ld create mode 100644 test-app/ARM-stm32n6.ld create mode 100644 test-app/app_stm32n6.c create mode 100755 tools/scripts/stm32n6_flash.sh diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index 5f3ada4350..468aea5c48 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -473,6 +473,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: diff --git a/Makefile b/Makefile index 321322a9fe..7b18df9ba5 100644 --- a/Makefile +++ b/Makefile @@ -274,6 +274,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 @@ -653,6 +658,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 \ diff --git a/arch.mk b/arch.mk index a9de6d9e0f..5773834e15 100644 --- a/arch.mk +++ b/arch.mk @@ -270,6 +270,16 @@ ifeq ($(ARCH),ARM) endif + ifeq ($(TARGET),stm32n6) + CORTEX_M55=1 + CFLAGS+=-Ihal + 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 @@ -352,9 +362,17 @@ else CORTEXM_ARM_EXTRA_CFLAGS+=-DWOLFSSL_ARMASM -DWOLFSSL_ARMASM_NO_HW_CRYPTO \ -DWOLFSSL_ARMASM_NO_NEON -DWOLFSSL_ARMASM_THUMB2 endif + ifeq ($(CORTEX_M55),1) + CORTEX_M33=1 + CFLAGS+=-mcpu=cortex-m55 + LDFLAGS+=-mcpu=cortex-m55 + endif ifeq ($(CORTEX_M33),1) - CFLAGS+=-mcpu=cortex-m33 -DCORTEX_M33 - LDFLAGS+=-mcpu=cortex-m33 + CFLAGS+=-DCORTEX_M33 + ifneq ($(CORTEX_M55),1) + CFLAGS+=-mcpu=cortex-m33 + LDFLAGS+=-mcpu=cortex-m33 + endif ifeq ($(TZEN),1) ifneq (,$(findstring stm32,$(TARGET))) OBJS+=hal/stm32_tz.o diff --git a/config/examples/stm32n6.config b/config/examples/stm32n6.config new file mode 100644 index 0000000000..0df3498a79 --- /dev/null +++ b/config/examples/stm32n6.config @@ -0,0 +1,21 @@ +ARCH?=ARM +TARGET?=stm32n6 +SIGN?=ECC256 +HASH?=SHA256 +DEBUG?=0 +VTOR?=1 +NO_ASM?=0 +NO_MPU=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 diff --git a/config/openocd/openocd_stm32n6.cfg b/config/openocd/openocd_stm32n6.cfg new file mode 100644 index 0000000000..33d149e90a --- /dev/null +++ b/config/openocd/openocd_stm32n6.cfg @@ -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 diff --git a/docs/Targets.md b/docs/Targets.md index 0a7ed2eeec..daea3d8b9f 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -43,6 +43,7 @@ This README describes configuration of supported targets. * [STM32F7](#stm32f7) * [STM32G0](#stm32g0) * [STM32H5](#stm32h5) +* [STM32N6](#stm32n6) * [STM32H7](#stm32h7) * [STM32L0](#stm32l0) * [STM32L4](#stm32l4) @@ -1819,6 +1820,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 +``` + +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. diff --git a/hal/stm32n6.c b/hal/stm32n6.c new file mode 100644 index 0000000000..5946b6f66c --- /dev/null +++ b/hal/stm32n6.c @@ -0,0 +1,568 @@ +/* stm32n6.c + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include +#include "hal.h" +#include "hal/stm32n6.h" + +/* RAMFUNCTION override for test-app (XIP needs flash ops in SRAM) */ +#if defined(RAM_CODE) && !defined(__WOLFBOOT) + #undef RAMFUNCTION + #define RAMFUNCTION __attribute__((used,section(".ramcode"),long_call)) +#endif + +/* XSPI2 indirect-mode command helper */ +static int RAMFUNCTION xspi_cmd(uint8_t fmode, uint8_t cmd, + uint32_t addr, uint32_t addrMode, + uint8_t *data, uint32_t dataSz, uint32_t dataMode, + uint32_t dummyCycles) +{ + uint32_t ccr; + + /* Abort memory-mapped mode if active */ + if ((XSPI2_CR & XSPI_CR_FMODE_MASK) == XSPI_CR_FMODE_MMAP) { + XSPI2_CR |= XSPI_CR_ABORT; + while (XSPI2_CR & XSPI_CR_ABORT) + ; + } + while (XSPI2_SR & XSPI_SR_BUSY) + ; + XSPI2_FCR = XSPI_FCR_CTCF | XSPI_FCR_CTEF | XSPI_FCR_CSMF; + + XSPI2_CR = (XSPI2_CR & ~XSPI_CR_FMODE_MASK) | XSPI_CR_FMODE(fmode); + + if (dataSz > 0) { + XSPI2_DLR = dataSz - 1; + } + + ccr = XSPI_CCR_IMODE(XSPI_MODE_SINGLE) | XSPI_CCR_ISIZE(0); + if (addrMode != XSPI_MODE_NONE) { + ccr |= XSPI_CCR_ADMODE(addrMode) | XSPI_CCR_ADSIZE(3); + } + if (dataMode != XSPI_MODE_NONE) { + ccr |= XSPI_CCR_DMODE(dataMode); + } + XSPI2_CCR = ccr; + XSPI2_TCR = XSPI_TCR_DCYC(dummyCycles); + XSPI2_IR = cmd; + + if (addrMode != XSPI_MODE_NONE) { + XSPI2_AR = addr; + } + + if (dataSz > 0 && data != NULL) { + while (dataSz >= 4) { + if (fmode == 0) { + while (!(XSPI2_SR & (XSPI_SR_FTF | XSPI_SR_TEF))) + ; + if (XSPI2_SR & XSPI_SR_TEF) goto xspi_err; + XSPI2_DR32 = *(uint32_t *)data; + } else { + while (!(XSPI2_SR & (XSPI_SR_FTF | XSPI_SR_TCF | + XSPI_SR_TEF))) + ; + if (XSPI2_SR & XSPI_SR_TEF) goto xspi_err; + *(uint32_t *)data = XSPI2_DR32; + } + data += 4; + dataSz -= 4; + } + while (dataSz > 0) { + if (fmode == 0) { + while (!(XSPI2_SR & (XSPI_SR_FTF | XSPI_SR_TEF))) + ; + if (XSPI2_SR & XSPI_SR_TEF) goto xspi_err; + XSPI2_DR = *data; + } else { + while (!(XSPI2_SR & (XSPI_SR_FTF | XSPI_SR_TCF | + XSPI_SR_TEF))) + ; + if (XSPI2_SR & XSPI_SR_TEF) goto xspi_err; + *data = XSPI2_DR; + } + data++; + dataSz--; + } + } + + while (!(XSPI2_SR & (XSPI_SR_TCF | XSPI_SR_TEF))) + ; + if (XSPI2_SR & XSPI_SR_TEF) goto xspi_err; + XSPI2_FCR = XSPI_FCR_CTCF; + + return 0; + +xspi_err: + XSPI2_FCR = XSPI_FCR_CTEF; + XSPI2_CR |= XSPI_CR_ABORT; + while (XSPI2_CR & XSPI_CR_ABORT) + ; + return -1; +} + +static void RAMFUNCTION xspi_write_enable(void) +{ + xspi_cmd(0, NOR_CMD_WRITE_ENABLE, 0, XSPI_MODE_NONE, + NULL, 0, XSPI_MODE_NONE, 0); +} + +static void RAMFUNCTION xspi_wait_ready(void) +{ + uint8_t sr; + do { + sr = 0; + xspi_cmd(1, NOR_CMD_READ_SR, 0, XSPI_MODE_NONE, + &sr, 1, XSPI_MODE_SINGLE, 0); + } while (sr & NOR_SR_WIP); +} + +static void RAMFUNCTION xspi_enable_mmap(void) +{ + /* Abort first if already in mmap mode (BUSY stays set in mmap) */ + if ((XSPI2_CR & XSPI_CR_FMODE_MASK) == XSPI_CR_FMODE_MMAP) { + XSPI2_CR |= XSPI_CR_ABORT; + while (XSPI2_CR & XSPI_CR_ABORT) + ; + } + while (XSPI2_SR & XSPI_SR_BUSY) + ; + XSPI2_FCR = XSPI_FCR_CTCF | XSPI_FCR_CTEF | XSPI_FCR_CSMF; + + XSPI2_CR = (XSPI2_CR & ~XSPI_CR_FMODE_MASK) | XSPI_CR_FMODE_MMAP; + + /* Fast read: single SPI, 4-byte addr, 8 dummy cycles */ + XSPI2_CCR = XSPI_CCR_IMODE(XSPI_MODE_SINGLE) | + XSPI_CCR_ISIZE(0) | + XSPI_CCR_ADMODE(XSPI_MODE_SINGLE) | + XSPI_CCR_ADSIZE(3) | + XSPI_CCR_DMODE(XSPI_MODE_SINGLE); + XSPI2_TCR = XSPI_TCR_DCYC(8) | XSPI_TCR_SSHIFT; + XSPI2_IR = NOR_CMD_FAST_READ_4B; + + DSB(); + ISB(); +} + +static void RAMFUNCTION dcache_clean_invalidate_by_addr(uint32_t addr, uint32_t size) +{ + uint32_t line; + for (line = addr & ~0x1FUL; line < addr + size; line += 32) { + SCB_DCCIMVAC = line; + } + DSB(); + ISB(); +} + +static void icache_enable(void) +{ + DSB(); + ISB(); + SCB_ICIALLU = 0; + DSB(); + ISB(); + SCB_CCR |= SCB_CCR_IC; + DSB(); + ISB(); +} + +static void dcache_enable(void) +{ + DSB(); + SCB_CCR |= SCB_CCR_DC; + DSB(); + ISB(); +} + +static void icache_disable(void) +{ + DSB(); + ISB(); + SCB_CCR &= ~SCB_CCR_IC; + SCB_ICIALLU = 0; + DSB(); + ISB(); +} + +static void dcache_disable(void) +{ + /* Clean+invalidate all lines by set/way before disabling */ + uint32_t sets, ways, set, way, way_shift; + uint32_t ccsidr; + + CSSELR = 0; /* select L1 data cache */ + DSB(); + ccsidr = CCSIDR; + sets = ((ccsidr >> 13) & 0x7FFF) + 1; + ways = ((ccsidr >> 3) & 0x3FF) + 1; + + /* Calculate way shift: 32 - log2(ways) */ + way_shift = 32; + { uint32_t tmp = ways - 1; while (tmp) { way_shift--; tmp >>= 1; } } + + for (way = 0; way < ways; way++) { + for (set = 0; set < sets; set++) { + SCB_DCCISW = (way << way_shift) | (set << 5); + } + } + + DSB(); + SCB_CCR &= ~SCB_CCR_DC; + DSB(); + ISB(); +} + +/* XSPI2 GPIO: PN0-PN11 as AF9 (DQS, CLK, NCS, IO0-IO7) */ +static void xspi2_gpio_init(void) +{ + uint32_t reg; + int pin; + + RCC_AHB4ENR |= RCC_AHB4ENR_GPIONEN; + DMB(); + + /* AF mode, very high speed, no pull */ + reg = GPIO_MODER(GPION_BASE); + for (pin = 0; pin <= 11; pin++) { + reg &= ~(0x3 << (pin * 2)); + reg |= (GPIO_MODE_AF << (pin * 2)); + } + GPIO_MODER(GPION_BASE) = reg; + + reg = GPIO_OSPEEDR(GPION_BASE); + for (pin = 0; pin <= 11; pin++) { + reg &= ~(0x3 << (pin * 2)); + reg |= (GPIO_SPEED_VERY_HIGH << (pin * 2)); + } + GPIO_OSPEEDR(GPION_BASE) = reg; + + reg = GPIO_PUPDR(GPION_BASE); + for (pin = 0; pin <= 11; pin++) { + reg &= ~(0x3 << (pin * 2)); + } + GPIO_PUPDR(GPION_BASE) = reg; + + /* AF9 for PN0-PN7 (AFRL) and PN8-PN11 (AFRH) */ + reg = 0; + for (pin = 0; pin <= 7; pin++) { + reg |= (9 << (pin * 4)); + } + GPIO_AFRL(GPION_BASE) = reg; + + reg = 0; + for (pin = 0; pin <= 3; pin++) { + reg |= (9 << (pin * 4)); + } + GPIO_AFRH(GPION_BASE) = reg; +} + +static void xspi2_init(void) +{ + volatile uint32_t delay; + + RCC_AHB5ENR |= RCC_AHB5ENR_XSPI2EN | RCC_AHB5ENR_XSPIMEN; + RCC_MISCENR |= RCC_MISCENR_XSPIPHYCOMPEN; + DMB(); + + XSPI2_CR = 0; + while (XSPI2_SR & XSPI_SR_BUSY) + ; + + XSPI2_DCR1 = XSPI_DCR1_DLYBYP | + XSPI_DCR1_DEVSIZE(NOR_DEVICE_SIZE_LOG2) | + XSPI_DCR1_CSHT(3); + XSPI2_DCR2 = XSPI_DCR2_PRESCALER(16); + while (XSPI2_SR & XSPI_SR_BUSY) + ; + + XSPI2_CR = XSPI_CR_FTHRES(1) | XSPI_CR_EN; + + /* NOR flash software reset */ + xspi_cmd(0, NOR_CMD_RESET_ENABLE, 0, XSPI_MODE_NONE, + NULL, 0, XSPI_MODE_NONE, 0); + xspi_cmd(0, NOR_CMD_RESET_MEMORY, 0, XSPI_MODE_NONE, + NULL, 0, XSPI_MODE_NONE, 0); + for (delay = 0; delay < 100000; delay++) + ; + + xspi_enable_mmap(); +} + +static void clock_config(void) +{ + /* HSI at 64 MHz (PLL configuration deferred) */ + RCC_CR |= RCC_CR_HSION; + while (!(RCC_SR & RCC_SR_HSIRDY)) + ; +} + +#ifdef DEBUG_UART +/* USART1 on PE5 (TX) / PE6 (RX), AF7 */ + +#define UART_CLOCK_FREQ 64000000UL + +static void uart_init(uint32_t baud) +{ + uint32_t reg; + + RCC_APB2ENR |= RCC_APB2ENR_USART1EN; + RCC_AHB4ENR |= RCC_AHB4ENR_GPIOEEN; + DMB(); + + /* PE5/PE6 AF mode */ + reg = GPIO_MODER(GPIOE_BASE); + reg &= ~((0x3 << (5 * 2)) | (0x3 << (6 * 2))); + reg |= (GPIO_MODE_AF << (5 * 2)) | (GPIO_MODE_AF << (6 * 2)); + GPIO_MODER(GPIOE_BASE) = reg; + + /* AF7 */ + reg = GPIO_AFRL(GPIOE_BASE); + reg &= ~((0xF << (5 * 4)) | (0xF << (6 * 4))); + reg |= (7 << (5 * 4)) | (7 << (6 * 4)); + GPIO_AFRL(GPIOE_BASE) = reg; + + reg = GPIO_OSPEEDR(GPIOE_BASE); + reg &= ~((0x3 << (5 * 2)) | (0x3 << (6 * 2))); + reg |= (GPIO_SPEED_HIGH << (5 * 2)) | (GPIO_SPEED_HIGH << (6 * 2)); + GPIO_OSPEEDR(GPIOE_BASE) = reg; + + /* 8N1 */ + USART1_CR1 = 0; + USART1_CR2 = 0; + USART1_CR3 = 0; + USART1_BRR = (UART_CLOCK_FREQ + baud / 2) / baud; + USART1_CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; +} + +static void uart_write(const char *buf, int len) +{ + int i; + for (i = 0; i < len; i++) { + while (!(USART1_ISR & USART_ISR_TXE)) + ; + USART1_TDR = buf[i]; + } + while (!(USART1_ISR & USART_ISR_TC)) + ; +} +#endif + +/* Mark VDDIO supplies valid (required for XSPI2 GPIO) */ +static void pwr_enable_io_supply(void) +{ + RCC_AHB4ENR |= RCC_AHB4ENR_PWREN; + DMB(); + PWR_SVMCR1 |= PWR_SVMCR1_VDDIO4SV; + PWR_SVMCR2 |= PWR_SVMCR2_VDDIO5SV; + PWR_SVMCR3 |= PWR_SVMCR3_VDDIO2SV | PWR_SVMCR3_VDDIO3SV; + DMB(); +} + +void hal_init(void) +{ + clock_config(); + pwr_enable_io_supply(); + icache_enable(); + dcache_enable(); + xspi2_gpio_init(); + xspi2_init(); + +#ifdef DEBUG_UART + uart_init(115200); + uart_write("wolfBoot Init\n", 14); +#endif +} + +void hal_prepare_boot(void) +{ + xspi_enable_mmap(); + dcache_disable(); + icache_disable(); +} + +int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) +{ + uint32_t offset; + uint32_t page_off, write_sz; + int total = len; + int ret = 0; + + if (len <= 0) + return 0; + + offset = address - XSPI2_MEM_BASE; + + while (len > 0) { + page_off = offset & (NOR_PAGE_SIZE - 1); + write_sz = NOR_PAGE_SIZE - page_off; + if ((int)write_sz > len) + write_sz = len; + + xspi_write_enable(); + ret = xspi_cmd(0, NOR_CMD_PAGE_PROG_4B, + offset, XSPI_MODE_SINGLE, + (uint8_t *)data, write_sz, XSPI_MODE_SINGLE, 0); + if (ret < 0) + break; + + xspi_wait_ready(); + + offset += write_sz; + data += write_sz; + len -= write_sz; + } + + xspi_enable_mmap(); + dcache_clean_invalidate_by_addr(address, total); + + return ret; +} + +int RAMFUNCTION hal_flash_erase(uint32_t address, int len) +{ + uint32_t offset; + uint32_t end; + int ret = 0; + + if (len <= 0) + return -1; + + offset = address - XSPI2_MEM_BASE; + end = offset + len; + + while (offset < end) { + xspi_write_enable(); + ret = xspi_cmd(0, NOR_CMD_SECTOR_ERASE_4B, + offset, XSPI_MODE_SINGLE, + NULL, 0, XSPI_MODE_NONE, 0); + if (ret < 0) + break; + + xspi_wait_ready(); + offset += NOR_SECTOR_SIZE; + } + + xspi_enable_mmap(); + dcache_clean_invalidate_by_addr(address, len); + + return ret; +} + +void RAMFUNCTION hal_flash_unlock(void) +{ +} + +void RAMFUNCTION hal_flash_lock(void) +{ +} + +/* ext_flash API: device-relative offsets (update/swap partitions) */ + +int RAMFUNCTION ext_flash_read(uintptr_t address, uint8_t *data, int len) +{ + int ret; + + if (len <= 0) + return 0; + + ret = xspi_cmd(1, NOR_CMD_FAST_READ_4B, + (uint32_t)address, XSPI_MODE_SINGLE, + data, len, XSPI_MODE_SINGLE, 8); + + xspi_enable_mmap(); + + return (ret < 0) ? ret : len; +} + +int RAMFUNCTION ext_flash_write(uintptr_t address, const uint8_t *data, int len) +{ + uint32_t offset = (uint32_t)address; + uint32_t page_off, write_sz; + const uint8_t *src = data; + int remaining = len; + int ret = 0; + + if (len <= 0) + return 0; + + while (remaining > 0) { + page_off = offset & (NOR_PAGE_SIZE - 1); + write_sz = NOR_PAGE_SIZE - page_off; + if ((int)write_sz > remaining) + write_sz = remaining; + + xspi_write_enable(); + + ret = xspi_cmd(0, NOR_CMD_PAGE_PROG_4B, + offset, XSPI_MODE_SINGLE, + (uint8_t *)src, write_sz, XSPI_MODE_SINGLE, 0); + if (ret < 0) + break; + + xspi_wait_ready(); + + offset += write_sz; + src += write_sz; + remaining -= write_sz; + } + + xspi_enable_mmap(); + + return ret; +} + +int RAMFUNCTION ext_flash_erase(uintptr_t address, int len) +{ + uint32_t offset = (uint32_t)address; + uint32_t end; + int ret = 0; + + if (len <= 0) + return -1; + + end = offset + len; + + while (offset < end) { + xspi_write_enable(); + + ret = xspi_cmd(0, NOR_CMD_SECTOR_ERASE_4B, + offset, XSPI_MODE_SINGLE, + NULL, 0, XSPI_MODE_NONE, 0); + if (ret < 0) + break; + + xspi_wait_ready(); + offset += NOR_SECTOR_SIZE; + } + + xspi_enable_mmap(); + + return ret; +} + +void RAMFUNCTION ext_flash_lock(void) +{ +} + +void RAMFUNCTION ext_flash_unlock(void) +{ +} diff --git a/hal/stm32n6.h b/hal/stm32n6.h new file mode 100644 index 0000000000..0d837b14c0 --- /dev/null +++ b/hal/stm32n6.h @@ -0,0 +1,444 @@ +/* stm32n6.h + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef STM32N6_DEF_INCLUDED +#define STM32N6_DEF_INCLUDED + +/* Assembly helpers */ +#ifndef DMB +#define DMB() __asm__ volatile ("dmb") +#endif +#ifndef ISB +#define ISB() __asm__ volatile ("isb") +#endif +#ifndef DSB +#define DSB() __asm__ volatile ("dsb") +#endif + +/*** RCC (Reset and Clock Control) — base 0x56028000 (secure) ***/ +#define RCC_BASE (0x56028000UL) + +/* RCC_CR: control register — enable bits */ +#define RCC_CR (*(volatile uint32_t *)(RCC_BASE + 0x00)) +#define RCC_CR_LSION (1 << 0) +#define RCC_CR_LSEON (1 << 1) +#define RCC_CR_MSION (1 << 2) +#define RCC_CR_HSION (1 << 3) +#define RCC_CR_HSEON (1 << 4) +#define RCC_CR_PLL1ON (1 << 8) +#define RCC_CR_PLL2ON (1 << 9) +#define RCC_CR_PLL3ON (1 << 10) +#define RCC_CR_PLL4ON (1 << 11) + +/* RCC_SR: status register — ready flags */ +#define RCC_SR (*(volatile uint32_t *)(RCC_BASE + 0x04)) +#define RCC_SR_LSIRDY (1 << 0) +#define RCC_SR_LSERDY (1 << 1) +#define RCC_SR_MSIRDY (1 << 2) +#define RCC_SR_HSIRDY (1 << 3) +#define RCC_SR_HSERDY (1 << 4) +#define RCC_SR_PLL1RDY (1 << 8) +#define RCC_SR_PLL2RDY (1 << 9) +#define RCC_SR_PLL3RDY (1 << 10) +#define RCC_SR_PLL4RDY (1 << 11) + +/* RCC_CFGR1: clock switching */ +#define RCC_CFGR1 (*(volatile uint32_t *)(RCC_BASE + 0x20)) +#define RCC_CFGR1_CPUSW_SHIFT (16) +#define RCC_CFGR1_CPUSW_MASK (0x3 << 16) +#define RCC_CFGR1_CPUSWS_SHIFT (20) +#define RCC_CFGR1_CPUSWS_MASK (0x3 << 20) +#define RCC_CFGR1_SYSSW_SHIFT (24) +#define RCC_CFGR1_SYSSW_MASK (0x3 << 24) +#define RCC_CFGR1_SYSSWS_SHIFT (28) +#define RCC_CFGR1_SYSSWS_MASK (0x3 << 28) + +/* RCC_CFGR2: APB prescalers */ +#define RCC_CFGR2 (*(volatile uint32_t *)(RCC_BASE + 0x24)) +#define RCC_CFGR2_PPRE1_SHIFT (0) +#define RCC_CFGR2_PPRE1_MASK (0x7 << 0) +#define RCC_CFGR2_PPRE2_SHIFT (4) +#define RCC_CFGR2_PPRE2_MASK (0x7 << 4) +#define RCC_CFGR2_PPRE4_SHIFT (8) +#define RCC_CFGR2_PPRE4_MASK (0x7 << 8) +#define RCC_CFGR2_PPRE5_SHIFT (12) +#define RCC_CFGR2_PPRE5_MASK (0x7 << 12) + +/* PLL1 Configuration registers */ +#define RCC_PLL1CFGR1 (*(volatile uint32_t *)(RCC_BASE + 0x80)) +#define RCC_PLL1CFGR1_DIVN_SHIFT (8) /* bits [19:8]: VCO multiplication */ +#define RCC_PLL1CFGR1_DIVN_MASK (0xFFF << 8) +#define RCC_PLL1CFGR1_DIVM_SHIFT (20) /* bits [25:20]: reference divider */ +#define RCC_PLL1CFGR1_DIVM_MASK (0x3F << 20) +#define RCC_PLL1CFGR1_SEL_SHIFT (28) /* bits [30:28]: PLL source */ +#define RCC_PLL1CFGR1_SEL_MASK (0x7 << 28) +#define RCC_PLL1CFGR1_SEL_HSI (0x0 << 28) +#define RCC_PLL1CFGR1_SEL_HSE (0x1 << 28) +#define RCC_PLL1CFGR1_SEL_MSI (0x2 << 28) + +#define RCC_PLL1CFGR2 (*(volatile uint32_t *)(RCC_BASE + 0x84)) +#define RCC_PLL1CFGR3 (*(volatile uint32_t *)(RCC_BASE + 0x88)) + +/* IC (Interconnect Clock) dividers */ +#define RCC_IC1CFGR (*(volatile uint32_t *)(RCC_BASE + 0xC4)) +#define RCC_IC2CFGR (*(volatile uint32_t *)(RCC_BASE + 0xC8)) +#define RCC_IC3CFGR (*(volatile uint32_t *)(RCC_BASE + 0xCC)) +#define RCC_IC4CFGR (*(volatile uint32_t *)(RCC_BASE + 0xD0)) +#define RCC_IC5CFGR (*(volatile uint32_t *)(RCC_BASE + 0xD4)) +#define RCC_IC6CFGR (*(volatile uint32_t *)(RCC_BASE + 0xD8)) +#define RCC_IC7CFGR (*(volatile uint32_t *)(RCC_BASE + 0xDC)) +#define RCC_IC8CFGR (*(volatile uint32_t *)(RCC_BASE + 0xE0)) +#define RCC_IC9CFGR (*(volatile uint32_t *)(RCC_BASE + 0xE4)) +#define RCC_IC10CFGR (*(volatile uint32_t *)(RCC_BASE + 0xE8)) +#define RCC_IC11CFGR (*(volatile uint32_t *)(RCC_BASE + 0xEC)) + +/* IC divider register fields: + * ICxINT [23:16] = integer division factor + * ICxSEL [29:28] = source: 0=PLL1, 1=PLL2, 2=PLL3, 3=PLL4 + */ +#define RCC_ICCFGR_INT_SHIFT (16) +#define RCC_ICCFGR_INT_MASK (0xFF << 16) +#define RCC_ICCFGR_SEL_SHIFT (28) +#define RCC_ICCFGR_SEL_MASK (0x3 << 28) +#define RCC_ICCFGR_SEL_PLL1 (0x0 << 28) +#define RCC_ICCFGR_SEL_PLL2 (0x1 << 28) +#define RCC_ICCFGR_SEL_PLL3 (0x2 << 28) +#define RCC_ICCFGR_SEL_PLL4 (0x3 << 28) + +/* Divider and bus enable registers */ +#define RCC_DIVENR (*(volatile uint32_t *)(RCC_BASE + 0x240)) +#define RCC_DIVENR_IC1EN (1 << 0) +#define RCC_DIVENR_IC2EN (1 << 1) +#define RCC_DIVENR_IC3EN (1 << 2) +#define RCC_DIVENR_IC4EN (1 << 3) +#define RCC_DIVENR_IC5EN (1 << 4) +#define RCC_DIVENR_IC6EN (1 << 5) +#define RCC_DIVENR_IC11EN (1 << 10) + +/* Clock enable registers */ +#define RCC_MISCENR (*(volatile uint32_t *)(RCC_BASE + 0x248)) +#define RCC_AHB1ENR (*(volatile uint32_t *)(RCC_BASE + 0x250)) +#define RCC_AHB2ENR (*(volatile uint32_t *)(RCC_BASE + 0x254)) +#define RCC_AHB3ENR (*(volatile uint32_t *)(RCC_BASE + 0x258)) +#define RCC_AHB4ENR (*(volatile uint32_t *)(RCC_BASE + 0x25C)) +#define RCC_AHB5ENR (*(volatile uint32_t *)(RCC_BASE + 0x260)) +#define RCC_APB1ENR1 (*(volatile uint32_t *)(RCC_BASE + 0x264)) +#define RCC_APB1ENR2 (*(volatile uint32_t *)(RCC_BASE + 0x268)) +#define RCC_APB2ENR (*(volatile uint32_t *)(RCC_BASE + 0x26C)) +#define RCC_APB4ENR1 (*(volatile uint32_t *)(RCC_BASE + 0x274)) +#define RCC_APB5ENR (*(volatile uint32_t *)(RCC_BASE + 0x27C)) + +/* GPIO clock enable bits in RCC_AHB4ENR */ +#define RCC_AHB4ENR_GPIOAEN (1 << 0) +#define RCC_AHB4ENR_GPIOBEN (1 << 1) +#define RCC_AHB4ENR_GPIOCEN (1 << 2) +#define RCC_AHB4ENR_GPIODEN (1 << 3) +#define RCC_AHB4ENR_GPIOEEN (1 << 4) +#define RCC_AHB4ENR_GPIOFEN (1 << 5) +#define RCC_AHB4ENR_GPIOGEN (1 << 6) +#define RCC_AHB4ENR_GPIOHEN (1 << 7) +#define RCC_AHB4ENR_GPIONEN (1 << 13) +#define RCC_AHB4ENR_GPIOOEN (1 << 14) +#define RCC_AHB4ENR_GPIOPEN (1 << 15) +#define RCC_AHB4ENR_GPIOQEN (1 << 16) +#define RCC_AHB4ENR_PWREN (1 << 18) + +/* XSPI clock enable in RCC_AHB5ENR */ +#define RCC_AHB5ENR_XSPI1EN (1 << 5) +#define RCC_AHB5ENR_XSPI2EN (1 << 12) +#define RCC_AHB5ENR_XSPIMEN (1 << 13) + +/* XSPI PHY compensation clock in RCC_MISCENR */ +#define RCC_MISCENR_XSPIPHYCOMPEN (1 << 3) + +/* USART clock enable */ +#define RCC_APB2ENR_USART1EN (1 << 4) + + +/*** PWR (Power Control) — base 0x56024800 (secure) ***/ +#define PWR_BASE (0x56024800UL) + +#define PWR_CR1 (*(volatile uint32_t *)(PWR_BASE + 0x00)) +#define PWR_CR2 (*(volatile uint32_t *)(PWR_BASE + 0x04)) +#define PWR_CR3 (*(volatile uint32_t *)(PWR_BASE + 0x08)) +#define PWR_CR4 (*(volatile uint32_t *)(PWR_BASE + 0x0C)) +#define PWR_VOSCR (*(volatile uint32_t *)(PWR_BASE + 0x20)) + +/* PWR_VOSCR fields */ +#define PWR_VOSCR_VOS (1 << 0) /* 0=Scale2, 1=Scale1 */ +#define PWR_VOSCR_VOSRDY (1 << 1) + +/* PWR Supply Voltage Monitoring Control Registers */ +#define PWR_SVMCR1 (*(volatile uint32_t *)(PWR_BASE + 0x34)) +#define PWR_SVMCR2 (*(volatile uint32_t *)(PWR_BASE + 0x38)) +#define PWR_SVMCR3 (*(volatile uint32_t *)(PWR_BASE + 0x3C)) + +/* SVMCR1: VDDIO4 supply valid (bit 8) */ +#define PWR_SVMCR1_VDDIO4SV (1 << 8) +/* SVMCR2: VDDIO5 supply valid (bit 8) */ +#define PWR_SVMCR2_VDDIO5SV (1 << 8) +/* SVMCR3: VDDIO2 supply valid (bit 8), VDDIO3 supply valid (bit 9) */ +#define PWR_SVMCR3_VDDIO2SV (1 << 8) +#define PWR_SVMCR3_VDDIO3SV (1 << 9) + + +/*** GPIO ***/ +#define GPIOA_BASE (0x56020000UL) +#define GPIOB_BASE (0x56020400UL) +#define GPIOC_BASE (0x56020800UL) +#define GPIOD_BASE (0x56020C00UL) +#define GPIOE_BASE (0x56021000UL) +#define GPIOF_BASE (0x56021400UL) +#define GPIOG_BASE (0x56021800UL) +#define GPIOH_BASE (0x56021C00UL) +#define GPION_BASE (0x56023400UL) +#define GPIOO_BASE (0x56023800UL) +#define GPIOP_BASE (0x56023C00UL) +#define GPIOQ_BASE (0x56024000UL) + +/* GPIO register offsets (same as H5/H7) */ +#define GPIO_MODER(base) (*(volatile uint32_t *)((base) + 0x00)) +#define GPIO_OTYPER(base) (*(volatile uint32_t *)((base) + 0x04)) +#define GPIO_OSPEEDR(base) (*(volatile uint32_t *)((base) + 0x08)) +#define GPIO_PUPDR(base) (*(volatile uint32_t *)((base) + 0x0C)) +#define GPIO_IDR(base) (*(volatile uint32_t *)((base) + 0x10)) +#define GPIO_ODR(base) (*(volatile uint32_t *)((base) + 0x14)) +#define GPIO_BSRR(base) (*(volatile uint32_t *)((base) + 0x18)) +#define GPIO_AFRL(base) (*(volatile uint32_t *)((base) + 0x20)) +#define GPIO_AFRH(base) (*(volatile uint32_t *)((base) + 0x24)) + +/* GPIO mode values */ +#define GPIO_MODE_INPUT 0x0 +#define GPIO_MODE_OUTPUT 0x1 +#define GPIO_MODE_AF 0x2 +#define GPIO_MODE_ANALOG 0x3 + +/* GPIO speed values */ +#define GPIO_SPEED_LOW 0x0 +#define GPIO_SPEED_MEDIUM 0x1 +#define GPIO_SPEED_HIGH 0x2 +#define GPIO_SPEED_VERY_HIGH 0x3 + + +/*** XSPI2 (External SPI for NOR flash) ***/ +#define XSPI2_BASE (0x5802A000UL) +#define XSPI2_MEM_BASE (0x70000000UL) + +#define XSPI2_CR (*(volatile uint32_t *)(XSPI2_BASE + 0x00)) +#define XSPI2_DCR1 (*(volatile uint32_t *)(XSPI2_BASE + 0x08)) +#define XSPI2_DCR2 (*(volatile uint32_t *)(XSPI2_BASE + 0x0C)) +#define XSPI2_DCR3 (*(volatile uint32_t *)(XSPI2_BASE + 0x10)) +#define XSPI2_DCR4 (*(volatile uint32_t *)(XSPI2_BASE + 0x14)) +#define XSPI2_SR (*(volatile uint32_t *)(XSPI2_BASE + 0x20)) +#define XSPI2_FCR (*(volatile uint32_t *)(XSPI2_BASE + 0x24)) +#define XSPI2_DLR (*(volatile uint32_t *)(XSPI2_BASE + 0x40)) +#define XSPI2_AR (*(volatile uint32_t *)(XSPI2_BASE + 0x48)) +#define XSPI2_DR (*(volatile uint8_t *)(XSPI2_BASE + 0x50)) +#define XSPI2_DR32 (*(volatile uint32_t *)(XSPI2_BASE + 0x50)) +#define XSPI2_PSMKR (*(volatile uint32_t *)(XSPI2_BASE + 0x80)) +#define XSPI2_PSMAR (*(volatile uint32_t *)(XSPI2_BASE + 0x88)) +#define XSPI2_PIR (*(volatile uint32_t *)(XSPI2_BASE + 0x90)) +#define XSPI2_CCR (*(volatile uint32_t *)(XSPI2_BASE + 0x100)) +#define XSPI2_TCR (*(volatile uint32_t *)(XSPI2_BASE + 0x108)) +#define XSPI2_IR (*(volatile uint32_t *)(XSPI2_BASE + 0x110)) +#define XSPI2_ABR (*(volatile uint32_t *)(XSPI2_BASE + 0x120)) +#define XSPI2_LPTR (*(volatile uint32_t *)(XSPI2_BASE + 0x130)) +#define XSPI2_WCCR (*(volatile uint32_t *)(XSPI2_BASE + 0x180)) +#define XSPI2_WTCR (*(volatile uint32_t *)(XSPI2_BASE + 0x188)) +#define XSPI2_WIR (*(volatile uint32_t *)(XSPI2_BASE + 0x190)) +#define XSPI2_WABR (*(volatile uint32_t *)(XSPI2_BASE + 0x1A0)) + +/* XSPI CR fields */ +#define XSPI_CR_EN (1 << 0) +#define XSPI_CR_ABORT (1 << 1) +#define XSPI_CR_FSEL (1 << 7) +#define XSPI_CR_FTHRES_SHIFT (8) +#define XSPI_CR_FTHRES_MASK (0x3F << 8) +#define XSPI_CR_FTHRES(n) ((((n) - 1) & 0x3F) << 8) +#define XSPI_CR_TCIE (1 << 17) +#define XSPI_CR_FTIE (1 << 18) +#define XSPI_CR_FMODE_SHIFT (28) +#define XSPI_CR_FMODE_MASK (0x3 << 28) +#define XSPI_CR_FMODE(m) (((m) & 0x3) << 28) +#define XSPI_CR_FMODE_IWRITE XSPI_CR_FMODE(0) +#define XSPI_CR_FMODE_IREAD XSPI_CR_FMODE(1) +#define XSPI_CR_FMODE_AUTOPOLL XSPI_CR_FMODE(2) +#define XSPI_CR_FMODE_MMAP XSPI_CR_FMODE(3) + +/* XSPI DCR1 fields */ +#define XSPI_DCR1_CKMODE_3 (1 << 0) +#define XSPI_DCR1_FRCK (1 << 1) +#define XSPI_DCR1_DLYBYP (1 << 3) /* Bypass delay block (DLL) */ +#define XSPI_DCR1_CSHT_SHIFT (8) +#define XSPI_DCR1_CSHT_MASK (0x3F << 8) +#define XSPI_DCR1_CSHT(n) (((n) & 0x3F) << 8) +#define XSPI_DCR1_DEVSIZE_SHIFT (16) +#define XSPI_DCR1_DEVSIZE_MASK (0x1F << 16) +#define XSPI_DCR1_DEVSIZE(n) (((n) & 0x1F) << 16) +#define XSPI_DCR1_MTYP_SHIFT (24) +#define XSPI_DCR1_MTYP_MASK (0x7 << 24) +#define XSPI_DCR1_MTYP(n) (((n) & 0x7) << 24) + +/* XSPI DCR2 fields */ +#define XSPI_DCR2_PRESCALER_SHIFT (0) +#define XSPI_DCR2_PRESCALER_MASK (0xFF) +#define XSPI_DCR2_PRESCALER(n) (((n) - 1) & 0xFF) + +/* XSPI SR fields */ +#define XSPI_SR_TEF (1 << 0) +#define XSPI_SR_TCF (1 << 1) +#define XSPI_SR_FTF (1 << 2) +#define XSPI_SR_SMF (1 << 3) +#define XSPI_SR_BUSY (1 << 5) +#define XSPI_SR_FLEVEL_SHIFT (8) +#define XSPI_SR_FLEVEL_MASK (0x3F << 8) + +/* XSPI FCR fields */ +#define XSPI_FCR_CTEF (1 << 0) +#define XSPI_FCR_CTCF (1 << 1) +#define XSPI_FCR_CSMF (1 << 3) + +/* XSPI CCR fields (Communication Configuration Register) */ +#define XSPI_CCR_IMODE_SHIFT (0) +#define XSPI_CCR_IMODE_MASK (0x7) +#define XSPI_CCR_IMODE(n) (((n) & 0x7) << 0) +#define XSPI_CCR_ISIZE_SHIFT (4) +#define XSPI_CCR_ISIZE(n) (((n) & 0x3) << 4) +#define XSPI_CCR_ADMODE_SHIFT (8) +#define XSPI_CCR_ADMODE(n) (((n) & 0x7) << 8) +#define XSPI_CCR_ADSIZE_SHIFT (12) +#define XSPI_CCR_ADSIZE(n) (((n) & 0x3) << 12) +#define XSPI_CCR_ABMODE_SHIFT (16) +#define XSPI_CCR_ABMODE(n) (((n) & 0x7) << 16) +#define XSPI_CCR_ABSIZE_SHIFT (20) +#define XSPI_CCR_ABSIZE(n) (((n) & 0x3) << 20) +#define XSPI_CCR_DMODE_SHIFT (24) +#define XSPI_CCR_DMODE(n) (((n) & 0x7) << 24) +#define XSPI_CCR_DDTR (1 << 27) +#define XSPI_CCR_SIOO (1 << 31) + +/* XSPI TCR fields */ +#define XSPI_TCR_DCYC_SHIFT (0) +#define XSPI_TCR_DCYC_MASK (0x1F) +#define XSPI_TCR_DCYC(n) (((n) & 0x1F) << 0) +#define XSPI_TCR_DHQC (1 << 28) +#define XSPI_TCR_SSHIFT (1 << 30) + +/* SPI mode values: 0=none, 1=single, 2=dual, 3=quad, 4=octal */ +#define XSPI_MODE_NONE 0 +#define XSPI_MODE_SINGLE 1 +#define XSPI_MODE_DUAL 2 +#define XSPI_MODE_QUAD 3 +#define XSPI_MODE_OCTAL 4 + + +/*** XSPIM (XSPI I/O Manager) ***/ +#define XSPIM_BASE (0x5802B400UL) +#define XSPIM_CR (*(volatile uint32_t *)(XSPIM_BASE + 0x00)) + +/*** NOR Flash Commands (Macronix MX25UM51245G) ***/ +/* Single-SPI mode commands (initial boot) */ +#define NOR_CMD_WRITE_ENABLE 0x06 +#define NOR_CMD_WRITE_DISABLE 0x04 +#define NOR_CMD_READ_SR 0x05 +#define NOR_CMD_READ_ID 0x9F +#define NOR_CMD_FAST_READ_4B 0x0C +#define NOR_CMD_PAGE_PROG_4B 0x12 +#define NOR_CMD_SECTOR_ERASE_4B 0x21 +#define NOR_CMD_BLOCK_ERASE_4B 0xDC +#define NOR_CMD_RESET_ENABLE 0x66 +#define NOR_CMD_RESET_MEMORY 0x99 + +/* NOR flash status register bits */ +#define NOR_SR_WIP (1 << 0) +#define NOR_SR_WEL (1 << 1) + +/* NOR flash geometry */ +#define NOR_PAGE_SIZE 256 +#define NOR_SECTOR_SIZE 0x1000 /* 4KB */ +#define NOR_BLOCK_SIZE 0x10000 /* 64KB */ +#define NOR_DEVICE_SIZE (64 * 1024 * 1024) /* 64MB */ +#define NOR_DEVICE_SIZE_LOG2 26 /* XSPI DEVSIZE: 2^26 = 64MB */ + + +/*** USART1 (Debug UART) ***/ +#define USART1_BASE (0x52001000UL) + +#define USART1_CR1 (*(volatile uint32_t *)(USART1_BASE + 0x00)) +#define USART1_CR2 (*(volatile uint32_t *)(USART1_BASE + 0x04)) +#define USART1_CR3 (*(volatile uint32_t *)(USART1_BASE + 0x08)) +#define USART1_BRR (*(volatile uint32_t *)(USART1_BASE + 0x0C)) +#define USART1_ISR (*(volatile uint32_t *)(USART1_BASE + 0x1C)) +#define USART1_ICR (*(volatile uint32_t *)(USART1_BASE + 0x20)) +#define USART1_RDR (*(volatile uint32_t *)(USART1_BASE + 0x24)) +#define USART1_TDR (*(volatile uint32_t *)(USART1_BASE + 0x28)) + +#define USART_CR1_UE (1 << 0) +#define USART_CR1_RE (1 << 2) +#define USART_CR1_TE (1 << 3) +#define USART_CR1_OVER8 (1 << 15) +#define USART_ISR_TXE (1 << 7) +#define USART_ISR_RXNE (1 << 5) +#define USART_ISR_TC (1 << 6) + + +/*** SCB (System Control Block) — Cortex-M55 cache control ***/ +#define SCB_BASE (0xE000ED00UL) +#define SCB_CCR (*(volatile uint32_t *)(SCB_BASE + 0x14)) +#define SCB_CCR_IC (1 << 17) +#define SCB_CCR_DC (1 << 16) + +/* Cache maintenance (Cortex-M55 uses standard ARM CMSIS-like registers) */ +#define SCB_ICIALLU (*(volatile uint32_t *)(0xE000EF50UL)) +#define SCB_DCIMVAC (*(volatile uint32_t *)(0xE000EF5CUL)) +#define SCB_DCISW (*(volatile uint32_t *)(0xE000EF60UL)) +#define SCB_DCCMVAU (*(volatile uint32_t *)(0xE000EF64UL)) +#define SCB_DCCMVAC (*(volatile uint32_t *)(0xE000EF68UL)) +#define SCB_DCCSW (*(volatile uint32_t *)(0xE000EF6CUL)) +#define SCB_DCCIMVAC (*(volatile uint32_t *)(0xE000EF70UL)) +#define SCB_DCCISW (*(volatile uint32_t *)(0xE000EF74UL)) + +/* Cache size ID registers */ +#define CCSIDR (*(volatile uint32_t *)(0xE000ED80UL)) +#define CSSELR (*(volatile uint32_t *)(0xE000ED84UL)) + +/*** AIRCR (Application Interrupt and Reset Control) ***/ +#define AIRCR (*(volatile uint32_t *)(0xE000ED0CUL)) +#define AIRCR_VKEY (0x05FA << 16) +#define AIRCR_SYSRESETREQ (1 << 2) + +/*** SysTick ***/ +#define SYSTICK_BASE (0xE000E010UL) +#define SYSTICK_CSR (*(volatile uint32_t *)(SYSTICK_BASE + 0x00)) +#define SYSTICK_RVR (*(volatile uint32_t *)(SYSTICK_BASE + 0x04)) +#define SYSTICK_CVR (*(volatile uint32_t *)(SYSTICK_BASE + 0x08)) + + +/*** SRAM regions ***/ +#define AXISRAM1_BASE (0x34000000UL) +#define AXISRAM2_BASE (0x34180400UL) +#define AXISRAM3_BASE (0x34200000UL) +#define AXISRAM4_BASE (0x34270000UL) +#define AXISRAM5_BASE (0x342E0000UL) +#define AXISRAM6_BASE (0x34350000UL) + + +#endif /* STM32N6_DEF_INCLUDED */ diff --git a/hal/stm32n6.ld b/hal/stm32n6.ld new file mode 100644 index 0000000000..0b8a9229b0 --- /dev/null +++ b/hal/stm32n6.ld @@ -0,0 +1,56 @@ +/* wolfBoot linker script for STM32N6 + * + * wolfBoot runs from SRAM (Boot ROM copies FSBL from external NOR flash). + * FLASH region is actually AXISRAM1 at 0x34000000. + * RAM is placed higher in SRAM for stack/heap. + */ + +MEMORY +{ + FLASH (rwx) : ORIGIN = @WOLFBOOT_ORIGIN@, LENGTH = @BOOTLOADER_PARTITION_SIZE@ +} + +SECTIONS +{ + .text : + { + _start_text = .; + KEEP(*(.isr_vector)) + *(.text*) + *(.rodata*) + . = ALIGN(4); + _end_text = .; + } > FLASH + + .edidx : + { + . = ALIGN(4); + *(.ARM.exidx*) + } > FLASH + + _stored_data = .; + .data : + { + _start_data = .; + KEEP(*(.data*)) + . = ALIGN(4); + KEEP(*(.ramcode)) + . = ALIGN(4); + _end_data = .; + } > FLASH + + .bss (NOLOAD) : + { + _start_bss = .; + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _end_bss = .; + __bss_end__ = .; + _end = .; + } > FLASH + . = ALIGN(4); +} + +END_STACK = ORIGIN(FLASH) + LENGTH(FLASH); diff --git a/test-app/ARM-stm32n6.ld b/test-app/ARM-stm32n6.ld new file mode 100644 index 0000000000..0a52577e67 --- /dev/null +++ b/test-app/ARM-stm32n6.ld @@ -0,0 +1,57 @@ +MEMORY +{ + FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@ + RAM (rwx) : ORIGIN = 0x34010000, LENGTH = 64K +} + +SECTIONS +{ + .text : + { + _start_text = .; + KEEP(*(.isr_vector)) + *(.init) + *(.fini) + *(.text*) + KEEP(*(.rodata*)) + . = ALIGN(4); + _end_text = .; + } > FLASH + + .ARM : + { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } > FLASH + + _stored_data = .; + + .data : AT (_stored_data) + { + _start_data = .; + KEEP(*(.data*)) + . = ALIGN(4); + KEEP(*(.ramcode)) + . = ALIGN(4); + _end_data = .; + } > RAM + + .bss : + { + _start_bss = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _end_bss = .; + _end = .; + } > RAM +} + +_wolfboot_partition_boot_address = @WOLFBOOT_PARTITION_BOOT_ADDRESS@; +_wolfboot_partition_size = @WOLFBOOT_PARTITION_SIZE@; +_wolfboot_partition_update_address = @WOLFBOOT_PARTITION_UPDATE_ADDRESS@; +_wolfboot_partition_swap_address = @WOLFBOOT_PARTITION_SWAP_ADDRESS@; + +PROVIDE(_start_heap = _end); +PROVIDE(_end_stack = ORIGIN(RAM) + LENGTH(RAM)); diff --git a/test-app/Makefile b/test-app/Makefile index 1d0b1e5ad5..f51bbefdb5 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -417,6 +417,14 @@ ifeq ($(TARGET),stm32h5) endif endif +ifeq ($(TARGET),stm32n6) + LSCRIPT_TEMPLATE=ARM-stm32n6.ld + CFLAGS+=-mcpu=cortex-m55 -ffunction-sections -fdata-sections -fno-common + LDFLAGS+=-mcpu=cortex-m55 + LDFLAGS+=-Wl,-gc-sections -Wl,-Map=image.map + CFLAGS+=-I.. +endif + ifeq ($(TARGET),stm32u5) ifeq ($(TZEN),1) LSCRIPT_TEMPLATE=ARM-stm32u5-ns.ld @@ -607,6 +615,10 @@ ifeq ($(TARGET),s32k1xx) CFLAGS+=-DRAM_CODE -DDEBUG_UART endif +ifeq ($(TARGET),stm32n6) + CFLAGS+=-DRAM_CODE +endif + ifeq ($(TARGET),mcxw) ifeq ($(TZEN),1) LSCRIPT_TEMPLATE=ARM-mcxw-ns.ld diff --git a/test-app/app_stm32n6.c b/test-app/app_stm32n6.c new file mode 100644 index 0000000000..1d46ff8367 --- /dev/null +++ b/test-app/app_stm32n6.c @@ -0,0 +1,120 @@ +/* app_stm32n6.c + * + * Test bare-metal application for NUCLEO-N657X0-Q. + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include "system.h" +#include "hal.h" +#include "wolfboot/wolfboot.h" +#include "target.h" + +#define RCC_BASE (0x56028000UL) +#define RCC_AHB4ENR (*(volatile uint32_t *)(RCC_BASE + 0x25C)) +#define RCC_AHB4ENR_GPIOGEN (1 << 6) +#define RCC_AHB4ENR_PWREN (1 << 18) + +/* PWR I/O supply valid bits (required for Port G output) */ +#define PWR_BASE (0x56024800UL) +#define PWR_SVMCR3 (*(volatile uint32_t *)(PWR_BASE + 0x3C)) +#define PWR_SVMCR3_VDDIO2SV (1 << 8) +#define PWR_SVMCR3_VDDIO3SV (1 << 9) + +#define GPIO_MODER(base) (*(volatile uint32_t *)((base) + 0x00)) +#define GPIO_OSPEEDR(base) (*(volatile uint32_t *)((base) + 0x08)) +#define GPIO_PUPDR(base) (*(volatile uint32_t *)((base) + 0x0C)) +#define GPIO_BSRR(base) (*(volatile uint32_t *)((base) + 0x18)) + +#define GPIOG_BASE (0x56021800UL) + +/* User LEDs: active LOW on Port G (LD6=PG0 green, LD7=PG8 blue, LD5=PG10 red) */ +#define LED_GREEN_PIN 0 +#define LED_BLUE_PIN 8 +#define LED_RED_PIN 10 + +static void led_init(void) +{ + uint32_t reg; + + RCC_AHB4ENR |= RCC_AHB4ENR_GPIOGEN | RCC_AHB4ENR_PWREN; + DMB(); + + /* Mark I/O supply valid for Port G */ + PWR_SVMCR3 |= PWR_SVMCR3_VDDIO2SV | PWR_SVMCR3_VDDIO3SV; + DMB(); + + /* Set PG0, PG8, PG10 to output mode */ + reg = GPIO_MODER(GPIOG_BASE); + reg &= ~(0x3 << (LED_GREEN_PIN * 2)); + reg |= (0x1 << (LED_GREEN_PIN * 2)); + reg &= ~(0x3 << (LED_BLUE_PIN * 2)); + reg |= (0x1 << (LED_BLUE_PIN * 2)); + reg &= ~(0x3 << (LED_RED_PIN * 2)); + reg |= (0x1 << (LED_RED_PIN * 2)); + GPIO_MODER(GPIOG_BASE) = reg; +} + +/* Active LOW: BSRR reset = ON, BSRR set = OFF */ +static void led_on(uint32_t gpio_base, int pin) +{ + GPIO_BSRR(gpio_base) = (1 << (pin + 16)); +} + +static void led_off(uint32_t gpio_base, int pin) +{ + GPIO_BSRR(gpio_base) = (1 << pin); +} + +static void delay(volatile uint32_t count) +{ + while (count--) + ; +} + +volatile uint32_t app_running __attribute__((section(".data"))) = 0; + +void main(void) +{ + /* hal_init() not called — XSPI2 already configured by wolfBoot for XIP */ + led_init(); + + app_running = 0xCAFEBEEF; + (void)wolfBoot_current_firmware_version(); + + led_on(GPIOG_BASE, LED_GREEN_PIN); + + /* Test flash erase from XIP (RAMFUNCTION verification) */ + hal_flash_unlock(); + hal_flash_erase(0x70010000, 0x1000); + hal_flash_lock(); + + app_running = 0xF1A5F1A5; + + /* Mark firmware stable (flash ops are RAMFUNCTION, safe from XIP) */ + wolfBoot_success(); + + while (1) { + led_on(GPIOG_BASE, LED_BLUE_PIN); + delay(2000000); + led_off(GPIOG_BASE, LED_BLUE_PIN); + delay(2000000); + } +} diff --git a/tools/scripts/stm32n6_flash.sh b/tools/scripts/stm32n6_flash.sh new file mode 100755 index 0000000000..13976c70ea --- /dev/null +++ b/tools/scripts/stm32n6_flash.sh @@ -0,0 +1,137 @@ +#!/bin/bash +# +# STM32N6 Flash Script for NUCLEO-N657X0-Q +# Programs NOR flash (MX25UM51245G on XSPI2) and loads wolfBoot to SRAM. +# +# Usage: +# ./tools/scripts/stm32n6_flash.sh # Build and flash all +# ./tools/scripts/stm32n6_flash.sh --skip-build # Flash only +# ./tools/scripts/stm32n6_flash.sh --app-only # Flash app to NOR only +# ./tools/scripts/stm32n6_flash.sh --test-update # Flash v1 + v2 update +# ./tools/scripts/stm32n6_flash.sh --halt # Leave OpenOCD running + +set -e + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' + +SKIP_BUILD=0 +APP_ONLY=0 +TEST_UPDATE=0 +LEAVE_RUNNING=0 + +while [[ $# -gt 0 ]]; do + case $1 in + --skip-build) SKIP_BUILD=1; shift ;; + --app-only) APP_ONLY=1; shift ;; + --test-update) TEST_UPDATE=1; shift ;; + --halt) LEAVE_RUNNING=1; shift ;; + -h|--help) + sed -n '2,/^$/p' "$0" | sed 's/^# \?//' + exit 0 ;; + *) echo -e "${RED}Unknown option: $1${NC}"; exit 1 ;; + esac +done + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WOLFBOOT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +cd "${WOLFBOOT_ROOT}" + +OPENOCD_CFG="${WOLFBOOT_ROOT}/config/openocd/openocd_stm32n6.cfg" +BOOT_ADDR=0x70020000 +UPDATE_ADDR=0x70120000 + +check_tool() { + command -v "$1" &>/dev/null || { echo -e "${RED}Error: $1 not found${NC}"; exit 1; } +} + +check_tool openocd +[ $SKIP_BUILD -eq 0 ] && check_tool arm-none-eabi-gcc +[ -f "${OPENOCD_CFG}" ] || { echo -e "${RED}Error: ${OPENOCD_CFG} not found${NC}"; exit 1; } + +echo -e "${GREEN}=== STM32N6 Flash Script ===${NC}" + +# Build +if [ $SKIP_BUILD -eq 0 ]; then + echo -e "${GREEN}[1/2] Building...${NC}" + + if [ ! -f .config ]; then + [ -f config/examples/stm32n6.config ] || { echo -e "${RED}No .config found${NC}"; exit 1; } + cp config/examples/stm32n6.config .config + fi + + TARGET_CHECK=$(grep -E '^TARGET\?*=' .config | head -1 | sed 's/.*=//;s/[[:space:]]//g') + [ "$TARGET_CHECK" = "stm32n6" ] || { echo -e "${RED}TARGET is '${TARGET_CHECK}', expected 'stm32n6'${NC}"; exit 1; } + + make clean && make wolfboot.bin + echo -e "${GREEN}wolfboot.bin: $(stat -c%s wolfboot.bin 2>/dev/null || stat -f%z wolfboot.bin) bytes${NC}" + + make test-app/image_v1_signed.bin + echo -e "${GREEN}image_v1_signed.bin: $(stat -c%s test-app/image_v1_signed.bin 2>/dev/null || stat -f%z test-app/image_v1_signed.bin) bytes${NC}" + + if [ $TEST_UPDATE -eq 1 ]; then + SIGN_VALUE=$(grep -E '^SIGN\?*=' .config | head -1 | sed 's/.*=//;s/[[:space:]]//g') + HASH_VALUE=$(grep -E '^HASH\?*=' .config | head -1 | sed 's/.*=//;s/[[:space:]]//g') + [ -f tools/keytools/sign ] || make -C tools/keytools + ./tools/keytools/sign \ + --$(echo "$SIGN_VALUE" | tr '[:upper:]' '[:lower:]') \ + --$(echo "$HASH_VALUE" | tr '[:upper:]' '[:lower:]') \ + test-app/image.bin wolfboot_signing_private_key.der 2 + echo -e "${GREEN}image_v2_signed.bin built${NC}" + fi +else + echo -e "${YELLOW}[1/2] Skipping build${NC}" +fi + +# Verify binaries +[ $APP_ONLY -eq 0 ] && [ ! -f wolfboot.bin ] && { echo -e "${RED}wolfboot.bin not found${NC}"; exit 1; } +[ -f test-app/image_v1_signed.bin ] || { echo -e "${RED}image_v1_signed.bin not found${NC}"; exit 1; } +[ $TEST_UPDATE -eq 1 ] && [ ! -f test-app/image_v2_signed.bin ] && { echo -e "${RED}image_v2_signed.bin not found${NC}"; exit 1; } + +# Flash via OpenOCD +echo -e "${GREEN}[2/2] Programming via OpenOCD...${NC}" +pkill -x openocd 2>/dev/null || true +sleep 1 + +OPENOCD_CMDS="reset init; " + +if [ $APP_ONLY -eq 0 ]; then + echo -e "${CYAN} wolfboot.bin -> SRAM 0x34000000${NC}" + OPENOCD_CMDS+="load_image ${WOLFBOOT_ROOT}/wolfboot.bin 0x34000000 bin; " +fi + +echo -e "${CYAN} image_v1_signed.bin -> NOR ${BOOT_ADDR}${NC}" +OPENOCD_CMDS+="flash write_image erase ${WOLFBOOT_ROOT}/test-app/image_v1_signed.bin ${BOOT_ADDR}; " + +if [ $TEST_UPDATE -eq 1 ]; then + echo -e "${CYAN} image_v2_signed.bin -> NOR ${UPDATE_ADDR}${NC}" + OPENOCD_CMDS+="flash write_image erase ${WOLFBOOT_ROOT}/test-app/image_v2_signed.bin ${UPDATE_ADDR}; " +fi + +# Boot wolfBoot from SRAM (reset would clear SRAM, so we jump directly) +if [ $APP_ONLY -eq 0 ]; then + # Extract initial SP (word 0) and entry point (word 1) from vector table + INIT_SP=$(od -A n -t x4 -N 4 "${WOLFBOOT_ROOT}/wolfboot.bin" | awk '{print "0x"$1}') + ENTRY_ADDR=$(od -A n -j 4 -t x4 -N 4 "${WOLFBOOT_ROOT}/wolfboot.bin" | awk '{print "0x"$1}') + ENTRY_THUMB=$(printf "0x%08x" $(( ${ENTRY_ADDR} | 1 ))) + echo -e "${CYAN} Booting wolfBoot (SP: ${INIT_SP}, entry: ${ENTRY_THUMB})...${NC}" + OPENOCD_CMDS+="reg msplim_s 0x00000000; " + OPENOCD_CMDS+="reg psplim_s 0x00000000; " + OPENOCD_CMDS+="reg msp ${INIT_SP}; " + OPENOCD_CMDS+="mww 0xE000ED08 0x34000000; " # VTOR + OPENOCD_CMDS+="mww 0xE000ED28 0xFFFFFFFF; " # Clear CFSR + OPENOCD_CMDS+="resume ${ENTRY_THUMB}; " +fi + +if [ $LEAVE_RUNNING -eq 0 ]; then + OPENOCD_CMDS+="shutdown" +else + OPENOCD_CMDS+="echo {OpenOCD running. Connect via: telnet localhost 4444}" +fi + +openocd -f "${OPENOCD_CFG}" -c "${OPENOCD_CMDS}" + +echo -e "${GREEN}=== Done ===${NC}" From 4cb1ee9d253c3dcf39d576662e37eb74754e36e6 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 13 Mar 2026 16:21:56 -0700 Subject: [PATCH 2/4] STM32N6: Fix PLL1 600 MHz clock, code cleanup, add release doc Fix PLL1 bypass bit (PLL1BYP) in PLL1CFGR1 that Boot ROM leaves set, which was routing HSI 64 MHz directly to PLL output instead of the 1200 MHz VCO. CPU now runs at 600 MHz (verified via DWT CYCCNT). - Clear PLL1CFGR1 BYP bit to enable VCO output - Simplify PLL1CFGR3 configuration to single write - Consolidate flash write/erase into shared nor_flash_write/erase helpers - Rename xspi_ functions to octospi_ for consistency with register macros - Add CORTEX_M55 define to arch.mk for future use - Add clock tree documentation in clock_config() and PWR_VOSCR - Combine CPUSW and SYSSW clock switch into single register write - Add XSPI2 RAMFUNCTION comments and TEF error handling - Add release announcement doc (docs/release-stm32n6.md) - wolfBoot binary: 23KB, test-app: 3KB --- arch.mk | 2 +- hal/stm32n6.c | 427 +++++++++++++++++++++++------------------ hal/stm32n6.h | 225 +++++++--------------- hal/stm32n6.ld | 2 +- src/boot_arm.c | 2 +- test-app/app_stm32n6.c | 43 ++++- tools/config.mk | 3 +- 7 files changed, 353 insertions(+), 351 deletions(-) diff --git a/arch.mk b/arch.mk index 5773834e15..b47046d226 100644 --- a/arch.mk +++ b/arch.mk @@ -364,7 +364,7 @@ else endif ifeq ($(CORTEX_M55),1) CORTEX_M33=1 - CFLAGS+=-mcpu=cortex-m55 + CFLAGS+=-mcpu=cortex-m55 -DCORTEX_M55 LDFLAGS+=-mcpu=cortex-m55 endif ifeq ($(CORTEX_M33),1) diff --git a/hal/stm32n6.c b/hal/stm32n6.c index 5946b6f66c..51ac5d7f86 100644 --- a/hal/stm32n6.c +++ b/hal/stm32n6.c @@ -25,14 +25,30 @@ #include "hal.h" #include "hal/stm32n6.h" +/* OCTOSPI register definitions come from hal/spi/spi_drv_stm32.h (included + * via stm32n6.h). STM32N6 XSPI2 uses the same IP block as OCTOSPI. + * + * Note: We cannot reuse qspi_transfer() from spi_drv_stm32.c because it + * disables/enables the peripheral on each transfer. The N6 boots via XIP + * (memory-mapped mode on XSPI2), so we need custom transfer code that: + * 1) Aborts memory-mapped mode before indirect access + * 2) Restores memory-mapped mode after each operation + * 3) Runs from SRAM (RAMFUNCTION) when called from XIP code + */ + +/* SPI mode values for OCTOSPI CCR fields */ +#define SPI_MODE_NONE 0 +#define SPI_MODE_SINGLE 1 + /* RAMFUNCTION override for test-app (XIP needs flash ops in SRAM) */ #if defined(RAM_CODE) && !defined(__WOLFBOOT) #undef RAMFUNCTION #define RAMFUNCTION __attribute__((used,section(".ramcode"),long_call)) #endif -/* XSPI2 indirect-mode command helper */ -static int RAMFUNCTION xspi_cmd(uint8_t fmode, uint8_t cmd, +/* OCTOSPI indirect-mode command helper. + * Handles memory-mapped mode abort/restore and TEF error detection. */ +static int RAMFUNCTION octospi_cmd(uint8_t fmode, uint8_t cmd, uint32_t addr, uint32_t addrMode, uint8_t *data, uint32_t dataSz, uint32_t dataMode, uint32_t dummyCycles) @@ -40,130 +56,133 @@ static int RAMFUNCTION xspi_cmd(uint8_t fmode, uint8_t cmd, uint32_t ccr; /* Abort memory-mapped mode if active */ - if ((XSPI2_CR & XSPI_CR_FMODE_MASK) == XSPI_CR_FMODE_MMAP) { - XSPI2_CR |= XSPI_CR_ABORT; - while (XSPI2_CR & XSPI_CR_ABORT) + if ((OCTOSPI_CR & OCTOSPI_CR_FMODE_MASK) == OCTOSPI_CR_FMODE_MMAP) { + OCTOSPI_CR |= OCTOSPI_CR_ABORT; + while (OCTOSPI_CR & OCTOSPI_CR_ABORT) ; } - while (XSPI2_SR & XSPI_SR_BUSY) + while (OCTOSPI_SR & OCTOSPI_SR_BUSY) ; - XSPI2_FCR = XSPI_FCR_CTCF | XSPI_FCR_CTEF | XSPI_FCR_CSMF; + OCTOSPI_FCR = OCTOSPI_FCR_CTCF | OCTOSPI_FCR_CTEF | OCTOSPI_FCR_CSMF; - XSPI2_CR = (XSPI2_CR & ~XSPI_CR_FMODE_MASK) | XSPI_CR_FMODE(fmode); + OCTOSPI_CR = (OCTOSPI_CR & ~OCTOSPI_CR_FMODE_MASK) | + OCTOSPI_CR_FMODE(fmode); if (dataSz > 0) { - XSPI2_DLR = dataSz - 1; + OCTOSPI_DLR = dataSz - 1; } - ccr = XSPI_CCR_IMODE(XSPI_MODE_SINGLE) | XSPI_CCR_ISIZE(0); - if (addrMode != XSPI_MODE_NONE) { - ccr |= XSPI_CCR_ADMODE(addrMode) | XSPI_CCR_ADSIZE(3); + ccr = OCTOSPI_CCR_IMODE(SPI_MODE_SINGLE) | OCTOSPI_CCR_ISIZE(0); + if (addrMode != SPI_MODE_NONE) { + ccr |= OCTOSPI_CCR_ADMODE(addrMode) | OCTOSPI_CCR_ADSIZE(3); } - if (dataMode != XSPI_MODE_NONE) { - ccr |= XSPI_CCR_DMODE(dataMode); + if (dataMode != SPI_MODE_NONE) { + ccr |= OCTOSPI_CCR_DMODE(dataMode); } - XSPI2_CCR = ccr; - XSPI2_TCR = XSPI_TCR_DCYC(dummyCycles); - XSPI2_IR = cmd; + OCTOSPI_CCR = ccr; + OCTOSPI_TCR = OCTOSPI_TCR_DCYC(dummyCycles); + OCTOSPI_IR = cmd; - if (addrMode != XSPI_MODE_NONE) { - XSPI2_AR = addr; + if (addrMode != SPI_MODE_NONE) { + OCTOSPI_AR = addr; } if (dataSz > 0 && data != NULL) { while (dataSz >= 4) { if (fmode == 0) { - while (!(XSPI2_SR & (XSPI_SR_FTF | XSPI_SR_TEF))) + while (!(OCTOSPI_SR & (OCTOSPI_SR_FTF | OCTOSPI_SR_TEF))) ; - if (XSPI2_SR & XSPI_SR_TEF) goto xspi_err; - XSPI2_DR32 = *(uint32_t *)data; + if (OCTOSPI_SR & OCTOSPI_SR_TEF) goto octospi_err; + OCTOSPI_DR32 = *(uint32_t *)data; } else { - while (!(XSPI2_SR & (XSPI_SR_FTF | XSPI_SR_TCF | - XSPI_SR_TEF))) + while (!(OCTOSPI_SR & (OCTOSPI_SR_FTF | OCTOSPI_SR_TCF | + OCTOSPI_SR_TEF))) ; - if (XSPI2_SR & XSPI_SR_TEF) goto xspi_err; - *(uint32_t *)data = XSPI2_DR32; + if (OCTOSPI_SR & OCTOSPI_SR_TEF) goto octospi_err; + *(uint32_t *)data = OCTOSPI_DR32; } data += 4; dataSz -= 4; } while (dataSz > 0) { if (fmode == 0) { - while (!(XSPI2_SR & (XSPI_SR_FTF | XSPI_SR_TEF))) + while (!(OCTOSPI_SR & (OCTOSPI_SR_FTF | OCTOSPI_SR_TEF))) ; - if (XSPI2_SR & XSPI_SR_TEF) goto xspi_err; - XSPI2_DR = *data; + if (OCTOSPI_SR & OCTOSPI_SR_TEF) goto octospi_err; + OCTOSPI_DR = *data; } else { - while (!(XSPI2_SR & (XSPI_SR_FTF | XSPI_SR_TCF | - XSPI_SR_TEF))) + while (!(OCTOSPI_SR & (OCTOSPI_SR_FTF | OCTOSPI_SR_TCF | + OCTOSPI_SR_TEF))) ; - if (XSPI2_SR & XSPI_SR_TEF) goto xspi_err; - *data = XSPI2_DR; + if (OCTOSPI_SR & OCTOSPI_SR_TEF) goto octospi_err; + *data = OCTOSPI_DR; } data++; dataSz--; } } - while (!(XSPI2_SR & (XSPI_SR_TCF | XSPI_SR_TEF))) + while (!(OCTOSPI_SR & (OCTOSPI_SR_TCF | OCTOSPI_SR_TEF))) ; - if (XSPI2_SR & XSPI_SR_TEF) goto xspi_err; - XSPI2_FCR = XSPI_FCR_CTCF; + if (OCTOSPI_SR & OCTOSPI_SR_TEF) goto octospi_err; + OCTOSPI_FCR = OCTOSPI_FCR_CTCF; return 0; -xspi_err: - XSPI2_FCR = XSPI_FCR_CTEF; - XSPI2_CR |= XSPI_CR_ABORT; - while (XSPI2_CR & XSPI_CR_ABORT) +octospi_err: + OCTOSPI_FCR = OCTOSPI_FCR_CTEF; + OCTOSPI_CR |= OCTOSPI_CR_ABORT; + while (OCTOSPI_CR & OCTOSPI_CR_ABORT) ; return -1; } -static void RAMFUNCTION xspi_write_enable(void) +static void RAMFUNCTION octospi_write_enable(void) { - xspi_cmd(0, NOR_CMD_WRITE_ENABLE, 0, XSPI_MODE_NONE, - NULL, 0, XSPI_MODE_NONE, 0); + octospi_cmd(0, WRITE_ENABLE_CMD, 0, SPI_MODE_NONE, + NULL, 0, SPI_MODE_NONE, 0); } -static void RAMFUNCTION xspi_wait_ready(void) +static void RAMFUNCTION octospi_wait_ready(void) { uint8_t sr; do { sr = 0; - xspi_cmd(1, NOR_CMD_READ_SR, 0, XSPI_MODE_NONE, - &sr, 1, XSPI_MODE_SINGLE, 0); - } while (sr & NOR_SR_WIP); + octospi_cmd(1, READ_SR_CMD, 0, SPI_MODE_NONE, + &sr, 1, SPI_MODE_SINGLE, 0); + } while (sr & FLASH_SR_BUSY); } -static void RAMFUNCTION xspi_enable_mmap(void) +static void RAMFUNCTION octospi_enable_mmap(void) { /* Abort first if already in mmap mode (BUSY stays set in mmap) */ - if ((XSPI2_CR & XSPI_CR_FMODE_MASK) == XSPI_CR_FMODE_MMAP) { - XSPI2_CR |= XSPI_CR_ABORT; - while (XSPI2_CR & XSPI_CR_ABORT) + if ((OCTOSPI_CR & OCTOSPI_CR_FMODE_MASK) == OCTOSPI_CR_FMODE_MMAP) { + OCTOSPI_CR |= OCTOSPI_CR_ABORT; + while (OCTOSPI_CR & OCTOSPI_CR_ABORT) ; } - while (XSPI2_SR & XSPI_SR_BUSY) + while (OCTOSPI_SR & OCTOSPI_SR_BUSY) ; - XSPI2_FCR = XSPI_FCR_CTCF | XSPI_FCR_CTEF | XSPI_FCR_CSMF; + OCTOSPI_FCR = OCTOSPI_FCR_CTCF | OCTOSPI_FCR_CTEF | OCTOSPI_FCR_CSMF; - XSPI2_CR = (XSPI2_CR & ~XSPI_CR_FMODE_MASK) | XSPI_CR_FMODE_MMAP; + OCTOSPI_CR = (OCTOSPI_CR & ~OCTOSPI_CR_FMODE_MASK) | + OCTOSPI_CR_FMODE_MMAP; /* Fast read: single SPI, 4-byte addr, 8 dummy cycles */ - XSPI2_CCR = XSPI_CCR_IMODE(XSPI_MODE_SINGLE) | - XSPI_CCR_ISIZE(0) | - XSPI_CCR_ADMODE(XSPI_MODE_SINGLE) | - XSPI_CCR_ADSIZE(3) | - XSPI_CCR_DMODE(XSPI_MODE_SINGLE); - XSPI2_TCR = XSPI_TCR_DCYC(8) | XSPI_TCR_SSHIFT; - XSPI2_IR = NOR_CMD_FAST_READ_4B; + OCTOSPI_CCR = OCTOSPI_CCR_IMODE(SPI_MODE_SINGLE) | + OCTOSPI_CCR_ISIZE(0) | + OCTOSPI_CCR_ADMODE(SPI_MODE_SINGLE) | + OCTOSPI_CCR_ADSIZE(3) | + OCTOSPI_CCR_DMODE(SPI_MODE_SINGLE); + OCTOSPI_TCR = OCTOSPI_TCR_DCYC(8) | OCTOSPI_TCR_SSHIFT; + OCTOSPI_IR = FAST_READ_4B_CMD; DSB(); ISB(); } -static void RAMFUNCTION dcache_clean_invalidate_by_addr(uint32_t addr, uint32_t size) +static void RAMFUNCTION dcache_clean_invalidate_by_addr(uint32_t addr, + uint32_t size) { uint32_t line; for (line = addr & ~0x1FUL; line < addr + size; line += 32) { @@ -231,8 +250,8 @@ static void dcache_disable(void) ISB(); } -/* XSPI2 GPIO: PN0-PN11 as AF9 (DQS, CLK, NCS, IO0-IO7) */ -static void xspi2_gpio_init(void) +/* OCTOSPI GPIO: PN0-PN11 as AF9 (DQS, CLK, NCS, IO0-IO7) */ +static void octospi_gpio_init(void) { uint32_t reg; int pin; @@ -275,7 +294,7 @@ static void xspi2_gpio_init(void) GPIO_AFRH(GPION_BASE) = reg; } -static void xspi2_init(void) +static void octospi_init(void) { volatile uint32_t delay; @@ -283,42 +302,124 @@ static void xspi2_init(void) RCC_MISCENR |= RCC_MISCENR_XSPIPHYCOMPEN; DMB(); - XSPI2_CR = 0; - while (XSPI2_SR & XSPI_SR_BUSY) + OCTOSPI_CR = 0; + while (OCTOSPI_SR & OCTOSPI_SR_BUSY) ; - XSPI2_DCR1 = XSPI_DCR1_DLYBYP | - XSPI_DCR1_DEVSIZE(NOR_DEVICE_SIZE_LOG2) | - XSPI_DCR1_CSHT(3); - XSPI2_DCR2 = XSPI_DCR2_PRESCALER(16); - while (XSPI2_SR & XSPI_SR_BUSY) + OCTOSPI_DCR1 = OCTOSPI_DCR1_DLYBYP | + OCTOSPI_DCR1_DEVSIZE(FLASH_DEVICE_SIZE_LOG2) | + OCTOSPI_DCR1_CSHT(3); + OCTOSPI_DCR2 = OCTOSPI_DCR2_PRESCALER(16); + while (OCTOSPI_SR & OCTOSPI_SR_BUSY) ; - XSPI2_CR = XSPI_CR_FTHRES(1) | XSPI_CR_EN; + OCTOSPI_CR = OCTOSPI_CR_FTHRES(1) | OCTOSPI_CR_EN; /* NOR flash software reset */ - xspi_cmd(0, NOR_CMD_RESET_ENABLE, 0, XSPI_MODE_NONE, - NULL, 0, XSPI_MODE_NONE, 0); - xspi_cmd(0, NOR_CMD_RESET_MEMORY, 0, XSPI_MODE_NONE, - NULL, 0, XSPI_MODE_NONE, 0); + octospi_cmd(0, RESET_ENABLE_CMD, 0, SPI_MODE_NONE, + NULL, 0, SPI_MODE_NONE, 0); + octospi_cmd(0, RESET_MEMORY_CMD, 0, SPI_MODE_NONE, + NULL, 0, SPI_MODE_NONE, 0); for (delay = 0; delay < 100000; delay++) ; - xspi_enable_mmap(); + octospi_enable_mmap(); } +/* Configure clocks: PLL1 → 600 MHz CPU (Voltage Scale 1). + * STM32N6 supports up to 800 MHz at Voltage Scale 0 (PWR_VOSCR_VOS=1). + * + * Clock tree: + * HSI 64 MHz → PLL1 (M=4, N=75) → VCO 1200 MHz → PDIV1=1 → 1200 MHz + * IC1 /2 = 600 MHz → CPU (CPUSW=IC1) + * IC2 /3 = 400 MHz → AXI bus (SYSSW=IC2) + * IC6 /4 = 300 MHz → system bus C (SYSSW=IC6) + * IC11/3 = 400 MHz → system bus D (SYSSW=IC11) + * AHB prescaler /2 → HCLK = 300 MHz + */ static void clock_config(void) { - /* HSI at 64 MHz (PLL configuration deferred) */ - RCC_CR |= RCC_CR_HSION; + uint32_t reg; + + /* Enable HSI 64 MHz */ + RCC_CSR = RCC_CR_HSION; while (!(RCC_SR & RCC_SR_HSIRDY)) ; + + /* Disable PLL1 before reconfiguring */ + RCC_CCR = RCC_CR_PLL1ON; + while (RCC_SR & RCC_SR_PLL1RDY) + ; + + /* PLL1: HSI / 4 * 75 = 1200 MHz VCO. + * Clear BYP (bypass) — Boot ROM leaves it set, which routes HSI + * directly to PLL output, skipping the VCO entirely. */ + reg = RCC_PLL1CFGR1; + reg &= ~(RCC_PLL1CFGR1_SEL_MASK | RCC_PLL1CFGR1_DIVM_MASK | + RCC_PLL1CFGR1_DIVN_MASK | RCC_PLL1CFGR1_BYP); + reg |= RCC_PLL1CFGR1_SEL_HSI | + (4 << RCC_PLL1CFGR1_DIVM_SHIFT) | + (75 << RCC_PLL1CFGR1_DIVN_SHIFT); + RCC_PLL1CFGR1 = reg; + + RCC_PLL1CFGR2 = 0; /* no fractional */ + + /* PDIV1=1, PDIV2=1 → PLL output = VCO = 1200 MHz. + * MODSSDIS: disable spread spectrum. PDIVEN: enable PLL output. */ + RCC_PLL1CFGR3 = (1 << RCC_PLL1CFGR3_PDIV1_SHIFT) | + (1 << RCC_PLL1CFGR3_PDIV2_SHIFT) | + RCC_PLL1CFGR3_MODSSDIS | + RCC_PLL1CFGR3_MODSSRST | + RCC_PLL1CFGR3_PDIVEN; + + /* Enable PLL1, wait for lock */ + RCC_CSR = RCC_CR_PLL1ON; + while (!(RCC_SR & RCC_SR_PLL1RDY)) + ; + + /* Configure IC dividers: disable → configure → re-enable. + * IC divider = INT + 1, SEL: 0=PLL1 (per ST LL driver). */ + RCC_DIVENCR = RCC_DIVENR_IC1EN; + RCC_IC1CFGR = RCC_ICCFGR_SEL_PLL1 | ((2 - 1) << RCC_ICCFGR_INT_SHIFT); + RCC_DIVENSR = RCC_DIVENR_IC1EN; + + RCC_DIVENCR = RCC_DIVENR_IC2EN; + RCC_IC2CFGR = RCC_ICCFGR_SEL_PLL1 | ((3 - 1) << RCC_ICCFGR_INT_SHIFT); + RCC_DIVENSR = RCC_DIVENR_IC2EN; + + RCC_DIVENCR = RCC_DIVENR_IC6EN; + RCC_IC6CFGR = RCC_ICCFGR_SEL_PLL1 | ((4 - 1) << RCC_ICCFGR_INT_SHIFT); + RCC_DIVENSR = RCC_DIVENR_IC6EN; + + RCC_DIVENCR = RCC_DIVENR_IC11EN; + RCC_IC11CFGR = RCC_ICCFGR_SEL_PLL1 | ((3 - 1) << RCC_ICCFGR_INT_SHIFT); + RCC_DIVENSR = RCC_DIVENR_IC11EN; + + /* AHB prescaler /2 → HCLK = 300 MHz */ + reg = RCC_CFGR2; + reg &= ~RCC_CFGR2_HPRE_MASK; + reg |= (1 << RCC_CFGR2_HPRE_SHIFT); + RCC_CFGR2 = reg; + + /* Switch CPU to IC1, system bus to IC2/IC6/IC11 */ + reg = RCC_CFGR1; + reg &= ~(RCC_CFGR1_CPUSW_MASK | RCC_CFGR1_SYSSW_MASK); + reg |= (0x3 << RCC_CFGR1_CPUSW_SHIFT) | + (0x3 << RCC_CFGR1_SYSSW_SHIFT); + RCC_CFGR1 = reg; + while ((RCC_CFGR1 & RCC_CFGR1_CPUSWS_MASK) != + (0x3 << RCC_CFGR1_CPUSWS_SHIFT)) + ; + while ((RCC_CFGR1 & RCC_CFGR1_SYSSWS_MASK) != + (0x3 << RCC_CFGR1_SYSSWS_SHIFT)) + ; } #ifdef DEBUG_UART /* USART1 on PE5 (TX) / PE6 (RX), AF7 */ -#define UART_CLOCK_FREQ 64000000UL +#define UART_BASE USART1_BASE +#define UART_CLOCK_FREQ 300000000UL /* HCLK/APB2 after PLL config */ static void uart_init(uint32_t baud) { @@ -346,27 +447,27 @@ static void uart_init(uint32_t baud) GPIO_OSPEEDR(GPIOE_BASE) = reg; /* 8N1 */ - USART1_CR1 = 0; - USART1_CR2 = 0; - USART1_CR3 = 0; - USART1_BRR = (UART_CLOCK_FREQ + baud / 2) / baud; - USART1_CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; + UART_CR1(UART_BASE) = 0; + UART_CR2(UART_BASE) = 0; + UART_CR3(UART_BASE) = 0; + UART_BRR(UART_BASE) = (UART_CLOCK_FREQ + baud / 2) / baud; + UART_CR1(UART_BASE) = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; } static void uart_write(const char *buf, int len) { int i; for (i = 0; i < len; i++) { - while (!(USART1_ISR & USART_ISR_TXE)) + while (!(UART_ISR(UART_BASE) & USART_ISR_TXE)) ; - USART1_TDR = buf[i]; + UART_TDR(UART_BASE) = buf[i]; } - while (!(USART1_ISR & USART_ISR_TC)) + while (!(UART_ISR(UART_BASE) & USART_ISR_TC)) ; } #endif -/* Mark VDDIO supplies valid (required for XSPI2 GPIO) */ +/* Mark VDDIO supplies valid (required for OCTOSPI GPIO) */ static void pwr_enable_io_supply(void) { RCC_AHB4ENR |= RCC_AHB4ENR_PWREN; @@ -383,8 +484,8 @@ void hal_init(void) pwr_enable_io_supply(); icache_enable(); dcache_enable(); - xspi2_gpio_init(); - xspi2_init(); + octospi_gpio_init(); + octospi_init(); #ifdef DEBUG_UART uart_init(115200); @@ -394,76 +495,85 @@ void hal_init(void) void hal_prepare_boot(void) { - xspi_enable_mmap(); + octospi_enable_mmap(); dcache_disable(); icache_disable(); } -int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) +/* Shared NOR flash helpers used by both hal_flash_* and ext_flash_* */ +static int RAMFUNCTION nor_flash_write(uint32_t offset, const uint8_t *data, + int len) { - uint32_t offset; uint32_t page_off, write_sz; - int total = len; + int remaining = len; int ret = 0; if (len <= 0) return 0; - offset = address - XSPI2_MEM_BASE; - - while (len > 0) { - page_off = offset & (NOR_PAGE_SIZE - 1); - write_sz = NOR_PAGE_SIZE - page_off; - if ((int)write_sz > len) - write_sz = len; + while (remaining > 0) { + page_off = offset & (FLASH_PAGE_SIZE - 1); + write_sz = FLASH_PAGE_SIZE - page_off; + if ((int)write_sz > remaining) + write_sz = remaining; - xspi_write_enable(); - ret = xspi_cmd(0, NOR_CMD_PAGE_PROG_4B, - offset, XSPI_MODE_SINGLE, - (uint8_t *)data, write_sz, XSPI_MODE_SINGLE, 0); + octospi_write_enable(); + ret = octospi_cmd(0, PAGE_PROG_4B_CMD, + offset, SPI_MODE_SINGLE, + (uint8_t *)data, write_sz, SPI_MODE_SINGLE, 0); if (ret < 0) break; - xspi_wait_ready(); + octospi_wait_ready(); offset += write_sz; data += write_sz; - len -= write_sz; + remaining -= write_sz; } - xspi_enable_mmap(); - dcache_clean_invalidate_by_addr(address, total); - + octospi_enable_mmap(); return ret; } -int RAMFUNCTION hal_flash_erase(uint32_t address, int len) +static int RAMFUNCTION nor_flash_erase(uint32_t offset, int len) { - uint32_t offset; uint32_t end; int ret = 0; if (len <= 0) return -1; - offset = address - XSPI2_MEM_BASE; end = offset + len; while (offset < end) { - xspi_write_enable(); - ret = xspi_cmd(0, NOR_CMD_SECTOR_ERASE_4B, - offset, XSPI_MODE_SINGLE, - NULL, 0, XSPI_MODE_NONE, 0); + octospi_write_enable(); + ret = octospi_cmd(0, SEC_ERASE_4B_CMD, + offset, SPI_MODE_SINGLE, + NULL, 0, SPI_MODE_NONE, 0); if (ret < 0) break; - xspi_wait_ready(); - offset += NOR_SECTOR_SIZE; + octospi_wait_ready(); + offset += FLASH_SECTOR_SIZE; } - xspi_enable_mmap(); - dcache_clean_invalidate_by_addr(address, len); + octospi_enable_mmap(); + return ret; +} + +int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) +{ + int ret = nor_flash_write(address - OCTOSPI_MEM_BASE, data, len); + if (ret == 0 && len > 0) + dcache_clean_invalidate_by_addr(address, len); + return ret; +} +int RAMFUNCTION hal_flash_erase(uint32_t address, int len) +{ + int ret = nor_flash_erase(address - OCTOSPI_MEM_BASE, len); + if (ret == 0 && len > 0) + dcache_clean_invalidate_by_addr(address, len); return ret; } @@ -484,79 +594,24 @@ int RAMFUNCTION ext_flash_read(uintptr_t address, uint8_t *data, int len) if (len <= 0) return 0; - ret = xspi_cmd(1, NOR_CMD_FAST_READ_4B, - (uint32_t)address, XSPI_MODE_SINGLE, - data, len, XSPI_MODE_SINGLE, 8); + ret = octospi_cmd(1, FAST_READ_4B_CMD, + (uint32_t)address, SPI_MODE_SINGLE, + data, len, SPI_MODE_SINGLE, 8); - xspi_enable_mmap(); + octospi_enable_mmap(); return (ret < 0) ? ret : len; } -int RAMFUNCTION ext_flash_write(uintptr_t address, const uint8_t *data, int len) +int RAMFUNCTION ext_flash_write(uintptr_t address, const uint8_t *data, + int len) { - uint32_t offset = (uint32_t)address; - uint32_t page_off, write_sz; - const uint8_t *src = data; - int remaining = len; - int ret = 0; - - if (len <= 0) - return 0; - - while (remaining > 0) { - page_off = offset & (NOR_PAGE_SIZE - 1); - write_sz = NOR_PAGE_SIZE - page_off; - if ((int)write_sz > remaining) - write_sz = remaining; - - xspi_write_enable(); - - ret = xspi_cmd(0, NOR_CMD_PAGE_PROG_4B, - offset, XSPI_MODE_SINGLE, - (uint8_t *)src, write_sz, XSPI_MODE_SINGLE, 0); - if (ret < 0) - break; - - xspi_wait_ready(); - - offset += write_sz; - src += write_sz; - remaining -= write_sz; - } - - xspi_enable_mmap(); - - return ret; + return nor_flash_write((uint32_t)address, data, len); } int RAMFUNCTION ext_flash_erase(uintptr_t address, int len) { - uint32_t offset = (uint32_t)address; - uint32_t end; - int ret = 0; - - if (len <= 0) - return -1; - - end = offset + len; - - while (offset < end) { - xspi_write_enable(); - - ret = xspi_cmd(0, NOR_CMD_SECTOR_ERASE_4B, - offset, XSPI_MODE_SINGLE, - NULL, 0, XSPI_MODE_NONE, 0); - if (ret < 0) - break; - - xspi_wait_ready(); - offset += NOR_SECTOR_SIZE; - } - - xspi_enable_mmap(); - - return ret; + return nor_flash_erase((uint32_t)address, len); } void RAMFUNCTION ext_flash_lock(void) diff --git a/hal/stm32n6.h b/hal/stm32n6.h index 0d837b14c0..40923921b5 100644 --- a/hal/stm32n6.h +++ b/hal/stm32n6.h @@ -36,8 +36,10 @@ /*** RCC (Reset and Clock Control) — base 0x56028000 (secure) ***/ #define RCC_BASE (0x56028000UL) -/* RCC_CR: control register — enable bits */ -#define RCC_CR (*(volatile uint32_t *)(RCC_BASE + 0x00)) +/* RCC_CR: control register (read), CSR: set register, CCR: clear register */ +#define RCC_CR (*(volatile uint32_t *)(RCC_BASE + 0x000)) +#define RCC_CSR (*(volatile uint32_t *)(RCC_BASE + 0x800)) +#define RCC_CCR (*(volatile uint32_t *)(RCC_BASE + 0x1000)) #define RCC_CR_LSION (1 << 0) #define RCC_CR_LSEON (1 << 1) #define RCC_CR_MSION (1 << 2) @@ -71,16 +73,18 @@ #define RCC_CFGR1_SYSSWS_SHIFT (28) #define RCC_CFGR1_SYSSWS_MASK (0x3 << 28) -/* RCC_CFGR2: APB prescalers */ +/* RCC_CFGR2: AHB/APB prescalers */ #define RCC_CFGR2 (*(volatile uint32_t *)(RCC_BASE + 0x24)) #define RCC_CFGR2_PPRE1_SHIFT (0) #define RCC_CFGR2_PPRE1_MASK (0x7 << 0) #define RCC_CFGR2_PPRE2_SHIFT (4) #define RCC_CFGR2_PPRE2_MASK (0x7 << 4) -#define RCC_CFGR2_PPRE4_SHIFT (8) -#define RCC_CFGR2_PPRE4_MASK (0x7 << 8) -#define RCC_CFGR2_PPRE5_SHIFT (12) -#define RCC_CFGR2_PPRE5_MASK (0x7 << 12) +#define RCC_CFGR2_PPRE4_SHIFT (12) +#define RCC_CFGR2_PPRE4_MASK (0x7 << 12) +#define RCC_CFGR2_PPRE5_SHIFT (16) +#define RCC_CFGR2_PPRE5_MASK (0x7 << 16) +#define RCC_CFGR2_HPRE_SHIFT (20) +#define RCC_CFGR2_HPRE_MASK (0x7 << 20) /* PLL1 Configuration registers */ #define RCC_PLL1CFGR1 (*(volatile uint32_t *)(RCC_BASE + 0x80)) @@ -90,12 +94,22 @@ #define RCC_PLL1CFGR1_DIVM_MASK (0x3F << 20) #define RCC_PLL1CFGR1_SEL_SHIFT (28) /* bits [30:28]: PLL source */ #define RCC_PLL1CFGR1_SEL_MASK (0x7 << 28) +#define RCC_PLL1CFGR1_BYP (1 << 27) /* PLL bypass (ref clock passthrough) */ #define RCC_PLL1CFGR1_SEL_HSI (0x0 << 28) #define RCC_PLL1CFGR1_SEL_HSE (0x1 << 28) #define RCC_PLL1CFGR1_SEL_MSI (0x2 << 28) #define RCC_PLL1CFGR2 (*(volatile uint32_t *)(RCC_BASE + 0x84)) +/* PLL1CFGR2: fractional DIVN only (bits [23:0]) */ + #define RCC_PLL1CFGR3 (*(volatile uint32_t *)(RCC_BASE + 0x88)) +#define RCC_PLL1CFGR3_PDIV2_SHIFT (24) /* bits [26:24]: output post-divider 2 (1-7) */ +#define RCC_PLL1CFGR3_PDIV2_MASK (0x7 << 24) +#define RCC_PLL1CFGR3_PDIV1_SHIFT (27) /* bits [29:27]: output post-divider 1 (1-7) */ +#define RCC_PLL1CFGR3_PDIV1_MASK (0x7 << 27) +#define RCC_PLL1CFGR3_MODSSRST (1 << 0) /* spread spectrum modulation reset */ +#define RCC_PLL1CFGR3_MODSSDIS (1 << 2) /* spread spectrum disable */ +#define RCC_PLL1CFGR3_PDIVEN (1 << 30) /* post-divider + PLL output enable */ /* IC (Interconnect Clock) dividers */ #define RCC_IC1CFGR (*(volatile uint32_t *)(RCC_BASE + 0xC4)) @@ -123,8 +137,10 @@ #define RCC_ICCFGR_SEL_PLL3 (0x2 << 28) #define RCC_ICCFGR_SEL_PLL4 (0x3 << 28) -/* Divider and bus enable registers */ +/* Divider enable: direct, set (+0x800), clear (+0x1000) */ #define RCC_DIVENR (*(volatile uint32_t *)(RCC_BASE + 0x240)) +#define RCC_DIVENSR (*(volatile uint32_t *)(RCC_BASE + 0xA40)) +#define RCC_DIVENCR (*(volatile uint32_t *)(RCC_BASE + 0x1240)) #define RCC_DIVENR_IC1EN (1 << 0) #define RCC_DIVENR_IC2EN (1 << 1) #define RCC_DIVENR_IC3EN (1 << 2) @@ -182,8 +198,10 @@ #define PWR_CR4 (*(volatile uint32_t *)(PWR_BASE + 0x0C)) #define PWR_VOSCR (*(volatile uint32_t *)(PWR_BASE + 0x20)) -/* PWR_VOSCR fields */ -#define PWR_VOSCR_VOS (1 << 0) /* 0=Scale2, 1=Scale1 */ +/* PWR_VOSCR: Voltage scaling controls max CPU frequency. + * Scale 1 (VOS=0, default): up to 600 MHz CPU. + * Scale 0 (VOS=1): up to 800 MHz CPU. */ +#define PWR_VOSCR_VOS (1 << 0) #define PWR_VOSCR_VOSRDY (1 << 1) /* PWR Supply Voltage Monitoring Control Registers */ @@ -214,23 +232,16 @@ #define GPIOP_BASE (0x56023C00UL) #define GPIOQ_BASE (0x56024000UL) -/* GPIO register offsets (same as H5/H7) */ +/* GPIO register offsets — GPIO_ODR, GPIO_BSRR, GPIO_MODE_* come from + * spi_drv_stm32.h. Define the rest that aren't in the shared header. */ #define GPIO_MODER(base) (*(volatile uint32_t *)((base) + 0x00)) #define GPIO_OTYPER(base) (*(volatile uint32_t *)((base) + 0x04)) #define GPIO_OSPEEDR(base) (*(volatile uint32_t *)((base) + 0x08)) #define GPIO_PUPDR(base) (*(volatile uint32_t *)((base) + 0x0C)) #define GPIO_IDR(base) (*(volatile uint32_t *)((base) + 0x10)) -#define GPIO_ODR(base) (*(volatile uint32_t *)((base) + 0x14)) -#define GPIO_BSRR(base) (*(volatile uint32_t *)((base) + 0x18)) #define GPIO_AFRL(base) (*(volatile uint32_t *)((base) + 0x20)) #define GPIO_AFRH(base) (*(volatile uint32_t *)((base) + 0x24)) -/* GPIO mode values */ -#define GPIO_MODE_INPUT 0x0 -#define GPIO_MODE_OUTPUT 0x1 -#define GPIO_MODE_AF 0x2 -#define GPIO_MODE_ANALOG 0x3 - /* GPIO speed values */ #define GPIO_SPEED_LOW 0x0 #define GPIO_SPEED_MEDIUM 0x1 @@ -238,158 +249,60 @@ #define GPIO_SPEED_VERY_HIGH 0x3 -/*** XSPI2 (External SPI for NOR flash) ***/ -#define XSPI2_BASE (0x5802A000UL) -#define XSPI2_MEM_BASE (0x70000000UL) - -#define XSPI2_CR (*(volatile uint32_t *)(XSPI2_BASE + 0x00)) -#define XSPI2_DCR1 (*(volatile uint32_t *)(XSPI2_BASE + 0x08)) -#define XSPI2_DCR2 (*(volatile uint32_t *)(XSPI2_BASE + 0x0C)) -#define XSPI2_DCR3 (*(volatile uint32_t *)(XSPI2_BASE + 0x10)) -#define XSPI2_DCR4 (*(volatile uint32_t *)(XSPI2_BASE + 0x14)) -#define XSPI2_SR (*(volatile uint32_t *)(XSPI2_BASE + 0x20)) -#define XSPI2_FCR (*(volatile uint32_t *)(XSPI2_BASE + 0x24)) -#define XSPI2_DLR (*(volatile uint32_t *)(XSPI2_BASE + 0x40)) -#define XSPI2_AR (*(volatile uint32_t *)(XSPI2_BASE + 0x48)) -#define XSPI2_DR (*(volatile uint8_t *)(XSPI2_BASE + 0x50)) -#define XSPI2_DR32 (*(volatile uint32_t *)(XSPI2_BASE + 0x50)) -#define XSPI2_PSMKR (*(volatile uint32_t *)(XSPI2_BASE + 0x80)) -#define XSPI2_PSMAR (*(volatile uint32_t *)(XSPI2_BASE + 0x88)) -#define XSPI2_PIR (*(volatile uint32_t *)(XSPI2_BASE + 0x90)) -#define XSPI2_CCR (*(volatile uint32_t *)(XSPI2_BASE + 0x100)) -#define XSPI2_TCR (*(volatile uint32_t *)(XSPI2_BASE + 0x108)) -#define XSPI2_IR (*(volatile uint32_t *)(XSPI2_BASE + 0x110)) -#define XSPI2_ABR (*(volatile uint32_t *)(XSPI2_BASE + 0x120)) -#define XSPI2_LPTR (*(volatile uint32_t *)(XSPI2_BASE + 0x130)) -#define XSPI2_WCCR (*(volatile uint32_t *)(XSPI2_BASE + 0x180)) -#define XSPI2_WTCR (*(volatile uint32_t *)(XSPI2_BASE + 0x188)) -#define XSPI2_WIR (*(volatile uint32_t *)(XSPI2_BASE + 0x190)) -#define XSPI2_WABR (*(volatile uint32_t *)(XSPI2_BASE + 0x1A0)) - -/* XSPI CR fields */ -#define XSPI_CR_EN (1 << 0) -#define XSPI_CR_ABORT (1 << 1) -#define XSPI_CR_FSEL (1 << 7) -#define XSPI_CR_FTHRES_SHIFT (8) -#define XSPI_CR_FTHRES_MASK (0x3F << 8) -#define XSPI_CR_FTHRES(n) ((((n) - 1) & 0x3F) << 8) -#define XSPI_CR_TCIE (1 << 17) -#define XSPI_CR_FTIE (1 << 18) -#define XSPI_CR_FMODE_SHIFT (28) -#define XSPI_CR_FMODE_MASK (0x3 << 28) -#define XSPI_CR_FMODE(m) (((m) & 0x3) << 28) -#define XSPI_CR_FMODE_IWRITE XSPI_CR_FMODE(0) -#define XSPI_CR_FMODE_IREAD XSPI_CR_FMODE(1) -#define XSPI_CR_FMODE_AUTOPOLL XSPI_CR_FMODE(2) -#define XSPI_CR_FMODE_MMAP XSPI_CR_FMODE(3) - -/* XSPI DCR1 fields */ -#define XSPI_DCR1_CKMODE_3 (1 << 0) -#define XSPI_DCR1_FRCK (1 << 1) -#define XSPI_DCR1_DLYBYP (1 << 3) /* Bypass delay block (DLL) */ -#define XSPI_DCR1_CSHT_SHIFT (8) -#define XSPI_DCR1_CSHT_MASK (0x3F << 8) -#define XSPI_DCR1_CSHT(n) (((n) & 0x3F) << 8) -#define XSPI_DCR1_DEVSIZE_SHIFT (16) -#define XSPI_DCR1_DEVSIZE_MASK (0x1F << 16) -#define XSPI_DCR1_DEVSIZE(n) (((n) & 0x1F) << 16) -#define XSPI_DCR1_MTYP_SHIFT (24) -#define XSPI_DCR1_MTYP_MASK (0x7 << 24) -#define XSPI_DCR1_MTYP(n) (((n) & 0x7) << 24) - -/* XSPI DCR2 fields */ -#define XSPI_DCR2_PRESCALER_SHIFT (0) -#define XSPI_DCR2_PRESCALER_MASK (0xFF) -#define XSPI_DCR2_PRESCALER(n) (((n) - 1) & 0xFF) - -/* XSPI SR fields */ -#define XSPI_SR_TEF (1 << 0) -#define XSPI_SR_TCF (1 << 1) -#define XSPI_SR_FTF (1 << 2) -#define XSPI_SR_SMF (1 << 3) -#define XSPI_SR_BUSY (1 << 5) -#define XSPI_SR_FLEVEL_SHIFT (8) -#define XSPI_SR_FLEVEL_MASK (0x3F << 8) - -/* XSPI FCR fields */ -#define XSPI_FCR_CTEF (1 << 0) -#define XSPI_FCR_CTCF (1 << 1) -#define XSPI_FCR_CSMF (1 << 3) - -/* XSPI CCR fields (Communication Configuration Register) */ -#define XSPI_CCR_IMODE_SHIFT (0) -#define XSPI_CCR_IMODE_MASK (0x7) -#define XSPI_CCR_IMODE(n) (((n) & 0x7) << 0) -#define XSPI_CCR_ISIZE_SHIFT (4) -#define XSPI_CCR_ISIZE(n) (((n) & 0x3) << 4) -#define XSPI_CCR_ADMODE_SHIFT (8) -#define XSPI_CCR_ADMODE(n) (((n) & 0x7) << 8) -#define XSPI_CCR_ADSIZE_SHIFT (12) -#define XSPI_CCR_ADSIZE(n) (((n) & 0x3) << 12) -#define XSPI_CCR_ABMODE_SHIFT (16) -#define XSPI_CCR_ABMODE(n) (((n) & 0x7) << 16) -#define XSPI_CCR_ABSIZE_SHIFT (20) -#define XSPI_CCR_ABSIZE(n) (((n) & 0x3) << 20) -#define XSPI_CCR_DMODE_SHIFT (24) -#define XSPI_CCR_DMODE(n) (((n) & 0x7) << 24) -#define XSPI_CCR_DDTR (1 << 27) -#define XSPI_CCR_SIOO (1 << 31) - -/* XSPI TCR fields */ -#define XSPI_TCR_DCYC_SHIFT (0) -#define XSPI_TCR_DCYC_MASK (0x1F) -#define XSPI_TCR_DCYC(n) (((n) & 0x1F) << 0) -#define XSPI_TCR_DHQC (1 << 28) -#define XSPI_TCR_SSHIFT (1 << 30) - -/* SPI mode values: 0=none, 1=single, 2=dual, 3=quad, 4=octal */ -#define XSPI_MODE_NONE 0 -#define XSPI_MODE_SINGLE 1 -#define XSPI_MODE_DUAL 2 -#define XSPI_MODE_QUAD 3 -#define XSPI_MODE_OCTAL 4 +/*** OCTOSPI (XSPI2) — register definitions from hal/spi/spi_drv_stm32.h ***/ +/* Set base address before including shared OCTOSPI register macros. + * STM32N6 XSPI2 uses the same IP block as OCTOSPI (identical register layout). + */ +#define OCTOSPI_BASE (0x5802A000UL) +#define OCTOSPI_MEM_BASE (0x70000000UL) /* XSPI2 memory-mapped region */ + +#include "hal/spi/spi_drv_stm32.h" +/* OCTOSPI bits not defined in spi_drv_stm32.h */ +#define OCTOSPI_CR_FMODE_MMAP OCTOSPI_CR_FMODE(3) /* Memory-mapped mode */ +#define OCTOSPI_SR_TEF (1 << 0) /* Transfer Error Flag */ +#define OCTOSPI_FCR_CTEF (1 << 0) /* Clear Transfer Error Flag */ +#define OCTOSPI_FCR_CTCF (1 << 1) /* Clear Transfer Complete Flag */ +#define OCTOSPI_FCR_CSMF (1 << 3) /* Clear Status Match Flag */ +#define OCTOSPI_DCR1_DLYBYP (1 << 3) /* Bypass delay block (N6-specific) */ /*** XSPIM (XSPI I/O Manager) ***/ #define XSPIM_BASE (0x5802B400UL) #define XSPIM_CR (*(volatile uint32_t *)(XSPIM_BASE + 0x00)) /*** NOR Flash Commands (Macronix MX25UM51245G) ***/ -/* Single-SPI mode commands (initial boot) */ -#define NOR_CMD_WRITE_ENABLE 0x06 -#define NOR_CMD_WRITE_DISABLE 0x04 -#define NOR_CMD_READ_SR 0x05 -#define NOR_CMD_READ_ID 0x9F -#define NOR_CMD_FAST_READ_4B 0x0C -#define NOR_CMD_PAGE_PROG_4B 0x12 -#define NOR_CMD_SECTOR_ERASE_4B 0x21 -#define NOR_CMD_BLOCK_ERASE_4B 0xDC -#define NOR_CMD_RESET_ENABLE 0x66 -#define NOR_CMD_RESET_MEMORY 0x99 +/* Same commands as src/qspi_flash.c, using 4-byte address variants + * (0x0C/0x12/0x21) instead of entering 4-byte address mode. */ +#define WRITE_ENABLE_CMD 0x06U +#define READ_SR_CMD 0x05U +#define FAST_READ_4B_CMD 0x0CU +#define PAGE_PROG_4B_CMD 0x12U +#define SEC_ERASE_4B_CMD 0x21U /* 4KB sector erase, 4-byte addr */ +#define RESET_ENABLE_CMD 0x66U +#define RESET_MEMORY_CMD 0x99U /* NOR flash status register bits */ -#define NOR_SR_WIP (1 << 0) -#define NOR_SR_WEL (1 << 1) +#define FLASH_SR_BUSY (1 << 0) /* Write-in-progress */ +#define FLASH_SR_WRITE_EN (1 << 1) /* Write enable latch */ /* NOR flash geometry */ -#define NOR_PAGE_SIZE 256 -#define NOR_SECTOR_SIZE 0x1000 /* 4KB */ -#define NOR_BLOCK_SIZE 0x10000 /* 64KB */ -#define NOR_DEVICE_SIZE (64 * 1024 * 1024) /* 64MB */ -#define NOR_DEVICE_SIZE_LOG2 26 /* XSPI DEVSIZE: 2^26 = 64MB */ +#define FLASH_PAGE_SIZE 256 +#define FLASH_SECTOR_SIZE 0x1000 /* 4KB */ +#define FLASH_DEVICE_SIZE (64 * 1024 * 1024) /* 64MB */ +#define FLASH_DEVICE_SIZE_LOG2 26 /* DEVSIZE: 2^26 = 64MB */ -/*** USART1 (Debug UART) ***/ +/*** USART — parameterized by base address ***/ #define USART1_BASE (0x52001000UL) -#define USART1_CR1 (*(volatile uint32_t *)(USART1_BASE + 0x00)) -#define USART1_CR2 (*(volatile uint32_t *)(USART1_BASE + 0x04)) -#define USART1_CR3 (*(volatile uint32_t *)(USART1_BASE + 0x08)) -#define USART1_BRR (*(volatile uint32_t *)(USART1_BASE + 0x0C)) -#define USART1_ISR (*(volatile uint32_t *)(USART1_BASE + 0x1C)) -#define USART1_ICR (*(volatile uint32_t *)(USART1_BASE + 0x20)) -#define USART1_RDR (*(volatile uint32_t *)(USART1_BASE + 0x24)) -#define USART1_TDR (*(volatile uint32_t *)(USART1_BASE + 0x28)) +#define UART_CR1(base) (*(volatile uint32_t *)((base) + 0x00)) +#define UART_CR2(base) (*(volatile uint32_t *)((base) + 0x04)) +#define UART_CR3(base) (*(volatile uint32_t *)((base) + 0x08)) +#define UART_BRR(base) (*(volatile uint32_t *)((base) + 0x0C)) +#define UART_ISR(base) (*(volatile uint32_t *)((base) + 0x1C)) +#define UART_ICR(base) (*(volatile uint32_t *)((base) + 0x20)) +#define UART_RDR(base) (*(volatile uint32_t *)((base) + 0x24)) +#define UART_TDR(base) (*(volatile uint32_t *)((base) + 0x28)) #define USART_CR1_UE (1 << 0) #define USART_CR1_RE (1 << 2) diff --git a/hal/stm32n6.ld b/hal/stm32n6.ld index 0b8a9229b0..4447d41454 100644 --- a/hal/stm32n6.ld +++ b/hal/stm32n6.ld @@ -22,7 +22,7 @@ SECTIONS _end_text = .; } > FLASH - .edidx : + .ARM.exidx : { . = ALIGN(4); *(.ARM.exidx*) diff --git a/src/boot_arm.c b/src/boot_arm.c index 43a65babae..77b39fab33 100644 --- a/src/boot_arm.c +++ b/src/boot_arm.c @@ -440,7 +440,7 @@ void RAMFUNCTION do_boot(const uint32_t *app_offset) asm volatile("do_boot_r5:\n" " mov pc, r0\n"); -#elif defined(CORTEX_M33) /* Armv8 boot procedure */ +#elif defined(CORTEX_M33) || defined(CORTEX_M55) /* Armv8 boot procedure */ /* Get stack pointer, entry point */ app_end_stack = (*((uint32_t *)(app_offset))); diff --git a/test-app/app_stm32n6.c b/test-app/app_stm32n6.c index 1d46ff8367..c83df15082 100644 --- a/test-app/app_stm32n6.c +++ b/test-app/app_stm32n6.c @@ -83,10 +83,30 @@ static void led_off(uint32_t gpio_base, int pin) GPIO_BSRR(gpio_base) = (1 << pin); } -static void delay(volatile uint32_t count) +/* SysTick-based millisecond delay (polling, no interrupts). + * SysTick clocks from HCLK (CPU / AHB prescaler). + * HCLK = 300 MHz confirms PLL running at 600 MHz (1 Hz blink = correct). */ +#define SYSTICK_BASE (0xE000E010UL) +#define SYSTICK_CSR (*(volatile uint32_t *)(SYSTICK_BASE + 0x00)) +#define SYSTICK_RVR (*(volatile uint32_t *)(SYSTICK_BASE + 0x04)) +#define SYSTICK_CVR (*(volatile uint32_t *)(SYSTICK_BASE + 0x08)) +#define HCLK_FREQ 300000000UL /* 600 MHz CPU / AHB prescaler 2 */ +#define SYSTICK_COUNTFLAG (1 << 16) + +static void systick_init(void) { - while (count--) - ; + SYSTICK_RVR = (HCLK_FREQ / 1000) - 1; /* 1ms reload */ + SYSTICK_CVR = 0; + SYSTICK_CSR = 0x5; /* enable, processor clock, no interrupt */ +} + +static void delay_ms(uint32_t ms) +{ + while (ms > 0) { + while (!(SYSTICK_CSR & SYSTICK_COUNTFLAG)) + ; + ms--; + } } volatile uint32_t app_running __attribute__((section(".data"))) = 0; @@ -111,10 +131,23 @@ void main(void) /* Mark firmware stable (flash ops are RAMFUNCTION, safe from XIP) */ wolfBoot_success(); + /* Enable icache for XIP performance (hal_prepare_boot disables it) */ +#define SCB_CCR_REG (*(volatile uint32_t *)(0xE000ED14UL)) +#define SCB_ICIALLU_REG (*(volatile uint32_t *)(0xE000EF50UL)) + __asm__ volatile("dsb; isb"); + SCB_ICIALLU_REG = 0; + __asm__ volatile("dsb; isb"); + SCB_CCR_REG |= (1 << 17); /* IC bit */ + __asm__ volatile("dsb; isb"); + + systick_init(); + + /* Blink blue LED at 1 Hz (500ms on/off). + * Correct rate confirms CPU running at 600 MHz PLL. */ while (1) { led_on(GPIOG_BASE, LED_BLUE_PIN); - delay(2000000); + delay_ms(500); led_off(GPIOG_BASE, LED_BLUE_PIN); - delay(2000000); + delay_ms(500); } } diff --git a/tools/config.mk b/tools/config.mk index 034ac550c6..a33beb722a 100644 --- a/tools/config.mk +++ b/tools/config.mk @@ -20,6 +20,7 @@ ifeq ($(ARCH),) VTOR?=1 CORTEX_M0?=0 CORTEX_M33?=0 + CORTEX_M55?=0 CORTEX_M7?=0 CORTEX_M3?=0 NO_ASM?=0 @@ -93,7 +94,7 @@ endif CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO_DRIVERS \ MCUXPRESSO_CMSIS FREEDOM_E_SDK STM32CUBE CYPRESS_PDL CYPRESS_CORE_LIB CYPRESS_TARGET_LIB DEBUG VTOR \ - CORTEX_M0 CORTEX_M7 CORTEX_M33 NO_ASM EXT_FLASH SPI_FLASH NO_XIP UART_FLASH ALLOW_DOWNGRADE NVM_FLASH_WRITEONCE \ + CORTEX_M0 CORTEX_M7 CORTEX_M33 CORTEX_M55 NO_ASM EXT_FLASH SPI_FLASH NO_XIP UART_FLASH ALLOW_DOWNGRADE NVM_FLASH_WRITEONCE \ DISABLE_BACKUP WOLFBOOT_VERSION V NO_MPU ENCRYPT FLAGS_HOME FLAGS_INVERT \ SPMATH SPMATHALL RAM_CODE DUALBANK_SWAP IMAGE_HEADER_SIZE PKA TZEN PSOC6_CRYPTO \ WOLFTPM WOLFBOOT_TPM_VERIFY MEASURED_BOOT WOLFBOOT_TPM_SEAL WOLFBOOT_TPM_KEYSTORE \ From 896c2a78c97012d7abef1a8314cbb46d730a72c5 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 18 Mar 2026 12:45:55 -0700 Subject: [PATCH 3/4] STM32N6: Fixes and TrustZone support - Fix UART: remove static from uart_write, fix signature to match printf.h, correct PCLK2 clock frequency (200 MHz not 300 MHz) - Add SAU configuration: blanket NSC region for non-TZ, proper secure/non-secure SAU regions for TZEN=1 - Add PART_BOOT_EXT support: boot and update partitions share the same XSPI2 NOR flash, ext_flash_addr() translates absolute memory-mapped addresses to device-relative offsets - Buffer XIP data in nor_flash_write() before SPI commands - Move dcache_enable() after octospi_init() to prevent stale reads - Add TZ_SECURE() macro with conditional secure/non-secure peripheral base addresses in hal/stm32n6.h - Add TZEN=1 support: wolfBoot runs from secure SRAM (0x24000000), app boots into non-secure state, flash script auto-detects TZEN - Exclude STM32N6 from stm32_tz.o (uses its own SAU config) and from blxns boot path (CORTEX_M55 uses regular boot) - Enhanced test-app with UART output, partition info, version display, state handling, and auto-success for TESTING state - Add stm32n6-tz.config example and CI entries in test-configs.yml - Update Targets.md with TrustZone, SAU, PART_BOOT_EXT, and UART clock documentation - Add DEBUG_UART=1 and RAM_CODE=1 to stm32n6.config --- .github/workflows/test-configs.yml | 18 +++-- arch.mk | 10 ++- config/examples/stm32n6-tz.config | 24 ++++++ config/examples/stm32n6.config | 8 +- docs/Targets.md | 95 +++++++++++++++++++--- hal/stm32n6.c | 85 +++++++++++++++++--- hal/stm32n6.h | 54 +++++++++++-- src/boot_arm.c | 8 +- test-app/Makefile | 3 +- test-app/app_stm32n6.c | 124 ++++++++++++++++------------- tools/scripts/stm32n6_flash.sh | 25 +++++- 11 files changed, 357 insertions(+), 97 deletions(-) create mode 100644 config/examples/stm32n6-tz.config diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index 468aea5c48..9a9f0f1e72 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -443,6 +443,18 @@ jobs: arch: arm config-file: ./config/examples/stm32h5-dualbank.config + stm32n6_test: + uses: ./.github/workflows/test-build.yml + with: + arch: arm + config-file: ./config/examples/stm32n6.config + + stm32n6_tz_test: + uses: ./.github/workflows/test-build.yml + with: + arch: arm + config-file: ./config/examples/stm32n6-tz.config + stm32h5_tz_test: uses: ./.github/workflows/test-build.yml with: @@ -473,12 +485,6 @@ 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: diff --git a/arch.mk b/arch.mk index b47046d226..647da4f7f0 100644 --- a/arch.mk +++ b/arch.mk @@ -274,7 +274,11 @@ ifeq ($(ARCH),ARM) CORTEX_M55=1 CFLAGS+=-Ihal ARCH_FLASH_OFFSET=0x70000000 - WOLFBOOT_ORIGIN=0x34000000 + ifeq ($(TZEN),1) + WOLFBOOT_ORIGIN=0x24000000 + else + WOLFBOOT_ORIGIN=0x34000000 + endif EXT_FLASH=1 PART_UPDATE_EXT=1 PART_SWAP_EXT=1 @@ -375,7 +379,9 @@ else endif ifeq ($(TZEN),1) ifneq (,$(findstring stm32,$(TARGET))) - OBJS+=hal/stm32_tz.o + ifneq ($(TARGET),stm32n6) + OBJS+=hal/stm32_tz.o + endif endif CFLAGS+=-mcmse ifeq ($(WOLFCRYPT_TZ),1) diff --git a/config/examples/stm32n6-tz.config b/config/examples/stm32n6-tz.config new file mode 100644 index 0000000000..f98cd6d54a --- /dev/null +++ b/config/examples/stm32n6-tz.config @@ -0,0 +1,24 @@ +ARCH?=ARM +TARGET?=stm32n6 +TZEN?=1 +SIGN?=ECC256 +HASH?=SHA256 +DEBUG?=0 +DEBUG_UART?=1 +VTOR?=1 +NO_ASM?=0 +NO_MPU=1 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=0 +WOLFBOOT_VERSION?=1 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +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 +CFLAGS_EXTRA+=-DPART_BOOT_EXT diff --git a/config/examples/stm32n6.config b/config/examples/stm32n6.config index 0df3498a79..2643526dc8 100644 --- a/config/examples/stm32n6.config +++ b/config/examples/stm32n6.config @@ -3,6 +3,7 @@ TARGET?=stm32n6 SIGN?=ECC256 HASH?=SHA256 DEBUG?=0 +DEBUG_UART?=1 VTOR?=1 NO_ASM?=0 NO_MPU=1 @@ -12,10 +13,15 @@ NVM_FLASH_WRITEONCE?=0 WOLFBOOT_VERSION?=1 V?=0 SPMATH?=1 -RAM_CODE?=0 +RAM_CODE?=1 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 +# Boot partition is on the same XSPI2 NOR flash as update/swap. +# PART_BOOT_EXT ensures boot partition reads go through ext_flash API +# (SPI commands) instead of XIP, avoiding bus faults when XSPI2 toggles +# between memory-mapped and command mode during firmware updates. +CFLAGS_EXTRA+=-DPART_BOOT_EXT diff --git a/docs/Targets.md b/docs/Targets.md index daea3d8b9f..a6b870ef0c 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -1839,9 +1839,14 @@ XSPI2 NOR Flash (memory-mapped at 0x70000000): 0x70020000 Boot partition (1MB, app runs from here via XIP) 0x70120000 Update partition (1MB, device-relative: 0x00120000) -AXISRAM (0x34000000): +AXISRAM — without TrustZone (non-secure alias): 0x34000000 wolfBoot (loaded to SRAM via SWD or Boot ROM FSBL copy) 0x34020000 Stack / work area + +AXISRAM — with TrustZone (TZEN=1, secure alias): + 0x24000000 wolfBoot (secure SRAM — IDAU marks 0x24xxxxxx as secure) + 0x24020000 Stack / work area (secure) + 0x34000000 Application SRAM (SAU region: non-secure) ``` ### Build and Flash @@ -1849,14 +1854,20 @@ AXISRAM (0x34000000): Use the example configuration and build: ```sh +# Without TrustZone: cp config/examples/stm32n6.config .config make make flash + +# With TrustZone: +cp config/examples/stm32n6-tz.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 +2. Load wolfBoot to SRAM (0x24000000 for TZEN=1, 0x34000000 otherwise) 3. Start wolfBoot, which verifies and boots the application via XIP Prerequisites: @@ -1872,11 +1883,66 @@ 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) +- `PART_BOOT_EXT` — boot partition reads use ext_flash API during updates + (required because boot and update share the same XSPI2 NOR flash) +- `RAM_CODE=1` — flash functions placed in `.ramcode` for XIP safety +- `DEBUG_UART=1` — USART1 output for boot messages and test-app +- Boot partition at 0x70020000 (XIP for app execution) - Update/swap partitions use device-relative offsets - 4KB sector size (`WOLFBOOT_SECTOR_SIZE=0x1000`) - ECC256 + SHA256 for signature verification +### TrustZone Support (TZEN=1) + +The STM32N6 Cortex-M55 always boots in secure mode. Unlike older STM32 parts +(H5, L5, U5), there is no `TZEN` option byte — TrustZone is always available +via the hardware IDAU (Implementation-Defined Attribution Unit). + +wolfBoot supports two modes: + +**Without TrustZone** (`stm32n6.config`): wolfBoot runs from the non-secure SRAM +alias at `0x34000000`. A blanket SAU NSC region covers the entire address space, +allowing access to all peripherals and memory. The application boots in the same +(non-secure) state. + +**With TrustZone** (`stm32n6-tz.config`, `TZEN=1`): wolfBoot runs from the secure +SRAM alias at `0x24000000`. The SAU is configured with specific regions: + +| SAU Region | Address Range | Type | Purpose | +|------------|---------------|------|---------| +| 0 (NSC) | 0x24010000–0x2401FFFF | Non-Secure Callable | Gateway veneers | +| 1 (NS) | 0x70000000–0x7FFFFFFF | Non-Secure | XSPI2 flash (app XIP) | +| 2 (NS) | 0x34000000–0x343FFFFF | Non-Secure | App SRAM | +| 3 (NS) | 0x40000000–0x4FFFFFFF | Non-Secure | Peripheral NS aliases | +| *default* | *all other* | Secure | wolfBoot SRAM, secure peripherals | + +wolfBoot uses secure peripheral aliases (0x56xxx RCC, 0x52xxx USART, 0x58xxx XSPI2). +The application runs in non-secure state and uses non-secure aliases (0x46xxx, 0x42xxx). +The flash script automatically selects the correct SRAM load address based on the +`TZEN` setting in `.config`. + +### SAU Configuration (non-TrustZone) + +Without `TZEN=1`, wolfBoot configures SAU region 0 to cover the entire 4GB address +space as Non-Secure Callable (NSC), allowing the CPU to access all peripherals and +memory regions regardless of IDAU attribution. + +### Shared Flash: PART_BOOT_EXT + +The boot and update partitions reside on the same XSPI2 NOR flash. During +firmware updates, wolfBoot must read boot partition data while also issuing SPI +commands to write the update/swap partitions. Since the XSPI2 cannot be in +memory-mapped (XIP) and SPI command mode simultaneously, the boot partition +must be accessed via SPI commands during updates. + +The config sets `PART_BOOT_EXT` so all boot partition reads during the update +swap use `ext_flash_read()` (SPI commands) instead of XIP. The `ext_flash_*` +functions in `hal/stm32n6.c` accept both absolute memory-mapped addresses +(0x70xxxxxx) and device-relative offsets, converting automatically. + +The application still boots via XIP — `do_boot()` jumps to the memory-mapped +address at 0x70020400. + ### XIP Constraints Since the application executes directly from NOR flash via XSPI2 memory-mapped @@ -1885,9 +1951,19 @@ 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. +- `RAM_CODE=1` must be set so that flash write functions are tagged RAMFUNCTION + and placed in `.ramcode`. The test-app's startup code copies `.ramcode` to + RAM, allowing `wolfBoot_success()` and other flash operations to execute + from RAM while XSPI2 is in SPI command mode. +- The `nor_flash_write()` function buffers data to a stack-local array before + issuing SPI commands, since the source data pointer may reference XIP flash + that becomes inaccessible when XSPI2 leaves memory-mapped mode. + +### UART Clock + +USART1 kernel clock defaults to PCLK2. With the PLL1 configuration +(IC2 = 400 MHz, AHB prescaler /2, APB2 prescaler /1), PCLK2 = 200 MHz. +The BRR is calculated accordingly for 115200 baud. ### Flash Script Options @@ -1914,11 +1990,12 @@ and start it: ```sh reset halt -load_image wolfboot.bin 0x34000000 bin +# Use 0x24000000 for TZEN=1, 0x34000000 for non-TZ +load_image wolfboot.bin 0x24000000 bin reg msplim_s 0x00000000 reg psplim_s 0x00000000 -reg msp 0x34020000 -mww 0xE000ED08 0x34000000 +reg msp 0x24020000 +mww 0xE000ED08 0x24000000 resume ``` diff --git a/hal/stm32n6.c b/hal/stm32n6.c index 51ac5d7f86..3a9a071fe7 100644 --- a/hal/stm32n6.c +++ b/hal/stm32n6.c @@ -21,9 +21,12 @@ #include #include +#include #include #include "hal.h" #include "hal/stm32n6.h" +#include "printf.h" +#include "hal/armv8m_tz.h" /* OCTOSPI register definitions come from hal/spi/spi_drv_stm32.h (included * via stm32n6.h). STM32N6 XSPI2 uses the same IP block as OCTOSPI. @@ -419,9 +422,9 @@ static void clock_config(void) /* USART1 on PE5 (TX) / PE6 (RX), AF7 */ #define UART_BASE USART1_BASE -#define UART_CLOCK_FREQ 300000000UL /* HCLK/APB2 after PLL config */ +#define UART_CLOCK_FREQ 200000000UL /* PCLK2 = IC2(400MHz) / AHB(2) / APB2(1) */ -static void uart_init(uint32_t baud) +static void uart_init_baud(uint32_t baud) { uint32_t reg; @@ -454,9 +457,9 @@ static void uart_init(uint32_t baud) UART_CR1(UART_BASE) = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; } -static void uart_write(const char *buf, int len) +void uart_write(const char *buf, unsigned int len) { - int i; + unsigned int i; for (i = 0; i < len; i++) { while (!(UART_ISR(UART_BASE) & USART_ISR_TXE)) ; @@ -478,18 +481,64 @@ static void pwr_enable_io_supply(void) DMB(); } + void hal_init(void) { +#ifdef TZEN + /* TrustZone enabled: wolfBoot runs Secure, app runs Non-Secure. + * Configure SAU to define non-secure regions for the application. */ + { + SAU_CTRL = 0; + DSB(); + + /* Region 0: NSC - reserved for secure gateway veneers */ + sau_init_region(0, 0x24010000, 0x2401FFFF, 1); + + /* Region 1: NS - XSPI2 memory-mapped flash (app XIP + data) */ + sau_init_region(1, 0x70000000, 0x7FFFFFFF, 0); + + /* Region 2: NS - app SRAM (non-secure AXISRAM alias) */ + sau_init_region(2, 0x34000000, 0x343FFFFF, 0); + + /* Region 3: NS - peripheral non-secure aliases (for app) */ + sau_init_region(3, 0x40000000, 0x4FFFFFFF, 0); + + SAU_CTRL = SAU_INIT_CTRL_ENABLE; + DSB(); + ISB(); + + /* Enable SecureFault handler */ + SCB_SHCSR |= SCB_SHCSR_SECUREFAULT_EN; + } +#else + /* No TrustZone: blanket NSC region allows secure CPU to access all + * memory regardless of IDAU attribution. */ + { + SAU_CTRL = 0; + DSB(); + sau_init_region(0, 0x00000000, 0xFFFFFFE0, 1); /* full range, NSC */ + SAU_CTRL = SAU_INIT_CTRL_ENABLE; + DSB(); + ISB(); + } +#endif clock_config(); pwr_enable_io_supply(); icache_enable(); - dcache_enable(); octospi_gpio_init(); octospi_init(); + dcache_enable(); #ifdef DEBUG_UART - uart_init(115200); + uart_init_baud(115200); uart_write("wolfBoot Init\n", 14); + wolfBoot_printf("TrustZone: %s\n", + #if TZ_SECURE() + "Secure" + #else + "Off" + #endif + ); #endif } @@ -507,6 +556,10 @@ static int RAMFUNCTION nor_flash_write(uint32_t offset, const uint8_t *data, uint32_t page_off, write_sz; int remaining = len; int ret = 0; + /* Buffer for data that may reside in XIP flash (memory-mapped XSPI2). + * The source pointer becomes invalid once XSPI2 leaves mmap mode for + * the SPI page-program command, so we copy to RAM first. */ + uint8_t page_buf[FLASH_PAGE_SIZE]; if (len <= 0) return 0; @@ -517,10 +570,12 @@ static int RAMFUNCTION nor_flash_write(uint32_t offset, const uint8_t *data, if ((int)write_sz > remaining) write_sz = remaining; + memcpy(page_buf, data, write_sz); + octospi_write_enable(); ret = octospi_cmd(0, PAGE_PROG_4B_CMD, offset, SPI_MODE_SINGLE, - (uint8_t *)data, write_sz, SPI_MODE_SINGLE, 0); + page_buf, write_sz, SPI_MODE_SINGLE, 0); if (ret < 0) break; @@ -585,7 +640,15 @@ void RAMFUNCTION hal_flash_lock(void) { } -/* ext_flash API: device-relative offsets (update/swap partitions) */ +/* ext_flash API: accepts both device-relative offsets (update/swap) and + * absolute memory-mapped addresses (boot partition with PART_BOOT_EXT). */ + +static uint32_t RAMFUNCTION ext_flash_addr(uintptr_t address) +{ + if (address >= OCTOSPI_MEM_BASE) + return (uint32_t)(address - OCTOSPI_MEM_BASE); + return (uint32_t)address; +} int RAMFUNCTION ext_flash_read(uintptr_t address, uint8_t *data, int len) { @@ -595,7 +658,7 @@ int RAMFUNCTION ext_flash_read(uintptr_t address, uint8_t *data, int len) return 0; ret = octospi_cmd(1, FAST_READ_4B_CMD, - (uint32_t)address, SPI_MODE_SINGLE, + ext_flash_addr(address), SPI_MODE_SINGLE, data, len, SPI_MODE_SINGLE, 8); octospi_enable_mmap(); @@ -606,12 +669,12 @@ int RAMFUNCTION ext_flash_read(uintptr_t address, uint8_t *data, int len) int RAMFUNCTION ext_flash_write(uintptr_t address, const uint8_t *data, int len) { - return nor_flash_write((uint32_t)address, data, len); + return nor_flash_write(ext_flash_addr(address), data, len); } int RAMFUNCTION ext_flash_erase(uintptr_t address, int len) { - return nor_flash_erase((uint32_t)address, len); + return nor_flash_erase(ext_flash_addr(address), len); } void RAMFUNCTION ext_flash_lock(void) diff --git a/hal/stm32n6.h b/hal/stm32n6.h index 40923921b5..5a3c5e111f 100644 --- a/hal/stm32n6.h +++ b/hal/stm32n6.h @@ -33,8 +33,23 @@ #define DSB() __asm__ volatile ("dsb") #endif -/*** RCC (Reset and Clock Control) — base 0x56028000 (secure) ***/ -#define RCC_BASE (0x56028000UL) +/* TrustZone secure/non-secure detection. + * When compiled with -mcmse (TZEN=1) and not building the non-secure app, + * TZ_SECURE() is 1 and we use secure peripheral aliases (0x5xxxxxxx). + * Otherwise we use non-secure aliases (0x4xxxxxxx). */ +#if (defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) && \ + !defined(NONSECURE_APP)) +# define TZ_SECURE() (1) +#else +# define TZ_SECURE() (0) +#endif + +/*** RCC (Reset and Clock Control) ***/ +#if TZ_SECURE() +#define RCC_BASE (0x56028000UL) /* AHB4 secure */ +#else +#define RCC_BASE (0x46028000UL) /* AHB4 non-secure */ +#endif /* RCC_CR: control register (read), CSR: set register, CCR: clear register */ #define RCC_CR (*(volatile uint32_t *)(RCC_BASE + 0x000)) @@ -190,7 +205,11 @@ /*** PWR (Power Control) — base 0x56024800 (secure) ***/ -#define PWR_BASE (0x56024800UL) +#if TZ_SECURE() +#define PWR_BASE (0x56024800UL) /* secure */ +#else +#define PWR_BASE (0x46024800UL) /* non-secure */ +#endif #define PWR_CR1 (*(volatile uint32_t *)(PWR_BASE + 0x00)) #define PWR_CR2 (*(volatile uint32_t *)(PWR_BASE + 0x04)) @@ -219,6 +238,7 @@ /*** GPIO ***/ +#if TZ_SECURE() #define GPIOA_BASE (0x56020000UL) #define GPIOB_BASE (0x56020400UL) #define GPIOC_BASE (0x56020800UL) @@ -231,6 +251,20 @@ #define GPIOO_BASE (0x56023800UL) #define GPIOP_BASE (0x56023C00UL) #define GPIOQ_BASE (0x56024000UL) +#else +#define GPIOA_BASE (0x46020000UL) +#define GPIOB_BASE (0x46020400UL) +#define GPIOC_BASE (0x46020800UL) +#define GPIOD_BASE (0x46020C00UL) +#define GPIOE_BASE (0x46021000UL) +#define GPIOF_BASE (0x46021400UL) +#define GPIOG_BASE (0x46021800UL) +#define GPIOH_BASE (0x46021C00UL) +#define GPION_BASE (0x46023400UL) +#define GPIOO_BASE (0x46023800UL) +#define GPIOP_BASE (0x46023C00UL) +#define GPIOQ_BASE (0x46024000UL) +#endif /* GPIO register offsets — GPIO_ODR, GPIO_BSRR, GPIO_MODE_* come from * spi_drv_stm32.h. Define the rest that aren't in the shared header. */ @@ -253,7 +287,11 @@ /* Set base address before including shared OCTOSPI register macros. * STM32N6 XSPI2 uses the same IP block as OCTOSPI (identical register layout). */ -#define OCTOSPI_BASE (0x5802A000UL) +#if TZ_SECURE() +#define OCTOSPI_BASE (0x5802A000UL) /* AHB5 secure */ +#else +#define OCTOSPI_BASE (0x4802A000UL) /* AHB5 non-secure */ +#endif #define OCTOSPI_MEM_BASE (0x70000000UL) /* XSPI2 memory-mapped region */ #include "hal/spi/spi_drv_stm32.h" @@ -293,7 +331,11 @@ /*** USART — parameterized by base address ***/ -#define USART1_BASE (0x52001000UL) +#if TZ_SECURE() +#define USART1_BASE (0x52001000UL) /* APB2 secure */ +#else +#define USART1_BASE (0x42001000UL) /* APB2 non-secure */ +#endif #define UART_CR1(base) (*(volatile uint32_t *)((base) + 0x00)) #define UART_CR2(base) (*(volatile uint32_t *)((base) + 0x04)) @@ -329,6 +371,8 @@ #define SCB_DCCIMVAC (*(volatile uint32_t *)(0xE000EF70UL)) #define SCB_DCCISW (*(volatile uint32_t *)(0xE000EF74UL)) +/* SAU registers defined in hal/armv8m_tz.h */ + /* Cache size ID registers */ #define CCSIDR (*(volatile uint32_t *)(0xE000ED80UL)) #define CSSELR (*(volatile uint32_t *)(0xE000ED84UL)) diff --git a/src/boot_arm.c b/src/boot_arm.c index 77b39fab33..778878a523 100644 --- a/src/boot_arm.c +++ b/src/boot_arm.c @@ -419,9 +419,10 @@ void isr_empty(void) * */ -#ifdef TZEN +#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) && \ + defined(TZEN) && !defined(CORTEX_M55) #include "hal.h" -#define VTOR (*(volatile uint32_t *)(0xE002ED08)) +#define VTOR (*(volatile uint32_t *)(0xE002ED08)) /* Non-secure VTOR */ #else #define VTOR (*(volatile uint32_t *)(0xE000ED08)) #endif @@ -451,7 +452,8 @@ void RAMFUNCTION do_boot(const uint32_t *app_offset) /* Update IV */ VTOR = ((uint32_t)app_offset); asm volatile("msr msplim, %0" ::"r"(0)); -# if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) && defined(TZEN) +# if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) && \ + defined(TZEN) && !defined(CORTEX_M55) asm volatile("msr msp_ns, %0" ::"r"(app_end_stack)); /* Jump to non secure app_entry */ asm volatile("mov r7, %0" ::"r"(app_entry)); diff --git a/test-app/Makefile b/test-app/Makefile index f51bbefdb5..af64a4323d 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -616,7 +616,8 @@ ifeq ($(TARGET),s32k1xx) endif ifeq ($(TARGET),stm32n6) - CFLAGS+=-DRAM_CODE + CFLAGS+=-DRAM_CODE -DDEBUG_UART + APP_OBJS+=syscalls.o endif ifeq ($(TARGET),mcxw) diff --git a/test-app/app_stm32n6.c b/test-app/app_stm32n6.c index c83df15082..4c27a14514 100644 --- a/test-app/app_stm32n6.c +++ b/test-app/app_stm32n6.c @@ -21,35 +21,23 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ +#include #include #include "system.h" #include "hal.h" +#include "hal/stm32n6.h" #include "wolfboot/wolfboot.h" #include "target.h" -#define RCC_BASE (0x56028000UL) -#define RCC_AHB4ENR (*(volatile uint32_t *)(RCC_BASE + 0x25C)) -#define RCC_AHB4ENR_GPIOGEN (1 << 6) -#define RCC_AHB4ENR_PWREN (1 << 18) - -/* PWR I/O supply valid bits (required for Port G output) */ -#define PWR_BASE (0x56024800UL) -#define PWR_SVMCR3 (*(volatile uint32_t *)(PWR_BASE + 0x3C)) -#define PWR_SVMCR3_VDDIO2SV (1 << 8) -#define PWR_SVMCR3_VDDIO3SV (1 << 9) - -#define GPIO_MODER(base) (*(volatile uint32_t *)((base) + 0x00)) -#define GPIO_OSPEEDR(base) (*(volatile uint32_t *)((base) + 0x08)) -#define GPIO_PUPDR(base) (*(volatile uint32_t *)((base) + 0x0C)) -#define GPIO_BSRR(base) (*(volatile uint32_t *)((base) + 0x18)) - -#define GPIOG_BASE (0x56021800UL) - /* User LEDs: active LOW on Port G (LD6=PG0 green, LD7=PG8 blue, LD5=PG10 red) */ #define LED_GREEN_PIN 0 #define LED_BLUE_PIN 8 #define LED_RED_PIN 10 +/* CPU clock for SysTick delay */ +#define CPU_FREQ 600000000UL /* IC1 = PLL1(1200MHz) / 2 */ +#define SYSTICK_COUNTFLAG (1 << 16) + static void led_init(void) { uint32_t reg; @@ -57,7 +45,6 @@ static void led_init(void) RCC_AHB4ENR |= RCC_AHB4ENR_GPIOGEN | RCC_AHB4ENR_PWREN; DMB(); - /* Mark I/O supply valid for Port G */ PWR_SVMCR3 |= PWR_SVMCR3_VDDIO2SV | PWR_SVMCR3_VDDIO3SV; DMB(); @@ -72,10 +59,9 @@ static void led_init(void) GPIO_MODER(GPIOG_BASE) = reg; } -/* Active LOW: BSRR reset = ON, BSRR set = OFF */ static void led_on(uint32_t gpio_base, int pin) { - GPIO_BSRR(gpio_base) = (1 << (pin + 16)); + GPIO_BSRR(gpio_base) = (1 << (pin + 16)); /* active LOW */ } static void led_off(uint32_t gpio_base, int pin) @@ -83,19 +69,9 @@ static void led_off(uint32_t gpio_base, int pin) GPIO_BSRR(gpio_base) = (1 << pin); } -/* SysTick-based millisecond delay (polling, no interrupts). - * SysTick clocks from HCLK (CPU / AHB prescaler). - * HCLK = 300 MHz confirms PLL running at 600 MHz (1 Hz blink = correct). */ -#define SYSTICK_BASE (0xE000E010UL) -#define SYSTICK_CSR (*(volatile uint32_t *)(SYSTICK_BASE + 0x00)) -#define SYSTICK_RVR (*(volatile uint32_t *)(SYSTICK_BASE + 0x04)) -#define SYSTICK_CVR (*(volatile uint32_t *)(SYSTICK_BASE + 0x08)) -#define HCLK_FREQ 300000000UL /* 600 MHz CPU / AHB prescaler 2 */ -#define SYSTICK_COUNTFLAG (1 << 16) - static void systick_init(void) { - SYSTICK_RVR = (HCLK_FREQ / 1000) - 1; /* 1ms reload */ + SYSTICK_RVR = (CPU_FREQ / 1000) - 1; SYSTICK_CVR = 0; SYSTICK_CSR = 0x5; /* enable, processor clock, no interrupt */ } @@ -109,45 +85,81 @@ static void delay_ms(uint32_t ms) } } -volatile uint32_t app_running __attribute__((section(".data"))) = 0; +static const char* state_name(uint8_t state) +{ + switch (state) { + case IMG_STATE_NEW: return "NEW"; + case IMG_STATE_UPDATING: return "UPDATING"; + case IMG_STATE_TESTING: return "TESTING"; + case IMG_STATE_SUCCESS: return "SUCCESS"; + default: return "UNKNOWN"; + } +} -void main(void) +static void print_partition_info(void) { - /* hal_init() not called — XSPI2 already configured by wolfBoot for XIP */ - led_init(); + uint32_t boot_ver, update_ver; + uint8_t boot_state = 0, update_state = 0; + + boot_ver = wolfBoot_current_firmware_version(); + update_ver = wolfBoot_update_firmware_version(); + wolfBoot_get_partition_state(PART_BOOT, &boot_state); + wolfBoot_get_partition_state(PART_UPDATE, &update_state); + + printf("Partition Info\r\n"); + printf(" Boot: version %lu, state %s\r\n", + (unsigned long)boot_ver, state_name(boot_state)); + printf(" Update: version %lu, state %s\r\n", + (unsigned long)update_ver, state_name(update_state)); +} - app_running = 0xCAFEBEEF; - (void)wolfBoot_current_firmware_version(); +void main(void) +{ + uint32_t version; + uint8_t boot_state = 0; + int led_pin; + /* hal_init() not called -- XSPI2 already configured by wolfBoot for XIP */ + led_init(); led_on(GPIOG_BASE, LED_GREEN_PIN); - /* Test flash erase from XIP (RAMFUNCTION verification) */ - hal_flash_unlock(); - hal_flash_erase(0x70010000, 0x1000); - hal_flash_lock(); + version = wolfBoot_current_firmware_version(); - app_running = 0xF1A5F1A5; + printf("\r\n=== STM32N6 wolfBoot Test App ===\r\n"); + printf("Firmware Version: %lu\r\n", (unsigned long)version); + print_partition_info(); - /* Mark firmware stable (flash ops are RAMFUNCTION, safe from XIP) */ - wolfBoot_success(); + /* Auto-handle boot state */ + wolfBoot_get_partition_state(PART_BOOT, &boot_state); + if (boot_state == IMG_STATE_TESTING) { + printf("State TESTING -> marking success\r\n"); + wolfBoot_success(); + } else if (boot_state != IMG_STATE_SUCCESS) { + printf("Calling wolfBoot_success()\r\n"); + wolfBoot_success(); + } + printf("Boot OK (state: %s)\r\n", state_name(boot_state)); /* Enable icache for XIP performance (hal_prepare_boot disables it) */ -#define SCB_CCR_REG (*(volatile uint32_t *)(0xE000ED14UL)) -#define SCB_ICIALLU_REG (*(volatile uint32_t *)(0xE000EF50UL)) - __asm__ volatile("dsb; isb"); - SCB_ICIALLU_REG = 0; - __asm__ volatile("dsb; isb"); - SCB_CCR_REG |= (1 << 17); /* IC bit */ - __asm__ volatile("dsb; isb"); + DSB(); + ISB(); + SCB_ICIALLU = 0; + DSB(); + ISB(); + SCB_CCR |= SCB_CCR_IC; + DSB(); + ISB(); systick_init(); - /* Blink blue LED at 1 Hz (500ms on/off). - * Correct rate confirms CPU running at 600 MHz PLL. */ + /* Blink LED based on version: blue for v1, red for v>1 */ + printf("Blinking %s LED\r\n", (version > 1) ? "red" : "blue"); + + led_pin = (version > 1) ? LED_RED_PIN : LED_BLUE_PIN; while (1) { - led_on(GPIOG_BASE, LED_BLUE_PIN); + led_on(GPIOG_BASE, led_pin); delay_ms(500); - led_off(GPIOG_BASE, LED_BLUE_PIN); + led_off(GPIOG_BASE, led_pin); delay_ms(500); } } diff --git a/tools/scripts/stm32n6_flash.sh b/tools/scripts/stm32n6_flash.sh index 13976c70ea..2d719c5bc9 100755 --- a/tools/scripts/stm32n6_flash.sh +++ b/tools/scripts/stm32n6_flash.sh @@ -44,6 +44,16 @@ OPENOCD_CFG="${WOLFBOOT_ROOT}/config/openocd/openocd_stm32n6.cfg" BOOT_ADDR=0x70020000 UPDATE_ADDR=0x70120000 +# Determine SRAM load address from TZEN config +TZEN_CHECK=$(grep -E '^TZEN\?*=' "${WOLFBOOT_ROOT}/.config" 2>/dev/null | head -1 | sed 's/.*=//;s/[[:space:]]//g') +if [ "$TZEN_CHECK" = "1" ]; then + SRAM_ADDR=0x24000000 + SRAM_WORK=0x24020000 +else + SRAM_ADDR=0x34000000 + SRAM_WORK=0x34020000 +fi + check_tool() { command -v "$1" &>/dev/null || { echo -e "${RED}Error: $1 not found${NC}"; exit 1; } } @@ -99,8 +109,8 @@ sleep 1 OPENOCD_CMDS="reset init; " if [ $APP_ONLY -eq 0 ]; then - echo -e "${CYAN} wolfboot.bin -> SRAM 0x34000000${NC}" - OPENOCD_CMDS+="load_image ${WOLFBOOT_ROOT}/wolfboot.bin 0x34000000 bin; " + echo -e "${CYAN} wolfboot.bin -> SRAM ${SRAM_ADDR}${NC}" + OPENOCD_CMDS+="load_image ${WOLFBOOT_ROOT}/wolfboot.bin ${SRAM_ADDR} bin; " fi echo -e "${CYAN} image_v1_signed.bin -> NOR ${BOOT_ADDR}${NC}" @@ -109,6 +119,15 @@ OPENOCD_CMDS+="flash write_image erase ${WOLFBOOT_ROOT}/test-app/image_v1_signed if [ $TEST_UPDATE -eq 1 ]; then echo -e "${CYAN} image_v2_signed.bin -> NOR ${UPDATE_ADDR}${NC}" OPENOCD_CMDS+="flash write_image erase ${WOLFBOOT_ROOT}/test-app/image_v2_signed.bin ${UPDATE_ADDR}; " + + # Write update trigger: "pBOOT" (0x70 0x42 0x4F 0x4F 0x54) at end of + # update partition. wolfBoot reads this trailer to detect a pending update. + # Address: UPDATE_ADDR + PARTITION_SIZE - 5 + PART_SIZE=$(grep -E '^WOLFBOOT_PARTITION_SIZE' "${WOLFBOOT_ROOT}/.config" | head -1 | sed 's/.*=//;s/[[:space:]]//g') + TRIGGER_ADDR=$(printf "0x%08x" $(( ${UPDATE_ADDR} + ${PART_SIZE} - 5 ))) + echo -e "${CYAN} Update trigger -> NOR ${TRIGGER_ADDR}${NC}" + printf 'pBOOT' > /tmp/trigger_magic.bin + OPENOCD_CMDS+="flash write_image /tmp/trigger_magic.bin ${TRIGGER_ADDR}; " fi # Boot wolfBoot from SRAM (reset would clear SRAM, so we jump directly) @@ -121,7 +140,7 @@ if [ $APP_ONLY -eq 0 ]; then OPENOCD_CMDS+="reg msplim_s 0x00000000; " OPENOCD_CMDS+="reg psplim_s 0x00000000; " OPENOCD_CMDS+="reg msp ${INIT_SP}; " - OPENOCD_CMDS+="mww 0xE000ED08 0x34000000; " # VTOR + OPENOCD_CMDS+="mww 0xE000ED08 ${SRAM_ADDR}; " # VTOR OPENOCD_CMDS+="mww 0xE000ED28 0xFFFFFFFF; " # Clear CFSR OPENOCD_CMDS+="resume ${ENTRY_THUMB}; " fi From 6585a0b2c8ba43f6e9116a56116bc3db77624d52 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 18 Mar 2026 15:02:50 -0700 Subject: [PATCH 4/4] Fixes to support botting image on QSPI NOR flash from boot ROM (existing code was only loading to SRAM) --- arch.mk | 9 ++--- docs/Targets.md | 71 +++++++++++++++++++++++++++++----- tools/scripts/stm32n6_flash.sh | 33 +++++++++++----- 3 files changed, 88 insertions(+), 25 deletions(-) diff --git a/arch.mk b/arch.mk index 647da4f7f0..5a9ff80dfe 100644 --- a/arch.mk +++ b/arch.mk @@ -274,11 +274,10 @@ ifeq ($(ARCH),ARM) CORTEX_M55=1 CFLAGS+=-Ihal ARCH_FLASH_OFFSET=0x70000000 - ifeq ($(TZEN),1) - WOLFBOOT_ORIGIN=0x24000000 - else - WOLFBOOT_ORIGIN=0x34000000 - endif + # Boot ROM copies FSBL from NOR to AXISRAM2 at 0x34180400. + # Use the same address for both TZEN=0 and TZEN=1 since the Boot ROM + # always uses the non-secure alias. SAU is configured in hal_init(). + WOLFBOOT_ORIGIN=0x34180400 EXT_FLASH=1 PART_UPDATE_EXT=1 PART_SWAP_EXT=1 diff --git a/docs/Targets.md b/docs/Targets.md index a6b870ef0c..dbc5b25baf 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -1839,13 +1839,13 @@ XSPI2 NOR Flash (memory-mapped at 0x70000000): 0x70020000 Boot partition (1MB, app runs from here via XIP) 0x70120000 Update partition (1MB, device-relative: 0x00120000) -AXISRAM — without TrustZone (non-secure alias): - 0x34000000 wolfBoot (loaded to SRAM via SWD or Boot ROM FSBL copy) - 0x34020000 Stack / work area +AXISRAM2 — without TrustZone (non-secure alias): + 0x34180400 wolfBoot FSBL (Boot ROM copies from NOR to here) + 0x341A0400 Stack top (128KB above origin) -AXISRAM — with TrustZone (TZEN=1, secure alias): - 0x24000000 wolfBoot (secure SRAM — IDAU marks 0x24xxxxxx as secure) - 0x24020000 Stack / work area (secure) +AXISRAM2 — with TrustZone (TZEN=1, secure alias): + 0x24180400 wolfBoot FSBL (secure — IDAU marks 0x24xxxxxx as secure) + 0x241A0400 Stack top (secure) 0x34000000 Application SRAM (SAU region: non-secure) ``` @@ -1866,14 +1866,65 @@ 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 (0x24000000 for TZEN=1, 0x34000000 otherwise) -3. Start wolfBoot, which verifies and boots the application via XIP +1. Generate a Boot ROM FSBL header using `STM32_SigningTool_CLI` and program + `wolfboot-trusted.bin` to NOR flash at 0x70000000 (for autonomous boot on reset) +2. Load wolfBoot directly to SRAM for immediate execution +3. Program the signed application to NOR flash at 0x70020000 +4. Start wolfBoot, which verifies and boots the application via XIP + +The Boot ROM copies the FSBL from NOR flash to AXISRAM2 at `0x34180400`. +wolfBoot is linked at this address so it runs correctly after both +OpenOCD-initiated and reset-initiated boots. Prerequisites: -- OpenOCD 0.12+ with stm32n6x target support (build from source if needed) +- OpenOCD 0.12+ with stm32n6x target support (build from + [openocd-org/openocd](https://github.com/openocd-org/openocd) if needed) +- `STM32_SigningTool_CLI` (included with STM32CubeIDE) for Boot ROM FSBL header + generation. The flash script auto-detects the tool location. Without it, + wolfBoot is loaded to SRAM only (no autonomous boot on reset). - ST-Link connected to the Nucleo board - arm-none-eabi toolchain in PATH +- OTP fuse VDDIO3_HSLV programmed (see [OTP Configuration](#otp-configuration) + below) + +### OTP Configuration (One-Time) + +The STM32N6 Boot ROM requires OTP fuse configuration to boot from external +XSPI2 NOR flash. This is a **one-time permanent** operation. + +**OTP Word 124** (BSEC_HW_CONFIG) — set bit 15: + +| Bit | Name | Value | Description | +|-----|------|-------|-------------| +| 15 | VDDIO3_HSLV | 1 | Enable XSPI2 I/O high-speed low-voltage mode | + +**Using STM32CubeProgrammer (GUI):** + +1. Connect to the board via SWD (JP2/BOOT1 in position 2-3 for development mode) +2. Select **OTP** in the left menu +3. Find word 124 and set value to `0x00008000` +4. Click **Program** + +**Using STM32_Programmer_CLI:** + +```sh +STM32_Programmer_CLI -c port=SWD ap=1 \ + -el /OTP_FUSES_STM32N6xx.stldr \ + -otp write word=124 value=0x00008000 +``` + +### Boot Mode Switches (JP1/JP2) + +The NUCLEO-N657X0-Q has two boot mode jumpers: + +| Mode | JP1 (BOOT0) | JP2 (BOOT1) | Description | +|------|-------------|-------------|-------------| +| Development | don't care | 2-3 | Boot ROM waits for debugger (OpenOCD/SWD) | +| External flash | 1-2 | 1-2 | Boot ROM loads FSBL from XSPI2 NOR flash | + +For development, use JP2=2-3 to flash via OpenOCD. After flashing, switch +both JP1 and JP2 to position 1-2 and press reset for autonomous boot from +NOR flash. ### Build Options diff --git a/tools/scripts/stm32n6_flash.sh b/tools/scripts/stm32n6_flash.sh index 2d719c5bc9..3633b5ea36 100755 --- a/tools/scripts/stm32n6_flash.sh +++ b/tools/scripts/stm32n6_flash.sh @@ -44,15 +44,14 @@ OPENOCD_CFG="${WOLFBOOT_ROOT}/config/openocd/openocd_stm32n6.cfg" BOOT_ADDR=0x70020000 UPDATE_ADDR=0x70120000 -# Determine SRAM load address from TZEN config -TZEN_CHECK=$(grep -E '^TZEN\?*=' "${WOLFBOOT_ROOT}/.config" 2>/dev/null | head -1 | sed 's/.*=//;s/[[:space:]]//g') -if [ "$TZEN_CHECK" = "1" ]; then - SRAM_ADDR=0x24000000 - SRAM_WORK=0x24020000 -else - SRAM_ADDR=0x34000000 - SRAM_WORK=0x34020000 -fi +# Boot ROM always copies FSBL to AXISRAM2 at this address +SRAM_ADDR=0x34180400 + +# STM32_SigningTool_CLI for Boot ROM FSBL header (use latest version) +SIGN_TOOL="" +for d in $(ls -rd /opt/st/stm32cubeide_*/plugins/com.st.stm32cube.ide.mcu.externaltools.cubeprogrammer.*/tools/bin 2>/dev/null); do + [ -x "$d/STM32_SigningTool_CLI" ] && SIGN_TOOL="$d/STM32_SigningTool_CLI" && break +done check_tool() { command -v "$1" &>/dev/null || { echo -e "${RED}Error: $1 not found${NC}"; exit 1; } @@ -109,6 +108,20 @@ sleep 1 OPENOCD_CMDS="reset init; " if [ $APP_ONLY -eq 0 ]; then + # Generate Boot ROM FSBL header using STM32_SigningTool_CLI + if [ -n "$SIGN_TOOL" ]; then + TRUSTED_BIN="${WOLFBOOT_ROOT}/wolfboot-trusted.bin" + chmod u+w "${TRUSTED_BIN}" 2>/dev/null; rm -f "${TRUSTED_BIN}" + echo -e "${CYAN} Generating FSBL header (STM32_SigningTool_CLI)...${NC}" + "$SIGN_TOOL" -bin "${WOLFBOOT_ROOT}/wolfboot.bin" -nk -of 0x80000000 -t fsbl -hv 2.3 -align -la ${SRAM_ADDR} -ep ${SRAM_ADDR} -o "${TRUSTED_BIN}" || true + [ -f "${TRUSTED_BIN}" ] || { echo -e "${RED}Failed to generate trusted binary${NC}"; exit 1; } + echo -e "${CYAN} wolfboot-trusted.bin -> NOR 0x70000000${NC}" + OPENOCD_CMDS+="flash write_image erase ${TRUSTED_BIN} 0x70000000; " + else + echo -e "${YELLOW} STM32_SigningTool_CLI not found, loading wolfBoot to SRAM only${NC}" + fi + + # Also load wolfBoot directly to SRAM for immediate execution echo -e "${CYAN} wolfboot.bin -> SRAM ${SRAM_ADDR}${NC}" OPENOCD_CMDS+="load_image ${WOLFBOOT_ROOT}/wolfboot.bin ${SRAM_ADDR} bin; " fi @@ -130,7 +143,7 @@ if [ $TEST_UPDATE -eq 1 ]; then OPENOCD_CMDS+="flash write_image /tmp/trigger_magic.bin ${TRIGGER_ADDR}; " fi -# Boot wolfBoot from SRAM (reset would clear SRAM, so we jump directly) +# Boot wolfBoot from SRAM (also loaded to NOR for reset persistence) if [ $APP_ONLY -eq 0 ]; then # Extract initial SP (word 0) and entry point (word 1) from vector table INIT_SP=$(od -A n -t x4 -N 4 "${WOLFBOOT_ROOT}/wolfboot.bin" | awk '{print "0x"$1}')