From 1416f2f8888eacc647b5a064db7078b3fe976394 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 10 Mar 2026 17:48:20 -0700 Subject: [PATCH 1/2] 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 a84cde0ccc..3918460111 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -449,6 +449,12 @@ jobs: arch: arm config-file: ./config/examples/stm32h5-tz-dualbank-otp-lms.config + stm32n6_test: + uses: ./.github/workflows/test-build.yml + with: + arch: arm + config-file: ./config/examples/stm32n6.config + stm32h7_test: uses: ./.github/workflows/test-build.yml with: diff --git a/Makefile b/Makefile index b993abb6dd..e34f2415cf 100644 --- a/Makefile +++ b/Makefile @@ -269,6 +269,11 @@ ifeq ($(TARGET),sama5d3) MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin endif +ifeq ($(TARGET),stm32n6) + # wolfBoot runs from SRAM, app from XIP on external NOR - no contiguous factory.bin + MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin +endif + ifeq ($(TARGET),rp2350) MAIN_TARGET:=include/target.h keytools wolfboot_signing_private_key.der pico-sdk-info endif @@ -647,6 +652,12 @@ stack-usage: wolfboot.bin image-header-size: wolfboot.bin $(Q)echo $(IMAGE_HEADER_SIZE) > .image_header_size +## Target-specific flash targets +ifeq ($(TARGET),stm32n6) +flash: wolfboot.bin test-app/image_v1_signed.bin + $(Q)tools/scripts/stm32n6_flash.sh --skip-build +endif + cppcheck: cppcheck -f --enable=warning --enable=portability \ diff --git a/arch.mk b/arch.mk index 10f4c6a0a1..1d4e401f10 100644 --- a/arch.mk +++ b/arch.mk @@ -263,6 +263,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 @@ -345,9 +355,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 3217652c0d..82052f9eb0 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -41,6 +41,7 @@ This README describes configuration of supported targets. * [STM32F7](#stm32f7) * [STM32G0](#stm32g0) * [STM32H5](#stm32h5) +* [STM32N6](#stm32n6) * [STM32H7](#stm32h7) * [STM32L0](#stm32l0) * [STM32L4](#stm32l4) @@ -1613,6 +1614,123 @@ c ``` +## STM32N6 + +The STM32N6 (Cortex-M55) has no internal flash — all firmware resides on external +NOR flash (Macronix MX25UM51245G, 64MB) connected via XSPI2. The on-chip Boot ROM +copies the FSBL (First Stage Boot Loader) from external flash to internal SRAM and +jumps to it. wolfBoot serves as the FSBL, performing image verification and +chain-loading the application from external flash in XIP (Execute-In-Place) mode. + +Tested on: **NUCLEO-N657X0-Q** (STM32N657X0H, MB1940) + +### Memory Layout + +``` +XSPI2 NOR Flash (memory-mapped at 0x70000000): + 0x70000000 FSBL header area (128KB, future autonomous boot) + 0x70010000 Swap partition (64KB, device-relative: 0x00010000) + 0x70020000 Boot partition (1MB, app runs from here via XIP) + 0x70120000 Update partition (1MB, device-relative: 0x00120000) + +AXISRAM (0x34000000): + 0x34000000 wolfBoot (loaded to SRAM via SWD or Boot ROM FSBL copy) + 0x34020000 Stack / work area +``` + +### Build and Flash + +Use the example configuration and build: + +```sh +cp config/examples/stm32n6.config .config +make +make flash +``` + +`make flash` uses OpenOCD with the stmqspi driver to: +1. Program the signed application to NOR flash at 0x70020000 +2. Load wolfBoot to SRAM at 0x34000000 +3. Start wolfBoot, which verifies and boots the application via XIP + +Prerequisites: +- OpenOCD 0.12+ with stm32n6x target support (build from source if needed) +- ST-Link connected to the Nucleo board +- arm-none-eabi toolchain in PATH + +### Build Options + +```sh +make TARGET=stm32n6 SIGN=ECC256 +``` + +The example config uses: +- `EXT_FLASH=1` with `PART_UPDATE_EXT=1` and `PART_SWAP_EXT=1` +- Boot partition at 0x70020000 (XIP, not marked EXT) +- Update/swap partitions use device-relative offsets +- 4KB sector size (`WOLFBOOT_SECTOR_SIZE=0x1000`) +- ECC256 + SHA256 for signature verification + +### XIP Constraints + +Since the application executes directly from NOR flash via XSPI2 memory-mapped +mode, the following constraints apply: + +- The application must NOT call `hal_init()` — XSPI2 is already configured by + wolfBoot for memory-mapped mode. Reinitializing XSPI2 would disable XIP and + crash the CPU. +- Calling `wolfBoot_success()` requires all flash write functions to be placed + in RAM (RAMFUNCTION). The HAL flash functions in `hal/stm32n6.c` need the + RAMFUNCTION attribute for this to work from an XIP application. + +### Flash Script Options + +The flash script supports several modes: + +```sh +./tools/scripts/stm32n6_flash.sh # Build and flash all +./tools/scripts/stm32n6_flash.sh --skip-build # Flash only (existing binaries) +./tools/scripts/stm32n6_flash.sh --app-only # Flash signed app only +./tools/scripts/stm32n6_flash.sh --test-update # Flash v1 boot + v2 update +./tools/scripts/stm32n6_flash.sh --halt # Leave OpenOCD running +``` + +### Debugging + +OpenOCD: + +```sh +openocd -f config/openocd/openocd_stm32n6.cfg +``` + +After OpenOCD starts, connect via telnet (port 4444). To manually load wolfBoot +and start it: + +```sh +reset halt +load_image wolfboot.bin 0x34000000 bin +reg msplim_s 0x00000000 +reg psplim_s 0x00000000 +reg msp 0x34020000 +mww 0xE000ED08 0x34000000 +resume +``` + +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 514f00cf4c..da78940f71 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -364,6 +364,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 @@ -530,6 +538,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 41008057003c032c527017bf27a7fd3055ba8a94 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 13 Mar 2026 16:21:56 -0700 Subject: [PATCH 2/2] 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 +- docs/release-stm32n6.md | 54 +++++ 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 +- 8 files changed, 407 insertions(+), 351 deletions(-) create mode 100644 docs/release-stm32n6.md diff --git a/arch.mk b/arch.mk index 1d4e401f10..c351906b65 100644 --- a/arch.mk +++ b/arch.mk @@ -357,7 +357,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/docs/release-stm32n6.md b/docs/release-stm32n6.md new file mode 100644 index 0000000000..3740324973 --- /dev/null +++ b/docs/release-stm32n6.md @@ -0,0 +1,54 @@ +# wolfBoot Secure Boot on the STM32N6 + +wolfSSL is announcing wolfBoot support for the STM32N6 series, starting with the +NUCLEO-N657X0-Q development board (STM32N657X0H). The STM32N6 is ST's first +Cortex-M55 microcontroller, designed for high-performance edge AI workloads with +a dedicated Neural Processing Unit (NPU). wolfBoot provides cryptographic +signature verification and secure firmware updates on this new platform. + +## About the STM32N6 + +The STM32N6 is built around the Arm Cortex-M55 core capable of running at up to +800 MHz, targeting edge AI applications such as vision, audio classification, +and anomaly detection. wolfBoot configures the CPU at 600 MHz (PLL1, Voltage +Scale 1) by default. Unlike most STM32 parts, the N6 has no internal flash — +all firmware resides on external NOR flash connected via the high-speed XSPI2 +interface, with over 4.2MB of on-chip SRAM available for code execution and +data. + +This flash-less architecture makes the STM32N6 a great fit for applications +that need large, updateable firmware images without being constrained by +internal flash size, while still benefiting from the performance of a modern +Cortex-M55 core with Helium vector extensions. + +## wolfBoot on the STM32N6 + +wolfBoot serves as the First Stage Boot Loader (FSBL) on the STM32N6. The +on-chip Boot ROM copies wolfBoot from external NOR flash into SRAM and executes +it. wolfBoot then verifies the application image signature and boots the +application, which runs directly from external flash via Execute-In-Place (XIP) +memory-mapped mode. + +The wolfBoot binary comes in at approximately 22KB, well within the 128KB FSBL +limit. The entire port is self-contained with no external dependencies — no +STM32Cube HAL, no CMSIS headers, no RTOS required. + +Signature verification uses ECC256 with SHA-256, and the port supports A/B +firmware updates with swap-based rollback, all operating on the external NOR +flash. + +## Tested Hardware + +- **Board**: NUCLEO-N657X0-Q (STM32N657X0H, MB1940) +- **Flash**: Macronix MX25UM51245G, 64MB NOR on XSPI2 +- **Build + Flash**: `make && make flash` using OpenOCD with ST-Link + +## Resources + +- [wolfBoot STM32N6 documentation](https://github.com/wolfSSL/wolfBoot/blob/master/docs/Targets.md#stm32n6) +- [wolfBoot GitHub repository](https://github.com/wolfSSL/wolfBoot) +- [NUCLEO-N657X0-Q product page](https://www.st.com/en/evaluation-tools/nucleo-n657x0-q.html) +- [wolfBoot video series with ST](https://www.wolfssl.com/st-wolfboot-video-series/) + +If you have any questions or run into any issues, contact us at +facts@wolfssl.com, or call us at +1 425 245 8247. 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 \