github.com/aykevl/tinygo@v0.5.0/tools/gen-device-avr.py (about)

     1  #!/usr/bin/env python3
     2  
     3  import sys
     4  import os
     5  from xml.dom import minidom
     6  from glob import glob
     7  from collections import OrderedDict
     8  import re
     9  
    10  class Device:
    11      # dummy
    12      pass
    13  
    14  def getText(element):
    15      strings = []
    16      for node in element.childNodes:
    17          if node.nodeType == node.TEXT_NODE:
    18              strings.append(node.data)
    19      return ''.join(strings)
    20  
    21  def formatText(text):
    22      text = re.sub('[ \t\n]+', ' ', text) # Collapse whitespace (like in HTML)
    23      text = text.replace('\\n ', '\n')
    24      text = text.strip()
    25      return text
    26  
    27  def readATDF(path):
    28      # Read Atmel device descriptor files.
    29      # See: http://packs.download.atmel.com
    30  
    31      device = Device()
    32  
    33      xml = minidom.parse(path)
    34      device = xml.getElementsByTagName('device')[0]
    35      deviceName = device.getAttribute('name')
    36      arch = device.getAttribute('architecture')
    37      family = device.getAttribute('family')
    38  
    39      memorySizes = {}
    40      for el in device.getElementsByTagName('address-space'):
    41          addressSpace = {
    42              'size': int(el.getAttribute('size'), 0),
    43              'segments': {},
    44          }
    45          memorySizes[el.getAttribute('name')] = addressSpace
    46          for segmentEl in el.getElementsByTagName('memory-segment'):
    47              addressSpace['segments'][segmentEl.getAttribute('name')] = int(segmentEl.getAttribute('size'), 0)
    48  
    49      device.interrupts = []
    50      for el in device.getElementsByTagName('interrupts')[0].getElementsByTagName('interrupt'):
    51          device.interrupts.append({
    52              'index':       int(el.getAttribute('index')),
    53              'name':        el.getAttribute('name'),
    54              'description': el.getAttribute('caption'),
    55          })
    56  
    57      allRegisters = {}
    58      commonRegisters = {}
    59  
    60      device.peripherals = []
    61      for el in xml.getElementsByTagName('modules')[0].getElementsByTagName('module'):
    62          peripheral = {
    63              'name':        el.getAttribute('name'),
    64              'description': el.getAttribute('caption'),
    65              'registers':   [],
    66          }
    67          device.peripherals.append(peripheral)
    68          for regElGroup in el.getElementsByTagName('register-group'):
    69              for regEl in regElGroup.getElementsByTagName('register'):
    70                  size = int(regEl.getAttribute('size'))
    71                  regName = regEl.getAttribute('name')
    72                  regOffset = int(regEl.getAttribute('offset'), 0)
    73                  reg = {
    74                      'description': regEl.getAttribute('caption'),
    75                      'bitfields':   [],
    76                      'array':       None,
    77                  }
    78                  if size == 1:
    79                      reg['variants'] = [{
    80                          'name':    regName,
    81                          'address': regOffset,
    82                      }]
    83                  elif size == 2:
    84                      reg['variants'] = [{
    85                          'name':    regName + 'L',
    86                          'address': regOffset,
    87                      }, {
    88                          'name':    regName + 'H',
    89                          'address': regOffset + 1,
    90                      }]
    91                  else:
    92                      # TODO
    93                      continue
    94  
    95                  for bitfieldEl in regEl.getElementsByTagName('bitfield'):
    96                      mask = bitfieldEl.getAttribute('mask')
    97                      if len(mask) == 2:
    98                          # Two devices (ATtiny102 and ATtiny104) appear to have
    99                          # an error in the bitfields, leaving out the '0x'
   100                          # prefix.
   101                          mask = '0x' + mask
   102                      reg['bitfields'].append({
   103                          'name':        regName + '_' + bitfieldEl.getAttribute('name'),
   104                          'description': bitfieldEl.getAttribute('caption'),
   105                          'value':       int(mask, 0),
   106                      })
   107  
   108                  if regName in allRegisters:
   109                      firstReg = allRegisters[regName]
   110                      if firstReg['register'] in firstReg['peripheral']['registers']:
   111                          firstReg['peripheral']['registers'].remove(firstReg['register'])
   112                      if firstReg['address'] != regOffset:
   113                          continue # TODO
   114                      commonRegisters = allRegisters[regName]['register']
   115                      continue
   116                  else:
   117                      allRegisters[regName] = {'address': regOffset, 'register': reg, 'peripheral': peripheral}
   118  
   119                  peripheral['registers'].append(reg)
   120  
   121      ramSize = 0 # for devices with no RAM
   122      for ramSegmentName in ['IRAM', 'INTERNAL_SRAM', 'SRAM']:
   123          if ramSegmentName in memorySizes['data']['segments']:
   124              ramSize = memorySizes['data']['segments'][ramSegmentName]
   125  
   126      device.metadata = {
   127          'file':             os.path.basename(path),
   128          'descriptorSource': 'http://packs.download.atmel.com/',
   129          'name':             deviceName,
   130          'nameLower':        deviceName.lower(),
   131          'description':      'Device information for the {}.'.format(deviceName),
   132          'arch':             arch,
   133          'family':           family,
   134          'flashSize':        memorySizes['prog']['size'],
   135          'ramSize':          ramSize,
   136          'numInterrupts':    len(device.interrupts),
   137      }
   138  
   139      return device
   140  
   141  def writeGo(outdir, device):
   142      # The Go module for this device.
   143      out = open(outdir + '/' + device.metadata['nameLower'] + '.go', 'w')
   144      pkgName = os.path.basename(outdir.rstrip('/'))
   145      out.write('''\
   146  // Automatically generated file. DO NOT EDIT.
   147  // Generated by gen-device-avr.py from {file}, see {descriptorSource}
   148  
   149  // +build {pkgName},{nameLower}
   150  
   151  // {description}
   152  package {pkgName}
   153  
   154  import "unsafe"
   155  
   156  // Special type that causes loads/stores to be volatile (necessary for
   157  // memory-mapped registers).
   158  //go:volatile
   159  type RegValue uint8
   160  
   161  // Some information about this device.
   162  const (
   163  	DEVICE     = "{name}"
   164  	ARCH       = "{arch}"
   165  	FAMILY     = "{family}"
   166  )
   167  '''.format(pkgName=pkgName, **device.metadata))
   168  
   169      out.write('\n// Interrupts\nconst (\n')
   170      for intr in device.interrupts:
   171          out.write('\tIRQ_{name} = {index} // {description}\n'.format(**intr))
   172      intrMax = max(map(lambda intr: intr['index'], device.interrupts))
   173      out.write('\tIRQ_max = {} // Highest interrupt number on this device.\n'.format(intrMax))
   174      out.write(')\n')
   175  
   176      out.write('\n// Peripherals.\nvar (')
   177      first = True
   178      for peripheral in device.peripherals:
   179          out.write('\n\t// {description}\n'.format(**peripheral))
   180          for register in peripheral['registers']:
   181              for variant in register['variants']:
   182                  out.write('\t{name} = (*RegValue)(unsafe.Pointer(uintptr(0x{address:x})))\n'.format(**variant))
   183      out.write(')\n')
   184  
   185      for peripheral in device.peripherals:
   186          if not sum(map(lambda r: len(r['bitfields']), peripheral['registers'])): continue
   187          out.write('\n// Bitfields for {name}: {description}\nconst('.format(**peripheral))
   188          for register in peripheral['registers']:
   189              if not register['bitfields']: continue
   190              for variant in register['variants']:
   191                  out.write('\n\t// {name}'.format(**variant))
   192                  if register['description']:
   193                      out.write(': {description}'.format(**register))
   194                  out.write('\n')
   195              for bitfield in register['bitfields']:
   196                  name = bitfield['name']
   197                  value = bitfield['value']
   198                  if '{:08b}'.format(value).count('1') == 1:
   199                      out.write('\t{name} = 0x{value:x}'.format(**bitfield))
   200                      if bitfield['description']:
   201                          out.write(' // {description}'.format(**bitfield))
   202                      out.write('\n')
   203                  else:
   204                      n = 0
   205                      for i in range(8):
   206                          if (value >> i) & 1 == 0: continue
   207                          out.write('\t{}{} = 0x{:x}'.format(name, n, 1 << i))
   208                          if bitfield['description']:
   209                              out.write(' // {description}'.format(**bitfield))
   210                          n += 1
   211                          out.write('\n')
   212          out.write(')\n')
   213  
   214  def writeAsm(outdir, device):
   215      # The interrupt vector, which is hard to write directly in Go.
   216      out = open(outdir + '/' + device.metadata['nameLower'] + '.s', 'w')
   217      out.write('''\
   218  ; Automatically generated file. DO NOT EDIT.
   219  ; Generated by gen-device-avr.py from {file}, see {descriptorSource}
   220  
   221  ; This is the default handler for interrupts, if triggered but not defined.
   222  ; Sleep inside so that an accidentally triggered interrupt won't drain the
   223  ; battery of a battery-powered device.
   224  .section .text.__vector_default
   225  .global  __vector_default
   226  __vector_default:
   227      sleep
   228      rjmp __vector_default
   229  
   230  ; Avoid the need for repeated .weak and .set instructions.
   231  .macro IRQ handler
   232      .weak  \\handler
   233      .set   \\handler, __vector_default
   234  .endm
   235  
   236  ; The interrupt vector of this device. Must be placed at address 0 by the linker.
   237  .section .vectors
   238  .global  __vectors
   239  '''.format(**device.metadata))
   240      num = 0
   241      for intr in device.interrupts:
   242          jmp = 'jmp'
   243          if device.metadata['flashSize'] <= 8 * 1024:
   244              # When a device has 8kB or less flash, rjmp (2 bytes) must be used
   245              # instead of jmp (4 bytes).
   246              # https://www.avrfreaks.net/forum/rjmp-versus-jmp
   247              jmp = 'rjmp'
   248          if intr['index'] < num:
   249              # Some devices have duplicate interrupts, probably for historical
   250              # reasons.
   251              continue
   252          while intr['index'] > num:
   253              out.write('    {jmp} __vector_default\n'.format(jmp=jmp))
   254              num += 1
   255          num += 1
   256          out.write('    {jmp} __vector_{name}\n'.format(jmp=jmp, **intr))
   257  
   258      out.write('''
   259      ; Define default implementations for interrupts, redirecting to
   260      ; __vector_default when not implemented.
   261  ''')
   262      for intr in device.interrupts:
   263          out.write('    IRQ __vector_{name}\n'.format(**intr))
   264  
   265  def writeLD(outdir, device):
   266      # Variables for the linker script.
   267      out = open(outdir + '/' + device.metadata['nameLower'] + '.ld', 'w')
   268      out.write('''\
   269  /* Automatically generated file. DO NOT EDIT. */
   270  /* Generated by gen-device-avr.py from {file}, see {descriptorSource} */
   271  
   272  __flash_size = 0x{flashSize:x};
   273  __ram_size   = 0x{ramSize:x};
   274  __num_isrs   = {numInterrupts};
   275  '''.format(**device.metadata))
   276      out.close()
   277  
   278  
   279  def generate(indir, outdir):
   280      for filepath in sorted(glob(indir + '/*.atdf')):
   281          print(filepath)
   282          device = readATDF(filepath)
   283          writeGo(outdir, device)
   284          writeAsm(outdir, device)
   285          writeLD(outdir, device)
   286  
   287  
   288  if __name__ == '__main__':
   289      indir = sys.argv[1] # directory with register descriptor files (*.atdf)
   290      outdir = sys.argv[2] # output directory
   291      generate(indir, outdir)