mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-07 20:43:40 +00:00
feat(machine/stm32): add STM32H7 and NUCLEO-H753ZI support
This commit is contained in:
committed by
Ron Evans
parent
40ed956d6c
commit
3797e89600
@@ -0,0 +1,179 @@
|
||||
//go:build stm32 && stm32h7
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"machine"
|
||||
_ "machine/usb/cdc"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initCLK()
|
||||
initMPU()
|
||||
|
||||
machine.InitSerial()
|
||||
|
||||
initTickTimer(&machine.TIM3)
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
func initCLK() {
|
||||
// 1. Enable SYSCFG
|
||||
stm32.RCC.APB4ENR.SetBits(stm32.RCC_APB4ENR_SYSCFGEN)
|
||||
|
||||
// H743/H753 have no SMPS; the NUCLEO-H753ZI runs VCORE from the internal
|
||||
// LDO (the CR3 reset state). Supply bits in CR3 are write-once after POR,
|
||||
// so keep LDOEN/BYPASS untouched and only enable the USB 3.3V level
|
||||
// detector needed by the USB transceivers.
|
||||
stm32.PWR.CR3.SetBits(stm32.PWR_CR3_USB33DEN)
|
||||
|
||||
// 3. Configure VOS1 (Scale 1)
|
||||
// RM0433 §6.8.4: ACTVOSRDY must be 1 (Run mode confirmed) before changing VOS.
|
||||
for stm32.PWR.CSR1.Get()&stm32.PWR_CSR1_ACTVOSRDY == 0 {
|
||||
}
|
||||
// RM0433: VOS1 is 0b11.
|
||||
stm32.PWR.D3CR.ReplaceBits(0b11<<stm32.PWR_D3CR_VOS_Pos, stm32.PWR_D3CR_VOS_Msk, 0)
|
||||
for stm32.PWR.D3CR.Get()&stm32.PWR_D3CR_VOSRDY == 0 {
|
||||
}
|
||||
|
||||
// 4. Enable HSE
|
||||
if machine.HSEBypass() {
|
||||
stm32.RCC.CR.SetBits(stm32.RCC_CR_HSEBYP | stm32.RCC_CR_HSEON)
|
||||
} else {
|
||||
stm32.RCC.CR.SetBits(stm32.RCC_CR_HSEON)
|
||||
}
|
||||
for stm32.RCC.CR.Get()&stm32.RCC_CR_HSERDY == 0 {
|
||||
}
|
||||
|
||||
// 5. Configure PLL1
|
||||
pll := machine.PLLParams400MHz()
|
||||
|
||||
// Source: HSE (2)
|
||||
stm32.RCC.PLLCKSELR.ReplaceBits(stm32.RCC_PLLCKSELR_PLLSRC_HSE, stm32.RCC_PLLCKSELR_PLLSRC_Msk, 0)
|
||||
// DIVM1
|
||||
stm32.RCC.PLLCKSELR.ReplaceBits(pll.M<<stm32.RCC_PLLCKSELR_DIVM1_Pos, stm32.RCC_PLLCKSELR_DIVM1_Msk, 0)
|
||||
|
||||
// PLL1CFGR: Wide VCO (0), Range based on pll.R (VCO input frequency)
|
||||
stm32.RCC.PLLCFGR.ReplaceBits(
|
||||
(stm32.RCC_PLLCFGR_PLL1VCOSEL_WideVCO<<stm32.RCC_PLLCFGR_PLL1VCOSEL_Pos)|
|
||||
(pll.R<<stm32.RCC_PLLCFGR_PLL1RGE_Pos),
|
||||
stm32.RCC_PLLCFGR_PLL1VCOSEL_Msk|stm32.RCC_PLLCFGR_PLL1RGE_Msk, 0)
|
||||
|
||||
// PLL1DIVR: DIVN1=pll.N, DIVP1=pll.P, DIVQ1=pll.Q
|
||||
// PLL1P = (VCO VCO_input * N) / P
|
||||
// PLL1Q = (VCO VCO_input * N) / Q
|
||||
stm32.RCC.PLL1DIVR.ReplaceBits(
|
||||
(pll.N-1)<<stm32.RCC_PLL1DIVR_DIVN1_Pos|(pll.P-1)<<stm32.RCC_PLL1DIVR_DIVP1_Pos|(pll.Q-1)<<stm32.RCC_PLL1DIVR_DIVQ1_Pos,
|
||||
stm32.RCC_PLL1DIVR_DIVN1_Msk|stm32.RCC_PLL1DIVR_DIVP1_Msk|stm32.RCC_PLL1DIVR_DIVQ1_Msk, 0)
|
||||
|
||||
// Enable PLL1P (SYSCLK=400MHz) and PLL1Q (SPI1/2/3 kernel=200MHz)
|
||||
stm32.RCC.PLLCFGR.SetBits(stm32.RCC_PLLCFGR_DIVP1EN | stm32.RCC_PLLCFGR_DIVQ1EN)
|
||||
|
||||
// Enable PLL1
|
||||
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLL1ON)
|
||||
for stm32.RCC.CR.Get()&stm32.RCC_CR_PLL1RDY == 0 {
|
||||
}
|
||||
|
||||
// 6. Bus Prescalers
|
||||
// D1CPRE=1 (0), HPRE=2 (8) -> HCLK=200MHz, D1PPRE (APB3)=2 (4) -> PCLK3=100MHz
|
||||
stm32.RCC.D1CFGR.ReplaceBits(
|
||||
(stm32.RCC_D1CFGR_D1CPRE_Div1<<stm32.RCC_D1CFGR_D1CPRE_Pos)|
|
||||
(stm32.RCC_D1CFGR_HPRE_Div2<<stm32.RCC_D1CFGR_HPRE_Pos)|
|
||||
(stm32.RCC_D1CFGR_D1PPRE_Div2<<stm32.RCC_D1CFGR_D1PPRE_Pos),
|
||||
stm32.RCC_D1CFGR_D1CPRE_Msk|stm32.RCC_D1CFGR_HPRE_Msk|stm32.RCC_D1CFGR_D1PPRE_Msk, 0)
|
||||
|
||||
// D2CFGR: D2PPRE1 (APB1)=2 (4) -> PCLK1=100MHz, D2PPRE2 (APB2)=2 (4) -> PCLK2=100MHz
|
||||
stm32.RCC.D2CFGR.ReplaceBits(
|
||||
(stm32.RCC_D2CFGR_D2PPRE1_Div2<<stm32.RCC_D2CFGR_D2PPRE1_Pos)|
|
||||
(stm32.RCC_D2CFGR_D2PPRE2_Div2<<stm32.RCC_D2CFGR_D2PPRE2_Pos),
|
||||
stm32.RCC_D2CFGR_D2PPRE1_Msk|stm32.RCC_D2CFGR_D2PPRE2_Msk, 0)
|
||||
|
||||
// D3CFGR: D3PPRE (APB4)=2 (4) -> PCLK4=100MHz
|
||||
stm32.RCC.D3CFGR.ReplaceBits(
|
||||
stm32.RCC_D3CFGR_D3PPRE_Div2<<stm32.RCC_D3CFGR_D3PPRE_Pos,
|
||||
stm32.RCC_D3CFGR_D3PPRE_Msk, 0)
|
||||
|
||||
// 7. Flash Latency
|
||||
// VOS1, 200MHz AXI clock -> 2 wait states, WRHIGHFREQ=2 (RM0433 Table 17).
|
||||
stm32.FLASH.ACR.ReplaceBits(2|2<<stm32.FLASH_ACR_WRHIGHFREQ_Pos,
|
||||
stm32.FLASH_ACR_LATENCY_Msk|stm32.FLASH_ACR_WRHIGHFREQ_Msk, 0)
|
||||
for stm32.FLASH.ACR.Get()&stm32.FLASH_ACR_LATENCY_Msk != 2 {
|
||||
}
|
||||
|
||||
// 8. Switch to PLL1
|
||||
// SW: PLL1 (3)
|
||||
stm32.RCC.CFGR.ReplaceBits(3<<stm32.RCC_CFGR_SW_Pos, stm32.RCC_CFGR_SW_Msk, 0)
|
||||
for (stm32.RCC.CFGR.Get() & stm32.RCC_CFGR_SWS_Msk) != (3 << stm32.RCC_CFGR_SWS_Pos) {
|
||||
}
|
||||
|
||||
// 9. Peripheral Kernel Clocks
|
||||
// I2C1,2,3 source: HSI_KER (2) to keep 64MHz timing compatibility.
|
||||
stm32.RCC.D2CCIP2R.ReplaceBits(stm32.RCC_D2CCIP2R_I2C123SEL_HSI_KER<<stm32.RCC_D2CCIP2R_I2C123SEL_Pos, stm32.RCC_D2CCIP2R_I2C123SEL_Msk, 0)
|
||||
// I2C4 source: HSI_KER (2)
|
||||
stm32.RCC.D3CCIPR.ReplaceBits(stm32.RCC_D3CCIPR_I2C4SEL_HSI_KER<<stm32.RCC_D3CCIPR_I2C4SEL_Pos, stm32.RCC_D3CCIPR_I2C4SEL_Msk, 0)
|
||||
|
||||
// SPI1,2,3 source: PLL1_Q (0) -> 200MHz
|
||||
stm32.RCC.D2CCIP1R.ReplaceBits(stm32.RCC_D2CCIP1R_SPI123SEL_PLL1_Q<<stm32.RCC_D2CCIP1R_SPI123SEL_Pos, stm32.RCC_D2CCIP1R_SPI123SEL_Msk, 0)
|
||||
// SPI4,5 source: APB (0) -> PCLK2 = 100MHz (PLL1-derived)
|
||||
stm32.RCC.D2CCIP1R.ReplaceBits(stm32.RCC_D2CCIP1R_SPI45SEL_APB<<stm32.RCC_D2CCIP1R_SPI45SEL_Pos, stm32.RCC_D2CCIP1R_SPI45SEL_Msk, 0)
|
||||
// SPI6 source: PCLK4 (0) -> 100MHz (PLL1-derived)
|
||||
stm32.RCC.D3CCIPR.ReplaceBits(stm32.RCC_D3CCIPR_SPI6SEL_RCC_PCLK4<<stm32.RCC_D3CCIPR_SPI6SEL_Pos, stm32.RCC_D3CCIPR_SPI6SEL_Msk, 0)
|
||||
|
||||
// 10. HSI48 — used as kernel clock for RNG and USB (RM0433 §33.3 requires ≤48 MHz).
|
||||
stm32.RCC.CR.SetBits(stm32.RCC_CR_HSI48ON)
|
||||
for stm32.RCC.CR.Get()&stm32.RCC_CR_HSI48RDY == 0 {
|
||||
}
|
||||
// RNGSEL and USBSEL reset value is 0x0 (HSI48 or PLL1_Q); set explicitly to HSI48.
|
||||
stm32.RCC.D2CCIP2R.ReplaceBits(
|
||||
stm32.RCC_D2CCIP2R_RNGSEL_HSI48<<stm32.RCC_D2CCIP2R_RNGSEL_Pos|
|
||||
stm32.RCC_D2CCIP2R_USBSEL_HSI48<<stm32.RCC_D2CCIP2R_USBSEL_Pos,
|
||||
stm32.RCC_D2CCIP2R_RNGSEL_Msk|stm32.RCC_D2CCIP2R_USBSEL_Msk, 0)
|
||||
|
||||
// 11. Enable CRS (Clock Recovery System) for HSI48 stabilization via USB SOF.
|
||||
stm32.RCC.APB1HENR.SetBits(stm32.RCC_APB1HENR_CRSEN)
|
||||
stm32.CRS.CFGR.ReplaceBits(stm32.CRS_CFGR_SYNCSRC_USB_SOF<<stm32.CRS_CFGR_SYNCSRC_Pos, stm32.CRS_CFGR_SYNCSRC_Msk, 0)
|
||||
stm32.CRS.CR.SetBits(stm32.CRS_CR_CEN | stm32.CRS_CR_AUTOTRIMEN)
|
||||
|
||||
// 12. Configure PLL2 for ADC (80MHz)
|
||||
// DIVM2 = pll.M
|
||||
stm32.RCC.PLLCKSELR.ReplaceBits(pll.M<<stm32.RCC_PLLCKSELR_DIVM2_Pos, stm32.RCC_PLLCKSELR_DIVM2_Msk, 0)
|
||||
|
||||
// PLL2CFGR: Wide VCO (0), Range based on pll.R (VCO input frequency)
|
||||
stm32.RCC.PLLCFGR.ReplaceBits(
|
||||
(stm32.RCC_PLLCFGR_PLL2VCOSEL_WideVCO<<stm32.RCC_PLLCFGR_PLL2VCOSEL_Pos)|
|
||||
(pll.R<<stm32.RCC_PLLCFGR_PLL2RGE_Pos),
|
||||
stm32.RCC_PLLCFGR_PLL2VCOSEL_Msk|stm32.RCC_PLLCFGR_PLL2RGE_Msk, 0)
|
||||
|
||||
// PLL2DIVR: DIVN2=pll.N, DIVP2=10 (Value 9)
|
||||
// PLL2P = (VCO VCO_input * N) / 10 = 80MHz
|
||||
stm32.RCC.PLL2DIVR.ReplaceBits(
|
||||
(pll.N-1)<<stm32.RCC_PLL2DIVR_DIVN2_Pos|9<<stm32.RCC_PLL2DIVR_DIVP2_Pos,
|
||||
stm32.RCC_PLL2DIVR_DIVN2_Msk|stm32.RCC_PLL2DIVR_DIVP2_Msk, 0)
|
||||
|
||||
// Enable DIVP2EN
|
||||
stm32.RCC.PLLCFGR.SetBits(stm32.RCC_PLLCFGR_DIVP2EN)
|
||||
|
||||
// Enable PLL2
|
||||
stm32.RCC.CR.SetBits(stm32.RCC_CR_PLL2ON)
|
||||
for stm32.RCC.CR.Get()&stm32.RCC_CR_PLL2RDY == 0 {
|
||||
}
|
||||
|
||||
// 12. ADC kernel clock source: PLL2_P (0).
|
||||
stm32.RCC.D3CCIPR.ReplaceBits(stm32.RCC_D3CCIPR_ADCSEL_PLL2_P<<stm32.RCC_D3CCIPR_ADCSEL_Pos, stm32.RCC_D3CCIPR_ADCSEL_Msk, 0)
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
//go:build stm32 && stm32h7
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Cortex-M7 cache size registers (ARM TRM Table 4-2, within SCB address space).
|
||||
var (
|
||||
scbCCSIDR = (*volatile.Register32)(unsafe.Pointer(uintptr(0xE000ED80))) // Cache Size ID Register (R)
|
||||
scbCSELR = (*volatile.Register32)(unsafe.Pointer(uintptr(0xE000ED84))) // Cache Size Selection Register (R/W)
|
||||
)
|
||||
|
||||
// Cortex-M7 cache maintenance registers (ARMv7-M Architecture Ref Manual Table B3-7).
|
||||
var (
|
||||
scbICIALLU = (*volatile.Register32)(unsafe.Pointer(uintptr(0xE000EF50))) // Invalidate all I-cache (W)
|
||||
scbDCISW = (*volatile.Register32)(unsafe.Pointer(uintptr(0xE000EF60))) // Invalidate D-cache by set/way (W)
|
||||
)
|
||||
|
||||
// RASR region attribute presets for this chip's memory map (ARMv7-M
|
||||
// Architecture Ref Manual §B3.5.5). The register layout itself
|
||||
// (arm.MPU_Type, arm.MPU_RASR_* field positions) is generic to any
|
||||
// ARMv7-M core (M3/M4/M7) and lives in device/arm; only these specific
|
||||
// size/type/permission combinations are STM32H7-specific.
|
||||
const (
|
||||
// SIZE field bits[5:1]: value = log2(region_bytes) - 1.
|
||||
mpuRASRSize2MB = 20 << arm.MPU_RASR_SIZE_Pos // 2MB = 2^21, field=20
|
||||
mpuRASRSize512KB = 18 << arm.MPU_RASR_SIZE_Pos // 512KB = 2^19, field=18
|
||||
mpuRASRSize512MB = 28 << arm.MPU_RASR_SIZE_Pos // 512MB = 2^29, field=28
|
||||
|
||||
// AP field bits[26:24].
|
||||
mpuRASRAPReadOnly = 0x6 << arm.MPU_RASR_AP_Pos // Privileged and unprivileged read-only
|
||||
mpuRASRAPFullAccess = 0x3 << arm.MPU_RASR_AP_Pos // Full access (privileged and unprivileged)
|
||||
|
||||
// Memory type encodings: TEX bits[21:19], S bit[18], C bit[17], B bit[16].
|
||||
// Normal, Write-Through, No Write-Allocate (TEX=000, C=1, B=0, S=0).
|
||||
mpuRASRNormalWT = arm.MPU_RASR_C
|
||||
// Normal, Write-Back, Write-Allocate (TEX=001, C=1, B=1, S=0).
|
||||
mpuRASRNormalWBWA = (1 << arm.MPU_RASR_TEX_Pos) | arm.MPU_RASR_C | arm.MPU_RASR_B
|
||||
// Shared Device memory (TEX=000, C=0, B=1, S=1).
|
||||
mpuRASRDevice = arm.MPU_RASR_S | arm.MPU_RASR_B
|
||||
)
|
||||
|
||||
// initMPU configures the Cortex-M7 MPU, then enables L1 instruction and data
|
||||
// caches. Must be called after initCLK() and before any peripheral access.
|
||||
//
|
||||
// Memory map configured:
|
||||
//
|
||||
// Region 0: Flash 0x08000000 2MB Normal WT, RO, executable
|
||||
// Region 1: AXI SRAM 0x24000000 512KB Normal WBWA, RW, no-execute
|
||||
// Region 2: Peripherals 0x40000000 512MB Shared Device, RW, no-execute
|
||||
//
|
||||
// Unmapped regions fall back to the ARMv7-M default privileged map via
|
||||
// PRIVDEFENA, keeping NVIC/SCB and other PPB accesses strongly-ordered.
|
||||
func initMPU() {
|
||||
// Disable MPU before reconfiguring regions.
|
||||
arm.MPU.CTRL.Set(0)
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
|
||||
// Region 0: Flash — Normal, Write-Through, read-only, executable.
|
||||
arm.MPU.RNR.Set(0)
|
||||
arm.MPU.RBAR.Set(0x08000000)
|
||||
arm.MPU.RASR.Set(mpuRASRNormalWT | mpuRASRAPReadOnly | mpuRASRSize2MB | arm.MPU_RASR_ENABLE)
|
||||
|
||||
// Region 1: AXI SRAM — Normal, Write-Back Write-Allocate, full access, no-execute.
|
||||
arm.MPU.RNR.Set(1)
|
||||
arm.MPU.RBAR.Set(0x24000000)
|
||||
arm.MPU.RASR.Set(arm.MPU_RASR_XN | mpuRASRNormalWBWA | mpuRASRAPFullAccess | mpuRASRSize512KB | arm.MPU_RASR_ENABLE)
|
||||
|
||||
// Region 2: Peripherals — Shared Device, full access, no-execute.
|
||||
arm.MPU.RNR.Set(2)
|
||||
arm.MPU.RBAR.Set(0x40000000)
|
||||
arm.MPU.RASR.Set(arm.MPU_RASR_XN | mpuRASRDevice | mpuRASRAPFullAccess | mpuRASRSize512MB | arm.MPU_RASR_ENABLE)
|
||||
|
||||
// Enable MemManage fault so MPU violations raise a MemFault rather than
|
||||
// hard-faulting directly.
|
||||
arm.SCB.SHCSR.SetBits(arm.SCB_SHCSR_MEMFAULTENA)
|
||||
|
||||
// Enable MPU with privileged default background map.
|
||||
arm.MPU.CTRL.Set(arm.MPU_CTRL_ENABLE | arm.MPU_CTRL_PRIVDEFENA)
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
|
||||
// Enable L1 caches now that the MPU defines cacheability for each region.
|
||||
initICache()
|
||||
initDCache()
|
||||
}
|
||||
|
||||
// initICache invalidates then enables the L1 instruction cache.
|
||||
func initICache() {
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
scbICIALLU.Set(0) // Invalidate all I-cache lines.
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
arm.SCB.CCR.SetBits(arm.SCB_CCR_IC)
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
}
|
||||
|
||||
// initDCache invalidates all D-cache lines by set/way then enables the cache.
|
||||
// Iterates over sets and ways read from CCSIDR so it works for any M7 cache
|
||||
// size (8–64 KB, always 4-way, 32-byte lines on STM32H743).
|
||||
func initDCache() {
|
||||
scbCSELR.Set(0) // Select L1 D-cache.
|
||||
arm.Asm("dsb 0xF")
|
||||
|
||||
ccsidr := scbCCSIDR.Get()
|
||||
numSets := (ccsidr >> 13) & 0x7FFF // NUMSETS field (value = sets-1)
|
||||
assoc := (ccsidr >> 3) & 0x3FF // ASSOCIATIVITY field (value = ways-1)
|
||||
|
||||
// Invalidate every set/way. For a 4-way cache the way index occupies
|
||||
// bits[31:30] of DCISW; the set index starts at bit 5 (32-byte line = 2^5).
|
||||
for set := uint32(0); set <= numSets; set++ {
|
||||
for way := uint32(0); way <= assoc; way++ {
|
||||
scbDCISW.Set((way << 30) | (set << 5))
|
||||
}
|
||||
}
|
||||
arm.Asm("dsb 0xF")
|
||||
|
||||
arm.SCB.CCR.SetBits(arm.SCB_CCR_DC)
|
||||
arm.Asm("dsb 0xF")
|
||||
arm.Asm("isb 0xF")
|
||||
}
|
||||
Reference in New Issue
Block a user