From 08b7c6e995ccc51c2a9917d293ae2645e296331c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Teichgr=C3=A4ber?= Date: Tue, 28 Jul 2026 22:22:36 +0200 Subject: [PATCH 1/5] gen-device-svd/tweak: stm32: add alias WWDG for WWDG1 --- tools/gen-device-svd/tweak.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/gen-device-svd/tweak.go b/tools/gen-device-svd/tweak.go index 8ee505da14..265691e037 100644 --- a/tools/gen-device-svd/tweak.go +++ b/tools/gen-device-svd/tweak.go @@ -15,7 +15,9 @@ func tweakDevice(d *Device, pkgName string) { // a register IWDG. On some devices, though, like the h723, // there are two registers, IWDG1 and IWDG2. In this case we // define an alias IWDG for IWDG1. - addUnnumberedAlias(d, "IWDG", "IWDG1") + addUnnumberedPeriphAlias(d, "IWDG", "IWDG1") + + addUnnumberedPeriphAlias(d, "WWDG", "WWDG1") for _, p := range d.Peripherals { switch p.GroupName { @@ -55,7 +57,7 @@ func tweakDevice(d *Device, pkgName string) { } } -func addUnnumberedAlias(d *Device, dest, src string) { +func addUnnumberedPeriphAlias(d *Device, dest, src string) { if _, ok := d.PeripheralDict[dest]; !ok { if p := d.PeripheralDict[src]; p != nil { p.Alias = dest From ea17e57fb88e7d1df69fdc4974f76b5622b6ef78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Teichgr=C3=A4ber?= Date: Tue, 28 Jul 2026 22:28:03 +0200 Subject: [PATCH 2/5] gen-device-svd: add IRQ alias mechanism, and alias TIM_CC to TIM1_CC This ensures that identifiers like stm32.IRQ_TIM_CC compile successfully even when the SVD file defines IRQ_TIM1_CC only. This complements the existing peripheral alias mechanism. --- tools/gen-device-svd/gen-device-svd.go | 4 ++++ tools/gen-device-svd/tweak.go | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/tools/gen-device-svd/gen-device-svd.go b/tools/gen-device-svd/gen-device-svd.go index ef9dedff97..6e9c993324 100755 --- a/tools/gen-device-svd/gen-device-svd.go +++ b/tools/gen-device-svd/gen-device-svd.go @@ -121,6 +121,7 @@ type Metadata struct { type Interrupt struct { Name string + Alias string HandlerName string PeripheralIndex int Value int // interrupt number @@ -1278,6 +1279,9 @@ const ( {{- end}} {{- end}} IRQ_{{.Name}} = {{.Value}} + {{- if .Alias}} + IRQ_{{.Alias}} = IRQ_{{.Name}} + {{- end}} {{- "\n"}} {{- end}} // Highest interrupt number on this device. diff --git a/tools/gen-device-svd/tweak.go b/tools/gen-device-svd/tweak.go index 265691e037..bbae3954f1 100644 --- a/tools/gen-device-svd/tweak.go +++ b/tools/gen-device-svd/tweak.go @@ -18,6 +18,7 @@ func tweakDevice(d *Device, pkgName string) { addUnnumberedPeriphAlias(d, "IWDG", "IWDG1") addUnnumberedPeriphAlias(d, "WWDG", "WWDG1") + addUnnumberedIRQAlias(d, "TIM_CC", "TIM1_CC") for _, p := range d.Peripherals { switch p.GroupName { @@ -65,6 +66,23 @@ func addUnnumberedPeriphAlias(d *Device, dest, src string) { } } +func addUnnumberedIRQAlias(d *Device, dest, src string) { + var iSrc *Interrupt + + for _, i := range d.Interrupts { + if i.Name == dest { + return + } + if i.Name == src { + iSrc = i + } + } + if iSrc == nil { + return + } + iSrc.Alias = dest +} + func stm32EnsureCCMROrder(registers []*PeripheralField) { for i, r := range registers { if i > 0 { From ae2a08c34b91a73d6a7a4213f77260133951490e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Teichgr=C3=A4ber?= Date: Tue, 28 Jul 2026 23:02:27 +0200 Subject: [PATCH 3/5] machine/stm32: add nucleo-h723zg support --- src/machine/board_nucleoh723zg.go | 97 ++++++++++++++++++++++++ src/machine/machine_stm32h7.go | 6 +- src/machine/machine_stm32h7_no_port_i.go | 13 ++++ src/machine/machine_stm32h7_port_i.go | 18 +++++ src/machine/machine_stm32h7_usb.go | 2 +- src/machine/usb.go | 2 +- src/runtime/runtime_stm32h7.go | 1 - src/runtime/runtime_stm32h7_usbcdc.go | 7 ++ targets/nucleo-h723zg.json | 13 ++++ targets/stm32h723xg.ld | 19 +++++ 10 files changed, 172 insertions(+), 6 deletions(-) create mode 100644 src/machine/board_nucleoh723zg.go create mode 100644 src/machine/machine_stm32h7_no_port_i.go create mode 100644 src/machine/machine_stm32h7_port_i.go create mode 100644 src/runtime/runtime_stm32h7_usbcdc.go create mode 100644 targets/nucleo-h723zg.json create mode 100644 targets/stm32h723xg.ld diff --git a/src/machine/board_nucleoh723zg.go b/src/machine/board_nucleoh723zg.go new file mode 100644 index 0000000000..5dcc669a22 --- /dev/null +++ b/src/machine/board_nucleoh723zg.go @@ -0,0 +1,97 @@ +//go:build nucleoh723zg + +package machine + +import ( + "device/stm32" + "runtime/interrupt" +) + +const xtalHz = 8_000_000 +const hseBypass = true + +const ( + // Arduino Pins + A0 = PA3 // ADC12_INP15 + A1 = PC0 // ADC123_INP10 + A2 = PC3 // ADC12_INP13 + A3 = PB1 // ADC12_INP5 + A4 = PC2 // ADC123_INP12 || I2C1_SDA + A5 = PF10 // ADC3_INP6 || I2C1_SCL + A6 = PF4 // ADC3_INP9 + A7 = PF5 // ADC3_INP4 + A8 = PF6 // ADC3_INP8 + + D0 = PB7 // LPUART1 + D1 = PB6 // LPUART1 + D2 = PG14 + D3 = PE13 // TIM3_CH3 + D4 = PE14 + D5 = PE11 // TIM1_CH2 / I2C1_SCL + D6 = PE9 // TIM1_CH2 + D7 = PG12 + D8 = PF3 + D9 = PD15 // TIM4_CH4 + D10 = PD14 // SPI1_CS || TIM4_CH3 + D11 = PB5 // SPI1_MOSI || TIM3_CH2 + D12 = PA6 // SPI1_MISO + D13 = PA5 // SPI1_SCK + D14 = PB9 + D15 = PB8 +) + +const ( + LED = LED_BUILTIN + LED_BUILTIN = LED_GREEN + LED_GREEN = PB0 + LED_YELLOW = PE1 + LED_RED = PB14 +) + +const ( + BUTTON = PC13 +) + +const ( + // UART pins + // PA2 and PA3 are connected to the ST-Link Virtual Com Port (VCP) + UART_TX_PIN = PD8 + UART_RX_PIN = PD9 + + // SPI + SPI1_SCK_PIN = PA5 + SPI1_SDI_PIN = PB5 + SPI1_SDO_PIN = PA6 + SPI0_SCK_PIN = SPI1_SCK_PIN + SPI0_SDI_PIN = SPI1_SDI_PIN + SPI0_SDO_PIN = SPI1_SDO_PIN + + // I2C pins + I2C0_SCL_PIN = PF1 // I2C2 + I2C0_SDA_PIN = PF0 // I2C2 +) + +var ( + // USART3 is the hardware serial port connected to the + // onboard ST-LINK debugger to be exposed as virtual COM + // port over USB on Nucleo boards. + UART1 = &_UART1 + _UART1 = UART{ + Buffer: NewRingBuffer(), + Bus: stm32.USART3, + TxAltFuncSelector: 7, + RxAltFuncSelector: 7, + } + DefaultUART = UART1 + + // I2C2 is documented, alias to I2C0 as well + I2C2 = &I2C{ + Bus: stm32.I2C2, + AltFuncSelector: 4, + } + I2C0 = I2C2 +) + +func init() { + UART1.Interrupt = interrupt.New(stm32.IRQ_USART3, _UART1.handleInterrupt) +} diff --git a/src/machine/machine_stm32h7.go b/src/machine/machine_stm32h7.go index f889953147..6727d41582 100644 --- a/src/machine/machine_stm32h7.go +++ b/src/machine/machine_stm32h7.go @@ -317,7 +317,7 @@ func (p Pin) getPort() *stm32.GPIO_Type { case 7: return stm32.GPIOH case 8: - return stm32.GPIOI + return pinGetPortI() case 9: return stm32.GPIOJ case 10: @@ -345,12 +345,12 @@ func (p Pin) enableClock() { stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOGEN) case stm32.GPIOH: stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOHEN) - case stm32.GPIOI: - stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOIEN) case stm32.GPIOJ: stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOJEN) case stm32.GPIOK: stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOKEN) + default: + p.enableClockOther() } } diff --git a/src/machine/machine_stm32h7_no_port_i.go b/src/machine/machine_stm32h7_no_port_i.go new file mode 100644 index 0000000000..721cef3919 --- /dev/null +++ b/src/machine/machine_stm32h7_no_port_i.go @@ -0,0 +1,13 @@ +//go:build stm32 && stm32h7 && stm32h723 + +package machine + +import ( + "device/stm32" +) + +func pinGetPortI() *stm32.GPIO_Type { + panic("machine: unknown port") +} + +func (p Pin) enableClockOther() {} diff --git a/src/machine/machine_stm32h7_port_i.go b/src/machine/machine_stm32h7_port_i.go new file mode 100644 index 0000000000..b018c9d84c --- /dev/null +++ b/src/machine/machine_stm32h7_port_i.go @@ -0,0 +1,18 @@ +//go:build stm32 && stm32h7 && !stm32h723 + +package machine + +import ( + "device/stm32" +) + +func pinGetPortI() *stm32.GPIO_Type { + return stm32.GPIOI +} + +func (p Pin) enableClockOther() { + switch p.getPort() { + case stm32.GPIOI: + stm32.RCC.AHB4ENR.SetBits(stm32.RCC_AHB4ENR_GPIOIEN) + } +} diff --git a/src/machine/machine_stm32h7_usb.go b/src/machine/machine_stm32h7_usb.go index 7673819b01..ff6e90bc85 100644 --- a/src/machine/machine_stm32h7_usb.go +++ b/src/machine/machine_stm32h7_usb.go @@ -1,4 +1,4 @@ -//go:build stm32 && stm32h7 +//go:build stm32 && stm32h7 && !stm32h723 package machine diff --git a/src/machine/usb.go b/src/machine/usb.go index 9fcc1997d7..91a4a4d6da 100644 --- a/src/machine/usb.go +++ b/src/machine/usb.go @@ -1,4 +1,4 @@ -//go:build sam || nrf52840 || rp2040 || rp2350 || stm32f4 || stm32f7 || stm32h7 +//go:build sam || nrf52840 || rp2040 || rp2350 || stm32f4 || stm32f7 || (stm32h7 && !stm32h723) package machine diff --git a/src/runtime/runtime_stm32h7.go b/src/runtime/runtime_stm32h7.go index 6f41eadaa6..741596bf21 100644 --- a/src/runtime/runtime_stm32h7.go +++ b/src/runtime/runtime_stm32h7.go @@ -5,7 +5,6 @@ package runtime import ( "device/stm32" "machine" - _ "machine/usb/cdc" ) func init() { diff --git a/src/runtime/runtime_stm32h7_usbcdc.go b/src/runtime/runtime_stm32h7_usbcdc.go new file mode 100644 index 0000000000..32726da65d --- /dev/null +++ b/src/runtime/runtime_stm32h7_usbcdc.go @@ -0,0 +1,7 @@ +//go:build stm32 && stm32h7 && !stm32h723 + +package runtime + +import ( + _ "machine/usb/cdc" +) diff --git a/targets/nucleo-h723zg.json b/targets/nucleo-h723zg.json new file mode 100644 index 0000000000..7e4e3dcc32 --- /dev/null +++ b/targets/nucleo-h723zg.json @@ -0,0 +1,13 @@ +{ + "inherits": ["cortex-m7"], + "build-tags": ["nucleoh723zg", "stm32h723", "stm32h7", "stm32"], + "serial": "uart", + "linkerscript": "targets/stm32h723xg.ld", + "extra-files": [ + "src/device/stm32/stm32h723.s" + ], + "flash-method": "openocd", + "openocd-interface": "stlink", + "openocd-target": "stm32h7x", + "openocd-commands": ["reset_config srst_only srst_nogate connect_assert_srst"] +} \ No newline at end of file diff --git a/targets/stm32h723xg.ld b/targets/stm32h723xg.ld new file mode 100644 index 0000000000..4957a18441 --- /dev/null +++ b/targets/stm32h723xg.ld @@ -0,0 +1,19 @@ + +MEMORY +{ + FLASH_TEXT (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + RAM (xrw) : ORIGIN = 0x24000000, LENGTH = 128K +} + +/* + * D2 SRAM (0x30000000, 32K on H723) and D3 SRAM (0x38000000, 16K) are not + * mapped here and unused by the runtime. If a future driver places DMA + * buffers there, add MPU regions for them in initMPU() + * (src/runtime/runtime_stm32h7_mpu.go) — they currently fall through to the + * default privileged WBWA map, so cache maintenance via DCacheClean/ + * DCacheInvalidate/DCacheFlush would silently miss those addresses. + */ + +_stack_size = 8K; + +INCLUDE "targets/arm.ld" From f614a43f1a6a4051596a569c3e84646e7ea963a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Teichgr=C3=A4ber?= Date: Tue, 28 Jul 2026 23:15:36 +0200 Subject: [PATCH 4/5] machine/stm32_moder_gpio: ConfigureAltFunc: add PinModeETH --- src/machine/machine_stm32_moder_gpio.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/machine/machine_stm32_moder_gpio.go b/src/machine/machine_stm32_moder_gpio.go index 6a45d47fae..39e9672a3e 100644 --- a/src/machine/machine_stm32_moder_gpio.go +++ b/src/machine/machine_stm32_moder_gpio.go @@ -39,6 +39,9 @@ const ( // for USB (DP/DM lines) PinModeUSB PinMode = 13 + + // for Ethernet + PinModeETH PinMode = 14 ) // Define several bitfields that have different names across chip families but @@ -158,6 +161,12 @@ func (p Pin) ConfigureAltFunc(config PinConfig, altFunc uint8) { case PinInputAnalog: port.MODER.ReplaceBits(gpioModeAnalog, gpioModeMask, pos) port.PUPDR.ReplaceBits(gpioPullFloating, gpioPullMask, pos) + + case PinModeETH: + port.MODER.ReplaceBits(gpioModeAlternate, gpioModeMask, pos) + port.OSPEEDR.ReplaceBits(gpioOutputSpeedVeryHigh, gpioOutputSpeedMask, pos) + port.PUPDR.ReplaceBits(gpioPullFloating, gpioPullMask, pos) + p.SetAltFunc(altFunc) } } From ef89581a1b840dd659a7cba950375782e8f1ae54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Teichgr=C3=A4ber?= Date: Wed, 29 Jul 2026 23:37:37 +0200 Subject: [PATCH 5/5] machine/stm32: support stm32h757_cm7 --- src/machine/machine_stm32h7_gpio.go | 8 ++++---- src/machine/machine_stm32h7_gpio_mp.go | 16 ++++++++++++++++ src/machine/machine_stm32h7_gpio_sp.go | 16 ++++++++++++++++ src/machine/machine_stm32h7_usb.go | 2 +- src/machine/usb.go | 2 +- src/runtime/runtime_stm32h7_usbcdc.go | 2 +- 6 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 src/machine/machine_stm32h7_gpio_mp.go create mode 100644 src/machine/machine_stm32h7_gpio_sp.go diff --git a/src/machine/machine_stm32h7_gpio.go b/src/machine/machine_stm32h7_gpio.go index fded183dd0..98faa2ee57 100644 --- a/src/machine/machine_stm32h7_gpio.go +++ b/src/machine/machine_stm32h7_gpio.go @@ -24,7 +24,7 @@ func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error { enableEXTIConfigRegisters() if callback == nil { - stm32.EXTI.CPUIMR1.ClearBits(1 << pin) + intrMaskReg().ClearBits(1 << pin) pinCallbacks[pin] = nil return nil } @@ -46,7 +46,7 @@ func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error { if (change & PinFalling) != 0 { stm32.EXTI.FTSR1.SetBits(1 << pin) } - stm32.EXTI.CPUIMR1.SetBits(1 << pin) + intrMaskReg().SetBits(1 << pin) intr := p.registerInterrupt() intr.SetPriority(0) @@ -98,10 +98,10 @@ func handlePinInterrupt15_10(interrupt.Interrupt) { } func handlePinInterrupt(pin uint8) { - if stm32.EXTI.CPUPR1.HasBits(1 << pin) { + if intrPendReg().HasBits(1 << pin) { // Writing 1 to the pending register clears the // pending flag for that bit - stm32.EXTI.CPUPR1.Set(1 << pin) + intrPendReg().Set(1 << pin) callback := pinCallbacks[pin] if callback != nil { diff --git a/src/machine/machine_stm32h7_gpio_mp.go b/src/machine/machine_stm32h7_gpio_mp.go new file mode 100644 index 0000000000..797fbd37eb --- /dev/null +++ b/src/machine/machine_stm32h7_gpio_mp.go @@ -0,0 +1,16 @@ +//go:build stm32 && stm32h7 && stm32h757_cm7 + +package machine + +import ( + "device/stm32" + "runtime/volatile" +) + +func intrMaskReg() *volatile.Register32 { + return &stm32.EXTI.C1IMR1 +} + +func intrPendReg() *volatile.Register32 { + return &stm32.EXTI.C1PR1 +} diff --git a/src/machine/machine_stm32h7_gpio_sp.go b/src/machine/machine_stm32h7_gpio_sp.go new file mode 100644 index 0000000000..338f4a332a --- /dev/null +++ b/src/machine/machine_stm32h7_gpio_sp.go @@ -0,0 +1,16 @@ +//go:build stm32 && stm32h7 && !stm32h757_cm7 + +package machine + +import ( + "device/stm32" + "runtime/volatile" +) + +func intrMaskReg() *volatile.Register32 { + return &stm32.EXTI.CPUIMR1 +} + +func intrPendReg() *volatile.Register32 { + return &stm32.EXTI.CPUPR1 +} diff --git a/src/machine/machine_stm32h7_usb.go b/src/machine/machine_stm32h7_usb.go index ff6e90bc85..a1dd1707d1 100644 --- a/src/machine/machine_stm32h7_usb.go +++ b/src/machine/machine_stm32h7_usb.go @@ -1,4 +1,4 @@ -//go:build stm32 && stm32h7 && !stm32h723 +//go:build stm32 && stm32h7 && !stm32h723 && !stm32h757_cm7 package machine diff --git a/src/machine/usb.go b/src/machine/usb.go index 91a4a4d6da..e095a33461 100644 --- a/src/machine/usb.go +++ b/src/machine/usb.go @@ -1,4 +1,4 @@ -//go:build sam || nrf52840 || rp2040 || rp2350 || stm32f4 || stm32f7 || (stm32h7 && !stm32h723) +//go:build sam || nrf52840 || rp2040 || rp2350 || stm32f4 || stm32f7 || (stm32h7 && !stm32h723 && !stm32h757_cm7) package machine diff --git a/src/runtime/runtime_stm32h7_usbcdc.go b/src/runtime/runtime_stm32h7_usbcdc.go index 32726da65d..7743abb12a 100644 --- a/src/runtime/runtime_stm32h7_usbcdc.go +++ b/src/runtime/runtime_stm32h7_usbcdc.go @@ -1,4 +1,4 @@ -//go:build stm32 && stm32h7 && !stm32h723 +//go:build stm32 && stm32h7 && !stm32h723 && !stm32h757_cm7 package runtime