@@ -39,6 +39,7 @@ This README describes configuration of supported targets.
3939* [ Renesas RZN2L] ( #renesas-rzn2l )
4040* [ SiFive HiFive1 RISC-V] ( #sifive-hifive1-risc-v )
4141* [ STM32C0] ( #stm32c0 )
42+ * [ STM32C5] ( #stm32c5 )
4243* [ STM32F1] ( #stm32f1 )
4344* [ STM32F4] ( #stm32f4 )
4445* [ STM32F7] ( #stm32f7 )
@@ -1820,6 +1821,174 @@ the new image. LD2 transitions from the slow (v1) blink to the fast
18201821transition.
18211822
18221823
1824+ # # STM32C5
1825+
1826+ The STM32C5 family (for example the STM32C5A3ZGT6 on NUCLEO-C5A3ZG) is
1827+ a mainstream Cortex-M33 part ** without TrustZone** , so the port is
1828+ single-image only (no ` -tz` or ` -ns` variants). On the -ZG variant:
1829+ 1 MB internal flash, 256 KB SRAM, 8 KB pages, ** 128-bit (quad-word)
1830+ flash write quantum** with per-quad-word ECC.
1831+
1832+ The HAL writes flash in 16-byte aligned quad-words. When wolfBoot
1833+ asks for a smaller or unaligned write, the HAL reads the surrounding
1834+ flash and merges so each programmed quad-word is a complete ECC
1835+ block - sub-quad-word writes leave ECC undefined and reads come back
1836+ with bit-flipped " corrected" data.
1837+
1838+ # ## Flash layout (stm32c5.config)
1839+
1840+ Dual-bank flash (2 x 512 KB, 8 KB pages). Bank 1 holds wolfBoot +
1841+ BOOT, bank 2 holds UPDATE + SWAP:
1842+
1843+ ` ` `
1844+ Bank 1:
1845+ 0x08000000 - 0x0800FFFF wolfBoot bootloader (64 KB)
1846+ 0x08010000 - 0x0807FFFF BOOT partition (0x70000, 448 KB)
1847+ Bank 2:
1848+ 0x08080000 - 0x080EFFFF UPDATE partition (0x70000, 448 KB)
1849+ 0x080F0000 - 0x080F1FFF SWAP sector (8 KB)
1850+ ` ` `
1851+
1852+ # ## Clock and UART
1853+
1854+ The reset SYSCLK is ** HSIDIV3 = HSIS / 3 = 16 MHz** (RCC_CFGR1.SW=0
1855+ selects HSIDIV3, RCC_CR1 reset value 0x22 = HSIDIV3ON | HSIDIV3RDY).
1856+ wolfBoot brings SYSCLK to ** 144 MHz HCLK** in `clock_psi_on ()` (called
1857+ from `hal_init ()` ) via the
1858+ PSIS clock chain (HSE 48 MHz reference -> PSI ref=48 MHz / out=144 MHz
1859+ -> PSIS), with all bus prescalers /1 (PCLK1 = PCLK2 = PCLK3 = 144 MHz),
1860+ flash 4 wait states and WRHIGHFREQ programming delay = 2.
1861+
1862+ By default (` WOLFBOOT_RESTORE_CLOCK` set in ` options.mk` ),
1863+ ` hal_prepare_boot()` switches SYSCLK back to HSIDIV3 before handoff
1864+ but ** leaves PSIS, PSI and HSE running** . The loaded firmware' s own
1865+ `clock_psi_on()` then just pushes SYSCLK back from HSIDIV3 to PSIS -
1866+ the HSE/PSI configuration it would have written is already in place,
1867+ so it skips the HSE startup wait and the PSI reconfiguration entirely.
1868+ This mirrors ST' s ` HAL_RCC_ResetSystemClock()` (the lightweight
1869+ restore) rather than the full ` HAL_RCC_Reset()` . Disabling HSE in
1870+ ` hal_prepare_boot()` and forcing the loaded firmware to re-enable it
1871+ on a back-to-back cycle is not reliable on this part; the lightweight
1872+ restore avoids that path entirely. Pass ` WOLFBOOT_RESTORE_CLOCK=0`
1873+ to skip the SYSCLK switch entirely and inherit PSIS @ 144 MHz directly.
1874+
1875+ UART is always available in the test-app and enabled in wolfBoot via
1876+ ` DEBUG_UART=1` (on by default in the example config). USART2_BRR is
1877+ computed for PCLK1 = 144 MHz. The NUCLEO-C5A3ZG ST-LINK virtual COM
1878+ port is wired to MCU pins 36/37 (PA2/PA3) - ** USART2** on AF7, 115200
1879+ 8N1, ** not USART1 on PA9/PA10** (PA9/PA10 only reach the Arduino
1880+ headers).
1881+
1882+ # ## Building
1883+
1884+ ` ` ` sh
1885+ cp config/examples/stm32c5.config .config
1886+ make clean
1887+ make
1888+ ` ` `
1889+
1890+ Default signing scheme is ECC256 + SHA256. Produces ` wolfboot.bin`
1891+ (~25 KB), ` test-app/image_v1_signed.bin` , and ` factory.bin` (BL +
1892+ signed v1).
1893+
1894+ # ## Flashing
1895+
1896+ Use ` STM32_Programmer_CLI` (from STM32CubeIDE or STM32CubeProgrammer
1897+ v2.22+). pyocd has no STM32C5 target as of this writing. The C5
1898+ debug access port is AP2; ` mode=UR` (under-reset) is the most
1899+ reliable connect mode while a previous image is running.
1900+
1901+ ` ` ` sh
1902+ STM32_Programmer_CLI -c port=swd mode=UR -e all \
1903+ -d factory.bin 0x08000000 -v -rst
1904+ ` ` `
1905+
1906+ The test app blinks LD2 (PG1, ** active low** ): five slow blinks on
1907+ v1 then it triggers an update and resets; v2 blinks fast forever
1908+ once `wolfBoot_success ()` is acknowledged.
1909+
1910+ # ## Testing an Update
1911+
1912+ Sign the test application as version 2 and flash it directly to the
1913+ update partition:
1914+
1915+ ` ` ` sh
1916+ ./tools/keytools/sign --ecc256 --sha256 \
1917+ test-app/image.bin wolfboot_signing_private_key.der 2
1918+ STM32_Programmer_CLI -c port=swd mode=UR \
1919+ -d test-app/image_v2_signed.bin 0x08080000 -v -rst
1920+ ` ` `
1921+
1922+ On reset wolfBoot detects the staged v2, the v1 test-app calls
1923+ ` wolfBoot_update_trigger()` after its blink sequence and resets,
1924+ wolfBoot performs the bank-to-bank swap, and v2 boots. With
1925+ ` DEBUG_UART=1` the UART log shows:
1926+
1927+ ` ` `
1928+ Booting version: 0x1
1929+ TEST APP / App version: 1 / triggering update -> reset
1930+ ... swap output ...
1931+ Booting version: 0x2
1932+ TEST APP / App version: 2 / update OK -- success confirmed
1933+ ` ` `
1934+
1935+ # ## DUALBANK_SWAP variant
1936+
1937+ ` config/examples/stm32c5-dualbank.config` builds wolfBoot with
1938+ ` DUALBANK_SWAP=1` , using the STM32C5' s `FLASH_OPTCR.SWAP_BANK`
1939+ option byte (bit 31) to flip which physical bank is mapped at
1940+ `0x08000000`. This replaces the copy-based swap with a single
1941+ option-byte toggle and a system reset - much faster, no swap
1942+ sector required.
1943+
1944+ Layout:
1945+
1946+ ```
1947+ Bank 1 (active by default):
1948+ 0x08000000 wolfBoot (64 KB)
1949+ 0x08010000 BOOT partition (448 KB)
1950+ Bank 2 (active after SWAP_BANK toggle):
1951+ 0x08080000 wolfBoot copy (64 KB)
1952+ 0x08090000 UPDATE partition (448 KB)
1953+ ```
1954+
1955+ `hal_init()` runs `fork_bootloader()` on the first boot, comparing
1956+ the contents of bank 1 and bank 2 and copying wolfBoot from bank 1
1957+ into bank 2 if they differ. This guarantees the chip can boot from
1958+ `0x08000000` regardless of which physical bank `SWAP_BANK` currently
1959+ maps there. Subsequent boots are no-ops because the two copies match.
1960+
1961+ Build, flash, and stage v2 are the same as the default config except
1962+ the UPDATE partition lives at `0x08090000`:
1963+
1964+ ```sh
1965+ cp config/examples/stm32c5-dualbank.config .config
1966+ make distclean && make
1967+ STM32_Programmer_CLI -c port=swd mode=UR -e all \
1968+ -d factory.bin 0x08000000 -v -rst
1969+ ./tools/keytools/sign --ecc256 --sha256 \
1970+ test-app/image.bin wolfboot_signing_private_key.der 2
1971+ STM32_Programmer_CLI -c port=swd mode=UR \
1972+ -d test-app/image_v2_signed.bin 0x08090000 -v -rst
1973+ ```
1974+
1975+ After the swap completes the partition addresses stay the same from
1976+ software' s perspective (` WOLFBOOT_PARTITION_BOOT_ADDRESS = 0x08010000`
1977+ keeps pointing at " current BOOT" ) - only the underlying bank is
1978+ different. Subsequent updates stage at ` 0x08090000` again.
1979+
1980+ # ## TrustZone (TZEN) is not supported
1981+
1982+ The STM32C5 silicon does not implement the ARMv8-M Security Extensions
1983+ (` __SAUREGION_PRESENT 0U` in the CMSIS device header) and has no
1984+ GTZC, no ` FLASH_NS_* ` / ` FLASH_SECCR* ` aliases, and no secure
1985+ peripheral address space. wolfBoot' s TrustZone ports (L5, U5, H5)
1986+ cannot be ported to the C5 - the hardware needed to partition memory
1987+ and peripherals into secure / non-secure worlds is absent. For
1988+ application security on the C5 use the MPU (`__MPU_PRESENT 1U`) and
1989+ flash RDP (Read Out Protection); both are wolfBoot-orthogonal.
1990+
1991+
18231992## STM32H5
18241993
18251994Like [STM32L5](#stm32l5) and [STM32U5](#stm32u5), STM32H5 support is also demonstrated
0 commit comments