Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/machine/board_bluepill.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"runtime/interrupt"
)

const xtalHz = 8_000_000

// Pins printed on the silkscreen
const (
C13 = PC13
Expand Down
2 changes: 2 additions & 0 deletions src/machine/board_feather-stm32f405.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"runtime/interrupt"
)

const xtalHz = 12_000_000

const (
NUM_DIGITAL_IO_PINS = 39
NUM_ANALOG_IO_PINS = 7
Expand Down
2 changes: 2 additions & 0 deletions src/machine/board_mksnanov3.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions src/machine/board_nucleof103rb.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"runtime/interrupt"
)

const xtalHz = 8_000_000

const (
LED = LED_BUILTIN
LED_BUILTIN = LED_GREEN
Expand Down
2 changes: 2 additions & 0 deletions src/machine/board_nucleof722ze.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"runtime/interrupt"
)

const xtalHz = 8_000_000

const (
LED = LED_BUILTIN
LED_BUILTIN = LED_GREEN
Expand Down
2 changes: 2 additions & 0 deletions src/machine/board_stm32f469disco.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"runtime/interrupt"
)

const xtalHz = 8_000_000

const (
LED = LED_BUILTIN
LED1 = LED_GREEN
Expand Down
2 changes: 2 additions & 0 deletions src/machine/board_stm32f4disco.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"runtime/interrupt"
)

const xtalHz = 8_000_000

const (
LED1 = LED_GREEN
LED2 = LED_ORANGE
Expand Down
10 changes: 10 additions & 0 deletions src/machine/machine_stm32_pll.go
Original file line number Diff line number Diff line change
@@ -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 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this intended to be referenced from outside the package? It seems to be used only internally for now, so I think unexported (pllParams) would be fine.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The machine.PLLParamsXXXMHz() functions are called from the runtime (e.g. from the src/runtime/runtime_stm32f405.go). So unfortunately this has to be exported unless we want to do massive refactoring and consolidate all clock initialization in either machine or runtime package.

@rdon-key rdon-key Jul 17, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation, that makes sense.

M, N, P, Q, R uint32
}
5 changes: 4 additions & 1 deletion src/machine/machine_stm32f103.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
27 changes: 27 additions & 0 deletions src/machine/machine_stm32f103_pll_72mhz.go
Original file line number Diff line number Diff line change
@@ -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")
}
}
3 changes: 2 additions & 1 deletion src/machine/machine_stm32f40x.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/machine/machine_stm32f469.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions src/machine/machine_stm32f4_pll_168mhz.go
Original file line number Diff line number Diff line change
@@ -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")
}
}
18 changes: 18 additions & 0 deletions src/machine/machine_stm32f4_pll_180mhz.go
Original file line number Diff line number Diff line change
@@ -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")
}
}
18 changes: 18 additions & 0 deletions src/machine/machine_stm32f7_pll_216mhz.go
Original file line number Diff line number Diff line change
@@ -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")
}
}
3 changes: 2 additions & 1 deletion src/machine/machine_stm32f7x2.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 7 additions & 4 deletions src/runtime/runtime_stm32f103.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/runtime_stm32f4.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 15 additions & 15 deletions src/runtime/runtime_stm32f405.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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<<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 PLL and wait for it to sync
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLLON)
Expand Down
38 changes: 20 additions & 18 deletions src/runtime/runtime_stm32f407.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,28 @@

package runtime

import "device/stm32"
import (
"device/stm32"
"machine"
)

/*
clock settings

+-------------+--------+
| HSE | 8mhz |
| SYSCLK | 168mhz |
| HCLK | 168mhz |
| APB2(PCLK2) | 84mhz |
| APB1(PCLK1) | 42mhz |
+-------------+--------+
+-------------+--------------------------------+
| HSE | selectable (xtal_8/12/16_mhz) |
| SYSCLK | 168mhz |
| HCLK | 168mhz |
| APB2(PCLK2) | 84mhz |
| APB1(PCLK1) | 42mhz |
+-------------+--------------------------------+
*/
const (
HSE_STARTUP_TIMEOUT = 0x0500
// PLL Options - See RM0090 Reference Manual pg. 95
PLL_M = 8 // PLL_VCO = (HSE_VALUE or HSI_VLAUE / PLL_M) * PLL_N
PLL_N = 336
PLL_P = 2 // SYSCLK = PLL_VCO / PLL_P
PLL_Q = 7 // USB OTS FS, SDIO and RNG Clock = PLL_VCO / PLL_Q
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)
)
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)
}
39 changes: 20 additions & 19 deletions src/runtime/runtime_stm32f469.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Loading
Loading