diff options
author | Ayke van Laethem <[email protected]> | 2018-09-13 00:55:18 +0200 |
---|---|---|
committer | Ayke van Laethem <[email protected]> | 2018-09-13 00:55:18 +0200 |
commit | 18b3e8d15628c66f3886bfabd4049b589e55c43a (patch) | |
tree | 2fb4036c9dc878bf6cf0ee879fcc8217e79c5456 /tools | |
parent | a46ed465c5f175121131a27e3ad1860a058033a2 (diff) | |
download | tinygo-18b3e8d15628c66f3886bfabd4049b589e55c43a.tar.gz tinygo-18b3e8d15628c66f3886bfabd4049b589e55c43a.zip |
gen-device: move to new tools directory
Diffstat (limited to 'tools')
-rwxr-xr-x | tools/gen-device-avr.py | 217 | ||||
-rwxr-xr-x | tools/gen-device-svd.py | 257 |
2 files changed, 474 insertions, 0 deletions
diff --git a/tools/gen-device-avr.py b/tools/gen-device-avr.py new file mode 100755 index 000000000..0e10df415 --- /dev/null +++ b/tools/gen-device-avr.py @@ -0,0 +1,217 @@ +#!/usr/bin/python3 + +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'): + reg['bitfields'].append({ + 'name': regName + '_' + bitfieldEl.getAttribute('name'), + 'description': bitfieldEl.getAttribute('caption'), + 'value': int(bitfieldEl.getAttribute('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) + + 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': memorySizes['data']['segments'].get('IRAM', memorySizes['data']['segments'].get('INTERNAL_SRAM')), + '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 "unsafe" + +// Magic type name for the compiler. +type __volatile uint8 + +// Export this magic type name. +type RegValue = __volatile + +// 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)(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']: + out.write('\t{name} = 0x{value:x}'.format(**bitfield)) + if bitfield['description']: + out.write(' // {description}'.format(**bitfield)) + out.write('\n') + out.write(')\n') + +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) + 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-svd.py b/tools/gen-device-svd.py new file mode 100755 index 000000000..3103ad621 --- /dev/null +++ b/tools/gen-device-svd.py @@ -0,0 +1,257 @@ +#!/usr/bin/python3 + +import sys +import os +from xml.dom import minidom +from glob import glob +from collections import OrderedDict +import re + +ARM_ARCHS = { + 'CM0': 'armv6m', + 'CM4': 'armv7em', +} + +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 readSVD(path): + # Read ARM SVD files. + device = Device() + xml = minidom.parse(path) + root = xml.getElementsByTagName('device')[0] + deviceName = getText(root.getElementsByTagName('name')[0]) + deviceDescription = getText(root.getElementsByTagName('description')[0]) + licenseText = formatText(getText(root.getElementsByTagName('licenseText')[0])) + cpu = root.getElementsByTagName('cpu')[0] + cpuName = getText(cpu.getElementsByTagName('name')[0]) + + device.peripherals = [] + + interrupts = OrderedDict() + + for periphEl in root.getElementsByTagName('peripherals')[0].getElementsByTagName('peripheral'): + name = getText(periphEl.getElementsByTagName('name')[0]) + description = getText(periphEl.getElementsByTagName('description')[0]) + baseAddress = int(getText(periphEl.getElementsByTagName('baseAddress')[0]), 0) + + peripheral = { + 'name': name, + 'description': description, + 'baseAddress': baseAddress, + 'registers': [], + } + device.peripherals.append(peripheral) + + for interrupt in periphEl.getElementsByTagName('interrupt'): + intrName = getText(interrupt.getElementsByTagName('name')[0]) + intrIndex = int(getText(interrupt.getElementsByTagName('value')[0])) + if intrName in interrupts: + if interrupts[intrName]['index'] != intrIndex: + raise ValueError('interrupt with the same name has different indexes: ' + intrName) + interrupts[intrName]['description'] += ' // ' + description + else: + interrupts[intrName] = { + 'name': intrName, + 'index': intrIndex, + 'description': description, + } + + regsEls = periphEl.getElementsByTagName('registers') + if regsEls: + for el in regsEls[0].childNodes: + if el.nodeName == 'register': + peripheral['registers'].append(parseSVDRegister(name, el, baseAddress)) + elif el.nodeName == 'cluster': + if el.getElementsByTagName('dim'): + continue # TODO + clusterPrefix = getText(el.getElementsByTagName('name')[0]) + '_' + clusterOffset = int(getText(el.getElementsByTagName('addressOffset')[0]), 0) + for regEl in el.childNodes: + if regEl.nodeName == 'register': + peripheral['registers'].append(parseSVDRegister(name, regEl, baseAddress + clusterOffset, clusterPrefix)) + else: + continue + + device.interrupts = interrupts.values() # TODO: sort by index + device.metadata = { + 'file': os.path.basename(path), + 'descriptorSource': 'https://github.com/NordicSemiconductor/nrfx/tree/master/mdk', + 'name': deviceName, + 'nameLower': deviceName.lower(), + 'description': deviceDescription, + 'licenseBlock': '\n// ' + licenseText.replace('\n', '\n// '), + 'arch': ARM_ARCHS[cpuName], + 'family': getText(root.getElementsByTagName('series')[0]), + } + + return device + +def parseSVDRegister(peripheralName, regEl, baseAddress, namePrefix=''): + regName = getText(regEl.getElementsByTagName('name')[0]) + regDescription = getText(regEl.getElementsByTagName('description')[0]) + offsetEls = regEl.getElementsByTagName('offset') + if not offsetEls: + offsetEls = regEl.getElementsByTagName('addressOffset') + address = baseAddress + int(getText(offsetEls[0]), 0) + + dimEls = regEl.getElementsByTagName('dim') + array = None + if dimEls: + array = int(getText(dimEls[0]), 0) + regName = regName.replace('[%s]', '') + + fields = [] + fieldsEls = regEl.getElementsByTagName('fields') + if fieldsEls: + for fieldEl in fieldsEls[0].childNodes: + if fieldEl.nodeName != 'field': + continue + fieldName = getText(fieldEl.getElementsByTagName('name')[0]) + descrEls = fieldEl.getElementsByTagName('description') + lsb = int(getText(fieldEl.getElementsByTagName('lsb')[0])) + msb = int(getText(fieldEl.getElementsByTagName('msb')[0])) + fields.append({ + 'name': '{}_{}{}_{}_Pos'.format(peripheralName, namePrefix, regName, fieldName), + 'description': 'Position of %s field.' % fieldName, + 'value': lsb, + }) + fields.append({ + 'name': '{}_{}{}_{}_Msk'.format(peripheralName, namePrefix, regName, fieldName), + 'description': 'Bit mask of %s field.' % fieldName, + 'value': (0xffffffff >> (31 - (msb - lsb))) << lsb, + }) + for enumEl in fieldEl.getElementsByTagName('enumeratedValue'): + enumName = getText(enumEl.getElementsByTagName('name')[0]) + enumDescription = getText(enumEl.getElementsByTagName('description')[0]) + enumValue = int(getText(enumEl.getElementsByTagName('value')[0]), 0) + fields.append({ + 'name': '{}_{}{}_{}_{}'.format(peripheralName, namePrefix, regName, fieldName, enumName), + 'description': enumDescription, + 'value': enumValue, + }) + + return { + 'name': namePrefix + regName, + 'address': address, + 'description': regDescription.replace('\n', ' '), + 'bitfields': fields, + 'array': array, + } + +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.py from {file}, see {descriptorSource} + +// +build {pkgName},{nameLower} + +// {description} +// {licenseBlock} +package {pkgName} + +import "unsafe" + +// Magic type name for the compiler. +type __volatile uint32 + +// Export this magic type name. +type RegValue = __volatile + +// 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') + + for peripheral in device.peripherals: + out.write('\n// {description}\ntype {name}_Type struct {{\n'.format(**peripheral)) + address = peripheral['baseAddress'] + padNumber = 0 + for register in peripheral['registers']: + if address > register['address']: + # In Nordic SVD files, these registers are deprecated or + # duplicates, so can be ignored. + #print('skip: %s.%s' % (peripheral['name'], register['name'])) + continue + + # insert padding, if needed + if address < register['address']: + numSkip = (register['address'] - address) // 4 + if numSkip == 1: + out.write('\t_padding{padNumber} __volatile\n'.format(padNumber=padNumber)) + else: + out.write('\t_padding{padNumber} [{num}]__volatile\n'.format(padNumber=padNumber, num=numSkip)) + padNumber += 1 + + regType = '__volatile' + if register['array'] is not None: + regType = '[{}]__volatile'.format(register['array']) + out.write('\t{name} {regType}\n'.format(**register, regType=regType)) + + # next address + if register['array'] is not None and 1: + address = register['address'] + 4 * register['array'] + else: + address = register['address'] + 4 + out.write('}\n') + + out.write('\n// Peripherals.\nvar (\n') + for peripheral in device.peripherals: + out.write('\t{name} = (*{name}_Type)(unsafe.Pointer(uintptr(0x{baseAddress:x}))) // {description}\n'.format(**peripheral)) + 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 + out.write('\n\t// {name}'.format(**register)) + if register['description']: + out.write(': {description}'.format(**register)) + out.write('\n') + for bitfield in register['bitfields']: + out.write('\t{name} = 0x{value:x}'.format(**bitfield)) + if bitfield['description']: + out.write(' // {description}'.format(**bitfield)) + out.write('\n') + out.write(')\n') + + +def generate(indir, outdir): + for filepath in sorted(glob(indir + '/*.svd')): + print(filepath) + device = readSVD(filepath) + writeGo(outdir, device) + + +if __name__ == '__main__': + indir = sys.argv[1] # directory with register descriptor files (*.svd, *.atdf) + outdir = sys.argv[2] # output directory + generate(indir, outdir) |