mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 06:38:42 +00:00
update tools/gen-device-svd w.r.t. recent changes to the structure of stm32-svd files (#5212)
* tools/gen-device-svd: orderPeripherals: prevent skipping base peripherals derived by name Recent SVDs from stm32-rs, like the one for stm32u595, define derivedFrom attributes that do not refer to a peripheral group name, but to a peripheral name. For instance, the peripheral I2C5 may be derived from "I2C1", not from "I2C". The previous algorithm records, in case the group name is non-empty, only the group name in the knownBasePeripherals map, not the name of the peripheral itself. So in case of the base peripheral I2C1 with group name I2C: although the peripheral gets added to the sorted list, it would be added to knownBasePeripherals with the group name "I2C" as key, not with "I2C1". A following peripheral, I2C5, derived from I2C1, with an empty group name, would be recorded as known with key "I2C5", but omitted from the sorted list, because "I2C1" is not recognized as known. The following peripheral SEC_I2C5, derived from I2C5, with empty group name, would be added to both the knownPeripherals map and the sorted list. So if, later, the sorted list is examined, it would find SEC_I2C5 earlier than its base peripheral I2C5, which would be missing from "peripheralDict", resulting in a nil pointer access. This patch makes sure that, to stay with the example, that "I2C1" is recorded as known too (not only the group name "I2C"), so that "I2C5" won't be skipped anymore, preventing the program from crashing. * tools/gen-device-svd: orderPeripherals: ensure ordered content of missingBasePeripherals After the first run, missingBasePeripherals may contain peripherals with dependencies that are not guaranteed to be in proper order. This change implements additional loop runs that try to reduce the size of the missingBasePeripherals as far as possible. [With recent SVDs from stm32-rs this change will not produce different results, though (since these source files contain already properly ordered peripherals).] * tools/gen-device-svd: Register: move dim array decoding to utility type dimArray This allows encoding of dim increment and array indices to be re-used by other elements supporting dim arrays. This change just restructures parts of register specific code, it does not change the output of the program. * tools/gen-device-svd: parseBitfields: support field dim arrays Patched SVD files from stm32-rs recently contain many fields with dim array parameters and names containing %s (like "CC%sIF"). This change adjusts parseBitfields so that these field elements get resolved. * tools/gen-device-svd: SVDField: allow multiple enumeratedValues In recent patched SVD files from stm32-rs there may be two enumeratedValues elements per SVDField, not just one. The SVD specification allows up to two entries (they may be used to define different enums for read and write access). This change extends SVDField and parseBitfields so that two enumeratedValues are processed like a single one. * tools/gen-device-svd: orderPeripherals: sort peripherals of same group with larger number of registers/bitfields first In group "TIM" there may be general purpose timers like TIM16 and advanced timers like TIM1. The advanced peripheral may contain a larger number of registers than the general purpose ones. TIM1 may contain CCR1..CCR4, SMCR and OR1, while TIM16 only knows about CCR1. Unfortunately in some SVDs, like the one for stm32g031, TIM16 is defined before TIM1. Since register and bitfield constants are generated taking only the first peripheral of a group into account, the resulting .go file may lack definitions for e.g. CCR2..CCR4, SMCR, and OR1. This change adjusts orderPeripherals so that, to stay with the example, a peripheral like TIM1 will be moved in front of TIM16, resulting in an output file containing the larger set of definitions. * tools/gen-device-svd: SVDEnumeration: support isDefault Recent SVD files created by stm32-rs use "isDefault" in enumeratedValue elements for purposes like the Div1 enum for clock prescaler registers without specifying a specific value. Previously, these enumeratedValues would be skipped because of the enumEl.Value == 0 condition, and the corresponding const definitions like "RCC_CFGR2_PPRE2_Div1 = 0x0" would be missing from the resulting .go files, so existing code relying on these constants would not compile anymore. This change adds a utility type enumDefaultResolver that helps finding an actual value that is unused by the enumeratedValues that are defined for the field. More examples for values marked as "isDefault", along with their resolved values: DAC_CR_WAVE1_Triangle => 2 IWDG_PR_PR_DivideBy256 => 6 DAC_CR_MAMP2_Amp4095 => 0xb * tools/gen-device-svd: support derivedFrom attribute at field level This ensures that some more constants are included in the .go files that would otherwise be skipped (like e.g. ADC_SMPR2_SMP1_Cycles* of some STM32 devices), which prevented compilation of some programs. To avoid extending a lot of func argument lists, and since there is no context.Context in use yet, this change introduces a global derivationContext. * tools/gen-device-svd: tweak: stm32: ensure USART_ISR_TXE/TXFNF are present * tools/gen-device-svd: stm32: ensure CCMR*_Output alternate registers are sorted first * tools/gen-device-svd: stm32: add IWDG peripheral alias if SVD defines IWDG1
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
)
|
||||
|
||||
var validName = regexp.MustCompile("^[a-zA-Z0-9_]+$")
|
||||
var validDimableName = regexp.MustCompile(`^((%s)|(%s)[_A-Za-z]{1}[_A-Za-z0-9]*)|([_A-Za-z]{1}[_A-Za-z0-9]*(\[%s\])?)|([_A-Za-z]{1}[_A-Za-z0-9]*(%s)?[_A-Za-z0-9]*)$`)
|
||||
var enumBitSpecifier = regexp.MustCompile("^#x*[01]+[01x]*$")
|
||||
|
||||
type SVDFile struct {
|
||||
@@ -60,22 +62,18 @@ type SVDRegister struct {
|
||||
}
|
||||
|
||||
type SVDField struct {
|
||||
DerivedFrom string `xml:"derivedFrom,attr"`
|
||||
Name string `xml:"name"`
|
||||
Description string `xml:"description"`
|
||||
Dim *string `xml:"dim"`
|
||||
DimIndex *string `xml:"dimIndex"`
|
||||
DimIncrement string `xml:"dimIncrement"`
|
||||
Lsb *uint32 `xml:"lsb"`
|
||||
Msb *uint32 `xml:"msb"`
|
||||
BitOffset *uint32 `xml:"bitOffset"`
|
||||
BitWidth *uint32 `xml:"bitWidth"`
|
||||
BitRange *string `xml:"bitRange"`
|
||||
EnumeratedValues struct {
|
||||
DerivedFrom string `xml:"derivedFrom,attr"`
|
||||
Name string `xml:"name"`
|
||||
EnumeratedValue []struct {
|
||||
Name string `xml:"name"`
|
||||
Description string `xml:"description"`
|
||||
Value string `xml:"value"`
|
||||
} `xml:"enumeratedValue"`
|
||||
} `xml:"enumeratedValues"`
|
||||
EnumeratedValues []SVDEnumeration `xml:"enumeratedValues"`
|
||||
}
|
||||
|
||||
type SVDCluster struct {
|
||||
@@ -89,6 +87,17 @@ type SVDCluster struct {
|
||||
AddressOffset string `xml:"addressOffset"`
|
||||
}
|
||||
|
||||
type SVDEnumeration struct {
|
||||
DerivedFrom string `xml:"derivedFrom,attr"`
|
||||
Name string `xml:"name"`
|
||||
EnumeratedValue []struct {
|
||||
Name string `xml:"name"`
|
||||
Description string `xml:"description"`
|
||||
Value string `xml:"value"`
|
||||
IsDefault bool `xml:"isDefault"`
|
||||
} `xml:"enumeratedValue"`
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
Metadata *Metadata
|
||||
Interrupts []*Interrupt
|
||||
@@ -120,6 +129,7 @@ type Interrupt struct {
|
||||
|
||||
type Peripheral struct {
|
||||
Name string
|
||||
Alias string
|
||||
GroupName string
|
||||
BaseAddress uint64
|
||||
Description string
|
||||
@@ -173,17 +183,15 @@ func splitLine(s string) []string {
|
||||
// Replace characters that are not allowed in a symbol name with a '_'. This is
|
||||
// useful to be able to process SVD files with errors.
|
||||
func cleanName(text string) string {
|
||||
if !validName.MatchString(text) {
|
||||
result := make([]rune, 0, len(text))
|
||||
for _, c := range text {
|
||||
if validName.MatchString(string(c)) {
|
||||
result = append(result, c)
|
||||
} else {
|
||||
result = append(result, '_')
|
||||
return cleanIdentifier(text, validName)
|
||||
}
|
||||
|
||||
func cleanDimableName(text string) string {
|
||||
return cleanIdentifier(text, validDimableName)
|
||||
}
|
||||
text = string(result)
|
||||
}
|
||||
|
||||
func cleanIdentifier(text string, valid *regexp.Regexp) string {
|
||||
text = cleanString(text, valid)
|
||||
if len(text) != 0 && (text[0] >= '0' && text[0] <= '9') {
|
||||
// Identifiers may not start with a number.
|
||||
// Add an underscore instead.
|
||||
@@ -192,6 +200,21 @@ func cleanName(text string) string {
|
||||
return text
|
||||
}
|
||||
|
||||
func cleanString(s string, valid *regexp.Regexp) string {
|
||||
if !valid.MatchString(s) {
|
||||
result := make([]rune, 0, len(s))
|
||||
for _, c := range s {
|
||||
if validName.MatchString(string(c)) {
|
||||
result = append(result, c)
|
||||
} else {
|
||||
result = append(result, '_')
|
||||
}
|
||||
}
|
||||
s = string(result)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func processSubCluster(p *Peripheral, cluster *SVDCluster, clusterOffset uint64, clusterName string, peripheralDict map[string]*Peripheral) []*Peripheral {
|
||||
var peripheralsList []*Peripheral
|
||||
clusterPrefix := clusterName + "_"
|
||||
@@ -353,6 +376,7 @@ func readSVD(path, sourceURL string) (*Device, error) {
|
||||
// comes later in the file. To make sure this works, sort the peripherals if
|
||||
// needed.
|
||||
orderedPeripherals := orderPeripherals(device.Peripherals)
|
||||
globalDerivationCtx.peripherals = orderedPeripherals
|
||||
|
||||
for _, periphEl := range orderedPeripherals {
|
||||
description := formatText(periphEl.Description)
|
||||
@@ -490,21 +514,42 @@ func orderPeripherals(input []SVDPeripheral) []*SVDPeripheral {
|
||||
var sortedPeripherals []*SVDPeripheral
|
||||
var missingBasePeripherals []*SVDPeripheral
|
||||
knownBasePeripherals := map[string]struct{}{}
|
||||
for i := range input {
|
||||
p := &input[i]
|
||||
|
||||
tryProcess := func(p *SVDPeripheral) {
|
||||
groupName := p.GroupName
|
||||
if groupName == "" {
|
||||
groupName = p.Name
|
||||
}
|
||||
if groupName != "" {
|
||||
knownBasePeripherals[groupName] = struct{}{}
|
||||
}
|
||||
knownBasePeripherals[p.Name] = struct{}{}
|
||||
if p.DerivedFrom != "" {
|
||||
if _, ok := knownBasePeripherals[p.DerivedFrom]; !ok {
|
||||
missingBasePeripherals = append(missingBasePeripherals, p)
|
||||
continue
|
||||
return
|
||||
}
|
||||
}
|
||||
sortedPeripherals = append(sortedPeripherals, p)
|
||||
}
|
||||
for i := range input {
|
||||
tryProcess(&input[i])
|
||||
}
|
||||
orderPeripheralsByNumBitfields(sortedPeripherals)
|
||||
|
||||
// missingBasePeripherals may still contain unordered entries;
|
||||
// repeat the process until missingBasePeripheral does not change anymore.
|
||||
prevNumPending := 0
|
||||
for {
|
||||
pending := missingBasePeripherals
|
||||
if len(pending) == prevNumPending {
|
||||
break
|
||||
}
|
||||
// reuse the same slice as input and for keeping track of
|
||||
// missing base periphal
|
||||
missingBasePeripherals = missingBasePeripherals[:0]
|
||||
for _, p := range pending {
|
||||
tryProcess(p)
|
||||
}
|
||||
prevNumPending = len(pending)
|
||||
}
|
||||
|
||||
// Let's hope all base peripherals are now included.
|
||||
sortedPeripherals = append(sortedPeripherals, missingBasePeripherals...)
|
||||
@@ -512,6 +557,51 @@ func orderPeripherals(input []SVDPeripheral) []*SVDPeripheral {
|
||||
return sortedPeripherals
|
||||
}
|
||||
|
||||
func orderPeripheralsByNumBitfields(list []*SVDPeripheral) {
|
||||
seenGroup := make(map[string]struct{})
|
||||
for i, p := range list {
|
||||
groupName := p.GroupName
|
||||
if groupName == "" || p.DerivedFrom != "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seenGroup[groupName]; ok {
|
||||
continue
|
||||
}
|
||||
iMax, nMax := -1, p.bitfieldCount()
|
||||
for j, p2 := range list[i+1:] {
|
||||
if p2.GroupName != groupName || p2.DerivedFrom != "" {
|
||||
continue
|
||||
}
|
||||
if n2 := p2.bitfieldCount(); n2 > nMax {
|
||||
iMax = i + 1 + j
|
||||
nMax = n2
|
||||
}
|
||||
}
|
||||
if iMax != -1 {
|
||||
pMax := list[iMax]
|
||||
// swap peripherals
|
||||
copy(list[i+1:iMax+1], list[i:iMax])
|
||||
list[i] = pMax
|
||||
seenGroup[groupName] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *SVDPeripheral) bitfieldCount() int {
|
||||
n := 0
|
||||
for _, r := range p.Registers {
|
||||
for _, f := range r.Fields {
|
||||
dim := decodeDim(f.Dim)
|
||||
if dim > 0 {
|
||||
n += dim
|
||||
} else {
|
||||
n++
|
||||
}
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func addInterrupt(interrupts map[string]*Interrupt, name, interruptName string, index int, description string) {
|
||||
if _, ok := interrupts[name]; ok {
|
||||
if interrupts[name].Value != index {
|
||||
@@ -544,14 +634,23 @@ func addInterrupt(interrupts map[string]*Interrupt, name, interruptName string,
|
||||
func parseBitfields(groupName, regName string, fieldEls []*SVDField, bitfieldPrefix string) ([]Constant, []Bitfield) {
|
||||
var fields []Constant
|
||||
var bitfields []Bitfield
|
||||
var enumDefault enumDefaultResolver
|
||||
enumSeen := map[string]int64{}
|
||||
for _, fieldEl := range fieldEls {
|
||||
|
||||
if fieldEl.DerivedFrom != "" {
|
||||
err := globalDerivationCtx.deriveField(fieldEl, fieldEls)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unable to derive field %q from %q: %v\n", fieldEl.Name, fieldEl.DerivedFrom, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Some bitfields (like the STM32H7x7) contain invalid bitfield
|
||||
// names like "CNT[31]". Replace invalid characters with "_" when
|
||||
// needed.
|
||||
fieldName := cleanName(fieldEl.Name)
|
||||
if !unicode.IsUpper(rune(fieldName[0])) && !unicode.IsDigit(rune(fieldName[0])) {
|
||||
fieldName = strings.ToUpper(fieldName)
|
||||
fieldNameTpl := cleanDimableName(fieldEl.Name)
|
||||
if !unicode.IsUpper(rune(fieldNameTpl[0])) && !unicode.IsDigit(rune(fieldNameTpl[0])) {
|
||||
fieldNameTpl = strings.ReplaceAll(strings.ToUpper(fieldNameTpl), "%S", "%s")
|
||||
}
|
||||
|
||||
// Find the lsb/msb that is encoded in various ways.
|
||||
@@ -581,23 +680,35 @@ func parseBitfields(groupName, regName string, fieldEls []*SVDField, bitfieldPre
|
||||
msb = uint32(m)
|
||||
} else {
|
||||
// this is an error. what to do?
|
||||
fmt.Fprintln(os.Stderr, "unable to find lsb/msb in field:", fieldName)
|
||||
fmt.Fprintln(os.Stderr, "unable to find lsb/msb in field:", fieldNameTpl)
|
||||
continue
|
||||
}
|
||||
|
||||
da := decodeDimArray(fieldEl.Dim, fieldEl.DimIndex, fieldEl.DimIncrement, "field", fieldNameTpl)
|
||||
da.rangeElems(func(ia int, _ uint32) bool {
|
||||
if da != nil {
|
||||
lsb += da.incr
|
||||
msb += da.incr
|
||||
}
|
||||
fieldName := da.replace(fieldNameTpl, ia)
|
||||
|
||||
// The enumerated values can be the same as another field, so to avoid
|
||||
// duplication SVD files can simply refer to another set of enumerated
|
||||
// values in the same register.
|
||||
// See: https://www.keil.com/pack/doc/CMSIS/SVD/html/elem_registers.html#elem_enumeratedValues
|
||||
enumeratedValues := fieldEl.EnumeratedValues
|
||||
for i := range fieldEl.EnumeratedValues {
|
||||
enumeratedValues := &fieldEl.EnumeratedValues[i]
|
||||
if enumeratedValues.DerivedFrom != "" {
|
||||
parts := strings.Split(enumeratedValues.DerivedFrom, ".")
|
||||
if len(parts) == 1 {
|
||||
found := false
|
||||
for _, otherFieldEl := range fieldEls {
|
||||
if otherFieldEl.EnumeratedValues.Name == parts[0] {
|
||||
for i := range otherFieldEl.EnumeratedValues {
|
||||
otherEnum := &otherFieldEl.EnumeratedValues[i]
|
||||
if otherEnum.Name == parts[0] {
|
||||
found = true
|
||||
enumeratedValues = otherFieldEl.EnumeratedValues
|
||||
*enumeratedValues = *otherEnum
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
@@ -610,6 +721,7 @@ func parseBitfields(groupName, regName string, fieldEls []*SVDField, bitfieldPre
|
||||
fmt.Fprintf(os.Stderr, "TODO: enumeratedValue.derivedFrom to a different register: %s\n", enumeratedValues.DerivedFrom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitfields = append(bitfields, Bitfield{
|
||||
Name: fieldName,
|
||||
@@ -633,12 +745,11 @@ func parseBitfields(groupName, regName string, fieldEls []*SVDField, bitfieldPre
|
||||
Value: 1 << lsb,
|
||||
})
|
||||
}
|
||||
for _, enumEl := range enumeratedValues.EnumeratedValue {
|
||||
for i := range fieldEl.EnumeratedValues {
|
||||
enumDefault.reset(1<<(msb+1-lsb) - 1)
|
||||
fields0Pos := len(fields)
|
||||
for _, enumEl := range fieldEl.EnumeratedValues[i].EnumeratedValue {
|
||||
enumName := enumEl.Name
|
||||
// Renesas has enum without actual values that we have to skip
|
||||
if enumEl.Value == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.EqualFold(enumName, "reserved") || !validName.MatchString(enumName) {
|
||||
continue
|
||||
@@ -646,7 +757,27 @@ func parseBitfields(groupName, regName string, fieldEls []*SVDField, bitfieldPre
|
||||
if !unicode.IsUpper(rune(enumName[0])) && !unicode.IsDigit(rune(enumName[0])) {
|
||||
enumName = strings.ToUpper(enumName)
|
||||
}
|
||||
enumName = fmt.Sprintf("%s_%s%s_%s_%s", groupName, bitfieldPrefix, regName, fieldName, enumName)
|
||||
enumDescription := formatText(enumEl.Description)
|
||||
|
||||
if enumEl.IsDefault {
|
||||
enumDefault.setDefaultAction(func(value uint64) {
|
||||
if value == 0 {
|
||||
// put zero value in front of other constants
|
||||
fields = slices.Insert(fields, fields0Pos, Constant{})
|
||||
appendConstant(fields[:fields0Pos], enumName, enumDescription, value, enumSeen)
|
||||
} else {
|
||||
fields = appendConstant(fields, enumName, enumDescription, value, enumSeen)
|
||||
}
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// Renesas has enum without actual values that we have to skip
|
||||
if enumEl.Value == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
var enumValue uint64
|
||||
var err error
|
||||
if strings.HasPrefix(enumEl.Value, "0b") {
|
||||
@@ -666,8 +797,113 @@ func parseBitfields(groupName, regName string, fieldEls []*SVDField, bitfieldPre
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
enumName = fmt.Sprintf("%s_%s%s_%s_%s", groupName, bitfieldPrefix, regName, fieldName, enumName)
|
||||
enumDefault.collectValue(enumValue)
|
||||
fields = appendConstant(fields, enumName, enumDescription, enumValue, enumSeen)
|
||||
}
|
||||
enumDefault.resolve()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
return fields, bitfields
|
||||
}
|
||||
|
||||
var globalDerivationCtx derivationContext
|
||||
|
||||
type derivationContext struct {
|
||||
peripherals []*SVDPeripheral
|
||||
}
|
||||
|
||||
func (ctx *derivationContext) deriveField(fieldEl *SVDField, localFieldEls []*SVDField) error {
|
||||
from := fieldEl.DerivedFrom
|
||||
parts := strings.Split(from, ".")
|
||||
srcName := parts[0]
|
||||
var srcFieldEl *SVDField
|
||||
switch len(parts) {
|
||||
case 3, 4:
|
||||
src, err := ctx.lookupGlobal(parts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srcFieldEl = src
|
||||
case 1:
|
||||
// resolve locally, in current register
|
||||
for _, f := range localFieldEls {
|
||||
if f == fieldEl {
|
||||
continue
|
||||
}
|
||||
if f.Name == srcName {
|
||||
srcFieldEl = f
|
||||
break
|
||||
}
|
||||
}
|
||||
if srcFieldEl == nil {
|
||||
return fmt.Errorf("not found")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("cannot decode source path")
|
||||
}
|
||||
|
||||
// copy enumeratedValues from source to current field
|
||||
if fieldEl.DimIndex == nil && strings.Contains(fieldEl.Name, "%s") {
|
||||
fieldEl.DimIndex = srcFieldEl.DimIndex
|
||||
fieldEl.DimIncrement = srcFieldEl.DimIncrement
|
||||
fieldEl.Dim = srcFieldEl.Dim
|
||||
}
|
||||
if fieldEl.Description == "" {
|
||||
fieldEl.Description = srcFieldEl.Description
|
||||
}
|
||||
if fieldEl.BitWidth == nil {
|
||||
fieldEl.BitWidth = srcFieldEl.BitWidth
|
||||
}
|
||||
if fieldEl.BitOffset == nil {
|
||||
fieldEl.BitOffset = srcFieldEl.BitOffset
|
||||
}
|
||||
if fieldEl.BitRange == nil {
|
||||
fieldEl.BitRange = srcFieldEl.BitRange
|
||||
}
|
||||
|
||||
fieldEl.EnumeratedValues = srcFieldEl.EnumeratedValues
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *derivationContext) lookupGlobal(path []string) (*SVDField, error) {
|
||||
curPath := path[:1]
|
||||
for _, p := range ctx.peripherals {
|
||||
if p.Name == path[0] {
|
||||
if len(path) == 4 {
|
||||
curPath = path[:2]
|
||||
for _, c := range p.Clusters {
|
||||
if c.Name == path[1] {
|
||||
return ctx.lookupFieldInRegs(path[2:], c.Registers, curPath)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("cluster not found: %q", path[2])
|
||||
|
||||
}
|
||||
return ctx.lookupFieldInRegs(path[1:], p.Registers, curPath)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("peripheral not found: %s", path[0])
|
||||
}
|
||||
|
||||
func (ctx *derivationContext) lookupFieldInRegs(path []string, registers []*SVDRegister, curPath []string) (*SVDField, error) {
|
||||
curPath = curPath[:len(curPath)+1]
|
||||
for _, r := range registers {
|
||||
if r.Name == path[0] {
|
||||
curPath = curPath[:len(curPath)+1]
|
||||
for _, f := range r.Fields {
|
||||
if f.Name == path[1] {
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("field not found: %q", strings.Join(curPath, "."))
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("register not found: %q", strings.Join(curPath, "."))
|
||||
}
|
||||
|
||||
func appendConstant(fields []Constant, enumName, enumDescription string, enumValue uint64, enumSeen map[string]int64) []Constant {
|
||||
// Avoid duplicate values. Duplicate names with the same value are
|
||||
// allowed, but the same name with a different value is not. Instead
|
||||
// of trying to work around those cases, remove the value entirely
|
||||
@@ -679,7 +915,7 @@ func parseBitfields(groupName, regName string, fieldEls []*SVDField, bitfieldPre
|
||||
if seenBefore {
|
||||
if previousEnumValue < 0 {
|
||||
// There was a mismatch before, ignore all equally named fields.
|
||||
continue
|
||||
return fields
|
||||
}
|
||||
if int64(enumValue) != previousEnumValue {
|
||||
// There is a mismatch. Mark it as such, and remove the
|
||||
@@ -692,7 +928,7 @@ func parseBitfields(groupName, regName string, fieldEls []*SVDField, bitfieldPre
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
return fields
|
||||
}
|
||||
enumSeen[enumName] = int64(enumValue)
|
||||
|
||||
@@ -701,9 +937,75 @@ func parseBitfields(groupName, regName string, fieldEls []*SVDField, bitfieldPre
|
||||
Description: enumDescription,
|
||||
Value: enumValue,
|
||||
})
|
||||
return fields
|
||||
}
|
||||
|
||||
// enumDefaultResolver helps determine the actual numeric value for an
|
||||
// enumeratedValue marked as the default (i.e., where "isDefault" is set).
|
||||
//
|
||||
// Some SVD files use "isDefault" to indicate a fallback value (e.g., Div1 in
|
||||
// clock prescaler registers) without specifying the exact value when it's not
|
||||
// critical. This type is used to collect all defined enumValues, and once
|
||||
// collection is complete, derive a sensible default value that does not conflict
|
||||
// with any explicitly defined ones.
|
||||
//
|
||||
// Typically, it prefers zero as a default if available; otherwise, it will
|
||||
// choose a suitable unused value below the field's maximum.
|
||||
type enumDefaultResolver struct {
|
||||
values []uint64
|
||||
maxValue uint64
|
||||
handleDefault func(value uint64)
|
||||
}
|
||||
|
||||
func (dr *enumDefaultResolver) reset(maxValue uint64) {
|
||||
dr.values = dr.values[:0]
|
||||
dr.maxValue = maxValue
|
||||
dr.handleDefault = nil
|
||||
}
|
||||
|
||||
func (dr *enumDefaultResolver) setDefaultAction(action func(v uint64)) {
|
||||
dr.handleDefault = action
|
||||
}
|
||||
|
||||
func (dr *enumDefaultResolver) collectValue(value uint64) {
|
||||
dr.values = append(dr.values, value)
|
||||
}
|
||||
|
||||
// resolve tries to find an actual value for the enumerated Value
|
||||
// marked as default.
|
||||
func (dr *enumDefaultResolver) resolve() {
|
||||
if dr.handleDefault == nil {
|
||||
return
|
||||
}
|
||||
list := dr.values
|
||||
n := len(list)
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
slices.Sort(list)
|
||||
|
||||
var value uint64
|
||||
// try to use zero as default value
|
||||
if list[0] == 0 {
|
||||
// not available, now try the highest value +1
|
||||
largest := list[n-1]
|
||||
if largest < dr.maxValue {
|
||||
value = largest + 1
|
||||
} else {
|
||||
value = 1
|
||||
// not available, now lookup the first free value
|
||||
for _, enumValue := range list[1:] {
|
||||
if value < enumValue {
|
||||
break
|
||||
}
|
||||
value = enumValue + 1
|
||||
if value == dr.maxValue {
|
||||
return
|
||||
}
|
||||
}
|
||||
return fields, bitfields
|
||||
}
|
||||
}
|
||||
dr.handleDefault(value)
|
||||
}
|
||||
|
||||
type Register struct {
|
||||
@@ -739,38 +1041,57 @@ func (r *Register) address() uint64 {
|
||||
}
|
||||
|
||||
func (r *Register) dim() int {
|
||||
if r.element.Dim == nil {
|
||||
return decodeDim(r.element.Dim)
|
||||
}
|
||||
|
||||
func decodeDim(s *string) int {
|
||||
if s == nil {
|
||||
return -1 // no dim elements
|
||||
}
|
||||
dim, err := strconv.ParseInt(*r.element.Dim, 0, 32)
|
||||
dim, err := strconv.ParseInt(*s, 0, 32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return int(dim)
|
||||
}
|
||||
|
||||
func (r *Register) dimIndex() []string {
|
||||
type dimArray struct {
|
||||
dim int
|
||||
idx []string
|
||||
incr uint32
|
||||
}
|
||||
|
||||
func decodeDimArray(dimSpec, dimIndex *string, dimIncr, elType, elName string) *dimArray {
|
||||
dim := decodeDim(dimSpec)
|
||||
if dim <= 0 {
|
||||
return nil
|
||||
}
|
||||
a := new(dimArray)
|
||||
a.dim = dim
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Println("register", r.name())
|
||||
fmt.Println(elType, elName)
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
dim := r.dim()
|
||||
if r.element.DimIndex == nil {
|
||||
if dim <= 0 {
|
||||
return nil
|
||||
incr, err := strconv.ParseUint(dimIncr, 0, 32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
a.incr = uint32(incr)
|
||||
|
||||
if dimIndex == nil {
|
||||
idx := make([]string, dim)
|
||||
for i := range idx {
|
||||
idx[i] = strconv.FormatInt(int64(i), 10)
|
||||
}
|
||||
return idx
|
||||
a.idx = idx
|
||||
return a
|
||||
}
|
||||
|
||||
t := strings.Split(*r.element.DimIndex, "-")
|
||||
t := strings.Split(*dimIndex, "-")
|
||||
if len(t) == 2 {
|
||||
// renesas uses hex letters e.g. A-B
|
||||
if strings.Contains("ABCDEFabcdef", t[0]) {
|
||||
@@ -797,18 +1118,41 @@ func (r *Register) dimIndex() []string {
|
||||
for i := x; i <= y; i++ {
|
||||
idx[i-x] = strconv.FormatInt(i, 10)
|
||||
}
|
||||
return idx
|
||||
a.idx = idx
|
||||
return a
|
||||
} else if len(t) > 2 {
|
||||
panic("invalid dimIndex")
|
||||
}
|
||||
|
||||
s := strings.Split(*r.element.DimIndex, ",")
|
||||
s := strings.Split(*dimIndex, ",")
|
||||
if len(s) != dim {
|
||||
panic("invalid dimIndex")
|
||||
}
|
||||
a.idx = s
|
||||
return a
|
||||
}
|
||||
|
||||
func (da *dimArray) replace(s string, i int) string {
|
||||
if da == nil {
|
||||
return s
|
||||
}
|
||||
if i >= len(da.idx) {
|
||||
return s
|
||||
}
|
||||
return strings.ReplaceAll(s, "%s", da.idx[i])
|
||||
}
|
||||
|
||||
func (da *dimArray) rangeElems(yield func(i int, incr uint32) bool) {
|
||||
if da == nil {
|
||||
yield(0, 0)
|
||||
return
|
||||
}
|
||||
for i := range da.dim {
|
||||
if !yield(i, uint32(i)*da.incr) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Register) size() int {
|
||||
if r.element.Size != nil {
|
||||
@@ -823,21 +1167,17 @@ func (r *Register) size() int {
|
||||
|
||||
func parseRegister(groupName string, regEl *SVDRegister, baseAddress uint64, bitfieldPrefix string) []*PeripheralField {
|
||||
reg := NewRegister(regEl, baseAddress)
|
||||
|
||||
if reg.dim() != -1 {
|
||||
dimIncrement, err := strconv.ParseUint(regEl.DimIncrement, 0, 32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if strings.Contains(reg.name(), "%s") {
|
||||
name := reg.name()
|
||||
da := decodeDimArray(regEl.Dim, regEl.DimIndex, regEl.DimIncrement, "register", name)
|
||||
if da != nil && strings.Contains(name, "%s") {
|
||||
// a "spaced array" of registers, special processing required
|
||||
// we need to generate a separate register for each "element"
|
||||
var results []*PeripheralField
|
||||
shortName := strings.ToUpper(strings.ReplaceAll(strings.ReplaceAll(reg.name(), "_%s", ""), "%s", ""))
|
||||
for i, j := range reg.dimIndex() {
|
||||
regAddress := reg.address() + (uint64(i) * dimIncrement)
|
||||
shortName := strings.ToUpper(strings.ReplaceAll(strings.ReplaceAll(name, "_%s", ""), "%s", ""))
|
||||
for i := range da.idx {
|
||||
regAddress := reg.address() + (uint64(i) * uint64(da.incr))
|
||||
results = append(results, &PeripheralField{
|
||||
Name: strings.ToUpper(strings.ReplaceAll(reg.name(), "%s", j)),
|
||||
Name: strings.ToUpper(da.replace(name, i)),
|
||||
Address: regAddress,
|
||||
Description: reg.description(),
|
||||
Array: -1,
|
||||
@@ -854,7 +1194,6 @@ func parseRegister(groupName string, regEl *SVDRegister, baseAddress uint64, bit
|
||||
}
|
||||
return results
|
||||
}
|
||||
}
|
||||
regName := reg.name()
|
||||
if !unicode.IsUpper(rune(regName[0])) && !unicode.IsDigit(rune(regName[0])) {
|
||||
regName = strings.ToUpper(regName)
|
||||
@@ -979,14 +1318,20 @@ var (
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
{{.Name}} = (*{{.GroupName}}_Type)(unsafe.Pointer(uintptr(0x{{printf "%x" .BaseAddress}})))
|
||||
{{- if .Alias}}
|
||||
{{.Alias}} = {{.Name}}
|
||||
{{- end}}
|
||||
{{- "\n"}}
|
||||
{{- end}}
|
||||
)
|
||||
|
||||
`))
|
||||
pkgName := filepath.Base(strings.TrimRight(outdir, "/"))
|
||||
tweakDevice(device, pkgName)
|
||||
|
||||
err = t.Execute(w, map[string]interface{}{
|
||||
"device": device,
|
||||
"pkgName": filepath.Base(strings.TrimRight(outdir, "/")),
|
||||
"pkgName": pkgName,
|
||||
"interruptMax": maxInterruptValue,
|
||||
"interruptSystem": interruptSystem,
|
||||
"interruptHandlers": interruptHandlers,
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func tweakDevice(d *Device, pkgName string) {
|
||||
if pkgName != "stm32" {
|
||||
// no-op for device types that do not need tweaks
|
||||
return
|
||||
}
|
||||
|
||||
// Source file machine_stm32_iwdg.go relies on the presence of
|
||||
// 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")
|
||||
|
||||
for _, p := range d.Peripherals {
|
||||
switch p.GroupName {
|
||||
case "TIM":
|
||||
// SVDs like stm32l4r5.svd define CCMR*_Input and _Output
|
||||
// alternate registers, with _Input sorted before _Output.
|
||||
// This would result in the _Output fields missing from the
|
||||
// TIM_type struct definition, hence compilation would fail.
|
||||
// Therefore we adjust the order of these alternate registers
|
||||
// accordingly.
|
||||
stm32EnsureCCMROrder(p.Registers)
|
||||
|
||||
case "USART":
|
||||
isr := p.lookupRegister("ISR")
|
||||
if isr == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Some of the upstream SVD files, like the one for stm32wl5x_cm4,
|
||||
// lack FIFO enabled variants of the USART ISR register,
|
||||
// even if the register manual defines them. To make sure
|
||||
// that TXFNF is not missing from the generated .go files,
|
||||
// we add TXFNF here in case FIFOEN is present.
|
||||
if p.lookupRegister("CR1").hasBitfield("FIFOEN") {
|
||||
stm32EnsureBit(isr, "TXFNF", "TXE", "USART_ISR_")
|
||||
}
|
||||
|
||||
// Svdtools handles the presence of alternate USART ISR registers,
|
||||
// like in case of the stm32l4r5, adjusting names like "ISR_enabled"
|
||||
// to "ISR", deleting "ISR_disabled" or "ISR_ALTERNATE" register definitions
|
||||
// from the SVD.
|
||||
// As this would result in USART_ISR_TXE definitions missing in the
|
||||
// generated .go file, a constant for TXE is added here
|
||||
// in case TXFNF is defined.
|
||||
stm32EnsureBit(isr, "TXE", "TXFNF", "USART_ISR_")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addUnnumberedAlias(d *Device, dest, src string) {
|
||||
if _, ok := d.PeripheralDict[dest]; !ok {
|
||||
if p := d.PeripheralDict[src]; p != nil {
|
||||
p.Alias = dest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stm32EnsureCCMROrder(registers []*PeripheralField) {
|
||||
for i, r := range registers {
|
||||
if i > 0 {
|
||||
prev := registers[i-1]
|
||||
if r.Address == prev.Address {
|
||||
// alternate field
|
||||
if strings.HasPrefix(prev.Name, "CCMR") && strings.HasPrefix(r.Name, "CCMR") && strings.HasSuffix(r.Name, "_Output") {
|
||||
// swap register pointers
|
||||
registers[i-1], registers[i] = r, prev
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stm32EnsureBit(reg *PeripheralField, want, have, prefix string) {
|
||||
iWant := -1
|
||||
iHave := -1
|
||||
wantConst := prefix + want
|
||||
haveConst := prefix + have
|
||||
for i := range reg.Constants {
|
||||
f := ®.Constants[i]
|
||||
if f.Name == wantConst {
|
||||
iWant = i
|
||||
break
|
||||
}
|
||||
if f.Name == haveConst {
|
||||
iHave = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if iHave != -1 && iWant == -1 {
|
||||
iWant = iHave + 1
|
||||
reg.Constants = slices.Insert(reg.Constants, iWant, reg.Constants[iHave])
|
||||
reg.Constants[iWant].Name = wantConst
|
||||
reg.Constants[iWant].Description = "Bit " + want + ". (added by gen-device-svd)"
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Peripheral) lookupRegister(name string) *PeripheralField {
|
||||
for _, r := range p.Registers {
|
||||
if r.Name == name {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PeripheralField) hasBitfield(name string) bool {
|
||||
if r == nil {
|
||||
return false
|
||||
}
|
||||
for i := range r.Bitfields {
|
||||
if r.Bitfields[i].Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user