stm32u5: configure system clock to 160 MHz

- Uses PLL1 to boost the system clock from the 4 MHz MSIS default to 160 MHz.
- Sets VOS to Range 1 (1.2V) and enables the EPOD booster for higher frequency support.
- Configures flash latency (4 wait states) and enables prefetch for 160 MHz operation.
- Updates CPU and APB timer frequencies in the machine package accordingly.
- Fixes LPUART baud rate divisor computation by using 64-bit arithmetic to prevent
overflow with the newly increased 160 MHz clock.

Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
deadprogram
2026-05-06 18:44:17 +02:00
committed by Ron Evans
parent 1be377c47d
commit 906a20d3bf
2 changed files with 48 additions and 12 deletions
+7 -5
View File
@@ -8,14 +8,14 @@ import (
)
func CPUFrequency() uint32 {
return 4_000_000
return 160_000_000
}
// Internal use: configured speed of the APB1 and APB2 timers, this should be kept
// in sync with any changes to runtime package which configures the oscillators
// and clock frequencies
const APB1_TIM_FREQ = 4e6 // 4MHz (MSI default)
const APB2_TIM_FREQ = 4e6 // 4MHz (MSI default)
const APB1_TIM_FREQ = 160e6 // 160MHz (PLL1: MSIS 4MHz × 80 / 1 / 2)
const APB2_TIM_FREQ = 160e6 // 160MHz (PLL1: MSIS 4MHz × 80 / 1 / 2)
//---------- UART related code
@@ -55,8 +55,10 @@ func (uart *UART) isLPUART1() bool {
// NOTE: keep this in sync with the runtime/runtime_stm32u5.go clock init code
func (uart *UART) getBaudRateDivisor(baudRate uint32) uint32 {
if uart.isLPUART1() {
// LPUART uses BRR = 256 * fclk / baud
return (256 * CPUFrequency()) / baudRate
// LPUART uses BRR = 256 * fclk / baud.
// Use 64-bit arithmetic to avoid overflow: at 160 MHz,
// 256 * 160_000_000 = 40_960_000_000 which exceeds uint32 max.
return uint32(uint64(256) * uint64(CPUFrequency()) / uint64(baudRate))
}
// USART requires BRR >= 16 for 16x oversampling (OVER8=0).
// A divisor below 16 is invalid per the STM32 reference manual and causes
+41 -7
View File
@@ -24,18 +24,52 @@ func buffered() int {
}
func initCLK() {
// Use MSI at 4MHz — the reset default clock configuration.
// The MCU boots with MSI at 4MHz, VOS Range 4, and 0 flash wait states.
// Configure SYSCLK to 160 MHz via PLL1 using MSIS (4 MHz reset default) as the source.
// Formula: Fout = Fin × N / M / R = 4 × 80 / 1 / 2 = 160 MHz
// - M = 1 (Div1), N = 80 (stored as N-1 = 79), R = 2 (Div2)
// - VCO input = 4 MHz (PLL1RGE Range1: 48 MHz), VCO output = 320 MHz
// VOS Range 1 (1.2V) is required for SYSCLK > 100 MHz (RM0456 §6.3.6).
// Enable PWR peripheral clock (required on STM32U5 before accessing PWR registers).
stm32.RCC.AHB3ENR.SetBits(stm32.RCC_AHB3ENR_PWREN)
_ = stm32.RCC.AHB3ENR.Get() // read-back for clock stabilization
// Switch from VOS Range 4 to Range 3. Range 4 doesn't support ADC (RM0456 §6.3.6).
// Range 3 supports up to 50 MHz HCLK and enables ADC. No flash wait-state change needed at 4 MHz.
// The EPOD booster must be enabled before changing VOS (RM0456 §10.5.4).
// Enable the EPOD booster before raising VOS (RM0456 §10.5.4).
stm32.PWR.VOSR.SetBits(stm32.PWR_VOSR_BOOSTEN)
stm32.PWR.VOSR.ReplaceBits(stm32.PWR_VOSR_VOS_Range3<<stm32.PWR_VOSR_VOS_Pos, stm32.PWR_VOSR_VOS_Msk, 0)
for !stm32.PWR.VOSR.HasBits(stm32.PWR_VOSR_VOSRDY) {
// Raise voltage scaling to Range 1 (1.2V) to support 160 MHz operation.
stm32.PWR.VOSR.ReplaceBits(stm32.PWR_VOSR_VOS_Range1<<stm32.PWR_VOSR_VOS_Pos, stm32.PWR_VOSR_VOS_Msk, 0)
// Wait for both the VOS regulator and the EPOD booster to become ready.
for !stm32.PWR.VOSR.HasBits(stm32.PWR_VOSR_VOSRDY | stm32.PWR_VOSR_BOOSTRDY) {
}
// Set Flash latency to 4 wait states and enable prefetch before raising the clock
// (required for 160 MHz at VOS Range 1, RM0456 §7.3.3).
stm32.FLASH.ACR.ReplaceBits(4, stm32.Flash_ACR_LATENCY_Msk, 0)
stm32.FLASH.ACR.SetBits(stm32.Flash_ACR_PRFTEN)
// Configure PLL1: source = MSIS (4 MHz), M = 1, PLL1RGE = 48 MHz, R output enabled.
stm32.RCC.PLL1CFGR.Set(
stm32.RCC_PLL1CFGR_PLL1SRC_MSIS |
(stm32.RCC_PLL1CFGR_PLL1RGE_Range1 << stm32.RCC_PLL1CFGR_PLL1RGE_Pos) |
(stm32.RCC_PLL1CFGR_PLL1M_Div1 << stm32.RCC_PLL1CFGR_PLL1M_Pos) |
stm32.RCC_PLL1CFGR_PLL1REN,
)
// Set PLL1 dividers: N = 80 (stored as N-1 = 79), R = 2 (Div2 = 1).
stm32.RCC.PLL1DIVR.Set(
(79 << stm32.RCC_PLL1DIVR_PLL1N_Pos) |
(stm32.RCC_PLL1DIVR_PLL1R_Div2 << stm32.RCC_PLL1DIVR_PLL1R_Pos),
)
// Enable PLL1 and wait for it to lock.
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLL1ON)
for !stm32.RCC.CR.HasBits(stm32.RCC_CR_PLL1RDY) {
}
// Switch SYSCLK to PLL1 and wait for the hardware to confirm the switch.
stm32.RCC.SetCFGR1_SW(stm32.RCC_CFGR1_SW_PLL)
for stm32.RCC.GetCFGR1_SWS() != stm32.RCC_CFGR1_SWS_PLL {
}
}