aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2023-05-17 23:08:55 +0200
committerRon Evans <[email protected]>2023-05-20 21:18:02 +0200
commit4d11d552db47f55147eb171bfa4e35ce33de267d (patch)
tree86d9b00fe205df681f730bc642521c2bbe35ab48 /tools
parentb43bd9e62a9648b995a397f7a2454799b2d7c9fa (diff)
downloadtinygo-4d11d552db47f55147eb171bfa4e35ce33de267d.tar.gz
tinygo-4d11d552db47f55147eb171bfa4e35ce33de267d.zip
avr: update gen-device-avr tool to support newer AVRs
This refactors gen-device-avr to output two different formats: one for all the existing AVR chips (that don't really have the concept of a peripheral, just a bunch of registers), and one for all the new chips like the ATtiny1616 (tinyAVR 1-series and 2-series) that have peripherals like the Cortex-M chips with type structs and instances. I checked the generated code for all the AVR chips we have support for (atmega1280, atmega1284p, atmega2560, atmega328p, atmega32u4, attiny85) and while the generated Go code did change, it looks safe to me.
Diffstat (limited to 'tools')
-rwxr-xr-xtools/gen-device-avr/gen-device-avr.go312
1 files changed, 211 insertions, 101 deletions
diff --git a/tools/gen-device-avr/gen-device-avr.go b/tools/gen-device-avr/gen-device-avr.go
index 6b7074a25..c660f8ca7 100755
--- a/tools/gen-device-avr/gen-device-avr.go
+++ b/tools/gen-device-avr/gen-device-avr.go
@@ -30,27 +30,31 @@ type AVRToolsDeviceFile struct {
Size string `xml:"size,attr"`
} `xml:"memory-segment"`
} `xml:"address-spaces>address-space"`
+ PeripheralInstances []struct {
+ Name string `xml:"name,attr"`
+ Caption string `xml:"caption,attr"`
+ RegisterGroup struct {
+ NameInModule string `xml:"name-in-module,attr"`
+ Offset string `xml:"offset,attr"`
+ } `xml:"register-group"`
+ } `xml:"peripherals>module>instance"`
Interrupts []*XMLInterrupt `xml:"interrupts>interrupt"`
} `xml:"devices>device"`
- Modules []struct {
- Name string `xml:"name,attr"`
- Caption string `xml:"caption,attr"`
- RegisterGroup struct {
+ PeripheralRegisterGroups []struct {
+ Name string `xml:"name,attr"`
+ Caption string `xml:"caption,attr"`
+ Registers []struct {
Name string `xml:"name,attr"`
Caption string `xml:"caption,attr"`
- Registers []struct {
- Name string `xml:"name,attr"`
- Caption string `xml:"caption,attr"`
- Offset string `xml:"offset,attr"`
- Size int `xml:"size,attr"`
- Bitfields []struct {
- Name string `xml:"name,attr"`
- Caption string `xml:"caption,attr"`
- Mask string `xml:"mask,attr"`
- } `xml:"bitfield"`
- } `xml:"register"`
- } `xml:"register-group"`
- } `xml:"modules>module"`
+ Offset string `xml:"offset,attr"`
+ Size uint64 `xml:"size,attr"`
+ Bitfields []struct {
+ Name string `xml:"name,attr"`
+ Caption string `xml:"caption,attr"`
+ Mask string `xml:"mask,attr"`
+ } `xml:"bitfield"`
+ } `xml:"register"`
+ } `xml:"modules>module>register-group"`
}
type XMLInterrupt struct {
@@ -61,9 +65,11 @@ type XMLInterrupt struct {
}
type Device struct {
- metadata map[string]interface{}
- interrupts []Interrupt
- peripherals []*Peripheral
+ metadata map[string]interface{}
+ interrupts []Interrupt
+ types []*PeripheralType
+ instances []*PeripheralInstance
+ oldStyle bool
}
// AddressSpace is the Go version of an XML element like the following:
@@ -94,22 +100,30 @@ type Interrupt struct {
Caption string
}
-type Peripheral struct {
+// Peripheral instance, for example PORTB
+type PeripheralInstance struct {
+ Name string
+ Caption string
+ Address uint64
+ Type *PeripheralType
+}
+
+// Peripheral type, for example PORT (if it's shared between different
+// instances, which is the case for new-style ATDF files).
+type PeripheralType struct {
Name string
Caption string
Registers []*Register
+ Instances []*PeripheralInstance
}
+// Single register or struct field in a peripheral type.
type Register struct {
- Caption string
- Variants []RegisterVariant
- Bitfields []Bitfield
- peripheral *Peripheral
-}
-
-type RegisterVariant struct {
- Name string
- Address int64
+ Caption string
+ Name string
+ Type string
+ Offset uint64 // offset, only for old-style ATDF files
+ Bitfields []Bitfield
}
type Bitfield struct {
@@ -159,86 +173,155 @@ func readATDF(path string) (*Device, error) {
}
}
- allRegisters := map[string]*Register{}
-
- var peripherals []*Peripheral
- for _, el := range xml.Modules {
- peripheral := &Peripheral{
- Name: el.Name,
- Caption: el.Caption,
+ // There appear to be two kinds of devices and ATDF files: those before
+ // ~2017 and those introduced as part of the tinyAVR (1 and 2 series).
+ // The newer devices are structured slightly differently, with peripherals
+ // laid out more like Cortex-M chips and one or more instances per chip.
+ // Older designs basically just have a bunch of registers with little
+ // structure in them.
+ // The code generated for these chips is quite different:
+ // * For old-style chips we'll generate a bunch of registers without
+ // peripherals (e.g. PORTB, DDRB, etc).
+ // * For new-style chips we'll generate proper peripheral structs like we
+ // do for Cortex-M chips.
+ oldStyle := true
+ for _, instanceEl := range device.PeripheralInstances {
+ if instanceEl.RegisterGroup.NameInModule == "" {
+ continue
}
- peripherals = append(peripherals, peripheral)
+ offset, err := strconv.ParseUint(instanceEl.RegisterGroup.Offset, 0, 16)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse offset %#v of peripheral %s: %v", instanceEl.RegisterGroup.Offset, instanceEl.Name, err)
+ }
+ if offset != 0 {
+ oldStyle = false
+ }
+ }
- regElGroup := el.RegisterGroup
- for _, regEl := range regElGroup.Registers {
- regOffset, err := strconv.ParseInt(regEl.Offset, 0, 64)
+ // Read all peripheral types.
+ var types []*PeripheralType
+ typeMap := make(map[string]*PeripheralType)
+ allRegisters := map[string]*Register{}
+ for _, registerGroupEl := range xml.PeripheralRegisterGroups {
+ var regs []*Register
+ regEls := registerGroupEl.Registers
+ if !oldStyle {
+ // We only need to sort registers when we're generating peripheral
+ // structs.
+ sort.SliceStable(regEls, func(i, j int) bool {
+ return regEls[i].Offset < regEls[j].Offset
+ })
+ }
+ addReg := func(reg *Register) {
+ if oldStyle {
+ // Check for duplicate registers (they happen).
+ if reg2 := allRegisters[reg.Name]; reg2 != nil {
+ return
+ }
+ allRegisters[reg.Name] = reg
+ }
+ regs = append(regs, reg)
+ }
+ offset := uint64(0)
+ for _, regEl := range regEls {
+ regOffset, err := strconv.ParseUint(regEl.Offset, 0, 64)
if err != nil {
return nil, fmt.Errorf("failed to parse offset %#v of register %s: %v", regEl.Offset, regEl.Name, err)
}
- reg := &Register{
- Caption: regEl.Caption,
- peripheral: peripheral,
- }
- switch regEl.Size {
- case 1:
- reg.Variants = []RegisterVariant{
- {
- Name: regEl.Name,
- Address: regOffset,
- },
+ if !oldStyle {
+ // Add some padding to the gap in the struct, if needed.
+ if offset < regOffset {
+ regs = append(regs, &Register{
+ Name: "_",
+ Type: fmt.Sprintf("[%d]volatile.Register8", regOffset-offset),
+ })
+ offset = regOffset
}
- case 2:
- reg.Variants = []RegisterVariant{
- {
- Name: regEl.Name + "L",
- Address: regOffset,
- },
- {
- Name: regEl.Name + "H",
- Address: regOffset + 1,
- },
+
+ // Check for overlapping registers.
+ if offset > regOffset {
+ return nil, fmt.Errorf("register %s in peripheral %s overlaps with another register", regEl.Name, registerGroupEl.Name)
}
- default:
- // TODO
- continue
}
+ var bitfields []Bitfield
for _, bitfieldEl := range regEl.Bitfields {
- mask := bitfieldEl.Mask
- if len(mask) == 2 {
+ maskString := bitfieldEl.Mask
+ if len(maskString) == 2 {
// Two devices (ATtiny102 and ATtiny104) appear to have an
// error in the bitfields, leaving out the '0x' prefix.
- mask = "0x" + mask
+ maskString = "0x" + maskString
}
- maskInt, err := strconv.ParseUint(mask, 0, 32)
+ mask, err := strconv.ParseUint(maskString, 0, 32)
if err != nil {
- return nil, fmt.Errorf("failed to parse mask %#v of bitfield %s: %v", mask, bitfieldEl.Name, err)
+ return nil, fmt.Errorf("failed to parse mask %#v of bitfield %s: %v", maskString, bitfieldEl.Name, err)
+ }
+ name := regEl.Name + "_" + bitfieldEl.Name
+ if !oldStyle {
+ name = registerGroupEl.Name + "_" + name
}
- reg.Bitfields = append(reg.Bitfields, Bitfield{
- Name: regEl.Name + "_" + bitfieldEl.Name,
+ bitfields = append(bitfields, Bitfield{
+ Name: name,
Caption: bitfieldEl.Caption,
- Mask: uint(maskInt),
+ Mask: uint(mask),
})
}
- if firstReg, ok := allRegisters[regEl.Name]; ok {
- // merge bit fields with previous register
- merged := append(firstReg.Bitfields, reg.Bitfields...)
- firstReg.Bitfields = make([]Bitfield, 0, len(merged))
- m := make(map[string]interface{})
- for _, field := range merged {
- if _, ok := m[field.Name]; !ok {
- m[field.Name] = nil
- firstReg.Bitfields = append(firstReg.Bitfields, field)
- }
- }
- continue
- } else {
- allRegisters[regEl.Name] = reg
+ switch regEl.Size {
+ case 1:
+ addReg(&Register{
+ Name: regEl.Name,
+ Type: "volatile.Register8",
+ Caption: regEl.Caption,
+ Offset: regOffset,
+ Bitfields: bitfields,
+ })
+ case 2:
+ addReg(&Register{
+ Name: regEl.Name + "L",
+ Type: "volatile.Register8",
+ Caption: regEl.Caption + " (lower bits)",
+ Offset: regOffset + 0,
+ })
+ addReg(&Register{
+ Name: regEl.Name + "H",
+ Type: "volatile.Register8",
+ Caption: regEl.Caption + " (upper bits)",
+ Offset: regOffset + 1,
+ })
+ default:
+ panic("todo: unknown size")
}
+ offset += regEl.Size
+ }
+ periphType := &PeripheralType{
+ Name: registerGroupEl.Name,
+ Caption: registerGroupEl.Caption,
+ Registers: regs,
+ }
+ types = append(types, periphType)
+ typeMap[periphType.Name] = periphType
+ }
- peripheral.Registers = append(peripheral.Registers, reg)
+ // Read all peripheral instances.
+ var instances []*PeripheralInstance
+ for _, instanceEl := range device.PeripheralInstances {
+ if instanceEl.RegisterGroup.NameInModule == "" {
+ continue
+ }
+ offset, err := strconv.ParseUint(instanceEl.RegisterGroup.Offset, 0, 16)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse offset %#v of peripheral %s: %v", instanceEl.RegisterGroup.Offset, instanceEl.Name, err)
}
+ periphType := typeMap[instanceEl.RegisterGroup.NameInModule]
+ instance := &PeripheralInstance{
+ Name: instanceEl.Name,
+ Caption: instanceEl.Caption,
+ Address: offset,
+ Type: periphType,
+ }
+ instances = append(instances, instance)
+ periphType.Instances = append(periphType.Instances, instance)
}
ramStart := int64(0)
@@ -298,8 +381,10 @@ func readATDF(path string) (*Device, error) {
"ramSize": ramSize,
"numInterrupts": len(device.Interrupts),
},
- interrupts: interrupts,
- peripherals: peripherals,
+ interrupts: interrupts,
+ types: types,
+ instances: instances,
+ oldStyle: oldStyle,
}, nil
}
@@ -358,25 +443,52 @@ func interrupt{{.Name}}() {
}
{{- end}}
+{{if .oldStyle -}}
// Peripherals.
-var ({{range .peripherals}}
+var (
+{{- range .instances}}
// {{.Caption}}
-{{range .Registers}}{{range .Variants}} {{.Name}} = (*volatile.Register8)(unsafe.Pointer(uintptr(0x{{printf "%x" .Address}})))
-{{end}}{{end}}{{end}})
+ {{range .Type.Registers -}}
+ {{if ne .Name "_" -}}
+ {{.Name}} = (*{{.Type}})(unsafe.Pointer(uintptr(0x{{printf "%x" .Offset}})))
+ {{end -}}
+ {{end -}}
+{{end}})
+{{else}}
+// Peripherals instances.
+var (
+{{- range .instances -}}
+ {{.Name}} = (*{{.Type.Name}}_Type)(unsafe.Pointer(uintptr(0x{{printf "%x" .Address}})))
+ {{- if .Caption}}// {{.Caption}}{{end}}
+{{end -}}
+)
+
+// Peripheral type definitions.
+
+{{range .types}}
+type {{.Name}}_Type struct {
+{{range .Registers -}}
+ {{.Name}} {{.Type}} {{if .Caption}} // {{.Caption}} {{end}}
+{{end -}}
+}
+{{end}}
+{{end}}
`))
err = t.Execute(w, map[string]interface{}{
"metadata": device.metadata,
"pkgName": filepath.Base(strings.TrimRight(outdir, "/")),
"interrupts": device.interrupts,
"interruptMax": maxInterruptNum,
- "peripherals": device.peripherals,
+ "instances": device.instances,
+ "types": device.types,
+ "oldStyle": device.oldStyle,
})
if err != nil {
return err
}
// Write bitfields.
- for _, peripheral := range device.peripherals {
+ for _, peripheral := range device.types {
// Only write bitfields when there are any.
numFields := 0
for _, r := range peripheral.Registers {
@@ -391,13 +503,11 @@ var ({{range .peripherals}}
if len(register.Bitfields) == 0 {
continue
}
- for _, variant := range register.Variants {
- fmt.Fprintf(w, "\n\t// %s", variant.Name)
- if register.Caption != "" {
- fmt.Fprintf(w, ": %s", register.Caption)
- }
- fmt.Fprintf(w, "\n")
+ fmt.Fprintf(w, "\n\t// %s", register.Name)
+ if register.Caption != "" {
+ fmt.Fprintf(w, ": %s", register.Caption)
}
+ fmt.Fprintf(w, "\n")
allBits := map[string]interface{}{}
for _, bitfield := range register.Bitfields {
if bits.OnesCount(bitfield.Mask) == 1 {