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)