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..c351906b65 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 -DCORTEX_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/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 new file mode 100644 index 0000000000..51ac5d7f86 --- /dev/null +++ b/hal/stm32n6.c @@ -0,0 +1,623 @@ +/* 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" + +/* 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 + +/* 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) +{ + uint32_t ccr; + + /* Abort memory-mapped mode if active */ + if ((OCTOSPI_CR & OCTOSPI_CR_FMODE_MASK) == OCTOSPI_CR_FMODE_MMAP) { + OCTOSPI_CR |= OCTOSPI_CR_ABORT; + while (OCTOSPI_CR & OCTOSPI_CR_ABORT) + ; + } + while (OCTOSPI_SR & OCTOSPI_SR_BUSY) + ; + OCTOSPI_FCR = OCTOSPI_FCR_CTCF | OCTOSPI_FCR_CTEF | OCTOSPI_FCR_CSMF; + + OCTOSPI_CR = (OCTOSPI_CR & ~OCTOSPI_CR_FMODE_MASK) | + OCTOSPI_CR_FMODE(fmode); + + if (dataSz > 0) { + OCTOSPI_DLR = dataSz - 1; + } + + 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 != SPI_MODE_NONE) { + ccr |= OCTOSPI_CCR_DMODE(dataMode); + } + OCTOSPI_CCR = ccr; + OCTOSPI_TCR = OCTOSPI_TCR_DCYC(dummyCycles); + OCTOSPI_IR = cmd; + + if (addrMode != SPI_MODE_NONE) { + OCTOSPI_AR = addr; + } + + if (dataSz > 0 && data != NULL) { + while (dataSz >= 4) { + if (fmode == 0) { + while (!(OCTOSPI_SR & (OCTOSPI_SR_FTF | OCTOSPI_SR_TEF))) + ; + if (OCTOSPI_SR & OCTOSPI_SR_TEF) goto octospi_err; + OCTOSPI_DR32 = *(uint32_t *)data; + } else { + while (!(OCTOSPI_SR & (OCTOSPI_SR_FTF | OCTOSPI_SR_TCF | + OCTOSPI_SR_TEF))) + ; + 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 (!(OCTOSPI_SR & (OCTOSPI_SR_FTF | OCTOSPI_SR_TEF))) + ; + if (OCTOSPI_SR & OCTOSPI_SR_TEF) goto octospi_err; + OCTOSPI_DR = *data; + } else { + while (!(OCTOSPI_SR & (OCTOSPI_SR_FTF | OCTOSPI_SR_TCF | + OCTOSPI_SR_TEF))) + ; + if (OCTOSPI_SR & OCTOSPI_SR_TEF) goto octospi_err; + *data = OCTOSPI_DR; + } + data++; + dataSz--; + } + } + + while (!(OCTOSPI_SR & (OCTOSPI_SR_TCF | OCTOSPI_SR_TEF))) + ; + if (OCTOSPI_SR & OCTOSPI_SR_TEF) goto octospi_err; + OCTOSPI_FCR = OCTOSPI_FCR_CTCF; + + return 0; + +octospi_err: + OCTOSPI_FCR = OCTOSPI_FCR_CTEF; + OCTOSPI_CR |= OCTOSPI_CR_ABORT; + while (OCTOSPI_CR & OCTOSPI_CR_ABORT) + ; + return -1; +} + +static void RAMFUNCTION octospi_write_enable(void) +{ + octospi_cmd(0, WRITE_ENABLE_CMD, 0, SPI_MODE_NONE, + NULL, 0, SPI_MODE_NONE, 0); +} + +static void RAMFUNCTION octospi_wait_ready(void) +{ + uint8_t sr; + do { + sr = 0; + octospi_cmd(1, READ_SR_CMD, 0, SPI_MODE_NONE, + &sr, 1, SPI_MODE_SINGLE, 0); + } while (sr & FLASH_SR_BUSY); +} + +static void RAMFUNCTION octospi_enable_mmap(void) +{ + /* Abort first if already in mmap mode (BUSY stays set in mmap) */ + if ((OCTOSPI_CR & OCTOSPI_CR_FMODE_MASK) == OCTOSPI_CR_FMODE_MMAP) { + OCTOSPI_CR |= OCTOSPI_CR_ABORT; + while (OCTOSPI_CR & OCTOSPI_CR_ABORT) + ; + } + while (OCTOSPI_SR & OCTOSPI_SR_BUSY) + ; + OCTOSPI_FCR = OCTOSPI_FCR_CTCF | OCTOSPI_FCR_CTEF | OCTOSPI_FCR_CSMF; + + OCTOSPI_CR = (OCTOSPI_CR & ~OCTOSPI_CR_FMODE_MASK) | + OCTOSPI_CR_FMODE_MMAP; + + /* Fast read: single SPI, 4-byte addr, 8 dummy cycles */ + 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) +{ + 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(); +} + +/* OCTOSPI GPIO: PN0-PN11 as AF9 (DQS, CLK, NCS, IO0-IO7) */ +static void octospi_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 octospi_init(void) +{ + volatile uint32_t delay; + + RCC_AHB5ENR |= RCC_AHB5ENR_XSPI2EN | RCC_AHB5ENR_XSPIMEN; + RCC_MISCENR |= RCC_MISCENR_XSPIPHYCOMPEN; + DMB(); + + OCTOSPI_CR = 0; + while (OCTOSPI_SR & OCTOSPI_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) + ; + + OCTOSPI_CR = OCTOSPI_CR_FTHRES(1) | OCTOSPI_CR_EN; + + /* NOR flash software reset */ + 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++) + ; + + 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) +{ + 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_BASE USART1_BASE +#define UART_CLOCK_FREQ 300000000UL /* HCLK/APB2 after PLL config */ + +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 */ + 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 (!(UART_ISR(UART_BASE) & USART_ISR_TXE)) + ; + UART_TDR(UART_BASE) = buf[i]; + } + while (!(UART_ISR(UART_BASE) & USART_ISR_TC)) + ; +} +#endif + +/* Mark VDDIO supplies valid (required for OCTOSPI 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(); + octospi_gpio_init(); + octospi_init(); + +#ifdef DEBUG_UART + uart_init(115200); + uart_write("wolfBoot Init\n", 14); +#endif +} + +void hal_prepare_boot(void) +{ + octospi_enable_mmap(); + dcache_disable(); + icache_disable(); +} + +/* 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 page_off, write_sz; + int remaining = len; + int ret = 0; + + if (len <= 0) + return 0; + + 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; + + 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; + + octospi_wait_ready(); + + offset += write_sz; + data += write_sz; + remaining -= write_sz; + } + + octospi_enable_mmap(); + return ret; +} + +static int RAMFUNCTION nor_flash_erase(uint32_t offset, int len) +{ + uint32_t end; + int ret = 0; + + if (len <= 0) + return -1; + + end = offset + len; + + while (offset < end) { + 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; + + octospi_wait_ready(); + offset += FLASH_SECTOR_SIZE; + } + + 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; +} + +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 = octospi_cmd(1, FAST_READ_4B_CMD, + (uint32_t)address, SPI_MODE_SINGLE, + data, len, SPI_MODE_SINGLE, 8); + + octospi_enable_mmap(); + + return (ret < 0) ? ret : len; +} + +int RAMFUNCTION ext_flash_write(uintptr_t address, const uint8_t *data, + int len) +{ + return nor_flash_write((uint32_t)address, data, len); +} + +int RAMFUNCTION ext_flash_erase(uintptr_t address, int len) +{ + return nor_flash_erase((uint32_t)address, len); +} + +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..40923921b5 --- /dev/null +++ b/hal/stm32n6.h @@ -0,0 +1,357 @@ +/* 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 (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) +#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: 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 (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)) +#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_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)) +#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 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) +#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: 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 */ +#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 — 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_AFRL(base) (*(volatile uint32_t *)((base) + 0x20)) +#define GPIO_AFRH(base) (*(volatile uint32_t *)((base) + 0x24)) + +/* GPIO speed values */ +#define GPIO_SPEED_LOW 0x0 +#define GPIO_SPEED_MEDIUM 0x1 +#define GPIO_SPEED_HIGH 0x2 +#define GPIO_SPEED_VERY_HIGH 0x3 + + +/*** 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) ***/ +/* 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 FLASH_SR_BUSY (1 << 0) /* Write-in-progress */ +#define FLASH_SR_WRITE_EN (1 << 1) /* Write enable latch */ + +/* NOR flash geometry */ +#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 */ + + +/*** USART — parameterized by base address ***/ +#define USART1_BASE (0x52001000UL) + +#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) +#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..4447d41454 --- /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 + + .ARM.exidx : + { + . = 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/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/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..c83df15082 --- /dev/null +++ b/test-app/app_stm32n6.c @@ -0,0 +1,153 @@ +/* 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); +} + +/* 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_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; + +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(); + + /* 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_ms(500); + led_off(GPIOG_BASE, LED_BLUE_PIN); + 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 \ 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}"