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