aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2019-11-28 16:11:54 +0100
committerRon Evans <[email protected]>2019-12-07 16:04:47 +0100
commit24259cbb5f7b59090bb51d469f18a5171299db82 (patch)
tree1d973bd549f6f0ff5634ca210b3c45d4e2e9d8d4 /tools
parent2f932a9eee13248a8cb2b31837c33d4d956f1362 (diff)
downloadtinygo-24259cbb5f7b59090bb51d469f18a5171299db82.tar.gz
tinygo-24259cbb5f7b59090bb51d469f18a5171299db82.zip
tools: rewrite gen-device-avr in Go
This brings a big speedup. Not counting gofmt time, `make gen-device-avr` became about 3x faster. In the future, it might be an idea to generate the AST in-memory and write it out already formatted.
Diffstat (limited to 'tools')
-rwxr-xr-xtools/gen-device-avr.py291
-rwxr-xr-xtools/gen-device-avr/gen-device-avr.go464
2 files changed, 464 insertions, 291 deletions
diff --git a/tools/gen-device-avr.py b/tools/gen-device-avr.py
deleted file mode 100755
index becd4cd8f..000000000
--- a/tools/gen-device-avr.py
+++ /dev/null
@@ -1,291 +0,0 @@
-#!/usr/bin/env python
-
-from __future__ import print_function
-
-import sys
-import os
-from xml.dom import minidom
-from glob import glob
-from collections import OrderedDict
-import re
-
-class Device:
- # dummy
- pass
-
-def getText(element):
- strings = []
- for node in element.childNodes:
- if node.nodeType == node.TEXT_NODE:
- strings.append(node.data)
- return ''.join(strings)
-
-def formatText(text):
- text = re.sub('[ \t\n]+', ' ', text) # Collapse whitespace (like in HTML)
- text = text.replace('\\n ', '\n')
- text = text.strip()
- return text
-
-def readATDF(path):
- # Read Atmel device descriptor files.
- # See: http://packs.download.atmel.com
-
- device = Device()
-
- xml = minidom.parse(path)
- device = xml.getElementsByTagName('device')[0]
- deviceName = device.getAttribute('name')
- arch = device.getAttribute('architecture')
- family = device.getAttribute('family')
-
- memorySizes = {}
- for el in device.getElementsByTagName('address-space'):
- addressSpace = {
- 'size': int(el.getAttribute('size'), 0),
- 'segments': {},
- }
- memorySizes[el.getAttribute('name')] = addressSpace
- for segmentEl in el.getElementsByTagName('memory-segment'):
- addressSpace['segments'][segmentEl.getAttribute('name')] = int(segmentEl.getAttribute('size'), 0)
-
- device.interrupts = []
- for el in device.getElementsByTagName('interrupts')[0].getElementsByTagName('interrupt'):
- device.interrupts.append({
- 'index': int(el.getAttribute('index')),
- 'name': el.getAttribute('name'),
- 'description': el.getAttribute('caption'),
- })
-
- allRegisters = {}
- commonRegisters = {}
-
- device.peripherals = []
- for el in xml.getElementsByTagName('modules')[0].getElementsByTagName('module'):
- peripheral = {
- 'name': el.getAttribute('name'),
- 'description': el.getAttribute('caption'),
- 'registers': [],
- }
- device.peripherals.append(peripheral)
- for regElGroup in el.getElementsByTagName('register-group'):
- for regEl in regElGroup.getElementsByTagName('register'):
- size = int(regEl.getAttribute('size'))
- regName = regEl.getAttribute('name')
- regOffset = int(regEl.getAttribute('offset'), 0)
- reg = {
- 'description': regEl.getAttribute('caption'),
- 'bitfields': [],
- 'array': None,
- }
- if size == 1:
- reg['variants'] = [{
- 'name': regName,
- 'address': regOffset,
- }]
- elif size == 2:
- reg['variants'] = [{
- 'name': regName + 'L',
- 'address': regOffset,
- }, {
- 'name': regName + 'H',
- 'address': regOffset + 1,
- }]
- else:
- # TODO
- continue
-
- for bitfieldEl in regEl.getElementsByTagName('bitfield'):
- mask = bitfieldEl.getAttribute('mask')
- if len(mask) == 2:
- # Two devices (ATtiny102 and ATtiny104) appear to have
- # an error in the bitfields, leaving out the '0x'
- # prefix.
- mask = '0x' + mask
- reg['bitfields'].append({
- 'name': regName + '_' + bitfieldEl.getAttribute('name'),
- 'description': bitfieldEl.getAttribute('caption'),
- 'value': int(mask, 0),
- })
-
- if regName in allRegisters:
- firstReg = allRegisters[regName]
- if firstReg['register'] in firstReg['peripheral']['registers']:
- firstReg['peripheral']['registers'].remove(firstReg['register'])
- if firstReg['address'] != regOffset:
- continue # TODO
- commonRegisters = allRegisters[regName]['register']
- continue
- else:
- allRegisters[regName] = {'address': regOffset, 'register': reg, 'peripheral': peripheral}
-
- peripheral['registers'].append(reg)
-
- ramSize = 0 # for devices with no RAM
- for ramSegmentName in ['IRAM', 'INTERNAL_SRAM', 'SRAM']:
- if ramSegmentName in memorySizes['data']['segments']:
- ramSize = memorySizes['data']['segments'][ramSegmentName]
-
- device.metadata = {
- 'file': os.path.basename(path),
- 'descriptorSource': 'http://packs.download.atmel.com/',
- 'name': deviceName,
- 'nameLower': deviceName.lower(),
- 'description': 'Device information for the {}.'.format(deviceName),
- 'arch': arch,
- 'family': family,
- 'flashSize': memorySizes['prog']['size'],
- 'ramSize': ramSize,
- 'numInterrupts': len(device.interrupts),
- }
-
- return device
-
-def writeGo(outdir, device):
- # The Go module for this device.
- out = open(outdir + '/' + device.metadata['nameLower'] + '.go', 'w')
- pkgName = os.path.basename(outdir.rstrip('/'))
- out.write('''\
-// Automatically generated file. DO NOT EDIT.
-// Generated by gen-device-avr.py from {file}, see {descriptorSource}
-
-// +build {pkgName},{nameLower}
-
-// {description}
-package {pkgName}
-
-import (
- "runtime/volatile"
- "unsafe"
-)
-
-// Some information about this device.
-const (
- DEVICE = "{name}"
- ARCH = "{arch}"
- FAMILY = "{family}"
-)
-'''.format(pkgName=pkgName, **device.metadata))
-
- out.write('\n// Interrupts\nconst (\n')
- for intr in device.interrupts:
- out.write('\tIRQ_{name} = {index} // {description}\n'.format(**intr))
- intrMax = max(map(lambda intr: intr['index'], device.interrupts))
- out.write('\tIRQ_max = {} // Highest interrupt number on this device.\n'.format(intrMax))
- out.write(')\n')
-
- out.write('\n// Peripherals.\nvar (')
- first = True
- for peripheral in device.peripherals:
- out.write('\n\t// {description}\n'.format(**peripheral))
- for register in peripheral['registers']:
- for variant in register['variants']:
- out.write('\t{name} = (*volatile.Register8)(unsafe.Pointer(uintptr(0x{address:x})))\n'.format(**variant))
- out.write(')\n')
-
- for peripheral in device.peripherals:
- if not sum(map(lambda r: len(r['bitfields']), peripheral['registers'])): continue
- out.write('\n// Bitfields for {name}: {description}\nconst('.format(**peripheral))
- for register in peripheral['registers']:
- if not register['bitfields']: continue
- for variant in register['variants']:
- out.write('\n\t// {name}'.format(**variant))
- if register['description']:
- out.write(': {description}'.format(**register))
- out.write('\n')
- for bitfield in register['bitfields']:
- name = bitfield['name']
- value = bitfield['value']
- if '{:08b}'.format(value).count('1') == 1:
- out.write('\t{name} = 0x{value:x}'.format(**bitfield))
- if bitfield['description']:
- out.write(' // {description}'.format(**bitfield))
- out.write('\n')
- else:
- n = 0
- for i in range(8):
- if (value >> i) & 1 == 0: continue
- out.write('\t{}{} = 0x{:x}'.format(name, n, 1 << i))
- if bitfield['description']:
- out.write(' // {description}'.format(**bitfield))
- n += 1
- out.write('\n')
- out.write(')\n')
-
-def writeAsm(outdir, device):
- # The interrupt vector, which is hard to write directly in Go.
- out = open(outdir + '/' + device.metadata['nameLower'] + '.s', 'w')
- out.write('''\
-; Automatically generated file. DO NOT EDIT.
-; Generated by gen-device-avr.py from {file}, see {descriptorSource}
-
-; This is the default handler for interrupts, if triggered but not defined.
-; Sleep inside so that an accidentally triggered interrupt won't drain the
-; battery of a battery-powered device.
-.section .text.__vector_default
-.global __vector_default
-__vector_default:
- sleep
- rjmp __vector_default
-
-; Avoid the need for repeated .weak and .set instructions.
-.macro IRQ handler
- .weak \\handler
- .set \\handler, __vector_default
-.endm
-
-; The interrupt vector of this device. Must be placed at address 0 by the linker.
-.section .vectors
-.global __vectors
-'''.format(**device.metadata))
- num = 0
- for intr in device.interrupts:
- jmp = 'jmp'
- if device.metadata['flashSize'] <= 8 * 1024:
- # When a device has 8kB or less flash, rjmp (2 bytes) must be used
- # instead of jmp (4 bytes).
- # https://www.avrfreaks.net/forum/rjmp-versus-jmp
- jmp = 'rjmp'
- if intr['index'] < num:
- # Some devices have duplicate interrupts, probably for historical
- # reasons.
- continue
- while intr['index'] > num:
- out.write(' {jmp} __vector_default\n'.format(jmp=jmp))
- num += 1
- num += 1
- out.write(' {jmp} __vector_{name}\n'.format(jmp=jmp, **intr))
-
- out.write('''
- ; Define default implementations for interrupts, redirecting to
- ; __vector_default when not implemented.
-''')
- for intr in device.interrupts:
- out.write(' IRQ __vector_{name}\n'.format(**intr))
-
-def writeLD(outdir, device):
- # Variables for the linker script.
- out = open(outdir + '/' + device.metadata['nameLower'] + '.ld', 'w')
- out.write('''\
-/* Automatically generated file. DO NOT EDIT. */
-/* Generated by gen-device-avr.py from {file}, see {descriptorSource} */
-
-__flash_size = 0x{flashSize:x};
-__ram_size = 0x{ramSize:x};
-__num_isrs = {numInterrupts};
-'''.format(**device.metadata))
- out.close()
-
-
-def generate(indir, outdir):
- for filepath in sorted(glob(indir + '/*.atdf')):
- print(filepath)
- device = readATDF(filepath)
- writeGo(outdir, device)
- writeAsm(outdir, device)
- writeLD(outdir, device)
-
-
-if __name__ == '__main__':
- indir = sys.argv[1] # directory with register descriptor files (*.atdf)
- outdir = sys.argv[2] # output directory
- generate(indir, outdir)
diff --git a/tools/gen-device-avr/gen-device-avr.go b/tools/gen-device-avr/gen-device-avr.go
new file mode 100755
index 000000000..d49162c9d
--- /dev/null
+++ b/tools/gen-device-avr/gen-device-avr.go
@@ -0,0 +1,464 @@
+package main
+
+import (
+ "bufio"
+ "encoding/xml"
+ "fmt"
+ "html/template"
+ "math/bits"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+type AVRToolsDeviceFile struct {
+ XMLName xml.Name `xml:"avr-tools-device-file"`
+ Devices []struct {
+ Name string `xml:"name,attr"`
+ Architecture string `xml:"architecture,attr"`
+ Family string `xml:"family,attr"`
+ AddressSpaces []struct {
+ Name string `xml:"name,attr"`
+ Size string `xml:"size,attr"`
+ MemorySegments []struct {
+ Name string `xml:"name,attr"`
+ Size string `xml:"size,attr"`
+ } `xml:"memory-segment"`
+ } `xml:"address-spaces>address-space"`
+ Interrupts []Interrupt `xml:"interrupts>interrupt"`
+ } `xml:"devices>device"`
+ Modules []struct {
+ Name string `xml:"name,attr"`
+ Caption string `xml:"caption,attr"`
+ RegisterGroup 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"`
+}
+
+type Device struct {
+ metadata map[string]interface{}
+ interrupts []Interrupt
+ peripherals []*Peripheral
+}
+
+type AddressSpace struct {
+ Size string
+ Segments map[string]int
+}
+
+type Interrupt struct {
+ Index int `xml:"index,attr"`
+ Name string `xml:"name,attr"`
+ Caption string `xml:"caption,attr"`
+}
+
+type Peripheral struct {
+ Name string
+ Caption string
+ Registers []*Register
+}
+
+type Register struct {
+ Caption string
+ Variants []RegisterVariant
+ Bitfields []Bitfield
+ peripheral *Peripheral
+}
+
+type RegisterVariant struct {
+ Name string
+ Address int64
+}
+
+type Bitfield struct {
+ Name string
+ Caption string
+ Mask uint
+}
+
+func readATDF(path string) (*Device, error) {
+ // Read Atmel device descriptor files.
+ // See: http://packs.download.atmel.com
+
+ // Open the XML file.
+ f, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ decoder := xml.NewDecoder(f)
+ xml := &AVRToolsDeviceFile{}
+ err = decoder.Decode(xml)
+ if err != nil {
+ return nil, err
+ }
+
+ device := xml.Devices[0]
+
+ memorySizes := make(map[string]*AddressSpace, len(device.AddressSpaces))
+ for _, el := range device.AddressSpaces {
+ memorySizes[el.Name] = &AddressSpace{
+ Size: el.Size,
+ Segments: make(map[string]int),
+ }
+ for _, segmentEl := range el.MemorySegments {
+ size, err := strconv.ParseInt(segmentEl.Size, 0, 32)
+ if err != nil {
+ return nil, err
+ }
+ memorySizes[el.Name].Segments[segmentEl.Name] = int(size)
+ }
+ }
+
+ allRegisters := map[string]*Register{}
+
+ var peripherals []*Peripheral
+ for _, el := range xml.Modules {
+ peripheral := &Peripheral{
+ Name: el.Name,
+ Caption: el.Caption,
+ }
+ peripherals = append(peripherals, peripheral)
+
+ regElGroup := el.RegisterGroup
+ for _, regEl := range regElGroup.Registers {
+ regOffset, err := strconv.ParseInt(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,
+ },
+ }
+ case 2:
+ reg.Variants = []RegisterVariant{
+ {
+ Name: regEl.Name + "L",
+ Address: regOffset,
+ },
+ {
+ Name: regEl.Name + "H",
+ Address: regOffset + 1,
+ },
+ }
+ default:
+ // TODO
+ continue
+ }
+
+ for _, bitfieldEl := range regEl.Bitfields {
+ mask := bitfieldEl.Mask
+ if len(mask) == 2 {
+ // Two devices (ATtiny102 and ATtiny104) appear to have an
+ // error in the bitfields, leaving out the '0x' prefix.
+ mask = "0x" + mask
+ }
+ maskInt, err := strconv.ParseUint(mask, 0, 32)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse mask %#v of bitfield %s: %v", mask, bitfieldEl.Name, err)
+ }
+ reg.Bitfields = append(reg.Bitfields, Bitfield{
+ Name: regEl.Name + "_" + bitfieldEl.Name,
+ Caption: bitfieldEl.Caption,
+ Mask: uint(maskInt),
+ })
+ }
+
+ if _, ok := allRegisters[regEl.Name]; ok {
+ firstReg := allRegisters[regEl.Name]
+ for i := 0; i < len(firstReg.peripheral.Registers); i++ {
+ if firstReg.peripheral.Registers[i] == firstReg {
+ firstReg.peripheral.Registers = append(firstReg.peripheral.Registers[:i], firstReg.peripheral.Registers[i+1:]...)
+ break
+ }
+ }
+ continue
+ } else {
+ allRegisters[regEl.Name] = reg
+ }
+
+ peripheral.Registers = append(peripheral.Registers, reg)
+ }
+ }
+
+ ramSize := 0 // for devices with no RAM
+ for _, ramSegmentName := range []string{"IRAM", "INTERNAL_SRAM", "SRAM"} {
+ if segment, ok := memorySizes["data"].Segments[ramSegmentName]; ok {
+ ramSize = segment
+ }
+ }
+
+ flashSize, err := strconv.ParseInt(memorySizes["prog"].Size, 0, 32)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Device{
+ metadata: map[string]interface{}{
+ "file": filepath.Base(path),
+ "descriptorSource": "http://packs.download.atmel.com/",
+ "name": device.Name,
+ "nameLower": strings.ToLower(device.Name),
+ "description": fmt.Sprintf("Device information for the %s.", device.Name),
+ "arch": device.Architecture,
+ "family": device.Family,
+ "flashSize": int(flashSize),
+ "ramSize": ramSize,
+ "numInterrupts": len(device.Interrupts),
+ },
+ interrupts: device.Interrupts,
+ peripherals: peripherals,
+ }, nil
+}
+
+func writeGo(outdir string, device *Device) error {
+ // The Go module for this device.
+ outf, err := os.Create(outdir + "/" + device.metadata["nameLower"].(string) + ".go")
+ if err != nil {
+ return err
+ }
+ defer outf.Close()
+ w := bufio.NewWriter(outf)
+
+ maxInterruptNum := 0
+ for _, intr := range device.interrupts {
+ if intr.Index > maxInterruptNum {
+ maxInterruptNum = intr.Index
+ }
+ }
+
+ t := template.Must(template.New("go").Parse(`// Automatically generated file. DO NOT EDIT.
+// Generated by gen-device-avr.go from {{.metadata.file}}, see {{.metadata.descriptorSource}}
+
+// +build {{.pkgName}},{{.metadata.nameLower}}
+
+// {{.metadata.description}}
+package {{.pkgName}}
+
+import (
+ "runtime/volatile"
+ "unsafe"
+)
+
+// Some information about this device.
+const (
+ DEVICE = "{{.metadata.name}}"
+ ARCH = "{{.metadata.arch}}"
+ FAMILY = "{{.metadata.family}}"
+)
+
+// Interrupts
+const ({{range .interrupts}}
+ IRQ_{{.Name}} = {{.Index}} // {{.Caption}}{{end}}
+ IRQ_max = {{.interruptMax}} // Highest interrupt number on this device.
+)
+
+// Peripherals.
+var ({{range .peripherals}}
+ // {{.Caption}}
+{{range .Registers}}{{range .Variants}} {{.Name}} = (*volatile.Register8)(unsafe.Pointer(uintptr(0x{{printf "%x" .Address}})))
+{{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,
+ })
+ if err != nil {
+ return err
+ }
+
+ // Write bitfields.
+ for _, peripheral := range device.peripherals {
+ // Only write bitfields when there are any.
+ numFields := 0
+ for _, r := range peripheral.Registers {
+ numFields += len(r.Bitfields)
+ }
+ if numFields == 0 {
+ continue
+ }
+
+ fmt.Fprintf(w, "\n// Bitfields for %s: %s\nconst(", peripheral.Name, peripheral.Caption)
+ for _, register := range peripheral.Registers {
+ 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")
+ }
+ for _, bitfield := range register.Bitfields {
+ if bits.OnesCount(bitfield.Mask) == 1 {
+ fmt.Fprintf(w, "\t%s = 0x%x", bitfield.Name, bitfield.Mask)
+ if len(bitfield.Caption) != 0 {
+ fmt.Fprintf(w, " // %s", bitfield.Caption)
+ }
+ fmt.Fprintf(w, "\n")
+ } else {
+ n := 0
+ for i := uint(0); i < 8; i++ {
+ if (bitfield.Mask>>i)&1 == 0 {
+ continue
+ }
+ fmt.Fprintf(w, "\t%s%d = 0x%x", bitfield.Name, n, 1<<i)
+ if len(bitfield.Caption) != 0 {
+ fmt.Fprintf(w, " // %s", bitfield.Caption)
+ }
+ n++
+ fmt.Fprintf(w, "\n")
+ }
+ }
+ }
+ }
+ fmt.Fprintf(w, ")\n")
+ }
+ return w.Flush()
+}
+
+func writeAsm(outdir string, device *Device) error {
+ // The interrupt vector, which is hard to write directly in Go.
+ out, err := os.Create(outdir + "/" + device.metadata["nameLower"].(string) + ".s")
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+ t := template.Must(template.New("asm").Parse(
+ `; Automatically generated file. DO NOT EDIT.
+; Generated by gen-device-avr.go from {{.file}}, see {{.descriptorSource}}
+
+; This is the default handler for interrupts, if triggered but not defined.
+; Sleep inside so that an accidentally triggered interrupt won't drain the
+; battery of a battery-powered device.
+.section .text.__vector_default
+.global __vector_default
+__vector_default:
+ sleep
+ rjmp __vector_default
+
+; Avoid the need for repeated .weak and .set instructions.
+.macro IRQ handler
+ .weak \handler
+ .set \handler, __vector_default
+.endm
+
+; The interrupt vector of this device. Must be placed at address 0 by the linker.
+.section .vectors
+.global __vectors
+`))
+ err = t.Execute(out, device.metadata)
+ if err != nil {
+ return err
+ }
+ num := 0
+ for _, intr := range device.interrupts {
+ jmp := "jmp"
+ if device.metadata["flashSize"].(int) <= 8*1024 {
+ // When a device has 8kB or less flash, rjmp (2 bytes) must be used
+ // instead of jmp (4 bytes).
+ // https://www.avrfreaks.net/forum/rjmp-versus-jmp
+ jmp = "rjmp"
+ }
+ if intr.Index < num {
+ // Some devices have duplicate interrupts, probably for historical
+ // reasons.
+ continue
+ }
+ for intr.Index > num {
+ fmt.Fprintf(out, " %s __vector_default\n", jmp)
+ num++
+ }
+ num++
+ fmt.Fprintf(out, " %s __vector_%s\n", jmp, intr.Name)
+ }
+
+ fmt.Fprint(out, `
+ ; Define default implementations for interrupts, redirecting to
+ ; __vector_default when not implemented.
+`)
+ for _, intr := range device.interrupts {
+ fmt.Fprintf(out, " IRQ __vector_%s\n", intr.Name)
+ }
+ return nil
+}
+
+func writeLD(outdir string, device *Device) error {
+ // Variables for the linker script.
+ out, err := os.Create(outdir + "/" + device.metadata["nameLower"].(string) + ".ld")
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+ t := template.Must(template.New("ld").Parse(`/* Automatically generated file. DO NOT EDIT. */
+/* Generated by gen-device-avr.go from {{.file}}, see {{.descriptorSource}} */
+
+__flash_size = 0x{{printf "%x" .flashSize}};
+__ram_size = 0x{{printf "%x" .ramSize}};
+__num_isrs = {{.numInterrupts}};
+`))
+ return t.Execute(out, device.metadata)
+}
+
+func generate(indir, outdir string) error {
+ matches, err := filepath.Glob(indir + "/*.atdf")
+ if err != nil {
+ return err
+ }
+ for _, filepath := range matches {
+ fmt.Println(filepath)
+ device, err := readATDF(filepath)
+ if err != nil {
+ return err
+ }
+ err = writeGo(outdir, device)
+ if err != nil {
+ return err
+ }
+ err = writeAsm(outdir, device)
+ if err != nil {
+ return err
+ }
+ err = writeLD(outdir, device)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func main() {
+ indir := os.Args[1] // directory with register descriptor files (*.atdf)
+ outdir := os.Args[2] // output directory
+ err := generate(indir, outdir)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+}