From a69b7e178940d2b88eaa65c55c6efe0e273fc28b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Teichgr=C3=A4ber?= Date: Tue, 28 Jul 2026 00:34:55 +0200 Subject: [PATCH 1/6] tools/gen-device-svd: fix USB_OTG_FS groupName inconsistency Now there will be distinct types like USB_OTG_FS_HOST_Type, USB_OTG_FS_DEVICE_Type, USB_OTG_FS_GLOBAL_Type, not just one USB_OTG_FS_Type that shadows the other types. See #5154. --- tools/gen-device-svd/gen-device-svd.go | 1 + tools/gen-device-svd/tweak.go | 33 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/tools/gen-device-svd/gen-device-svd.go b/tools/gen-device-svd/gen-device-svd.go index ef9dedff97..866042319a 100755 --- a/tools/gen-device-svd/gen-device-svd.go +++ b/tools/gen-device-svd/gen-device-svd.go @@ -390,6 +390,7 @@ func readSVD(path, sourceURL string) (*Device, error) { if groupName == "" { groupName = cleanName(periphEl.Name) } + groupName = tweakPeriphGroup(periphEl, groupName) for _, interrupt := range periphEl.Interrupts { addInterrupt(interrupts, interrupt.Name, interrupt.Name, interrupt.Index, description) diff --git a/tools/gen-device-svd/tweak.go b/tools/gen-device-svd/tweak.go index 8ee505da14..d55f5e8883 100644 --- a/tools/gen-device-svd/tweak.go +++ b/tools/gen-device-svd/tweak.go @@ -5,6 +5,39 @@ import ( "strings" ) +func tweakPeriphGroup(p *SVDPeripheral, groupName string) string { + groupName, _ = tweakUSBOTGGroupName(p.Name, groupName) + return groupName +} + +// Some peripherals, like OTG_{FS,HS}_{DEVICE,HOST,GLOBAL} +// share the same group name USB_OTG_{FS,HS}. +// Since currently the group name decides about the name of the +// peripheral type declaration (which is reasonable for peripherals +// like TIMn), in case of the OTG peripherals it results in only +// one of the peripherals being present in the device .go file, +// the other definitions get ignored, as they are wrongly recognized +// as identical definitions, which they are not. +// To avoid this behaviour, tweakUSBOTGroupName adjusts the group name +// in these cases. +func tweakUSBOTGGroupName(periphName, groupName string) (string, bool) { + speed, found := strings.CutPrefix(groupName, "USB_OTG_") + if !found { + return groupName, false + } + // speed normally is one of "FS" or "HS" + if !strings.HasPrefix(periphName, "OTG") { + return groupName, false + } + speedPart := "_" + speed + "_" + i := strings.Index(periphName, speedPart) + if i == -1 { + return groupName, false + } + return groupName + periphName[i+len(speedPart)-1:], true + +} + func tweakDevice(d *Device, pkgName string) { if pkgName != "stm32" { // no-op for device types that do not need tweaks From a643a97bf64a0c66b88a43ab49889c4bc7a84ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Teichgr=C3=A4ber?= Date: Tue, 28 Jul 2026 00:51:50 +0200 Subject: [PATCH 2/6] src/machine/stm32*otgfs*.go: adapt to USB OTG specific changes device/stm32 Now there is stm32.OTG_FS_DEVICE and stm32.OTG_FS_PWRCLK, and GLOBAL related flags now have "_GLOBAL" in their names. --- src/machine/machine_stm32_otgfs_usb.go | 48 ++++++++++----------- src/machine/machine_stm32f4_otgfs_novbus.go | 2 +- src/machine/machine_stm32f4_otgfs_vbus.go | 4 +- src/machine/machine_stm32f7_otgfs_vbus.go | 8 ++-- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/machine/machine_stm32_otgfs_usb.go b/src/machine/machine_stm32_otgfs_usb.go index 47f70936d5..e8ed3f89ed 100644 --- a/src/machine/machine_stm32_otgfs_usb.go +++ b/src/machine/machine_stm32_otgfs_usb.go @@ -25,8 +25,8 @@ const ( // OTG FS register blocks. var ( - otgDevice = (*usbDeviceRegs)(unsafe.Pointer(stm32.OTG_FS_DEVICE)) - otgPower = (*usbPowerRegs)(unsafe.Pointer(stm32.OTG_FS_PWRCLK)) + otgDevice = stm32.OTG_FS_DEVICE + otgPower = stm32.OTG_FS_PWRCLK ) // usbDeviceRegs represents the USB device-mode control block at base+0x800. @@ -143,22 +143,22 @@ const ( // GINTSTS / GINTMSK bits (values taken from stm32f405 SVD constants). const ( - gintRXFLVL = uint32(stm32.USB_OTG_FS_GINTSTS_RXFLVL) // 0x10 - gintUSBRST = uint32(stm32.USB_OTG_FS_GINTSTS_USBRST) // 0x1000 - gintENUMDNE = uint32(stm32.USB_OTG_FS_GINTSTS_ENUMDNE) // 0x2000 - gintUSBSUSP = uint32(stm32.USB_OTG_FS_GINTSTS_USBSUSP) // 0x800 - gintWKUPINT = uint32(stm32.USB_OTG_FS_GINTSTS_WKUPINT) // 0x80000000 - gintIEPINT = uint32(stm32.USB_OTG_FS_GINTSTS_IEPINT) // 0x40000 - gintOEPINT = uint32(stm32.USB_OTG_FS_GINTSTS_OEPINT) // 0x80000 + gintRXFLVL = uint32(stm32.USB_OTG_FS_GLOBAL_GINTSTS_RXFLVL) // 0x10 + gintUSBRST = uint32(stm32.USB_OTG_FS_GLOBAL_GINTSTS_USBRST) // 0x1000 + gintENUMDNE = uint32(stm32.USB_OTG_FS_GLOBAL_GINTSTS_ENUMDNE) // 0x2000 + gintUSBSUSP = uint32(stm32.USB_OTG_FS_GLOBAL_GINTSTS_USBSUSP) // 0x800 + gintWKUPINT = uint32(stm32.USB_OTG_FS_GLOBAL_GINTSTS_WKUPINT) // 0x80000000 + gintIEPINT = uint32(stm32.USB_OTG_FS_GLOBAL_GINTSTS_IEPINT) // 0x40000 + gintOEPINT = uint32(stm32.USB_OTG_FS_GLOBAL_GINTSTS_OEPINT) // 0x80000 ) // GRSTCTL bits. const ( - grstCSRST = uint32(stm32.USB_OTG_FS_GRSTCTL_CSRST) // core soft reset - grstRXFFLSH = uint32(stm32.USB_OTG_FS_GRSTCTL_RXFFLSH) // RX FIFO flush - grstTXFFLSH = uint32(stm32.USB_OTG_FS_GRSTCTL_TXFFLSH) // TX FIFO flush - grstTXFNUM_Pos = uint32(stm32.USB_OTG_FS_GRSTCTL_TXFNUM_Pos) - grstAHBIDL = uint32(stm32.USB_OTG_FS_GRSTCTL_AHBIDL) // AHB master idle + grstCSRST = uint32(stm32.USB_OTG_FS_GLOBAL_GRSTCTL_CSRST) // core soft reset + grstRXFFLSH = uint32(stm32.USB_OTG_FS_GLOBAL_GRSTCTL_RXFFLSH) // RX FIFO flush + grstTXFFLSH = uint32(stm32.USB_OTG_FS_GLOBAL_GRSTCTL_TXFFLSH) // TX FIFO flush + grstTXFNUM_Pos = uint32(stm32.USB_OTG_FS_GLOBAL_GRSTCTL_TXFNUM_Pos) + grstAHBIDL = uint32(stm32.USB_OTG_FS_GLOBAL_GRSTCTL_AHBIDL) // AHB master idle ) // FIFO size layout in 32-bit words (total budget = 320 words). @@ -236,11 +236,11 @@ func (dev *USBDevice) Configure(config UARTConfig) { // ---- 4. Force device mode, set turnaround time -------------------------- gusbcfg := stm32.OTG_FS_GLOBAL.GUSBCFG.Get() - gusbcfg &^= stm32.USB_OTG_FS_GUSBCFG_FHMOD | - stm32.USB_OTG_FS_GUSBCFG_FDMOD | - stm32.USB_OTG_FS_GUSBCFG_TRDT_Msk - gusbcfg |= stm32.USB_OTG_FS_GUSBCFG_FDMOD | - (9 << stm32.USB_OTG_FS_GUSBCFG_TRDT_Pos) // turnaround time = 9 for 216MHz HCLK + gusbcfg &^= stm32.USB_OTG_FS_GLOBAL_GUSBCFG_FHMOD | + stm32.USB_OTG_FS_GLOBAL_GUSBCFG_FDMOD | + stm32.USB_OTG_FS_GLOBAL_GUSBCFG_TRDT_Msk + gusbcfg |= stm32.USB_OTG_FS_GLOBAL_GUSBCFG_FDMOD | + (9 << stm32.USB_OTG_FS_GLOBAL_GUSBCFG_TRDT_Pos) // turnaround time = 9 for 216MHz HCLK stm32.OTG_FS_GLOBAL.GUSBCFG.Set(gusbcfg) // ---- 5. PHY / VBUS configuration (platform-specific) -------------------- @@ -301,7 +301,7 @@ func (dev *USBDevice) Configure(config UARTConfig) { otgDevice.DOEPMSK.Set(depintXFRC | depintSTUP) // Enable global interrupt - stm32.OTG_FS_GLOBAL.GAHBCFG.SetBits(stm32.USB_OTG_FS_GAHBCFG_GINT) + stm32.OTG_FS_GLOBAL.GAHBCFG.SetBits(stm32.USB_OTG_FS_GLOBAL_GAHBCFG_GINT) // ---- 11. Register and enable NVIC interrupt ----------------------------- @@ -405,10 +405,10 @@ func handleRxFIFO() { for stm32.OTG_FS_GLOBAL.GINTSTS.HasBits(gintRXFLVL) { status := stm32.OTG_FS_GLOBAL.GRXSTSP_Device.Get() - ep := status & stm32.USB_OTG_FS_GRXSTSP_Device_EPNUM_Msk - bcnt := (status & stm32.USB_OTG_FS_GRXSTSP_Device_BCNT_Msk) >> - stm32.USB_OTG_FS_GRXSTSP_Device_BCNT_Pos - pktsts := (status >> stm32.USB_OTG_FS_GRXSTSP_Device_PKTSTS_Pos) & 0xF + ep := status & stm32.USB_OTG_FS_GLOBAL_GRXSTSP_Device_EPNUM_Msk + bcnt := (status & stm32.USB_OTG_FS_GLOBAL_GRXSTSP_Device_BCNT_Msk) >> + stm32.USB_OTG_FS_GLOBAL_GRXSTSP_Device_BCNT_Pos + pktsts := (status >> stm32.USB_OTG_FS_GLOBAL_GRXSTSP_Device_PKTSTS_Pos) & 0xF pep := ep // GRXSTSP.EPNUM is already a physical endpoint (0–3) diff --git a/src/machine/machine_stm32f4_otgfs_novbus.go b/src/machine/machine_stm32f4_otgfs_novbus.go index 19b35d5e3e..aa046bd6b1 100644 --- a/src/machine/machine_stm32f4_otgfs_novbus.go +++ b/src/machine/machine_stm32f4_otgfs_novbus.go @@ -8,5 +8,5 @@ import "device/stm32" // GCCFG.PWRDWN deactivates the PHY power-down; NOVBUSSENS skips the VBUS pin // check so boards without PA9 connected to VBUS still enumerate. func initOTGFSPHY() { - stm32.OTG_FS_GLOBAL.GCCFG.Set(stm32.USB_OTG_FS_GCCFG_PWRDWN) + stm32.OTG_FS_GLOBAL.GCCFG.Set(stm32.USB_OTG_FS_GLOBAL_GCCFG_PWRDWN) } diff --git a/src/machine/machine_stm32f4_otgfs_vbus.go b/src/machine/machine_stm32f4_otgfs_vbus.go index e9830d9969..bb66505959 100644 --- a/src/machine/machine_stm32f4_otgfs_vbus.go +++ b/src/machine/machine_stm32f4_otgfs_vbus.go @@ -9,7 +9,7 @@ import "device/stm32" // check so boards without PA9 connected to VBUS still enumerate. func initOTGFSPHY() { stm32.OTG_FS_GLOBAL.GCCFG.Set( - stm32.USB_OTG_FS_GCCFG_PWRDWN | // enable FS PHY - stm32.USB_OTG_FS_GCCFG_NOVBUSSENS, // bypass VBUS sensing + stm32.USB_OTG_FS_GLOBAL_GCCFG_PWRDWN | // enable FS PHY + stm32.USB_OTG_FS_GLOBAL_GCCFG_NOVBUSSENS, // bypass VBUS sensing ) } diff --git a/src/machine/machine_stm32f7_otgfs_vbus.go b/src/machine/machine_stm32f7_otgfs_vbus.go index 97638084c2..6df6bd5faf 100644 --- a/src/machine/machine_stm32f7_otgfs_vbus.go +++ b/src/machine/machine_stm32f7_otgfs_vbus.go @@ -20,15 +20,15 @@ func initOTGFSPHY() { } // Enable FS PHY. - stm32.OTG_FS_GLOBAL.GCCFG.SetBits(stm32.USB_OTG_FS_GCCFG_PWRDWN) + stm32.OTG_FS_GLOBAL.GCCFG.SetBits(stm32.USB_OTG_FS_GLOBAL_GCCFG_PWRDWN) // Disable hardware VBUS detection (F7 uses VBDEN, opposite polarity to F4's NOVBUSSENS). // Clearing this prevents the peripheral from gating enumeration on PA9 VBUS level. - stm32.OTG_FS_GLOBAL.GCCFG.ClearBits(stm32.USB_OTG_FS_GCCFG_VBDEN) + stm32.OTG_FS_GLOBAL.GCCFG.ClearBits(stm32.USB_OTG_FS_GLOBAL_GCCFG_VBDEN) // Override B-session valid so GOTGCTL-based detection reports device connected. stm32.OTG_FS_GLOBAL.GOTGCTL.SetBits( - stm32.USB_OTG_FS_GOTGCTL_BVALOEN | // enable B-valid override - stm32.USB_OTG_FS_GOTGCTL_BVALOVAL, // set B-valid = 1 + stm32.USB_OTG_FS_GLOBAL_GOTGCTL_BVALOEN | // enable B-valid override + stm32.USB_OTG_FS_GLOBAL_GOTGCTL_BVALOVAL, // set B-valid = 1 ) } From 420f30355a9a3047adf7f121b03ed480dbe030f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Teichgr=C3=A4ber?= Date: Tue, 28 Jul 2026 00:39:13 +0200 Subject: [PATCH 3/6] tools/gen-device-svd: processCluster: fix increment calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For non-array clusters, extend lastAddress to the position behind the last element — so that the last element, which is part of the cluster, is actually included in the size calcution. This removes a warning at generation stage, if the calculated size does not match the struct's size. --- tools/gen-device-svd/gen-device-svd.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/gen-device-svd/gen-device-svd.go b/tools/gen-device-svd/gen-device-svd.go index 866042319a..91804936f5 100755 --- a/tools/gen-device-svd/gen-device-svd.go +++ b/tools/gen-device-svd/gen-device-svd.go @@ -325,7 +325,9 @@ func processCluster(p *Peripheral, clusters []*SVDCluster, peripheralDict map[st lastReg := clusterRegisters[len(clusterRegisters)-1] lastAddress := lastReg.Address if lastReg.Array != -1 { - lastAddress = lastReg.Address + uint64(lastReg.Array*lastReg.ElementSize) + lastAddress += uint64(lastReg.Array * lastReg.ElementSize) + } else { + lastAddress += uint64(lastReg.ElementSize) } firstAddress := clusterRegisters[0].Address dimIncrement = int(lastAddress - firstAddress) From 27349eec8995e2bbd5d619d3098d91c702d2c2aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Teichgr=C3=A4ber?= Date: Tue, 28 Jul 2026 00:28:38 +0200 Subject: [PATCH 4/6] tools/gen-device-svd: process[Sub]Cluster: use decodeDimArray This is a preparatory step towards merging clusters separated into e.g. DIEP0 and DIEP%s. --- tools/gen-device-svd/gen-device-svd.go | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/tools/gen-device-svd/gen-device-svd.go b/tools/gen-device-svd/gen-device-svd.go index 91804936f5..27afee5a6c 100755 --- a/tools/gen-device-svd/gen-device-svd.go +++ b/tools/gen-device-svd/gen-device-svd.go @@ -77,7 +77,7 @@ type SVDField struct { } type SVDCluster struct { - Dim *int `xml:"dim"` + Dim *string `xml:"dim"` DimIncrement string `xml:"dimIncrement"` DimIndex *string `xml:"dimIndex"` Name string `xml:"name"` @@ -231,13 +231,8 @@ func processSubCluster(p *Peripheral, cluster *SVDCluster, clusterOffset uint64, if err != nil { panic(err) } - subdim := *subClusterEl.Dim - subdimIncrement, err := strconv.ParseInt(subClusterEl.DimIncrement, 0, 32) - if err != nil { - panic(err) - } - - if subdim > 1 { + subDA := decodeDimArray(subClusterEl.Dim, subClusterEl.DimIndex, subClusterEl.DimIncrement, "subCluster", subclusterName) + if subDA != nil && subDA.dim > 1 { subcpRegisters := []*PeripheralField{} for _, regEl := range subClusterEl.Registers { subcpRegisters = append(subcpRegisters, parseRegister(p.GroupName, regEl, p.BaseAddress+clusterOffset+subclusterOffset, subclusterPrefix)...) @@ -248,8 +243,8 @@ func processSubCluster(p *Peripheral, cluster *SVDCluster, clusterOffset uint64, Address: p.BaseAddress + clusterOffset + subclusterOffset, Description: subClusterEl.Description, Registers: subcpRegisters, - Array: subdim, - ElementSize: int(subdimIncrement), + Array: subDA.dim, + ElementSize: int(subDA.incr), ShortName: clusterPrefix + subclusterName, }) } else { @@ -290,7 +285,8 @@ func processCluster(p *Peripheral, clusters []*SVDCluster, peripheralDict map[st panic(err) } var dim, dimIncrement int - if cluster.Dim == nil { + da := decodeDimArray(cluster.Dim, cluster.DimIndex, cluster.DimIncrement, "cluster", clusterName) + if da == nil { // Nordic SVD have sub-clusters with another sub-clusters. if clusterOffset == 0 || len(cluster.Clusters) > 0 { peripheralsList = append(peripheralsList, processSubCluster(p, cluster, clusterOffset, clusterName, peripheralDict)...) @@ -299,15 +295,11 @@ func processCluster(p *Peripheral, clusters []*SVDCluster, peripheralDict map[st dim = -1 dimIncrement = -1 } else { - dim = *cluster.Dim + dim = da.dim if dim == 1 { dimIncrement = -1 } else { - inc, err := strconv.ParseUint(cluster.DimIncrement, 0, 32) - if err != nil { - panic(err) - } - dimIncrement = int(inc) + dimIncrement = int(da.incr) } } clusterRegisters := []*PeripheralField{} From c8d50aed81f74450d0638066f4f32019a079f3bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Teichgr=C3=A4ber?= Date: Tue, 28 Jul 2026 00:43:03 +0200 Subject: [PATCH 5/6] tools/gen-device-svd: move clusterName derivation to method of SVDCluster This allows later reuse. --- tools/gen-device-svd/gen-device-svd.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/gen-device-svd/gen-device-svd.go b/tools/gen-device-svd/gen-device-svd.go index 27afee5a6c..a25dbe39f9 100755 --- a/tools/gen-device-svd/gen-device-svd.go +++ b/tools/gen-device-svd/gen-device-svd.go @@ -272,13 +272,18 @@ func processSubCluster(p *Peripheral, cluster *SVDCluster, clusterOffset uint64, return peripheralsList } +func (cluster *SVDCluster) name() string { + clusterName := strings.ReplaceAll(cluster.Name, "[%s]", "") + if cluster.DimIndex != nil { + clusterName = strings.ReplaceAll(clusterName, "%s", "") + } + return clusterName +} + func processCluster(p *Peripheral, clusters []*SVDCluster, peripheralDict map[string]*Peripheral) []*Peripheral { var peripheralsList []*Peripheral for _, cluster := range clusters { - clusterName := strings.ReplaceAll(cluster.Name, "[%s]", "") - if cluster.DimIndex != nil { - clusterName = strings.ReplaceAll(clusterName, "%s", "") - } + clusterName := cluster.name() clusterPrefix := clusterName + "_" clusterOffset, err := strconv.ParseUint(cluster.AddressOffset, 0, 32) if err != nil { From c23c2a013a8298e33b81c382dff3897df2de058a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Teichgr=C3=A4ber?= Date: Tue, 28 Jul 2026 00:49:36 +0200 Subject: [PATCH 6/6] tools/gen-device-svd: processCluster: merge DIEP0 + DIEP%s like adjacent clusters This improves OTG_{HS|FS}_DEVICE_Type structure, and makes DIEP and DOEP types more useful. Also, it adjusts corresponding register flag names (using ".._DIEP_.." rather than ".._DIEP0_.."). --- tools/gen-device-svd/gen-device-svd.go | 34 +++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/tools/gen-device-svd/gen-device-svd.go b/tools/gen-device-svd/gen-device-svd.go index a25dbe39f9..227b6fd10b 100755 --- a/tools/gen-device-svd/gen-device-svd.go +++ b/tools/gen-device-svd/gen-device-svd.go @@ -282,7 +282,12 @@ func (cluster *SVDCluster) name() string { func processCluster(p *Peripheral, clusters []*SVDCluster, peripheralDict map[string]*Peripheral) []*Peripheral { var peripheralsList []*Peripheral - for _, cluster := range clusters { + skipNext := false + for i, cluster := range clusters { + if skipNext { + skipNext = false + continue + } clusterName := cluster.name() clusterPrefix := clusterName + "_" clusterOffset, err := strconv.ParseUint(cluster.AddressOffset, 0, 32) @@ -299,6 +304,33 @@ func processCluster(p *Peripheral, clusters []*SVDCluster, peripheralDict map[st } dim = -1 dimIncrement = -1 + if i+1 < len(clusters) { + // If the next cluster is an array and has a name matching + // the current cluster's name, like DIEP%s matches DIEP0, + // and if it is directly adjacent to the current cluster, + // merge the next cluster into the current one consistently. + // This avoids having types like ..DEVICE_DIEP0_Type and + // ..DEVICE_DIEP_Type at the same time, also + next := clusters[i+1] + nextClusterName := next.name() + if strings.HasPrefix(clusterName, nextClusterName) { + if nextDA := decodeDimArray(next.Dim, next.DimIndex, next.DimIncrement, "cluster", nextClusterName); nextDA != nil { + nextClusterOffset, err := strconv.ParseUint(next.AddressOffset, 0, 32) + if err != nil { + panic(err) + } + // Test if current and next clusters are adjacent. + if uint32(nextClusterOffset-clusterOffset) == nextDA.incr { + // merge + skipNext = true + dim = 1 + nextDA.dim + dimIncrement = int(nextDA.incr) + clusterName = nextClusterName + clusterPrefix = clusterName + "_" + } + } + } + } } else { dim = da.dim if dim == 1 {