From 0f06efba1484d3d1de5494c68bdbc5594ac3a03b Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 12 May 2026 09:49:29 -0700 Subject: [PATCH] Add wolfBoot support for STM32G4 --- .github/workflows/test-configs.yml | 6 + arch.mk | 4 + config/examples/stm32g4.config | 24 +++ docs/Targets.md | 74 ++++++++ hal/stm32g4.c | 284 +++++++++++++++++++++++++++++ hal/stm32g4.h | 177 ++++++++++++++++++ hal/stm32g4.ld | 50 +++++ test-app/Makefile | 17 ++ test-app/app_stm32g4.c | 94 ++++++++++ test-app/led.c | 25 +++ 10 files changed, 755 insertions(+) create mode 100644 config/examples/stm32g4.config create mode 100644 hal/stm32g4.c create mode 100644 hal/stm32g4.h create mode 100644 hal/stm32g4.ld create mode 100644 test-app/app_stm32g4.c diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index 4bf84a8555..804fe9e926 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -444,6 +444,12 @@ jobs: arch: arm config-file: ./config/examples/stm32g0.config + stm32g4_test: + uses: ./.github/workflows/test-build.yml + with: + arch: arm + config-file: ./config/examples/stm32g4.config + stm32h5_test: uses: ./.github/workflows/test-build.yml with: diff --git a/arch.mk b/arch.mk index c53bfb52ab..3af2bf55d6 100644 --- a/arch.mk +++ b/arch.mk @@ -167,6 +167,10 @@ ifeq ($(ARCH),ARM) ARCH_FLASH_OFFSET=0x08000000 endif + ifeq ($(TARGET),stm32g4) + ARCH_FLASH_OFFSET=0x08000000 + endif + ifeq ($(TARGET),stm32f1) CORTEX_M3=1 NO_ARM_ASM=1 diff --git a/config/examples/stm32g4.config b/config/examples/stm32g4.config new file mode 100644 index 0000000000..42a32ce3d3 --- /dev/null +++ b/config/examples/stm32g4.config @@ -0,0 +1,24 @@ +ARCH?=ARM +TARGET?=stm32g4 +SIGN?=ECC256 +HASH?=SHA256 +DEBUG?=0 +VTOR?=1 +NO_ASM?=0 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=1 +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 + +# Single-bank 512KB flash, 2KB pages. +# Layout: 32K boot, 232K app, 232K update, 16K swap. +WOLFBOOT_SECTOR_SIZE?=0x800 +WOLFBOOT_PARTITION_SIZE?=0x3A000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08008000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x08042000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x0807C000 diff --git a/docs/Targets.md b/docs/Targets.md index 5eadc8763a..86678e8005 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -45,6 +45,7 @@ This README describes configuration of supported targets. * [STM32F4](#stm32f4) * [STM32F7](#stm32f7) * [STM32G0](#stm32g0) +* [STM32G4](#stm32g4) * [STM32H5](#stm32h5) * [STM32H7](#stm32h7) * [STM32L0](#stm32l0) @@ -527,6 +528,79 @@ add-symbol-file test-app/image.elf 0x08008100 mon reset init ``` +## STM32G4 + +Supports STM32G4 single-bank Category 3 parts (verified on NUCLEO-G491RE, +STM32G491RET6: 512KB flash, 96KB SRAM, Cortex-M4F). + +The HAL boots at 170 MHz using HSI16 + PLL with PWR Range 1 Boost mode +(RM0440 6.1.4), 4 flash wait states, prefetch + I/D-cache enabled. + +Example 512KB partitioning on STM32G491RE: + +- Sector size: 2KB +- wolfBoot partition size: 32KB +- Application partition size: 232KB + +```C +#define WOLFBOOT_SECTOR_SIZE 0x800 /* 2 KB */ +#define WOLFBOOT_PARTITION_BOOT_ADDRESS 0x08008000 +#define WOLFBOOT_PARTITION_SIZE 0x3A000 /* 232 KB */ +#define WOLFBOOT_PARTITION_UPDATE_ADDRESS 0x08042000 +#define WOLFBOOT_PARTITION_SWAP_ADDRESS 0x0807C000 /* 16 KB swap */ +``` + +### Building STM32G4 + +Reference configuration (see [/config/examples/stm32g4.config](/config/examples/stm32g4.config)). +You can copy this to wolfBoot root as `.config`: `cp ./config/examples/stm32g4.config .config`. +To build you can use `make`. + +The TARGET for this is `stm32g4`: `make TARGET=stm32g4`. +Cortex-M4F is the default ARM core for this target. +The option `NVM_FLASH_WRITEONCE=1` is mandatory: the IAP driver writes one +64-bit doubleword per location and cannot rewrite without an erase first. +The default signing scheme is ECC256 with SHA256. + +NUCLEO-G491RE has no HSE, so HSI16 is used as the PLL source. The ST-LINK +virtual COM port is wired to LPUART1 on PA2 (TX) / PA3 (RX) via AF12. +Optional boot logs over LPUART1 at 115200 8N1 are enabled with +`CFLAGS_EXTRA+=-DDEBUG_UART`. + +This target is single-bank only -- keep `DUALBANK_SWAP=0`. + +### STM32G4 Programming + +Compile requirements: `make TARGET=stm32g4 NVM_FLASH_WRITEONCE=1` + +The output is a single `factory.bin` that includes `wolfboot.bin` and +`test-app/image_v1_signed.bin` combined together. This should be programmed +to the flash start address `0x08000000`. + +Flash using the STM32CubeProgrammer CLI: + +``` +STM32_Programmer_CLI -c port=swd -d factory.bin 0x08000000 +``` + +### STM32G4 Debugging + +Use `make DEBUG=1` and program firmware again. + +Start GDB server on port 3333: + +``` +ST-LINK_gdbserver -d -e -r 1 -p 3333 +OR +st-util -p 3333 +``` + +``` +arm-none-eabi-gdb +add-symbol-file test-app/image.elf 0x08008100 +mon reset init +``` + ## STM32C0 Supports STM32C0x0/STM32C0x1. Instructions are for the STM Nucleo-C031C6 dev board. diff --git a/hal/stm32g4.c b/hal/stm32g4.c new file mode 100644 index 0000000000..57744eaf59 --- /dev/null +++ b/hal/stm32g4.c @@ -0,0 +1,284 @@ +/* stm32g4.c + * + * Copyright (C) 2026 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 "stm32g4.h" + +#ifndef NVM_FLASH_WRITEONCE +# error "wolfBoot STM32G4 HAL: no WRITEONCE support detected. Please define NVM_FLASH_WRITEONCE" +#endif + + +static RAMFUNCTION void flash_wait_complete(void) +{ + while ((FLASH_SR & FLASH_SR_BSY) == FLASH_SR_BSY) + ; +} + +static void RAMFUNCTION flash_clear_errors(void) +{ + FLASH_SR |= (FLASH_SR_OPERR | FLASH_SR_PROGERR | FLASH_SR_WRPERR | + FLASH_SR_PGAERR | FLASH_SR_SIZERR | FLASH_SR_PGSERR | + FLASH_SR_MISERR | FLASH_SR_FASTERR | FLASH_SR_RDERR | + FLASH_SR_OPTVERR); +} + +int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) +{ + int i = 0; + uint32_t *src, *dst; + flash_clear_errors(); + FLASH_CR |= FLASH_CR_PG; + + while (i < len) { + flash_clear_errors(); + if ((len - i > 3) && ((((address + i) & 0x07) == 0) && + ((((uint32_t)data) + i) & 0x07) == 0)) { + src = (uint32_t *)data; + dst = (uint32_t *)address; + flash_wait_complete(); + dst[i >> 2] = src[i >> 2]; + dst[(i >> 2) + 1] = src[(i >> 2) + 1]; + flash_wait_complete(); + i += 8; + } else { + uint32_t val[2]; + uint8_t *vbytes = (uint8_t *)(val); + int off = (address + i) - (((address + i) >> 3) << 3); + uint32_t base_addr = address & (~0x07); /* aligned to 64 bit */ + int u32_idx = (i >> 2); + dst = (uint32_t *)(base_addr); + val[0] = dst[u32_idx]; + val[1] = dst[u32_idx + 1]; + while ((off < 8) && (i < len)) + vbytes[off++] = data[i++]; + dst[u32_idx] = val[0]; + dst[u32_idx + 1] = val[1]; + flash_wait_complete(); + } + } + if ((FLASH_SR & FLASH_SR_EOP) == FLASH_SR_EOP) + FLASH_SR |= FLASH_SR_EOP; + FLASH_CR &= ~FLASH_CR_PG; + return 0; +} + +void RAMFUNCTION hal_flash_unlock(void) +{ + flash_wait_complete(); + if ((FLASH_CR & FLASH_CR_LOCK) != 0) { + FLASH_KEY = FLASH_KEY1; + DMB(); + FLASH_KEY = FLASH_KEY2; + DMB(); + while ((FLASH_CR & FLASH_CR_LOCK) != 0) + ; + } +} + +void RAMFUNCTION hal_flash_lock(void) +{ + flash_wait_complete(); + if ((FLASH_CR & FLASH_CR_LOCK) == 0) + FLASH_CR |= FLASH_CR_LOCK; +} + + +int RAMFUNCTION hal_flash_erase(uint32_t address, int len) +{ + uint32_t end_address; + uint32_t p; + uint32_t page_number; + uint32_t reg; + + if (len == 0) + return -1; + address -= FLASHMEM_ADDRESS_SPACE; + end_address = address + len - 1; + for (p = address; p < end_address; p += FLASH_PAGE_SIZE) { + flash_wait_complete(); + flash_clear_errors(); + page_number = (p >> 11) & FLASH_CR_PNB_MASK; + reg = FLASH_CR & ~(FLASH_CR_PNB_MASK << FLASH_CR_PNB_SHIFT); + FLASH_CR = reg | (page_number << FLASH_CR_PNB_SHIFT) | FLASH_CR_PER; + DMB(); + FLASH_CR |= FLASH_CR_STRT; + flash_wait_complete(); + FLASH_CR &= ~FLASH_CR_PER; + } + return 0; +} + +static void clock_pll_off(void) +{ + uint32_t reg32; + + /* Switch back to HSI16 as SYSCLK source before turning off the PLL. */ + reg32 = RCC_CFGR; + reg32 &= ~RCC_CFGR_SW_MASK; + RCC_CFGR = (reg32 | RCC_CFGR_SW_HSI); + DMB(); + while (((RCC_CFGR >> RCC_CFGR_SWS_SHIFT) & RCC_CFGR_SW_MASK) + != RCC_CFGR_SW_HSI) + ; + RCC_CR &= ~RCC_CR_PLLON; + DMB(); + while ((RCC_CR & RCC_CR_PLLRDY) != 0) + ; +} + +/* Bring SYSCLK up to 170 MHz from HSI16 + PLL with Range 1 Boost mode. + * Sequence per RM0440 - 6.1.4: PWR boost must precede flash wait states + * and PLL enable. */ +static void clock_pll_on(int powersave) +{ + uint32_t reg32; + uint32_t pllm = 4; + uint32_t plln = 85; + uint32_t pllr = 2; + uint32_t flash_waitstates = FLASH_ACR_LATENCY_4WS; + + (void)powersave; + + /* Enable PWR clock so VOS/R1MODE writes take effect. */ + RCC_APB1ENR1 |= RCC_APB1ENR1_PWREN; + (void)RCC_APB1ENR1; + + /* Voltage scaling Range 1 (boost-capable). */ + reg32 = PWR_CR1; + reg32 = (reg32 & ~PWR_CR1_VOS_MASK) | PWR_CR1_VOS_RANGE1; + PWR_CR1 = reg32; + while ((PWR_SR2 & PWR_SR2_VOSF) != 0) + ; + /* Clear R1MODE to enable Boost (required above 150 MHz). */ + PWR_CR5 &= ~PWR_CR5_R1MODE; + + /* HSI16 already on at reset; be explicit. */ + RCC_CR |= RCC_CR_HSION; + DMB(); + while ((RCC_CR & RCC_CR_HSIRDY) == 0) + ; + + /* Set flash wait states + caches before raising CPU frequency. */ + FLASH_ACR = (FLASH_ACR & ~FLASH_ACR_LATENCY_MASK) + | flash_waitstates + | FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN; + while ((FLASH_ACR & FLASH_ACR_LATENCY_MASK) != flash_waitstates) + ; + + /* PLL must be off before reprogramming PLLCFGR. */ + RCC_CR &= ~RCC_CR_PLLON; + while ((RCC_CR & RCC_CR_PLLRDY) != 0) + ; + + /* PLL: SRC=HSI16, M=4 (raw 3), N=85, R=2 (raw 0), enable R output. + * VCO = 16 MHz / 4 * 85 = 340 MHz. SYSCLK = 340 / 2 = 170 MHz. */ + reg32 = RCC_PLLCFGR_PLLSRC_HSI16; + reg32 |= ((pllm - 1) << RCC_PLLCFGR_PLLM_SHIFT); + reg32 |= (plln << RCC_PLLCFGR_PLLN_SHIFT); + reg32 |= (((pllr >> 1) - 1) << RCC_PLLCFGR_PLLR_SHIFT); + reg32 |= RCC_PLLCFGR_PLLREN; + RCC_PLLCFGR = reg32; + DMB(); + + RCC_CR |= RCC_CR_PLLON; + while ((RCC_CR & RCC_CR_PLLRDY) == 0) + ; + + /* Switch SYSCLK source to PLL R. */ + reg32 = RCC_CFGR; + reg32 = (reg32 & ~RCC_CFGR_SW_MASK) | RCC_CFGR_SW_PLL; + RCC_CFGR = reg32; + DMB(); + while (((RCC_CFGR >> RCC_CFGR_SWS_SHIFT) & RCC_CFGR_SW_MASK) + != RCC_CFGR_SW_PLL) + ; + + /* SYSCFG clock for any post-init configuration. */ + RCC_APB2ENR |= RCC_APB2ENR_SYSCFGEN; +} + + +/* Optional debug UART on NUCLEO-G491RE ST-LINK VCP: LPUART1 PA2/PA3 AF12. + * Available when: wolfBoot with DEBUG_UART, or test-app (always). */ +#if defined(DEBUG_UART) || !defined(__WOLFBOOT) + +void uart_init(void) +{ + uint32_t reg; + const uint32_t tx_pin = NUCLEO_G491_UART_TX_PIN; + const uint32_t rx_pin = NUCLEO_G491_UART_RX_PIN; + + RCC_AHB2ENR |= RCC_AHB2ENR_GPIOAEN; + (void)RCC_AHB2ENR; + RCC_APB1ENR2 |= RCC_APB1ENR2_LPUART1EN; + (void)RCC_APB1ENR2; + + /* PA2, PA3 -> alternate function (MODER = 10b). */ + reg = GPIOA_MODER & ~((0x3u << (tx_pin * 2)) | (0x3u << (rx_pin * 2))); + GPIOA_MODER = reg | ((0x2u << (tx_pin * 2)) | (0x2u << (rx_pin * 2))); + + /* High speed (11b) so 115200 edges are clean even at 170 MHz. */ + GPIOA_OSPEEDR |= (0x3u << (tx_pin * 2)) | (0x3u << (rx_pin * 2)); + + /* AF12 (LPUART1) on PA2 and PA3 - AFRL bits [11:8] and [15:12]. */ + reg = GPIOA_AFRL & ~((0xFu << (tx_pin * 4)) | (0xFu << (rx_pin * 4))); + GPIOA_AFRL = reg | ((NUCLEO_G491_UART_AF << (tx_pin * 4)) + | (NUCLEO_G491_UART_AF << (rx_pin * 4))); + + LPUART1_CR1 = 0; + LPUART1_BRR = STM32G4_LPUART1_BRR(STM32G4_UART_BAUD); + LPUART1_CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; + while ((LPUART1_ISR & USART_ISR_TEACK) == 0) + ; +} + +void uart_write(const char *buf, unsigned int len) +{ + unsigned int i; + for (i = 0; i < len; i++) { + while ((LPUART1_ISR & USART_ISR_TXE) == 0) + ; + LPUART1_TDR = (uint32_t)buf[i]; + } +} + +#endif /* DEBUG_UART || !__WOLFBOOT */ + + +void hal_init(void) +{ + clock_pll_on(0); +#if defined(DEBUG_UART) && defined(__WOLFBOOT) + uart_init(); +#endif +} + +void RAMFUNCTION hal_prepare_boot(void) +{ +#ifdef SPI_FLASH + spi_flash_release(); +#endif +#ifdef WOLFBOOT_RESTORE_CLOCK + clock_pll_off(); +#endif +} diff --git a/hal/stm32g4.h b/hal/stm32g4.h new file mode 100644 index 0000000000..b14f41ae54 --- /dev/null +++ b/hal/stm32g4.h @@ -0,0 +1,177 @@ +/* stm32g4.h + * + * Copyright (C) 2026 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 + */ + +/* STM32G4 family (e.g. STM32G491RE on NUCLEO-G491RE). + * Cortex-M4F, no TrustZone. Single-bank 512 KB flash, 2 KB pages. + * Reference: RM0440. Shared register map for the wolfBoot HAL and + * the test-app -- both pull in the same definitions so wire-up + * doesn't drift between them. */ + +#ifndef _STM32G4_H_ +#define _STM32G4_H_ + +#include + +/* Assembly helpers */ +#define DMB() __asm__ volatile ("dmb") +#define ISB() __asm__ volatile ("isb") +#define DSB() __asm__ volatile ("dsb") + + +/* -------- RCC (AHB1 + 0x1000 = 0x40021000) -------- RM0440 - 7.4 -------- */ +#define RCC_BASE (0x40021000) +#define RCC_CR (*(volatile uint32_t *)(RCC_BASE + 0x00)) +#define RCC_CFGR (*(volatile uint32_t *)(RCC_BASE + 0x08)) +#define RCC_PLLCFGR (*(volatile uint32_t *)(RCC_BASE + 0x0C)) +#define RCC_CRRCR (*(volatile uint32_t *)(RCC_BASE + 0x98)) +#define RCC_CCIPR (*(volatile uint32_t *)(RCC_BASE + 0x88)) +#define RCC_AHB2ENR (*(volatile uint32_t *)(RCC_BASE + 0x4C)) +#define RCC_APB1ENR1 (*(volatile uint32_t *)(RCC_BASE + 0x58)) +#define RCC_APB1ENR2 (*(volatile uint32_t *)(RCC_BASE + 0x5C)) +#define RCC_APB2ENR (*(volatile uint32_t *)(RCC_BASE + 0x60)) + +#define RCC_CR_PLLRDY (1 << 25) +#define RCC_CR_PLLON (1 << 24) +#define RCC_CR_HSIRDY (1 << 10) +#define RCC_CR_HSION (1 << 8) + +#define RCC_CFGR_SW_HSI 0x1 +#define RCC_CFGR_SW_PLL 0x3 +#define RCC_CFGR_SW_MASK 0x3 +#define RCC_CFGR_SWS_SHIFT 2 + +#define RCC_PLLCFGR_PLLREN (1 << 24) +#define RCC_PLLCFGR_PLLR_SHIFT 25 +#define RCC_PLLCFGR_PLLN_SHIFT 8 +#define RCC_PLLCFGR_PLLM_SHIFT 4 +#define RCC_PLLCFGR_PLLSRC_HSI16 0x2 + +#define RCC_CRRCR_HSI48ON (1 << 0) +#define RCC_CRRCR_HSI48RDY (1 << 1) + +#define RCC_AHB2ENR_GPIOAEN (1 << 0) +#define RCC_APB1ENR1_PWREN (1 << 28) +#define RCC_APB1ENR2_LPUART1EN (1 << 0) +#define RCC_APB2ENR_SYSCFGEN (1 << 0) + + +/* -------- PWR (APB1 = 0x40007000) -------- RM0440 - 6.4 -------- */ +#define PWR_BASE (0x40007000) +#define PWR_CR1 (*(volatile uint32_t *)(PWR_BASE + 0x00)) +#define PWR_CR5 (*(volatile uint32_t *)(PWR_BASE + 0x80)) +#define PWR_SR2 (*(volatile uint32_t *)(PWR_BASE + 0x14)) + +#define PWR_CR1_VOS_SHIFT 9 +#define PWR_CR1_VOS_MASK (0x3 << PWR_CR1_VOS_SHIFT) +#define PWR_CR1_VOS_RANGE1 (0x1 << PWR_CR1_VOS_SHIFT) +#define PWR_CR5_R1MODE (1 << 0) /* 0 = Range 1 Boost */ +#define PWR_SR2_VOSF (1 << 10) + + +/* -------- FLASH (AHB1 + 0x2000 = 0x40022000) -------- RM0440 - 3.7 -------- */ +#define FLASH_BASE (0x40022000) +#define FLASH_ACR (*(volatile uint32_t *)(FLASH_BASE + 0x00)) +#define FLASH_KEY (*(volatile uint32_t *)(FLASH_BASE + 0x08)) +#define FLASH_SR (*(volatile uint32_t *)(FLASH_BASE + 0x10)) +#define FLASH_CR (*(volatile uint32_t *)(FLASH_BASE + 0x14)) + +#define FLASHMEM_ADDRESS_SPACE (0x08000000) +#define FLASH_PAGE_SIZE (0x800) /* 2KB */ + +#define FLASH_ACR_LATENCY_MASK (0x7) +#define FLASH_ACR_PRFTEN (1 << 8) +#define FLASH_ACR_ICEN (1 << 9) +#define FLASH_ACR_DCEN (1 << 10) +#define FLASH_ACR_LATENCY_4WS (0x4) + +/* G4 has a single BSY at bit 16 (no BSY1/BSY2 like G0). */ +#define FLASH_SR_BSY (1 << 16) +#define FLASH_SR_OPTVERR (1 << 15) +#define FLASH_SR_RDERR (1 << 14) +#define FLASH_SR_FASTERR (1 << 9) +#define FLASH_SR_MISERR (1 << 8) +#define FLASH_SR_PGSERR (1 << 7) +#define FLASH_SR_SIZERR (1 << 6) +#define FLASH_SR_PGAERR (1 << 5) +#define FLASH_SR_WRPERR (1 << 4) +#define FLASH_SR_PROGERR (1 << 3) +#define FLASH_SR_OPERR (1 << 1) +#define FLASH_SR_EOP (1 << 0) + +/* G491 (Cat 3) is single-bank: PNB is 8 bits at [10:3], no BKER. */ +#define FLASH_CR_LOCK (1UL << 31) +#define FLASH_CR_STRT (1 << 16) +#define FLASH_CR_PER (1 << 1) +#define FLASH_CR_PG (1 << 0) +#define FLASH_CR_PNB_SHIFT 3 +#define FLASH_CR_PNB_MASK 0xFF + +#define FLASH_KEY1 (0x45670123) +#define FLASH_KEY2 (0xCDEF89AB) + + +/* -------- GPIOA (AHB2 = 0x48000000) -------- RM0440 - 9.4 -------- */ +#define GPIOA_BASE (0x48000000) +#define GPIOA_MODER (*(volatile uint32_t *)(GPIOA_BASE + 0x00)) +#define GPIOA_OTYPER (*(volatile uint32_t *)(GPIOA_BASE + 0x04)) +#define GPIOA_OSPEEDR (*(volatile uint32_t *)(GPIOA_BASE + 0x08)) +#define GPIOA_PUPDR (*(volatile uint32_t *)(GPIOA_BASE + 0x0C)) +#define GPIOA_ODR (*(volatile uint32_t *)(GPIOA_BASE + 0x14)) +#define GPIOA_BSRR (*(volatile uint32_t *)(GPIOA_BASE + 0x18)) +#define GPIOA_AFRL (*(volatile uint32_t *)(GPIOA_BASE + 0x20)) +#define GPIOA_AFRH (*(volatile uint32_t *)(GPIOA_BASE + 0x24)) + + +/* -------- LPUART1 (APB1 = 0x40008000) -------- RM0440 - 38.7 -------- */ +#define LPUART1_BASE (0x40008000) +#define LPUART1_CR1 (*(volatile uint32_t *)(LPUART1_BASE + 0x00)) +#define LPUART1_BRR (*(volatile uint32_t *)(LPUART1_BASE + 0x0C)) +#define LPUART1_ISR (*(volatile uint32_t *)(LPUART1_BASE + 0x1C)) +#define LPUART1_TDR (*(volatile uint32_t *)(LPUART1_BASE + 0x28)) + +#define USART_CR1_UE (1 << 0) +#define USART_CR1_RE (1 << 2) +#define USART_CR1_TE (1 << 3) +#define USART_ISR_TEACK (1 << 21) +#define USART_ISR_TXE (1 << 7) + + +/* -------- SYSCLK / boot defaults -------- */ +#define STM32G4_SYSCLK_HZ (170000000UL) +#define STM32G4_UART_BAUD (115200UL) +/* LPUART_BRR = (256 * f_LPUART) / baud. PCLK1 = SYSCLK on default prescalers. */ +#define STM32G4_LPUART1_BRR(baud) \ + ((uint32_t)(((uint64_t)256u * STM32G4_SYSCLK_HZ) / (baud))) + + +/* -------- NUCLEO-G491RE board specifics -------- */ +#define NUCLEO_G491_LED_LD2_PIN (5) /* PA5 */ +#define NUCLEO_G491_UART_TX_PIN (2) /* PA2 - LPUART1_TX */ +#define NUCLEO_G491_UART_RX_PIN (3) /* PA3 - LPUART1_RX */ +#define NUCLEO_G491_UART_AF (12) + + +/* Public helpers implemented in hal/stm32g4.c. Available to wolfBoot when + * DEBUG_UART is set, and to the test-app at all times. */ +void uart_init(void); +void uart_write(const char *buf, unsigned int len); + +#endif /* _STM32G4_H_ */ diff --git a/hal/stm32g4.ld b/hal/stm32g4.ld new file mode 100644 index 0000000000..13c8b13d78 --- /dev/null +++ b/hal/stm32g4.ld @@ -0,0 +1,50 @@ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = @BOOTLOADER_PARTITION_SIZE@ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00018000 /* 96 KB on G491RE */ +} + +SECTIONS +{ + .text : + { + _start_text = .; + KEEP(*(.isr_vector)) + *(.text*) + *(.rodata*) + . = ALIGN(4); + _end_text = .; + } > FLASH + + .edidx : + { + . = ALIGN(4); + *(.ARM.exidx*) + } > FLASH + + _stored_data = .; + .data : AT (_stored_data) + { + _start_data = .; + KEEP(*(.data*)) + . = ALIGN(4); + KEEP(*(.ramcode)) + . = ALIGN(4); + _end_data = .; + } > RAM + + .bss (NOLOAD) : + { + _start_bss = .; + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _end_bss = .; + __bss_end__ = .; + _end = .; + } > RAM + . = ALIGN(4); +} + +END_STACK = ORIGIN(RAM) + LENGTH(RAM); diff --git a/test-app/Makefile b/test-app/Makefile index 5a8f3f77ef..912f0acd5b 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -873,6 +873,23 @@ endif ifeq ($(TARGET),stm32g0) CFLAGS+=-DNVM_FLASH_WRITEONCE=1 endif +ifeq ($(TARGET),stm32g4) + CFLAGS+=-DNVM_FLASH_WRITEONCE=1 + CFLAGS+=-I../hal + LDFLAGS+=-Wl,-gc-sections + # Test app uses printf via syscalls.o (routes _write -> uart_write). + # syscalls.c references uart_write/uart_vprintf from string.o; string.o + # is already pulled in when DEBUG_UART=1. Pull it in unconditionally so + # printf is available even when DEBUG_UART is off. + ifeq ($(DEBUG_UART),) + APP_OBJS+=../src/string.o + endif + APP_OBJS+=syscalls.o + # _Min_Heap_Size is consumed by syscalls.c _sbrk(); ARM.ld does not + # define a heap, so anchor it at zero. _sbrk is gc'd when malloc is + # unused, but the symbol must still resolve. + LDFLAGS+=-Wl,--defsym=_Min_Heap_Size=0 +endif ifeq ($(TARGET),stm32c0) CFLAGS+=-DNVM_FLASH_WRITEONCE=1 endif diff --git a/test-app/app_stm32g4.c b/test-app/app_stm32g4.c new file mode 100644 index 0000000000..f774402e1c --- /dev/null +++ b/test-app/app_stm32g4.c @@ -0,0 +1,94 @@ +/* app_stm32g4.c + * + * Test bare-metal application for the STM32G4 (NUCLEO-G491RE). + * + * Copyright (C) 2026 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 "hal.h" +#include "stm32g4.h" +#include "wolfboot/wolfboot.h" +#include "target.h" + +#ifdef TARGET_stm32g4 + +static void led_init(void) +{ + uint32_t reg; + RCC_AHB2ENR |= RCC_AHB2ENR_GPIOAEN; + reg = RCC_AHB2ENR; + (void)reg; + + reg = GPIOA_MODER & ~(0x3u << (NUCLEO_G491_LED_LD2_PIN * 2)); + GPIOA_MODER = reg | (0x1u << (NUCLEO_G491_LED_LD2_PIN * 2)); + GPIOA_PUPDR &= ~(0x3u << (NUCLEO_G491_LED_LD2_PIN * 2)); +} + +static void led_on(void) +{ + GPIOA_BSRR = (1u << NUCLEO_G491_LED_LD2_PIN); +} + +static void led_off(void) +{ + GPIOA_BSRR = (1u << (NUCLEO_G491_LED_LD2_PIN + 16)); +} + +static void busy_delay(uint32_t count) +{ + volatile uint32_t i; + for (i = 0; i < count; i++) + __asm__ volatile ("nop"); +} + +void main(void) +{ + uint32_t version; + uint32_t on_ticks, off_ticks; + + hal_init(); + led_init(); + uart_init(); + + version = wolfBoot_current_firmware_version(); + printf("TEST APP\r\n"); + printf("App version: %lu\r\n", (unsigned long)version); + + /* v1: slow blink. v2+: fast blink + mark update successful. */ + if (version >= 2) { + wolfBoot_success(); + printf("Update confirmed.\r\n"); + on_ticks = 200000; + off_ticks = 200000; + } else { + on_ticks = 600000; + off_ticks = 600000; + } + + while (1) { + led_on(); + busy_delay(on_ticks); + led_off(); + busy_delay(off_ticks); + } +} + +#endif /* TARGET_stm32g4 */ diff --git a/test-app/led.c b/test-app/led.c index f9f99a0073..24cdf7e5df 100644 --- a/test-app/led.c +++ b/test-app/led.c @@ -230,3 +230,28 @@ void boot_led_off(void) } #endif /* TARGET_stm32l4 */ + +#ifdef TARGET_stm32g4 +#include "stm32g4.h" +/* NUCLEO-G491RE user LED LD2 on PA5. */ + +void boot_led_on(void) +{ + uint32_t reg; + const uint32_t pin = NUCLEO_G491_LED_LD2_PIN; + RCC_AHB2ENR |= RCC_AHB2ENR_GPIOAEN; + reg = RCC_AHB2ENR; /* read-back so GPIOA clock is live before MODER write */ + (void)reg; + reg = GPIOA_MODER & ~(0x03 << (pin * 2)); + GPIOA_MODER = reg | (1 << (pin * 2)); + reg = GPIOA_PUPDR & ~(0x03 << (pin * 2)); + GPIOA_PUPDR = reg | (1 << (pin * 2)); + GPIOA_BSRR = (1 << pin); /* BSRR is write-only; bare assignment, not |= */ +} + +void boot_led_off(void) +{ + GPIOA_BSRR = (1 << (NUCLEO_G491_LED_LD2_PIN + 16)); +} + +#endif /* TARGET_stm32g4 */