/* stm32.c * * STM32H5 HAL support for PUF example (tested on NUCLEO-H563ZI). * Provides USART3 init/output, printf retarget, RNG stub, and time stub. * * To port to a different MCU, replace this file with your platform's * UART and RNG implementation. The integration points are: * void hal_init(void) - called once at startup before printf * int custom_rand_gen_block(unsigned char* output, unsigned int sz) - * wolfCrypt RNG callback wired via CUSTOM_RAND_GENERATE_BLOCK in * user_settings.h. Implement using your MCU's hardware TRNG. * unsigned long my_time(unsigned long* timer) - monotonic time * * Copyright (C) 2006-2026 wolfSSL Inc. * * This file is part of wolfSSL. * * wolfSSL 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. * * wolfSSL 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 /* -------------------------------------------------------------------------- */ /* STM32H5 USART3 (ST-LINK VCP) bare-metal driver */ /* -------------------------------------------------------------------------- */ /* STM32H563 register bases. * TZEN=0: use non-secure aliases (0x4xxx). * TZEN=1: use secure aliases (0x5xxx). */ #ifdef STM32H5_TZEN #define RCC_BASE 0x54020C00u #define GPIOD_BASE 0x52020C00u #define USART3_BASE 0x50004800u #else #define RCC_BASE 0x44020C00u #define GPIOD_BASE 0x42020C00u #define USART3_BASE 0x40004800u #endif #define RCC_AHB2ENR (*(volatile uint32_t *)(RCC_BASE + 0x8Cu)) #define RCC_APB1ENR (*(volatile uint32_t *)(RCC_BASE + 0x9Cu)) /* GPIO registers */ #define GPIO_MODER(b) (*(volatile uint32_t *)((b) + 0x00u)) #define GPIO_OSPEEDR(b) (*(volatile uint32_t *)((b) + 0x08u)) #define GPIO_AFRH(b) (*(volatile uint32_t *)((b) + 0x24u)) /* USART3 registers */ #define USART3_CR1 (*(volatile uint32_t *)(USART3_BASE + 0x00u)) #define USART3_RDR (*(volatile uint32_t *)(USART3_BASE + 0x24u)) #define USART3_ICR (*(volatile uint32_t *)(USART3_BASE + 0x20u)) #define USART3_CR2 (*(volatile uint32_t *)(USART3_BASE + 0x04u)) #define USART3_CR3 (*(volatile uint32_t *)(USART3_BASE + 0x08u)) #define USART3_BRR (*(volatile uint32_t *)(USART3_BASE + 0x0Cu)) #define USART3_ISR (*(volatile uint32_t *)(USART3_BASE + 0x1Cu)) #define USART3_TDR (*(volatile uint32_t *)(USART3_BASE + 0x28u)) #define USART3_PRESC (*(volatile uint32_t *)(USART3_BASE + 0x2Cu)) /* After reset on STM32H563: * HSI = 64 MHz, HSIDIV = /2 (reset value) -> SYSCLK = 32 MHz * HPRE = /1 -> HCLK = 32 MHz * PPRE1 = /1 -> PCLK1 = 32 MHz * The example never reprograms RCC, so PCLK1 stays at 32 MHz and the * USART3 BRR below is correct. If this code is ported into a project * that brings up the PLL, recompute UART_PCLK_HZ from the actual * RCC settings. */ #define UART_PCLK_HZ 32000000u #define UART_BAUD_HZ 115200u static void delay(volatile uint32_t n) { while (n--) { } } static void uart_init(void) { uint32_t moder, afr; /* Enable GPIOD clock */ RCC_AHB2ENR |= (1u << 3); /* Enable USART3 clock (APB1LENR bit 18) */ RCC_APB1ENR |= (1u << 18); delay(100); /* Configure PD8 (TX) as AF7, push-pull, high speed */ moder = GPIO_MODER(GPIOD_BASE); moder &= ~(3u << 16); moder |= (2u << 16); /* Alternate function */ GPIO_MODER(GPIOD_BASE) = moder; GPIO_OSPEEDR(GPIOD_BASE) |= (3u << 16); /* High speed for PD8 */ afr = GPIO_AFRH(GPIOD_BASE); afr &= ~(0xFu << 0); afr |= (7u << 0); /* AF7 = USART3 TX on PD8 */ GPIO_AFRH(GPIOD_BASE) = afr; /* Configure PD9 (RX) as AF7 as well. Needed for the interactive menu; * the original one-shot example was transmit-only. MODER pin 9 is bits * [19:18]; AFRH pin 9 is bits [7:4]. */ moder = GPIO_MODER(GPIOD_BASE); moder &= ~(3u << 18); moder |= (2u << 18); GPIO_MODER(GPIOD_BASE) = moder; afr = GPIO_AFRH(GPIOD_BASE); afr &= ~(0xFu << 4); afr |= (7u << 4); GPIO_AFRH(GPIOD_BASE) = afr; /* Configure USART3 for UART_BAUD_HZ at the post-reset PCLK1 (see * UART_PCLK_HZ comment above). 32 MHz / 115200 ~= 278. */ USART3_CR1 = 0; USART3_CR2 = 0; USART3_CR3 = 0; USART3_PRESC = 0; USART3_BRR = UART_PCLK_HZ / UART_BAUD_HZ; USART3_CR1 = (1u << 3) | (1u << 2); /* TE | RE */ delay(10); USART3_CR1 |= (1u << 0); /* UE */ delay(100); } static void uart_putc(char c) { while ((USART3_ISR & (1u << 7)) == 0) { } USART3_TDR = (uint32_t)c; } /* Retarget _write for printf via USART3 */ int _write(int fd, const char *buf, int len) { int i; (void)fd; for (i = 0; i < len; i++) { if (buf[i] == '\n') uart_putc('\r'); uart_putc(buf[i]); } return len; } /* -------------------------------------------------------------------------- */ /* STM32H5 Hardware RNG (TRNG) driver */ /* -------------------------------------------------------------------------- */ /* RCC clock control */ #define RCC_CR (*(volatile uint32_t *)(RCC_BASE + 0x00u)) #define RCC_CR_HSI48ON (1u << 12) #define RCC_CR_HSI48RDY (1u << 13) #define RCC_CCIPR5 (*(volatile uint32_t *)(RCC_BASE + 0xE8u)) #define RCC_CCIPR5_RNGSEL_Msk (3u << 4) /* RNG peripheral */ #ifdef STM32H5_TZEN #define RNG_BASE 0x520C0800u #else #define RNG_BASE 0x420C0800u #endif #define RNG_CR (*(volatile uint32_t *)(RNG_BASE + 0x00u)) #define RNG_SR (*(volatile uint32_t *)(RNG_BASE + 0x04u)) #define RNG_DR (*(volatile uint32_t *)(RNG_BASE + 0x08u)) #define RNG_CR_RNGEN (1u << 2) #define RNG_CR_CONDRST (1u << 30) #define RNG_CR_CONFIG3_SHIFT 8u #define RNG_CR_CONFIG2_SHIFT 13u #define RNG_CR_CLKDIV_SHIFT 16u #define RNG_CR_CONFIG1_SHIFT 20u #define RNG_SR_DRDY (1u << 0) #define RNG_SR_CECS (1u << 1) #define RNG_SR_SECS (1u << 2) #define RNG_SR_CEIS (1u << 5) #define RNG_SR_SEIS (1u << 6) static void rng_init(void) { uint32_t rng_cr; /* Enable HSI48 as RNG kernel clock source */ RCC_CR |= RCC_CR_HSI48ON; while ((RCC_CR & RCC_CR_HSI48RDY) == 0u) { } /* Select HSI48 for RNG clock */ RCC_CCIPR5 &= ~RCC_CCIPR5_RNGSEL_Msk; RCC_AHB2ENR |= (1u << 18); /* RNG clock enable */ delay(100); /* Build the desired CR value (config bits, RNGEN cleared). The * NIST-SP800-90B compliant config recommended by ST RM0481 for * HSI48 is CONFIG1=0x0F, CONFIG3=0x0D, CLKDIV/CONFIG2 = 0. */ rng_cr = RNG_CR; rng_cr &= ~(0x1Fu << RNG_CR_CONFIG1_SHIFT); rng_cr &= ~(0x7u << RNG_CR_CLKDIV_SHIFT); rng_cr &= ~(0x3u << RNG_CR_CONFIG2_SHIFT); rng_cr &= ~(0x7u << RNG_CR_CONFIG3_SHIFT); rng_cr &= ~RNG_CR_RNGEN; rng_cr |= (0x0Fu << RNG_CR_CONFIG1_SHIFT); rng_cr |= (0x0Du << RNG_CR_CONFIG3_SHIFT); /* STM32H5 RNG init sequence (RM0481 28.6.2): * 1. Write CR with CONDRST=1 and the new config bits in the same * access. CONDRST holds the conditioning logic in reset and * latches the config. * 2. Write CR again with CONDRST=0 and RNGEN=1 to release the * reset and start generation. The bit does not auto-clear - * software must drive it back to 0. * 3. Wait for the first random word: SR.DRDY=1. */ RNG_CR = RNG_CR_CONDRST | rng_cr; RNG_CR = rng_cr | RNG_CR_RNGEN; while ((RNG_SR & RNG_SR_DRDY) == 0u) { } } static int rng_get_word(uint32_t *out) { uint32_t timeout = 100000u; while ((RNG_SR & RNG_SR_DRDY) == 0u) { if ((RNG_SR & (RNG_SR_CECS | RNG_SR_SECS | RNG_SR_CEIS | RNG_SR_SEIS)) != 0u) { rng_init(); timeout = 100000u; continue; } if (--timeout == 0u) return -1; } *out = RNG_DR; return 0; } /* wolfCrypt custom RNG block generator using STM32H5 TRNG */ int custom_rand_gen_block(unsigned char *output, unsigned int sz) { uint32_t word; while (sz >= 4u) { if (rng_get_word(&word) != 0) return -1; output[0] = (unsigned char)word; output[1] = (unsigned char)(word >> 8); output[2] = (unsigned char)(word >> 16); output[3] = (unsigned char)(word >> 24); output += 4; sz -= 4; } if (sz > 0u) { if (rng_get_word(&word) != 0) return -1; while (sz-- > 0u) { *output++ = (unsigned char)word; word >>= 8; } } return 0; } /* -------------------------------------------------------------------------- */ /* hal_init - platform initialization entry point */ /* -------------------------------------------------------------------------- */ void hal_init(void) { uart_init(); rng_init(); } /* Custom time function */ unsigned long my_time(unsigned long* timer) { static unsigned long t = 1000; if (timer) *timer = t; return t++; } /* Blocking single-character read, used by the interactive demo menu. */ int uart_getc(void) { /* ISR bit 5 = RXNE (receive register not empty), bit 3 = ORE (overrun). * A pasted block arrives back-to-back with no flow control, so clear ORE * (ICR bit 3) rather than let it wedge the receiver. */ for (;;) { if ((USART3_ISR & (1u << 3)) != 0u) USART3_ICR = (1u << 3); if ((USART3_ISR & (1u << 5)) != 0u) break; } return (int)(USART3_RDR & 0xFFu); } /* Discard anything latched in the receiver (line noise at reset). */ void uart_drain(void) { volatile uint32_t sink; while ((USART3_ISR & (1u << 5)) != 0u) { sink = USART3_RDR; (void)sink; } }