diff --git a/src/machine/board_bluepill.go b/src/machine/board_bluepill.go index 83f527d575..ed158cf12e 100644 --- a/src/machine/board_bluepill.go +++ b/src/machine/board_bluepill.go @@ -7,6 +7,8 @@ import ( "runtime/interrupt" ) +const xtalHz = 8_000_000 + // Pins printed on the silkscreen const ( C13 = PC13 diff --git a/src/machine/board_feather-stm32f405.go b/src/machine/board_feather-stm32f405.go index 4a184bad51..e512cc3cc1 100644 --- a/src/machine/board_feather-stm32f405.go +++ b/src/machine/board_feather-stm32f405.go @@ -7,6 +7,8 @@ import ( "runtime/interrupt" ) +const xtalHz = 12_000_000 + const ( NUM_DIGITAL_IO_PINS = 39 NUM_ANALOG_IO_PINS = 7 diff --git a/src/machine/board_mksnanov3.go b/src/machine/board_mksnanov3.go index 35fef682d4..70b3c64992 100644 --- a/src/machine/board_mksnanov3.go +++ b/src/machine/board_mksnanov3.go @@ -10,6 +10,8 @@ import ( "runtime/interrupt" ) +const xtalHz = 8_000_000 + // LED is also wired to the SD card card detect (CD) pin. const LED = PD12 diff --git a/src/machine/board_nucleof103rb.go b/src/machine/board_nucleof103rb.go index af7a99e9df..5be08afd55 100644 --- a/src/machine/board_nucleof103rb.go +++ b/src/machine/board_nucleof103rb.go @@ -7,6 +7,8 @@ import ( "runtime/interrupt" ) +const xtalHz = 8_000_000 + const ( LED = LED_BUILTIN LED_BUILTIN = LED_GREEN diff --git a/src/machine/board_nucleof722ze.go b/src/machine/board_nucleof722ze.go index c9a0b01b04..63d6c2f9ad 100644 --- a/src/machine/board_nucleof722ze.go +++ b/src/machine/board_nucleof722ze.go @@ -7,6 +7,8 @@ import ( "runtime/interrupt" ) +const xtalHz = 8_000_000 + const ( LED = LED_BUILTIN LED_BUILTIN = LED_GREEN diff --git a/src/machine/board_stm32f469disco.go b/src/machine/board_stm32f469disco.go index 8fb5cde2ea..13419352c4 100644 --- a/src/machine/board_stm32f469disco.go +++ b/src/machine/board_stm32f469disco.go @@ -7,6 +7,8 @@ import ( "runtime/interrupt" ) +const xtalHz = 8_000_000 + const ( LED = LED_BUILTIN LED1 = LED_GREEN diff --git a/src/machine/board_stm32f4disco.go b/src/machine/board_stm32f4disco.go index d048fcacf4..c377063a0a 100644 --- a/src/machine/board_stm32f4disco.go +++ b/src/machine/board_stm32f4disco.go @@ -7,6 +7,8 @@ import ( "runtime/interrupt" ) +const xtalHz = 8_000_000 + const ( LED1 = LED_GREEN LED2 = LED_ORANGE diff --git a/src/machine/machine_stm32_pll.go b/src/machine/machine_stm32_pll.go new file mode 100644 index 0000000000..ceacac0959 --- /dev/null +++ b/src/machine/machine_stm32_pll.go @@ -0,0 +1,10 @@ +//go:build stm32 + +package machine + +// PLLParams holds the HSE main-PLL dividers/multipliers (RCC_PLLCFGR M/N/P/Q/R +// fields) needed to reach a chip's target VCO/SYSCLK frequency from a given +// crystal frequency. R is left zero on chips without a PLLR output. +type PLLParams struct { + M, N, P, Q, R uint32 +} diff --git a/src/machine/machine_stm32f103.go b/src/machine/machine_stm32f103.go index d946ef4aae..b1fd4b3c67 100644 --- a/src/machine/machine_stm32f103.go +++ b/src/machine/machine_stm32f103.go @@ -12,7 +12,10 @@ import ( ) func CPUFrequency() uint32 { - return 72000000 + pll := PLLParams72MHz() + // Prediv/Mul are raw RCC_CFGR register field values: the prescaler is + // encoded as (divisor-1) and the multiplier as (multiplier-2). + return xtalHz / (pll.Prediv + 1) * (pll.Mul + 2) } var deviceIDAddr = []uintptr{0x1FFFF7E8, 0x1FFFF7EC, 0x1FFFF7F0} diff --git a/src/machine/machine_stm32f103_pll_72mhz.go b/src/machine/machine_stm32f103_pll_72mhz.go new file mode 100644 index 0000000000..c5d2f2841e --- /dev/null +++ b/src/machine/machine_stm32f103_pll_72mhz.go @@ -0,0 +1,27 @@ +//go:build stm32 && stm32f103 + +package machine + +import "device/stm32" + +// F103PLLParams holds the HSE prescaler (PLLXTPRE) and PLL multiplier +// (PLLMUL) needed to reach 72MHz SYSCLK from a given crystal frequency. The +// F1 PLL has no dedicated input divider, only an optional /2 HSE prescaler. +type F103PLLParams struct { + Prediv uint32 + Mul uint32 +} + +func PLLParams72MHz() F103PLLParams { + switch xtalHz { + case 8_000_000: + return F103PLLParams{Prediv: stm32.RCC_CFGR_PLLXTPRE_Div1, Mul: stm32.RCC_CFGR_PLLMUL_Mul9} + case 12_000_000: + return F103PLLParams{Prediv: stm32.RCC_CFGR_PLLXTPRE_Div1, Mul: stm32.RCC_CFGR_PLLMUL_Mul6} + case 16_000_000: + // 16MHz / 2 (PLLXTPRE) x9 = 72MHz. + return F103PLLParams{Prediv: stm32.RCC_CFGR_PLLXTPRE_Div2, Mul: stm32.RCC_CFGR_PLLMUL_Mul9} + default: + panic("unsupported xtal frequency") + } +} diff --git a/src/machine/machine_stm32f40x.go b/src/machine/machine_stm32f40x.go index 953c7fab4a..5fcbbcbe86 100644 --- a/src/machine/machine_stm32f40x.go +++ b/src/machine/machine_stm32f40x.go @@ -3,7 +3,8 @@ package machine func CPUFrequency() uint32 { - return 168000000 + pll := PLLParams168MHz() + return xtalHz / pll.M * pll.N / pll.P } // Internal use: configured speed of the APB1 and APB2 timers, this should be kept diff --git a/src/machine/machine_stm32f469.go b/src/machine/machine_stm32f469.go index dfda91fc3d..9d0c7e473a 100644 --- a/src/machine/machine_stm32f469.go +++ b/src/machine/machine_stm32f469.go @@ -3,7 +3,8 @@ package machine func CPUFrequency() uint32 { - return 180000000 + pll := PLLParams180MHz() + return xtalHz / pll.M * pll.N / pll.P } // Internal use: configured speed of the APB1 and APB2 timers, this should be kept diff --git a/src/machine/machine_stm32f4_pll_168mhz.go b/src/machine/machine_stm32f4_pll_168mhz.go new file mode 100644 index 0000000000..17ad97a9b7 --- /dev/null +++ b/src/machine/machine_stm32f4_pll_168mhz.go @@ -0,0 +1,20 @@ +//go:build stm32f4 && (stm32f405 || stm32f407) + +package machine + +// PLLParams168MHz returns the HSE PLL dividers needed to reach a 336MHz VCO +// (168MHz SYSCLK, P=2, Q=7) for the configured crystal frequency. M is chosen +// to bring the PLL input (HSE/M) to 2MHz, the value RM0090 pg. 95 recommends +// to minimize jitter; the previous hardcoded F407 table used 1MHz. +func PLLParams168MHz() PLLParams { + switch xtalHz { + case 8_000_000: + return PLLParams{M: 4, N: 168, P: 2, Q: 7} + case 12_000_000: + return PLLParams{M: 6, N: 168, P: 2, Q: 7} + case 16_000_000: + return PLLParams{M: 8, N: 168, P: 2, Q: 7} + default: + panic("unsupported xtal frequency") + } +} diff --git a/src/machine/machine_stm32f4_pll_180mhz.go b/src/machine/machine_stm32f4_pll_180mhz.go new file mode 100644 index 0000000000..4272b9d715 --- /dev/null +++ b/src/machine/machine_stm32f4_pll_180mhz.go @@ -0,0 +1,18 @@ +//go:build stm32f4 && stm32f469 + +package machine + +// PLLParams180MHz returns the HSE PLL dividers needed to reach a 360MHz VCO +// (180MHz SYSCLK, P=2, Q=7, R=6) for the configured crystal frequency. +func PLLParams180MHz() PLLParams { + switch xtalHz { + case 8_000_000: + return PLLParams{M: 4, N: 180, P: 2, Q: 7, R: 6} + case 12_000_000: + return PLLParams{M: 6, N: 180, P: 2, Q: 7, R: 6} + case 16_000_000: + return PLLParams{M: 8, N: 180, P: 2, Q: 7, R: 6} + default: + panic("unsupported xtal frequency") + } +} diff --git a/src/machine/machine_stm32f7_pll_216mhz.go b/src/machine/machine_stm32f7_pll_216mhz.go new file mode 100644 index 0000000000..8237ff6248 --- /dev/null +++ b/src/machine/machine_stm32f7_pll_216mhz.go @@ -0,0 +1,18 @@ +//go:build stm32 && stm32f7x2 + +package machine + +// PLLParams216MHz returns the HSE PLL dividers needed to reach a 432MHz VCO +// (216MHz SYSCLK, P=2, Q=9) for the configured crystal frequency. +func PLLParams216MHz() PLLParams { + switch xtalHz { + case 8_000_000: + return PLLParams{M: 4, N: 216, P: 2, Q: 9} + case 12_000_000: + return PLLParams{M: 6, N: 216, P: 2, Q: 9} + case 16_000_000: + return PLLParams{M: 8, N: 216, P: 2, Q: 9} + default: + panic("unsupported xtal frequency") + } +} diff --git a/src/machine/machine_stm32f7x2.go b/src/machine/machine_stm32f7x2.go index 350b5fd7bd..7f201a4372 100644 --- a/src/machine/machine_stm32f7x2.go +++ b/src/machine/machine_stm32f7x2.go @@ -9,7 +9,8 @@ import ( ) func CPUFrequency() uint32 { - return 216000000 + pll := PLLParams216MHz() + return xtalHz / pll.M * pll.N / pll.P } // Internal use: configured speed of the APB1 and APB2 timers, this should be kept diff --git a/src/runtime/runtime_stm32f103.go b/src/runtime/runtime_stm32f103.go index 702d773897..0b68ac0db0 100644 --- a/src/runtime/runtime_stm32f103.go +++ b/src/runtime/runtime_stm32f103.go @@ -31,8 +31,10 @@ func buffered() int { return machine.Serial.Buffered() } -// initCLK sets clock to 72MHz using HSE 8MHz crystal w/ PLL X 9 (8MHz x 9 = 72MHz). +// initCLK sets clock to 72MHz using the board's HSE crystal (defined by the xtalHz) w/ PLL. func initCLK() { + pll := machine.PLLParams72MHz() + stm32.FLASH.ACR.SetBits(stm32.FLASH_ACR_LATENCY_WS2) // Two wait states, per datasheet stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PPRE1_Div2 << stm32.RCC_CFGR_PPRE1_Pos) // prescale PCLK1 = HCLK/2 stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PPRE2_Div1 << stm32.RCC_CFGR_PPRE2_Pos) // prescale PCLK2 = HCLK/1 @@ -49,9 +51,10 @@ func initCLK() { for !stm32.RCC.CR.HasBits(stm32.RCC_CR_HSIRDY) { } - stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PLLSRC) // set PLL source to HSE - stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PLLMUL_Mul9 << stm32.RCC_CFGR_PLLMUL_Pos) // multiply by 9 - stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON) // enable the PLL + stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PLLSRC) // set PLL source to HSE + stm32.RCC.CFGR.SetBits(pll.Prediv << stm32.RCC_CFGR_PLLXTPRE_Pos) // optional HSE /2 prescaler + stm32.RCC.CFGR.SetBits(pll.Mul << stm32.RCC_CFGR_PLLMUL_Pos) // PLL multiplier + stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON) // enable the PLL // wait for the PLLRDY flag for !stm32.RCC.CR.HasBits(stm32.RCC_CR_PLLRDY) { diff --git a/src/runtime/runtime_stm32f4.go b/src/runtime/runtime_stm32f4.go index be6b73255b..c323b34937 100644 --- a/src/runtime/runtime_stm32f4.go +++ b/src/runtime/runtime_stm32f4.go @@ -73,7 +73,7 @@ func initCLK() { // PCLK1 = HCLK / 4 stm32.RCC.CFGR.SetBits(stm32.RCC_CFGR_PPRE1_Div4 << stm32.RCC_CFGR_PPRE1_Pos) // Configure the main PLL - stm32.RCC.PLLCFGR.Set(PLL_CFGR) + stm32.RCC.PLLCFGR.Set(pllCFGR()) // Enable main PLL stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON) // Wait till the main PLL is ready diff --git a/src/runtime/runtime_stm32f405.go b/src/runtime/runtime_stm32f405.go index 8481a206e5..f4df584cf0 100644 --- a/src/runtime/runtime_stm32f405.go +++ b/src/runtime/runtime_stm32f405.go @@ -8,15 +8,15 @@ import ( ) const ( - // +----------------------+ - // | Clock Settings | - // +-------------+--------+ - // | HSE | 12mhz | - // | SYSCLK | 168mhz | - // | HCLK | 168mhz | - // | APB1(PCLK1) | 42mhz | - // | APB2(PCLK2) | 84mhz | - // +-------------+--------+ + // +---------------------------------------------+ + // | Clock Settings | + // +-------------+-------------------------------+ + // | HSE | selectable (xtal_8/12/16_mhz) | + // | SYSCLK | 168mhz | + // | HCLK | 168mhz | + // | APB1(PCLK1) | 42mhz | + // | APB2(PCLK2) | 84mhz | + // +-------------+-------------------------------+ HCLK_FREQ_HZ = 168000000 PCLK1_FREQ_HZ = HCLK_FREQ_HZ / 4 PCLK2_FREQ_HZ = HCLK_FREQ_HZ / 2 @@ -29,11 +29,6 @@ const ( PLL_SRC_HSE = 1 << stm32.RCC_PLLCFGR_PLLSRC_Pos // use HSE for PLL and PLLI2S PLL_SRC_HSI = 0 // use HSI for PLL and PLLI2S - PLL_DIV_M = 6 << stm32.RCC_PLLCFGR_PLLM_Pos - PLL_MLT_N = 168 << stm32.RCC_PLLCFGR_PLLN_Pos - PLL_DIV_P = ((2 >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP_Pos - PLL_DIV_Q = 7 << stm32.RCC_PLLCFGR_PLLQ_Pos - SYSCLK_SRC_PLL = stm32.RCC_CFGR_SW_PLL << stm32.RCC_CFGR_SW_Pos SYSCLK_STAT_PLL = stm32.RCC_CFGR_SWS_PLL << stm32.RCC_CFGR_SWS_Pos @@ -94,7 +89,12 @@ func initOSC() { } // set HSE as PLL source and configure clock divisors - stm32.RCC.PLLCFGR.Set(PLL_SRC_HSE | PLL_DIV_M | PLL_MLT_N | PLL_DIV_P | PLL_DIV_Q) + pll := machine.PLLParams168MHz() + stm32.RCC.PLLCFGR.Set(PLL_SRC_HSE | + pll.M<>1)-1)<> 1) - 1) << stm32.RCC_PLLCFGR_PLLP_Pos) | - (1 << stm32.RCC_PLLCFGR_PLLSRC_Pos) | (PLL_Q << stm32.RCC_PLLCFGR_PLLQ_Pos) -) +const HSE_STARTUP_TIMEOUT = 0x0500 + +// pllCFGR builds the RCC_PLLCFGR value for the current xtal - see RM0090 +// Reference Manual pg. 95. +func pllCFGR() uint32 { + pll := machine.PLLParams168MHz() + return pll.M | (pll.N << stm32.RCC_PLLCFGR_PLLN_Pos) | (((pll.P >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP_Pos) | + (1 << stm32.RCC_PLLCFGR_PLLSRC_Pos) | (pll.Q << stm32.RCC_PLLCFGR_PLLQ_Pos) +} diff --git a/src/runtime/runtime_stm32f469.go b/src/runtime/runtime_stm32f469.go index 38343dc0d6..9a540e16dc 100644 --- a/src/runtime/runtime_stm32f469.go +++ b/src/runtime/runtime_stm32f469.go @@ -2,27 +2,28 @@ package runtime -import "device/stm32" +import ( + "device/stm32" + "machine" +) /* clock settings - +-------------+--------+ - | HSE | 8mhz | - | SYSCLK | 180mhz | - | HCLK | 180mhz | - | APB2(PCLK2) | 90mhz | - | APB1(PCLK1) | 45mhz | - +-------------+--------+ + +-------------+--------------------------------+ + | HSE | selectable (xtal_8/12/16_mhz) | + | SYSCLK | 180mhz | + | HCLK | 180mhz | + | APB2(PCLK2) | 90mhz | + | APB1(PCLK1) | 45mhz | + +-------------+--------------------------------+ */ -const ( - HSE_STARTUP_TIMEOUT = 0x0500 - // PLL Options - See RM0386 Reference Manual pg. 148 - PLL_M = 8 // PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N - PLL_N = 360 - PLL_P = 2 // SYSCLK = PLL_VCO / PLL_P - PLL_Q = 7 // USB OTS FS, SDIO and RNG Clock = PLL_VCO / PLL_Q - PLL_R = 6 // DSI - PLL_CFGR = PLL_M | (PLL_N << stm32.RCC_PLLCFGR_PLLN_Pos) | (((PLL_P >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP_Pos) | - (1 << stm32.RCC_PLLCFGR_PLLSRC_Pos) | (PLL_Q << stm32.RCC_PLLCFGR_PLLQ_Pos) | (PLL_R << stm32.RCC_PLLCFGR_PLLR_Pos) -) +const HSE_STARTUP_TIMEOUT = 0x0500 + +// pllCFGR builds the RCC_PLLCFGR value for the current xtal - see RM0386 +// Reference Manual pg. 148. +func pllCFGR() uint32 { + pll := machine.PLLParams180MHz() + return pll.M | (pll.N << stm32.RCC_PLLCFGR_PLLN_Pos) | (((pll.P >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP_Pos) | + (1 << stm32.RCC_PLLCFGR_PLLSRC_Pos) | (pll.Q << stm32.RCC_PLLCFGR_PLLQ_Pos) | (pll.R << stm32.RCC_PLLCFGR_PLLR_Pos) +} diff --git a/src/runtime/runtime_stm32f7x2.go b/src/runtime/runtime_stm32f7x2.go index af78d70b7b..7084994d41 100644 --- a/src/runtime/runtime_stm32f7x2.go +++ b/src/runtime/runtime_stm32f7x2.go @@ -10,21 +10,15 @@ import ( /* clock settings - +-------------+--------+ - | HSE | 8mhz | - | SYSCLK | 216mhz | - | HCLK | 216mhz | - | APB1(PCLK1) | 27mhz | - | APB2(PCLK2) | 108mhz | - +-------------+--------+ + +-------------+--------------------------------+ + | HSE | selectable (xtal_8/12/16_mhz) | + | SYSCLK | 216mhz | + | HCLK | 216mhz | + | APB1(PCLK1) | 27mhz | + | APB2(PCLK2) | 108mhz | + +-------------+--------------------------------+ */ -const ( - HSE_STARTUP_TIMEOUT = 0x0500 - PLL_M = 4 - PLL_N = 216 - PLL_P = 2 - PLL_Q = 9 -) +const HSE_STARTUP_TIMEOUT = 0x0500 func init() { initCLK() @@ -107,12 +101,13 @@ func initOsc() { } // Configure the PLL: HSE as source, use SVD constants for positions. + pll := machine.PLLParams216MHz() stm32.RCC.PLLCFGR.Set( (stm32.RCC_PLLCFGR_PLLSRC_HSE << stm32.RCC_PLLCFGR_PLLSRC_Pos) | - (PLL_M << stm32.RCC_PLLCFGR_PLLM_Pos) | - (PLL_N << stm32.RCC_PLLCFGR_PLLN_Pos) | - (((PLL_P >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP_Pos) | - (PLL_Q << stm32.RCC_PLLCFGR_PLLQ_Pos)) + (pll.M << stm32.RCC_PLLCFGR_PLLM_Pos) | + (pll.N << stm32.RCC_PLLCFGR_PLLN_Pos) | + (((pll.P >> 1) - 1) << stm32.RCC_PLLCFGR_PLLP_Pos) | + (pll.Q << stm32.RCC_PLLCFGR_PLLQ_Pos)) // Enable the PLL, wait until ready stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON)