mirror of https://github.com/tinygo-org/tinygo.git
Browse Source
This should make it more maintainable. Another big advantage that generation time (including gofmt) is now 3 times faster. No real attempt at refactoring has been made, that will need to be done at a later time.pull/781/head
Ayke van Laethem
5 years ago
committed by
Ron Evans
6 changed files with 988 additions and 672 deletions
@ -1,645 +0,0 @@ |
|||
#!/usr/bin/env python |
|||
|
|||
from __future__ import print_function |
|||
|
|||
import sys |
|||
import os |
|||
from xml.etree import ElementTree |
|||
from glob import glob |
|||
from collections import OrderedDict |
|||
import re |
|||
import argparse |
|||
|
|||
validName = re.compile('^[a-zA-Z0-9_]+$') |
|||
|
|||
|
|||
class Device: |
|||
# dummy |
|||
pass |
|||
|
|||
def getText(element): |
|||
if element is None: |
|||
return "None" |
|||
return ''.join(element.itertext()) |
|||
|
|||
def formatText(text): |
|||
text = re.sub('[ \t\n]+', ' ', text) # Collapse whitespace (like in HTML) |
|||
text = text.replace('\\n ', '\n') |
|||
text = text.strip() |
|||
return text |
|||
|
|||
# Replace characters that are not allowed in a symbol name with a '_'. This is |
|||
# useful to be able to process SVD files with errors. |
|||
def cleanName(text): |
|||
if not validName.match(text): |
|||
return ''.join(list(map(lambda c: c if validName.match(c) else '_', text))) |
|||
return text |
|||
|
|||
def readSVD(path, sourceURL): |
|||
# Read ARM SVD files. |
|||
device = Device() |
|||
xml = ElementTree.parse(path) |
|||
root = xml.getroot() |
|||
deviceName = getText(root.find('name')) |
|||
deviceDescription = getText(root.find('description')).strip() |
|||
licenseTexts = root.findall('licenseText') |
|||
if len(licenseTexts) == 0: |
|||
licenseText = None |
|||
elif len(licenseTexts) == 1: |
|||
licenseText = formatText(getText(licenseTexts[0])) |
|||
else: |
|||
raise ValueError('multiple <licenseText> elements') |
|||
|
|||
device.peripherals = [] |
|||
peripheralDict = {} |
|||
groups = {} |
|||
|
|||
interrupts = OrderedDict() |
|||
|
|||
for periphEl in root.findall('./peripherals/peripheral'): |
|||
name = getText(periphEl.find('name')) |
|||
descriptionTags = periphEl.findall('description') |
|||
description = '' |
|||
if descriptionTags: |
|||
description = formatText(getText(descriptionTags[0])) |
|||
baseAddress = int(getText(periphEl.find('baseAddress')), 0) |
|||
groupNameTags = periphEl.findall('groupName') |
|||
groupName = None |
|||
if groupNameTags: |
|||
# Some group names (for example the STM32H7A3x) have an invalid |
|||
# group name. Replace invalid characters with '_'. |
|||
groupName = cleanName(getText(groupNameTags[0])) |
|||
|
|||
interruptEls = periphEl.findall('interrupt') |
|||
for interrupt in interruptEls: |
|||
intrName = getText(interrupt.find('name')) |
|||
intrIndex = int(getText(interrupt.find('value'))) |
|||
addInterrupt(interrupts, intrName, intrIndex, description) |
|||
# As a convenience, also use the peripheral name as the interrupt |
|||
# name. Only do that for the nrf for now, as the stm32 .svd files |
|||
# don't always put interrupts in the correct peripheral... |
|||
if len(interruptEls) == 1 and deviceName.startswith('nrf'): |
|||
addInterrupt(interrupts, name, intrIndex, description) |
|||
|
|||
if periphEl.get('derivedFrom') or groupName in groups: |
|||
if periphEl.get('derivedFrom'): |
|||
derivedFromName = periphEl.get('derivedFrom') |
|||
derivedFrom = peripheralDict[derivedFromName] |
|||
else: |
|||
derivedFrom = groups[groupName] |
|||
peripheral = { |
|||
'name': name, |
|||
'groupName': derivedFrom['groupName'], |
|||
'description': description or derivedFrom['description'], |
|||
'baseAddress': baseAddress, |
|||
} |
|||
device.peripherals.append(peripheral) |
|||
peripheralDict[name] = peripheral |
|||
if 'subtypes' in derivedFrom: |
|||
for subtype in derivedFrom['subtypes']: |
|||
subp = { |
|||
'name': name + "_"+subtype['clusterName'], |
|||
'groupName': subtype['groupName'], |
|||
'description': subtype['description'], |
|||
'baseAddress': baseAddress, |
|||
} |
|||
device.peripherals.append(subp) |
|||
continue |
|||
|
|||
peripheral = { |
|||
'name': name, |
|||
'groupName': groupName or name, |
|||
'description': description, |
|||
'baseAddress': baseAddress, |
|||
'registers': [], |
|||
'subtypes': [], |
|||
} |
|||
device.peripherals.append(peripheral) |
|||
peripheralDict[name] = peripheral |
|||
|
|||
if groupName and groupName not in groups: |
|||
groups[groupName] = peripheral |
|||
|
|||
regsEls = periphEl.findall('registers') |
|||
if regsEls: |
|||
if len(regsEls) != 1: |
|||
raise ValueError('expected just one <registers> in a <peripheral>') |
|||
for register in regsEls[0].findall('register'): |
|||
peripheral['registers'].extend(parseRegister(groupName or name, register, baseAddress)) |
|||
for cluster in regsEls[0].findall('cluster'): |
|||
clusterName = getText(cluster.find('name')).replace('[%s]', '') |
|||
if cluster.find('dimIndex') is not None: |
|||
clusterName = clusterName.replace('%s', '') |
|||
clusterDescription = getText(cluster.find('description')) |
|||
clusterPrefix = clusterName + '_' |
|||
clusterOffset = int(getText(cluster.find('addressOffset')), 0) |
|||
if cluster.find('dim') is None: |
|||
if clusterOffset == 0: |
|||
# make this a separate peripheral |
|||
cpRegisters = [] |
|||
for regEl in cluster.findall('register'): |
|||
cpRegisters.extend(parseRegister(groupName, regEl, baseAddress, clusterName+"_")) |
|||
# handle sub-clusters of registers |
|||
for subClusterEl in cluster.findall('cluster'): |
|||
subclusterName = getText(subClusterEl.find('name')).replace('[%s]', '') |
|||
subclusterDescription = getText(subClusterEl.find('description')) |
|||
subclusterPrefix = subclusterName + '_' |
|||
subclusterOffset = int(getText(subClusterEl.find('addressOffset')), 0) |
|||
subdim = int(getText(subClusterEl.find('dim'))) |
|||
subdimIncrement = int(getText(subClusterEl.find('dimIncrement')), 16) |
|||
|
|||
if subdim > 1: |
|||
subcpRegisters = [] |
|||
subregSize = 0 |
|||
for regEl in subClusterEl.findall('register'): |
|||
subregSize += int(getText(regEl.find('size'))) |
|||
subcpRegisters.extend(parseRegister(groupName, regEl, baseAddress + subclusterOffset, subclusterPrefix)) |
|||
cpRegisters.append({ |
|||
'name': subclusterName, |
|||
'address': baseAddress + subclusterOffset, |
|||
'description': subclusterDescription, |
|||
'registers': subcpRegisters, |
|||
'array': subdim, |
|||
'elementsize': subdimIncrement, |
|||
}) |
|||
else: |
|||
for regEl in subClusterEl.findall('register'): |
|||
cpRegisters.extend(parseRegister(getText(regEl.find('name')), regEl, baseAddress + subclusterOffset, subclusterPrefix)) |
|||
|
|||
cpRegisters.sort(key=lambda r: r['address']) |
|||
clusterPeripheral = { |
|||
'name': name+ "_" +clusterName, |
|||
'groupName': groupName+ "_" +clusterName, |
|||
'description': description+ " - " +clusterName, |
|||
'clusterName': clusterName, |
|||
'baseAddress': baseAddress, |
|||
'registers': cpRegisters, |
|||
} |
|||
device.peripherals.append(clusterPeripheral) |
|||
peripheral['subtypes'].append(clusterPeripheral) |
|||
continue |
|||
dim = None |
|||
dimIncrement = None |
|||
else: |
|||
dim = int(getText(cluster.find('dim'))) |
|||
if dim == 1: |
|||
dimIncrement = None |
|||
else: |
|||
dimIncrement = int(getText(cluster.find('dimIncrement')), 0) |
|||
clusterRegisters = [] |
|||
for regEl in cluster.findall('register'): |
|||
clusterRegisters.extend(parseRegister(groupName or name, regEl, baseAddress + clusterOffset, clusterPrefix)) |
|||
clusterRegisters.sort(key=lambda r: r['address']) |
|||
if dimIncrement is None: |
|||
lastReg = clusterRegisters[-1] |
|||
lastAddress = lastReg['address'] |
|||
if lastReg['array'] is not None: |
|||
lastAddress = lastReg['address'] + lastReg['array'] * lastReg['elementsize'] |
|||
firstAddress = clusterRegisters[0]['address'] |
|||
dimIncrement = lastAddress - firstAddress |
|||
peripheral['registers'].append({ |
|||
'name': clusterName, |
|||
'address': baseAddress + clusterOffset, |
|||
'description': clusterDescription, |
|||
'registers': clusterRegisters, |
|||
'array': dim, |
|||
'elementsize': dimIncrement, |
|||
}) |
|||
peripheral['registers'].sort(key=lambda r: r['address']) |
|||
|
|||
device.interrupts = sorted(interrupts.values(), key=lambda v: v['index']) |
|||
licenseBlock = '' |
|||
if licenseText is not None: |
|||
licenseBlock = '// ' + licenseText.replace('\n', '\n// ') |
|||
licenseBlock = '\n'.join(map(str.rstrip, licenseBlock.split('\n'))) # strip trailing whitespace |
|||
device.metadata = { |
|||
'file': os.path.basename(path), |
|||
'descriptorSource': sourceURL, |
|||
'name': deviceName, |
|||
'nameLower': deviceName.lower(), |
|||
'description': deviceDescription, |
|||
'licenseBlock': licenseBlock, |
|||
} |
|||
|
|||
return device |
|||
|
|||
def addInterrupt(interrupts, intrName, intrIndex, description): |
|||
if intrName in interrupts: |
|||
if interrupts[intrName]['index'] != intrIndex: |
|||
# Note: some SVD files like the one for STM32H7x7 contain mistakes. |
|||
# Instead of throwing an error, simply log it. |
|||
print ('interrupt with the same name has different indexes: %s (%d vs %d)' |
|||
% (intrName, interrupts[intrName]['index'], intrIndex)) |
|||
if description not in interrupts[intrName]['description'].split(' // '): |
|||
interrupts[intrName]['description'] += ' // ' + description |
|||
else: |
|||
interrupts[intrName] = { |
|||
'name': intrName, |
|||
'index': intrIndex, |
|||
'description': description, |
|||
} |
|||
|
|||
def parseBitfields(groupName, regName, fieldsEls, bitfieldPrefix=''): |
|||
fields = [] |
|||
if fieldsEls: |
|||
for fieldEl in fieldsEls[0].findall('field'): |
|||
# Some bitfields (like the STM32H7x7) contain invalid bitfield |
|||
# names like 'CNT[31]'. Replace invalid characters with '_' when |
|||
# needed. |
|||
fieldName = cleanName(getText(fieldEl.find('name'))) |
|||
if not fieldName[0].isupper() and not fieldName[0].isdigit(): |
|||
fieldName = fieldName.upper() |
|||
if len(fieldEl.findall('lsb')) == 1 and len(fieldEl.findall('msb')) == 1: |
|||
# try to use lsb/msb tags |
|||
lsb = int(getText(fieldEl.findall('lsb')[0])) |
|||
msb = int(getText(fieldEl.findall('msb')[0])) |
|||
elif len(fieldEl.findall('bitOffset')) > 0 and len(fieldEl.findall('bitWidth')) > 0: |
|||
# try to use bitOffset/bitWidth tags |
|||
lsb = int(getText(fieldEl.find('bitOffset'))) |
|||
msb = int(getText(fieldEl.find('bitWidth'))) + lsb - 1 |
|||
elif len(fieldEl.findall('bitRange')) > 0: |
|||
# try use bitRange |
|||
bitRangeTags = fieldEl.findall('bitRange') |
|||
lsb = int(getText(bitRangeTags[0]).split(":")[1][:-1]) |
|||
msb = int(getText(bitRangeTags[0]).split(":")[0][1:]) |
|||
else: |
|||
# this is an error. what to do? |
|||
print("unable to find lsb/msb in field:", fieldName) |
|||
|
|||
fields.append({ |
|||
'name': '{}_{}{}_{}_Pos'.format(groupName, bitfieldPrefix, regName, fieldName), |
|||
'description': 'Position of %s field.' % fieldName, |
|||
'value': lsb, |
|||
}) |
|||
fields.append({ |
|||
'name': '{}_{}{}_{}_Msk'.format(groupName, bitfieldPrefix, regName, fieldName), |
|||
'description': 'Bit mask of %s field.' % fieldName, |
|||
'value': (0xffffffff >> (31 - (msb - lsb))) << lsb, |
|||
}) |
|||
if lsb == msb: # single bit |
|||
fields.append({ |
|||
'name': '{}_{}{}_{}'.format(groupName, bitfieldPrefix, regName, fieldName), |
|||
'description': 'Bit %s.' % fieldName, |
|||
'value': 1 << lsb, |
|||
}) |
|||
for enumEl in fieldEl.findall('enumeratedValues/enumeratedValue'): |
|||
enumName = getText(enumEl.find('name')) |
|||
if not enumName[0].isupper() and not enumName[0].isdigit(): |
|||
enumName = enumName.upper() |
|||
enumDescription = getText(enumEl.find('description')).replace('\n', ' ') |
|||
enumValue = int(getText(enumEl.find('value')), 0) |
|||
fields.append({ |
|||
'name': '{}_{}{}_{}_{}'.format(groupName, bitfieldPrefix, regName, fieldName, enumName), |
|||
'description': enumDescription, |
|||
'value': enumValue, |
|||
}) |
|||
return fields |
|||
|
|||
class Register: |
|||
def __init__(self, element, baseAddress): |
|||
self.element = element |
|||
self.baseAddress = baseAddress |
|||
|
|||
def name(self): |
|||
return getText(self.element.find('name')).replace('[%s]', '') |
|||
|
|||
def description(self): |
|||
return getText(self.element.find('description')).replace('\n', ' ') |
|||
|
|||
def address(self): |
|||
offsetEls = self.element.findall('offset') |
|||
if not offsetEls: |
|||
offsetEls = self.element.findall('addressOffset') |
|||
return self.baseAddress + int(getText(offsetEls[0]), 0) |
|||
|
|||
def dim(self): |
|||
dimEls = self.element.findall('dim') |
|||
if len(dimEls) == 0: |
|||
return None |
|||
elif len(dimEls) == 1: |
|||
return int(getText(dimEls[0]), 0) |
|||
else: |
|||
raise ValueError('expected at most one <dim> element in %s register' % self.name()) |
|||
|
|||
def size(self): |
|||
size = 4 |
|||
elSizes = self.element.findall('size') |
|||
if elSizes: |
|||
size = int(getText(elSizes[0]), 0) // 8 |
|||
return size |
|||
|
|||
|
|||
def parseRegister(groupName, regEl, baseAddress, bitfieldPrefix=''): |
|||
reg = Register(regEl, baseAddress) |
|||
|
|||
fieldsEls = regEl.findall('fields') |
|||
|
|||
if reg.dim() is not None: |
|||
dimIncrement = int(getText(regEl.find('dimIncrement')), 0) |
|||
if "%s" in reg.name(): |
|||
# a "spaced array" of registers, special processing required |
|||
# we need to generate a separate register for each "element" |
|||
results = [] |
|||
for i in range(reg.dim()): |
|||
regAddress = reg.address() + (i * dimIncrement) |
|||
results.append({ |
|||
'name': reg.name().replace('%s', str(i)), |
|||
'address': regAddress, |
|||
'description': reg.description(), |
|||
'bitfields': [], |
|||
'array': None, |
|||
'elementsize': reg.size(), |
|||
}) |
|||
# set first result bitfield |
|||
shortName = reg.name().replace('_%s', '').replace('%s', '').upper() |
|||
results[0]['bitfields'] = parseBitfields(groupName, shortName, fieldsEls, bitfieldPrefix) |
|||
return results |
|||
regName = reg.name() |
|||
if not regName[0].isupper() and not regName[0].isdigit(): |
|||
regName = regName.upper() |
|||
return [{ |
|||
'name': regName, |
|||
'address': reg.address(), |
|||
'description': reg.description(), |
|||
'bitfields': parseBitfields(groupName, regName, fieldsEls, bitfieldPrefix), |
|||
'array': reg.dim(), |
|||
'elementsize': reg.size(), |
|||
}] |
|||
|
|||
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-svd.py from {file}, see {descriptorSource} |
|||
|
|||
// +build {pkgName},{nameLower} |
|||
|
|||
// {description} |
|||
// |
|||
{licenseBlock} |
|||
package {pkgName} |
|||
|
|||
import ( |
|||
"runtime/volatile" |
|||
"unsafe" |
|||
) |
|||
|
|||
// Some information about this device. |
|||
const ( |
|||
DEVICE = "{name}" |
|||
) |
|||
'''.format(pkgName=pkgName, **device.metadata)) |
|||
|
|||
out.write('\n// Interrupt numbers\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') |
|||
|
|||
# Define actual peripheral pointers. |
|||
out.write('\n// Peripherals.\nvar (\n') |
|||
for peripheral in device.peripherals: |
|||
out.write('\t{name} = (*{groupName}_Type)(unsafe.Pointer(uintptr(0x{baseAddress:x}))) // {description}\n'.format(**peripheral)) |
|||
out.write(')\n') |
|||
|
|||
# Define peripheral struct types. |
|||
for peripheral in device.peripherals: |
|||
if 'registers' not in peripheral: |
|||
# This peripheral was derived from another peripheral. No new type |
|||
# needs to be defined for it. |
|||
continue |
|||
out.write('\n// {description}\ntype {groupName}_Type struct {{\n'.format(**peripheral)) |
|||
address = peripheral['baseAddress'] |
|||
for register in peripheral['registers']: |
|||
if address > register['address'] and 'registers' not in register : |
|||
# In Nordic SVD files, these registers are deprecated or |
|||
# duplicates, so can be ignored. |
|||
#print('skip: %s.%s %s - %s %s' % (peripheral['name'], register['name'], address, register['address'], register['elementsize'])) |
|||
continue |
|||
eSize = register['elementsize'] |
|||
if eSize == 4: |
|||
regType = 'volatile.Register32' |
|||
elif eSize == 2: |
|||
regType = 'volatile.Register16' |
|||
elif eSize == 1: |
|||
regType = 'volatile.Register8' |
|||
else: |
|||
eSize = 4 |
|||
regType = 'volatile.Register32' |
|||
|
|||
# insert padding, if needed |
|||
if address < register['address']: |
|||
bytesNeeded = register['address'] - address |
|||
if bytesNeeded == 1: |
|||
out.write('\t_ {regType}\n'.format(regType='volatile.Register8')) |
|||
elif bytesNeeded == 2: |
|||
out.write('\t_ {regType}\n'.format(regType='volatile.Register16')) |
|||
elif bytesNeeded == 3: |
|||
out.write('\t_ [3]{regType}\n'.format(regType='volatile.Register8')) |
|||
else: |
|||
numSkip = (register['address'] - address) // eSize |
|||
if numSkip == 1: |
|||
out.write('\t_ {regType}\n'.format(regType=regType)) |
|||
else: |
|||
out.write('\t_ [{num}]{regType}\n'.format(num=numSkip, regType=regType)) |
|||
address = register['address'] |
|||
|
|||
lastCluster = False |
|||
if 'registers' in register: |
|||
# This is a cluster, not a register. Create the cluster type. |
|||
regType = 'struct {\n' |
|||
subaddress = register['address'] |
|||
for subregister in register['registers']: |
|||
if subregister['elementsize'] == 4: |
|||
subregType = 'volatile.Register32' |
|||
elif subregister['elementsize'] == 2: |
|||
subregType = 'volatile.Register16' |
|||
elif subregister['elementsize'] == 1: |
|||
subregType = 'volatile.Register8' |
|||
|
|||
if subregister['array']: |
|||
subregType = '[{}]{}'.format(subregister['array'], subregType) |
|||
if subaddress != subregister['address']: |
|||
bytesNeeded = subregister['address'] - subaddress |
|||
if bytesNeeded == 1: |
|||
regType += '\t\t_ {subregType}\n'.format(subregType='volatile.Register8') |
|||
elif bytesNeeded == 2: |
|||
regType += '\t\t_ {subregType}\n'.format(subregType='volatile.Register16') |
|||
else: |
|||
numSkip = (subregister['address'] - subaddress) |
|||
if numSkip < 1: |
|||
continue |
|||
elif numSkip == 1: |
|||
regType += '\t\t_ {subregType}\n'.format(subregType='volatile.Register8') |
|||
else: |
|||
regType += '\t\t_ [{num}]{subregType}\n'.format(num=numSkip, subregType='volatile.Register8') |
|||
subaddress += bytesNeeded |
|||
if subregister['array'] is not None: |
|||
subregSize = subregister['array'] * subregister['elementsize'] |
|||
else: |
|||
subregSize = subregister['elementsize'] |
|||
subaddress += subregSize |
|||
regType += '\t\t{name} {subregType}\n'.format(name=subregister['name'], subregType=subregType) |
|||
if register['array'] is not None: |
|||
if subaddress != register['address'] + register['elementsize']: |
|||
numSkip = ((register['address'] + register['elementsize']) - subaddress) // subregSize |
|||
if numSkip <= 1: |
|||
regType += '\t\t_ {subregType}\n'.format(subregType=subregType) |
|||
else: |
|||
regType += '\t\t_ [{num}]{subregType}\n'.format(num=numSkip, subregType=subregType) |
|||
else: |
|||
lastCluster = True |
|||
regType += '\t}' |
|||
address = subaddress |
|||
if register['array'] is not None: |
|||
regType = '[{}]{}'.format(register['array'], regType) |
|||
out.write('\t{name} {regType}\n'.format(name=register['name'], regType=regType)) |
|||
|
|||
# next address |
|||
if lastCluster is True: |
|||
lastCluster = False |
|||
elif register['array'] is not None: |
|||
address = register['address'] + register['elementsize'] * register['array'] |
|||
else: |
|||
address = register['address'] + register['elementsize'] |
|||
out.write('}\n') |
|||
|
|||
# Define bitfields. |
|||
for peripheral in device.peripherals: |
|||
if 'registers' not in peripheral: |
|||
# This peripheral was derived from another peripheral. Bitfields are |
|||
# already defined. |
|||
continue |
|||
out.write('\n// Bitfields for {name}: {description}\nconst('.format(**peripheral)) |
|||
for register in peripheral['registers']: |
|||
if register.get('bitfields'): |
|||
writeGoRegisterBitfields(out, register, register['name']) |
|||
for subregister in register.get('registers', []): |
|||
writeGoRegisterBitfields(out, subregister, register['name'] + '.' + subregister['name']) |
|||
out.write(')\n') |
|||
|
|||
def writeGoRegisterBitfields(out, register, name): |
|||
out.write('\n\t// {}'.format(name)) |
|||
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') |
|||
|
|||
|
|||
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-svd.py from {file}, see {descriptorSource} |
|||
|
|||
// {description} |
|||
// |
|||
{licenseBlock} |
|||
|
|||
.syntax unified |
|||
|
|||
// This is the default handler for interrupts, if triggered but not defined. |
|||
.section .text.Default_Handler |
|||
.global Default_Handler |
|||
.type Default_Handler, %function |
|||
Default_Handler: |
|||
wfe |
|||
b Default_Handler |
|||
|
|||
// Avoid the need for repeated .weak and .set instructions. |
|||
.macro IRQ handler |
|||
.weak \\handler |
|||
.set \\handler, Default_Handler |
|||
.endm |
|||
|
|||
// Must set the "a" flag on the section: |
|||
// https://svnweb.freebsd.org/base/stable/11/sys/arm/arm/locore-v4.S?r1=321049&r2=321048&pathrev=321049 |
|||
// https://sourceware.org/binutils/docs/as/Section.html#ELF-Version |
|||
.section .isr_vector, "a", %progbits |
|||
.global __isr_vector |
|||
// Interrupt vector as defined by Cortex-M, starting with the stack top. |
|||
// On reset, SP is initialized with *0x0 and PC is loaded with *0x4, loading |
|||
// _stack_top and Reset_Handler. |
|||
.long _stack_top |
|||
.long Reset_Handler |
|||
.long NMI_Handler |
|||
.long HardFault_Handler |
|||
.long MemoryManagement_Handler |
|||
.long BusFault_Handler |
|||
.long UsageFault_Handler |
|||
.long 0 |
|||
.long 0 |
|||
.long 0 |
|||
.long 0 |
|||
.long SVC_Handler |
|||
.long DebugMon_Handler |
|||
.long 0 |
|||
.long PendSV_Handler |
|||
.long SysTick_Handler |
|||
|
|||
// Extra interrupts for peripherals defined by the hardware vendor. |
|||
'''.format(**device.metadata)) |
|||
num = 0 |
|||
for intr in device.interrupts: |
|||
if intr['index'] == num - 1: |
|||
continue |
|||
if intr['index'] < num: |
|||
raise ValueError('interrupt numbers are not sorted') |
|||
while intr['index'] > num: |
|||
out.write(' .long 0\n') |
|||
num += 1 |
|||
num += 1 |
|||
out.write(' .long {name}_IRQHandler\n'.format(**intr)) |
|||
|
|||
out.write(''' |
|||
// Define default implementations for interrupts, redirecting to |
|||
// Default_Handler when not implemented. |
|||
IRQ NMI_Handler |
|||
IRQ HardFault_Handler |
|||
IRQ MemoryManagement_Handler |
|||
IRQ BusFault_Handler |
|||
IRQ UsageFault_Handler |
|||
IRQ SVC_Handler |
|||
IRQ DebugMon_Handler |
|||
IRQ PendSV_Handler |
|||
IRQ SysTick_Handler |
|||
''') |
|||
for intr in device.interrupts: |
|||
out.write(' IRQ {name}_IRQHandler\n'.format(**intr)) |
|||
|
|||
def generate(indir, outdir, sourceURL): |
|||
if not os.path.isdir(indir): |
|||
print('cannot find input directory:', indir, file=sys.stderr) |
|||
sys.exit(1) |
|||
if not os.path.isdir(outdir): |
|||
os.mkdir(outdir) |
|||
infiles = glob(indir + '/*.svd') |
|||
if not infiles: |
|||
print('no .svd files found:', indir, file=sys.stderr) |
|||
sys.exit(1) |
|||
for filepath in sorted(infiles): |
|||
print(filepath) |
|||
device = readSVD(filepath, sourceURL) |
|||
writeGo(outdir, device) |
|||
writeAsm(outdir, device) |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
parser = argparse.ArgumentParser(description='Generate Go register descriptors and interrupt vectors from .svd files') |
|||
parser.add_argument('indir', metavar='indir', type=str, |
|||
help='input directory containing .svd files') |
|||
parser.add_argument('outdir', metavar='outdir', type=str, |
|||
help='output directory') |
|||
parser.add_argument('--source', metavar='source', type=str, |
|||
help='output directory', |
|||
default='<unknown>') |
|||
args = parser.parse_args() |
|||
generate(args.indir, args.outdir, args.source) |
@ -0,0 +1,971 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"bufio" |
|||
"encoding/xml" |
|||
"flag" |
|||
"fmt" |
|||
"os" |
|||
"path/filepath" |
|||
"regexp" |
|||
"sort" |
|||
"strconv" |
|||
"strings" |
|||
"text/template" |
|||
"unicode" |
|||
) |
|||
|
|||
var validName = regexp.MustCompile("^[a-zA-Z0-9_]+$") |
|||
|
|||
type SVDFile struct { |
|||
XMLName xml.Name `xml:"device"` |
|||
Name string `xml:"name"` |
|||
Description string `xml:"description"` |
|||
LicenseText string `xml:"licenseText"` |
|||
Peripherals []struct { |
|||
Name string `xml:"name"` |
|||
Description string `xml:"description"` |
|||
BaseAddress string `xml:"baseAddress"` |
|||
GroupName string `xml:"groupName"` |
|||
DerivedFrom string `xml:"derivedFrom,attr"` |
|||
Interrupts []struct { |
|||
Name string `xml:"name"` |
|||
Index int `xml:"value"` |
|||
} `xml:"interrupt"` |
|||
Registers []*SVDRegister `xml:"registers>register"` |
|||
Clusters []*SVDCluster `xml:"registers>cluster"` |
|||
} `xml:"peripherals>peripheral"` |
|||
} |
|||
|
|||
type SVDRegister struct { |
|||
Name string `xml:"name"` |
|||
Description string `xml:"description"` |
|||
Dim *string `xml:"dim"` |
|||
DimIncrement string `xml:"dimIncrement"` |
|||
Size *string `xml:"size"` |
|||
Fields []*SVDField `xml:"fields>field"` |
|||
Offset *string `xml:"offset"` |
|||
AddressOffset *string `xml:"addressOffset"` |
|||
} |
|||
|
|||
type SVDField struct { |
|||
Name string `xml:"name"` |
|||
Description string `xml:"description"` |
|||
Lsb *uint32 `xml:"lsb"` |
|||
Msb *uint32 `xml:"msb"` |
|||
BitOffset *uint32 `xml:"bitOffset"` |
|||
BitWidth *uint32 `xml:"bitWidth"` |
|||
BitRange *string `xml:"bitRange"` |
|||
EnumeratedValues []struct { |
|||
Name string `xml:"name"` |
|||
Description string `xml:"description"` |
|||
Value string `xml:"value"` |
|||
} `xml:"enumeratedValues>enumeratedValue"` |
|||
} |
|||
|
|||
type SVDCluster struct { |
|||
Dim *int `xml:"dim"` |
|||
DimIncrement string `xml:"dimIncrement"` |
|||
DimIndex *string `xml:"dimIndex"` |
|||
Name string `xml:"name"` |
|||
Description string `xml:"description"` |
|||
Registers []*SVDRegister `xml:"register"` |
|||
Clusters []*SVDCluster `xml:"cluster"` |
|||
AddressOffset string `xml:"addressOffset"` |
|||
} |
|||
|
|||
type Device struct { |
|||
metadata map[string]string |
|||
interrupts []*interrupt |
|||
peripherals []*peripheral |
|||
} |
|||
|
|||
type interrupt struct { |
|||
Name string |
|||
peripheralIndex int |
|||
Value int // interrupt number
|
|||
Description string |
|||
} |
|||
|
|||
type peripheral struct { |
|||
Name string |
|||
GroupName string |
|||
BaseAddress uint64 |
|||
Description string |
|||
ClusterName string |
|||
registers []*PeripheralField |
|||
subtypes []*peripheral |
|||
} |
|||
|
|||
// A PeripheralField is a single field in a peripheral type. It may be a full
|
|||
// peripheral or a cluster within a peripheral.
|
|||
type PeripheralField struct { |
|||
name string |
|||
address uint64 |
|||
description string |
|||
registers []*PeripheralField // contains fields if this is a cluster
|
|||
array int |
|||
elementSize int |
|||
bitfields []Bitfield |
|||
} |
|||
|
|||
type Bitfield struct { |
|||
name string |
|||
description string |
|||
value uint32 |
|||
} |
|||
|
|||
func formatText(text string) string { |
|||
text = regexp.MustCompile(`[ \t\n]+`).ReplaceAllString(text, " ") // Collapse whitespace (like in HTML)
|
|||
text = strings.Replace(text, "\\n ", "\n", -1) |
|||
text = strings.TrimSpace(text) |
|||
return text |
|||
} |
|||
|
|||
// Replace characters that are not allowed in a symbol name with a '_'. This is
|
|||
// useful to be able to process SVD files with errors.
|
|||
func cleanName(text string) string { |
|||
if !validName.MatchString(text) { |
|||
result := make([]rune, 0, len(text)) |
|||
for _, c := range text { |
|||
if validName.MatchString(string(c)) { |
|||
result = append(result, c) |
|||
} else { |
|||
result = append(result, '_') |
|||
} |
|||
} |
|||
text = string(result) |
|||
} |
|||
return text |
|||
} |
|||
|
|||
// Read ARM SVD files.
|
|||
func readSVD(path, sourceURL string) (*Device, error) { |
|||
// Open the XML file.
|
|||
f, err := os.Open(path) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
defer f.Close() |
|||
decoder := xml.NewDecoder(f) |
|||
device := &SVDFile{} |
|||
err = decoder.Decode(device) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
peripheralDict := map[string]*peripheral{} |
|||
groups := map[string]*peripheral{} |
|||
|
|||
interrupts := make(map[string]*interrupt) |
|||
var peripheralsList []*peripheral |
|||
|
|||
for _, periphEl := range device.Peripherals { |
|||
description := formatText(periphEl.Description) |
|||
baseAddress, err := strconv.ParseUint(periphEl.BaseAddress, 0, 32) |
|||
if err != nil { |
|||
return nil, fmt.Errorf("invalid base address: %w", err) |
|||
} |
|||
// Some group names (for example the STM32H7A3x) have an invalid
|
|||
// group name. Replace invalid characters with "_".
|
|||
groupName := cleanName(periphEl.GroupName) |
|||
|
|||
for _, interrupt := range periphEl.Interrupts { |
|||
addInterrupt(interrupts, interrupt.Name, interrupt.Index, description) |
|||
// As a convenience, also use the peripheral name as the interrupt
|
|||
// name. Only do that for the nrf for now, as the stm32 .svd files
|
|||
// don't always put interrupts in the correct peripheral...
|
|||
if len(periphEl.Interrupts) == 1 && strings.HasPrefix(device.Name, "nrf") { |
|||
addInterrupt(interrupts, periphEl.Name, interrupt.Index, description) |
|||
} |
|||
} |
|||
|
|||
if _, ok := groups[groupName]; ok || periphEl.DerivedFrom != "" { |
|||
var derivedFrom *peripheral |
|||
if periphEl.DerivedFrom != "" { |
|||
derivedFrom = peripheralDict[periphEl.DerivedFrom] |
|||
} else { |
|||
derivedFrom = groups[groupName] |
|||
} |
|||
p := &peripheral{ |
|||
Name: periphEl.Name, |
|||
GroupName: derivedFrom.GroupName, |
|||
Description: description, |
|||
BaseAddress: baseAddress, |
|||
} |
|||
if p.Description == "" { |
|||
p.Description = derivedFrom.Description |
|||
} |
|||
peripheralsList = append(peripheralsList, p) |
|||
peripheralDict[p.Name] = p |
|||
for _, subtype := range derivedFrom.subtypes { |
|||
peripheralsList = append(peripheralsList, &peripheral{ |
|||
Name: periphEl.Name + "_" + subtype.ClusterName, |
|||
GroupName: subtype.GroupName, |
|||
Description: subtype.Description, |
|||
BaseAddress: baseAddress, |
|||
}) |
|||
} |
|||
continue |
|||
} |
|||
|
|||
p := &peripheral{ |
|||
Name: periphEl.Name, |
|||
GroupName: groupName, |
|||
Description: description, |
|||
BaseAddress: baseAddress, |
|||
registers: []*PeripheralField{}, |
|||
} |
|||
if p.GroupName == "" { |
|||
p.GroupName = periphEl.Name |
|||
} |
|||
peripheralsList = append(peripheralsList, p) |
|||
peripheralDict[periphEl.Name] = p |
|||
|
|||
if _, ok := groups[groupName]; !ok && groupName != "" { |
|||
groups[groupName] = p |
|||
} |
|||
|
|||
for _, register := range periphEl.Registers { |
|||
regName := groupName // preferably use the group name
|
|||
if regName == "" { |
|||
regName = periphEl.Name // fall back to peripheral name
|
|||
} |
|||
p.registers = append(p.registers, parseRegister(regName, register, baseAddress, "")...) |
|||
} |
|||
for _, cluster := range periphEl.Clusters { |
|||
clusterName := strings.Replace(cluster.Name, "[%s]", "", -1) |
|||
if cluster.DimIndex != nil { |
|||
clusterName = strings.Replace(clusterName, "%s", "", -1) |
|||
} |
|||
clusterPrefix := clusterName + "_" |
|||
clusterOffset, err := strconv.ParseUint(cluster.AddressOffset, 0, 32) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
var dim, dimIncrement int |
|||
if cluster.Dim == nil { |
|||
if clusterOffset == 0 { |
|||
// make this a separate peripheral
|
|||
cpRegisters := []*PeripheralField{} |
|||
for _, regEl := range cluster.Registers { |
|||
cpRegisters = append(cpRegisters, parseRegister(groupName, regEl, baseAddress, clusterName+"_")...) |
|||
} |
|||
// handle sub-clusters of registers
|
|||
for _, subClusterEl := range cluster.Clusters { |
|||
subclusterName := strings.Replace(subClusterEl.Name, "[%s]", "", -1) |
|||
subclusterPrefix := subclusterName + "_" |
|||
subclusterOffset, err := strconv.ParseUint(subClusterEl.AddressOffset, 0, 32) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
subdim := *subClusterEl.Dim |
|||
subdimIncrement, err := strconv.ParseInt(subClusterEl.DimIncrement, 0, 32) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
|
|||
if subdim > 1 { |
|||
subcpRegisters := []*PeripheralField{} |
|||
subregSize := 0 |
|||
for _, regEl := range subClusterEl.Registers { |
|||
size, err := strconv.ParseInt(*regEl.Size, 0, 32) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
subregSize += int(size) |
|||
subcpRegisters = append(subcpRegisters, parseRegister(groupName, regEl, baseAddress+subclusterOffset, subclusterPrefix)...) |
|||
} |
|||
cpRegisters = append(cpRegisters, &PeripheralField{ |
|||
name: subclusterName, |
|||
address: baseAddress + subclusterOffset, |
|||
description: subClusterEl.Description, |
|||
registers: subcpRegisters, |
|||
array: subdim, |
|||
elementSize: int(subdimIncrement), |
|||
}) |
|||
} else { |
|||
for _, regEl := range subClusterEl.Registers { |
|||
cpRegisters = append(cpRegisters, parseRegister(regEl.Name, regEl, baseAddress+subclusterOffset, subclusterPrefix)...) |
|||
} |
|||
} |
|||
} |
|||
|
|||
sort.SliceStable(cpRegisters, func(i, j int) bool { |
|||
return cpRegisters[i].address < cpRegisters[j].address |
|||
}) |
|||
clusterPeripheral := &peripheral{ |
|||
Name: periphEl.Name + "_" + clusterName, |
|||
GroupName: groupName + "_" + clusterName, |
|||
Description: description + " - " + clusterName, |
|||
ClusterName: clusterName, |
|||
BaseAddress: baseAddress, |
|||
registers: cpRegisters, |
|||
} |
|||
peripheralsList = append(peripheralsList, clusterPeripheral) |
|||
peripheralDict[clusterPeripheral.Name] = clusterPeripheral |
|||
p.subtypes = append(p.subtypes, clusterPeripheral) |
|||
continue |
|||
} |
|||
dim = -1 |
|||
dimIncrement = -1 |
|||
} else { |
|||
dim = *cluster.Dim |
|||
if dim == 1 { |
|||
dimIncrement = -1 |
|||
} else { |
|||
inc, err := strconv.ParseUint(cluster.DimIncrement, 0, 32) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
dimIncrement = int(inc) |
|||
} |
|||
} |
|||
clusterRegisters := []*PeripheralField{} |
|||
for _, regEl := range cluster.Registers { |
|||
regName := groupName |
|||
if regName == "" { |
|||
regName = periphEl.Name |
|||
} |
|||
clusterRegisters = append(clusterRegisters, parseRegister(regName, regEl, baseAddress+clusterOffset, clusterPrefix)...) |
|||
} |
|||
sort.SliceStable(clusterRegisters, func(i, j int) bool { |
|||
return clusterRegisters[i].address < clusterRegisters[j].address |
|||
}) |
|||
if dimIncrement == -1 { |
|||
lastReg := clusterRegisters[len(clusterRegisters)-1] |
|||
lastAddress := lastReg.address |
|||
if lastReg.array != -1 { |
|||
lastAddress = lastReg.address + uint64(lastReg.array*lastReg.elementSize) |
|||
} |
|||
firstAddress := clusterRegisters[0].address |
|||
dimIncrement = int(lastAddress - firstAddress) |
|||
} |
|||
p.registers = append(p.registers, &PeripheralField{ |
|||
name: clusterName, |
|||
address: baseAddress + clusterOffset, |
|||
description: cluster.Description, |
|||
registers: clusterRegisters, |
|||
array: dim, |
|||
elementSize: dimIncrement, |
|||
}) |
|||
} |
|||
sort.SliceStable(p.registers, func(i, j int) bool { |
|||
return p.registers[i].address < p.registers[j].address |
|||
}) |
|||
} |
|||
|
|||
// Make a sorted list of interrupts.
|
|||
interruptList := make([]*interrupt, 0, len(interrupts)) |
|||
for _, intr := range interrupts { |
|||
interruptList = append(interruptList, intr) |
|||
} |
|||
sort.SliceStable(interruptList, func(i, j int) bool { |
|||
if interruptList[i].Value != interruptList[j].Value { |
|||
return interruptList[i].Value < interruptList[j].Value |
|||
} |
|||
return interruptList[i].peripheralIndex < interruptList[j].peripheralIndex |
|||
}) |
|||
|
|||
// Properly format the license block, with comments.
|
|||
licenseBlock := "" |
|||
if text := formatText(device.LicenseText); text != "" { |
|||
licenseBlock = "// " + strings.Replace(text, "\n", "\n// ", -1) |
|||
licenseBlock = regexp.MustCompile(`\s+\n`).ReplaceAllString(licenseBlock, "\n") |
|||
} |
|||
|
|||
return &Device{ |
|||
metadata: map[string]string{ |
|||
"file": filepath.Base(path), |
|||
"descriptorSource": sourceURL, |
|||
"name": device.Name, |
|||
"nameLower": strings.ToLower(device.Name), |
|||
"description": strings.TrimSpace(device.Description), |
|||
"licenseBlock": licenseBlock, |
|||
}, |
|||
interrupts: interruptList, |
|||
peripherals: peripheralsList, |
|||
}, nil |
|||
} |
|||
|
|||
func addInterrupt(interrupts map[string]*interrupt, name string, index int, description string) { |
|||
if _, ok := interrupts[name]; ok { |
|||
if interrupts[name].Value != index { |
|||
// Note: some SVD files like the one for STM32H7x7 contain mistakes.
|
|||
// Instead of throwing an error, simply log it.
|
|||
fmt.Fprintf(os.Stderr, "interrupt with the same name has different indexes: %s (%d vs %d)", |
|||
name, interrupts[name].Value, index) |
|||
} |
|||
parts := strings.Split(interrupts[name].Description, " // ") |
|||
hasDescription := false |
|||
for _, part := range parts { |
|||
if part == description { |
|||
hasDescription = true |
|||
} |
|||
} |
|||
if !hasDescription { |
|||
interrupts[name].Description += " // " + description |
|||
} |
|||
} else { |
|||
interrupts[name] = &interrupt{ |
|||
Name: name, |
|||
peripheralIndex: len(interrupts), |
|||
Value: index, |
|||
Description: description, |
|||
} |
|||
} |
|||
} |
|||
|
|||
func parseBitfields(groupName, regName string, fieldEls []*SVDField, bitfieldPrefix string) []Bitfield { |
|||
var fields []Bitfield |
|||
for _, fieldEl := range fieldEls { |
|||
// Some bitfields (like the STM32H7x7) contain invalid bitfield
|
|||
// names like "CNT[31]". Replace invalid characters with "_" when
|
|||
// needed.
|
|||
fieldName := cleanName(fieldEl.Name) |
|||
if !unicode.IsUpper(rune(fieldName[0])) && !unicode.IsDigit(rune(fieldName[0])) { |
|||
fieldName = strings.ToUpper(fieldName) |
|||
} |
|||
|
|||
// Find the lsb/msb that is encoded in various ways.
|
|||
// Standards are great, that's why there are so many to choose from!
|
|||
var lsb, msb uint32 |
|||
if fieldEl.Lsb != nil && fieldEl.Msb != nil { |
|||
// try to use lsb/msb tags
|
|||
lsb = *fieldEl.Lsb |
|||
msb = *fieldEl.Msb |
|||
} else if fieldEl.BitOffset != nil && fieldEl.BitWidth != nil { |
|||
// try to use bitOffset/bitWidth tags
|
|||
lsb = *fieldEl.BitOffset |
|||
msb = *fieldEl.BitWidth + lsb - 1 |
|||
} else if fieldEl.BitRange != nil { |
|||
// try use bitRange
|
|||
// example string: "[20:16]"
|
|||
parts := strings.Split(strings.Trim(*fieldEl.BitRange, "[]"), ":") |
|||
l, err := strconv.ParseUint(parts[1], 0, 32) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
lsb = uint32(l) |
|||
m, err := strconv.ParseUint(parts[0], 0, 32) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
msb = uint32(m) |
|||
} else { |
|||
// this is an error. what to do?
|
|||
fmt.Fprintln(os.Stderr, "unable to find lsb/msb in field:", fieldName) |
|||
continue |
|||
} |
|||
|
|||
fields = append(fields, Bitfield{ |
|||
name: fmt.Sprintf("%s_%s%s_%s_Pos", groupName, bitfieldPrefix, regName, fieldName), |
|||
description: fmt.Sprintf("Position of %s field.", fieldName), |
|||
value: lsb, |
|||
}) |
|||
fields = append(fields, Bitfield{ |
|||
name: fmt.Sprintf("%s_%s%s_%s_Msk", groupName, bitfieldPrefix, regName, fieldName), |
|||
description: fmt.Sprintf("Bit mask of %s field.", fieldName), |
|||
value: (0xffffffff >> (31 - (msb - lsb))) << lsb, |
|||
}) |
|||
if lsb == msb { // single bit
|
|||
fields = append(fields, Bitfield{ |
|||
name: fmt.Sprintf("%s_%s%s_%s", groupName, bitfieldPrefix, regName, fieldName), |
|||
description: fmt.Sprintf("Bit %s.", fieldName), |
|||
value: 1 << lsb, |
|||
}) |
|||
} |
|||
for _, enumEl := range fieldEl.EnumeratedValues { |
|||
enumName := enumEl.Name |
|||
if !unicode.IsUpper(rune(enumName[0])) && !unicode.IsDigit(rune(enumName[0])) { |
|||
enumName = strings.ToUpper(enumName) |
|||
} |
|||
enumDescription := strings.Replace(enumEl.Description, "\n", " ", -1) |
|||
enumValue, err := strconv.ParseUint(enumEl.Value, 0, 32) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
fields = append(fields, Bitfield{ |
|||
name: fmt.Sprintf("%s_%s%s_%s_%s", groupName, bitfieldPrefix, regName, fieldName, enumName), |
|||
description: enumDescription, |
|||
value: uint32(enumValue), |
|||
}) |
|||
} |
|||
} |
|||
return fields |
|||
} |
|||
|
|||
type Register struct { |
|||
element *SVDRegister |
|||
baseAddress uint64 |
|||
} |
|||
|
|||
func NewRegister(element *SVDRegister, baseAddress uint64) *Register { |
|||
return &Register{ |
|||
element: element, |
|||
baseAddress: baseAddress, |
|||
} |
|||
} |
|||
|
|||
func (r *Register) name() string { |
|||
return strings.Replace(r.element.Name, "[%s]", "", -1) |
|||
} |
|||
|
|||
func (r *Register) description() string { |
|||
return strings.Replace(r.element.Description, "\n", " ", -1) |
|||
} |
|||
|
|||
func (r *Register) address() uint64 { |
|||
offsetString := r.element.Offset |
|||
if offsetString == nil { |
|||
offsetString = r.element.AddressOffset |
|||
} |
|||
addr, err := strconv.ParseUint(*offsetString, 0, 32) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
return r.baseAddress + addr |
|||
} |
|||
|
|||
func (r *Register) dim() int { |
|||
if r.element.Dim == nil { |
|||
return -1 // no dim elements
|
|||
} |
|||
dim, err := strconv.ParseInt(*r.element.Dim, 0, 32) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
return int(dim) |
|||
} |
|||
|
|||
func (r *Register) size() int { |
|||
if r.element.Size != nil { |
|||
size, err := strconv.ParseInt(*r.element.Size, 0, 32) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
return int(size) / 8 |
|||
} |
|||
return 4 |
|||
} |
|||
|
|||
func parseRegister(groupName string, regEl *SVDRegister, baseAddress uint64, bitfieldPrefix string) []*PeripheralField { |
|||
reg := NewRegister(regEl, baseAddress) |
|||
|
|||
if reg.dim() != -1 { |
|||
dimIncrement, err := strconv.ParseUint(regEl.DimIncrement, 0, 32) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
if strings.Contains(reg.name(), "%s") { |
|||
// a "spaced array" of registers, special processing required
|
|||
// we need to generate a separate register for each "element"
|
|||
var results []*PeripheralField |
|||
for i := uint64(0); i < uint64(reg.dim()); i++ { |
|||
regAddress := reg.address() + (i * dimIncrement) |
|||
results = append(results, &PeripheralField{ |
|||
name: strings.Replace(reg.name(), "%s", strconv.FormatUint(i, 10), -1), |
|||
address: regAddress, |
|||
description: reg.description(), |
|||
array: -1, |
|||
elementSize: reg.size(), |
|||
}) |
|||
} |
|||
// set first result bitfield
|
|||
shortName := strings.ToUpper(strings.Replace(strings.Replace(reg.name(), "_%s", "", -1), "%s", "", -1)) |
|||
results[0].bitfields = parseBitfields(groupName, shortName, regEl.Fields, bitfieldPrefix) |
|||
return results |
|||
} |
|||
} |
|||
regName := reg.name() |
|||
if !unicode.IsUpper(rune(regName[0])) && !unicode.IsDigit(rune(regName[0])) { |
|||
regName = strings.ToUpper(regName) |
|||
} |
|||
|
|||
return []*PeripheralField{&PeripheralField{ |
|||
name: regName, |
|||
address: reg.address(), |
|||
description: reg.description(), |
|||
bitfields: parseBitfields(groupName, regName, regEl.Fields, bitfieldPrefix), |
|||
array: reg.dim(), |
|||
elementSize: reg.size(), |
|||
}} |
|||
} |
|||
|
|||
// The Go module for this device.
|
|||
func writeGo(outdir string, device *Device) error { |
|||
outf, err := os.Create(filepath.Join(outdir, device.metadata["nameLower"]+".go")) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
defer outf.Close() |
|||
w := bufio.NewWriter(outf) |
|||
|
|||
maxInterruptValue := 0 |
|||
for _, intr := range device.interrupts { |
|||
if intr.Value > maxInterruptValue { |
|||
maxInterruptValue = intr.Value |
|||
} |
|||
} |
|||
|
|||
t := template.Must(template.New("go").Parse(`// Automatically generated file. DO NOT EDIT.
|
|||
// Generated by gen-device-svd.go from {{.metadata.file}}, see {{.metadata.descriptorSource}}
|
|||
|
|||
// +build {{.pkgName}},{{.metadata.nameLower}}
|
|||
|
|||
// {{.metadata.description}}
|
|||
//
|
|||
{{.metadata.licenseBlock}} |
|||
package {{.pkgName}} |
|||
|
|||
import ( |
|||
"runtime/volatile" |
|||
"unsafe" |
|||
) |
|||
|
|||
// Some information about this device.
|
|||
const ( |
|||
DEVICE = "{{.metadata.name}}" |
|||
) |
|||
|
|||
// Interrupt numbers
|
|||
const ({{range .interrupts}} |
|||
IRQ_{{.Name}} = {{.Value}} // {{.Description}}{{end}}
|
|||
IRQ_max = {{.interruptMax}} // Highest interrupt number on this device.
|
|||
) |
|||
|
|||
// Peripherals.
|
|||
var ( |
|||
{{range .peripherals}} {{.Name}} = (*{{.GroupName}}_Type)(unsafe.Pointer(uintptr(0x{{printf "%x" .BaseAddress}}))) // {{.Description}}
|
|||
{{end}}) |
|||
`)) |
|||
err = t.Execute(w, map[string]interface{}{ |
|||
"metadata": device.metadata, |
|||
"interrupts": device.interrupts, |
|||
"peripherals": device.peripherals, |
|||
"pkgName": filepath.Base(strings.TrimRight(outdir, "/")), |
|||
"interruptMax": maxInterruptValue, |
|||
}) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
// Define peripheral struct types.
|
|||
for _, peripheral := range device.peripherals { |
|||
if peripheral.registers == nil { |
|||
// This peripheral was derived from another peripheral. No new type
|
|||
// needs to be defined for it.
|
|||
continue |
|||
} |
|||
fmt.Fprintf(w, "\n// %s\ntype %s_Type struct {\n", peripheral.Description, peripheral.GroupName) |
|||
address := peripheral.BaseAddress |
|||
for _, register := range peripheral.registers { |
|||
if register.registers == nil && address > register.address { |
|||
// In Nordic SVD files, these registers are deprecated or
|
|||
// duplicates, so can be ignored.
|
|||
//fmt.Fprintf(os.Stderr, "skip: %s.%s 0x%x - 0x%x %d\n", peripheral.Name, register.name, address, register.address, register.elementSize)
|
|||
continue |
|||
} |
|||
|
|||
var regType string |
|||
eSize := register.elementSize |
|||
switch eSize { |
|||
case 4: |
|||
regType = "volatile.Register32" |
|||
case 2: |
|||
regType = "volatile.Register16" |
|||
case 1: |
|||
regType = "volatile.Register8" |
|||
default: |
|||
eSize = 4 |
|||
regType = "volatile.Register32" |
|||
} |
|||
|
|||
// insert padding, if needed
|
|||
if address < register.address { |
|||
bytesNeeded := register.address - address |
|||
switch bytesNeeded { |
|||
case 1: |
|||
w.WriteString("\t_ volatile.Register8\n") |
|||
case 2: |
|||
w.WriteString("\t_ volatile.Register16\n") |
|||
case 3: |
|||
w.WriteString("\t_ [3]volatile.Register8\n") |
|||
default: |
|||
numSkip := (register.address - address) / uint64(eSize) |
|||
if numSkip == 1 { |
|||
fmt.Fprintf(w, "\t_ %s\n", regType) |
|||
} else { |
|||
fmt.Fprintf(w, "\t_ [%d]%s\n", numSkip, regType) |
|||
} |
|||
} |
|||
address = register.address |
|||
} |
|||
|
|||
lastCluster := false |
|||
if register.registers != nil { |
|||
// This is a cluster, not a register. Create the cluster type.
|
|||
regType = "struct {\n" |
|||
subaddress := register.address |
|||
var subregSize uint64 |
|||
var subregType string |
|||
for _, subregister := range register.registers { |
|||
switch subregister.elementSize { |
|||
case 4: |
|||
subregType = "volatile.Register32" |
|||
case 2: |
|||
subregType = "volatile.Register16" |
|||
case 1: |
|||
subregType = "volatile.Register8" |
|||
} |
|||
if subregType == "" { |
|||
panic("unknown element size") |
|||
} |
|||
|
|||
if subregister.array != -1 { |
|||
subregType = fmt.Sprintf("[%d]%s", subregister.array, subregType) |
|||
} |
|||
if subaddress != subregister.address { |
|||
bytesNeeded := subregister.address - subaddress |
|||
if bytesNeeded == 1 { |
|||
regType += "\t\t_ volatile.Register8\n" |
|||
} else if bytesNeeded == 2 { |
|||
regType += "\t\t_ volatile.Register16\n" |
|||
} else { |
|||
numSkip := (subregister.address - subaddress) |
|||
if numSkip < 1 { |
|||
continue |
|||
} else if numSkip == 1 { |
|||
regType += "\t\t_ volatile.Register8\n" |
|||
} else { |
|||
regType += fmt.Sprintf("\t\t_ [%d]volatile.Register8\n", numSkip) |
|||
} |
|||
} |
|||
subaddress += bytesNeeded |
|||
} |
|||
if subregister.array != -1 { |
|||
subregSize = uint64(subregister.array * subregister.elementSize) |
|||
} else { |
|||
subregSize = uint64(subregister.elementSize) |
|||
} |
|||
subaddress += subregSize |
|||
regType += fmt.Sprintf("\t\t%s %s\n", subregister.name, subregType) |
|||
} |
|||
if register.array != -1 { |
|||
if subaddress != register.address+uint64(register.elementSize) { |
|||
numSkip := ((register.address + uint64(register.elementSize)) - subaddress) / subregSize |
|||
if numSkip <= 1 { |
|||
regType += fmt.Sprintf("\t\t_ %s\n", subregType) |
|||
} else { |
|||
regType += fmt.Sprintf("\t\t_ [%d]%s\n", numSkip, subregType) |
|||
} |
|||
} |
|||
} else { |
|||
lastCluster = true |
|||
} |
|||
regType += "\t}" |
|||
address = subaddress |
|||
} |
|||
|
|||
if register.array != -1 { |
|||
regType = fmt.Sprintf("[%d]%s", register.array, regType) |
|||
} |
|||
fmt.Fprintf(w, "\t%s %s\n", register.name, regType) |
|||
|
|||
// next address
|
|||
if lastCluster { |
|||
lastCluster = false |
|||
} else if register.array != -1 { |
|||
address = register.address + uint64(register.elementSize*register.array) |
|||
} else { |
|||
address = register.address + uint64(register.elementSize) |
|||
} |
|||
} |
|||
w.WriteString("}\n") |
|||
} |
|||
|
|||
// Define bitfields.
|
|||
for _, peripheral := range device.peripherals { |
|||
if peripheral.registers == nil { |
|||
// This peripheral was derived from another peripheral. Bitfields are
|
|||
// already defined.
|
|||
continue |
|||
} |
|||
fmt.Fprintf(w, "\n// Bitfields for %s: %s\nconst(", peripheral.Name, peripheral.Description) |
|||
for _, register := range peripheral.registers { |
|||
if len(register.bitfields) != 0 { |
|||
writeGoRegisterBitfields(w, register, register.name) |
|||
} |
|||
if register.registers == nil { |
|||
continue |
|||
} |
|||
for _, subregister := range register.registers { |
|||
writeGoRegisterBitfields(w, subregister, register.name+"."+subregister.name) |
|||
} |
|||
} |
|||
w.WriteString(")\n") |
|||
} |
|||
|
|||
return w.Flush() |
|||
} |
|||
|
|||
func writeGoRegisterBitfields(w *bufio.Writer, register *PeripheralField, name string) { |
|||
w.WriteString("\n\t// " + name) |
|||
if register.description != "" { |
|||
w.WriteString(": " + register.description) |
|||
} |
|||
w.WriteByte('\n') |
|||
for _, bitfield := range register.bitfields { |
|||
fmt.Fprintf(w, "\t%s = 0x%x", bitfield.name, bitfield.value) |
|||
if bitfield.description != "" { |
|||
w.WriteString(" // " + bitfield.description) |
|||
} |
|||
w.WriteByte('\n') |
|||
} |
|||
} |
|||
|
|||
// The interrupt vector, which is hard to write directly in Go.
|
|||
func writeAsm(outdir string, device *Device) error { |
|||
outf, err := os.Create(filepath.Join(outdir, device.metadata["nameLower"]+".s")) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
defer outf.Close() |
|||
w := bufio.NewWriter(outf) |
|||
|
|||
t := template.Must(template.New("go").Parse(`// Automatically generated file. DO NOT EDIT.
|
|||
// Generated by gen-device-svd.go from {{.file}}, see {{.descriptorSource}}
|
|||
|
|||
// {{.description}}
|
|||
//
|
|||
{{.licenseBlock}} |
|||
|
|||
.syntax unified |
|||
|
|||
// This is the default handler for interrupts, if triggered but not defined.
|
|||
.section .text.Default_Handler |
|||
.global Default_Handler |
|||
.type Default_Handler, %function |
|||
Default_Handler: |
|||
wfe |
|||
b Default_Handler |
|||
|
|||
// Avoid the need for repeated .weak and .set instructions.
|
|||
.macro IRQ handler |
|||
.weak \handler |
|||
.set \handler, Default_Handler |
|||
.endm |
|||
|
|||
// Must set the "a" flag on the section:
|
|||
// https://svnweb.freebsd.org/base/stable/11/sys/arm/arm/locore-v4.S?r1=321049&r2=321048&pathrev=321049
|
|||
// https://sourceware.org/binutils/docs/as/Section.html#ELF-Version
|
|||
.section .isr_vector, "a", %progbits |
|||
.global __isr_vector |
|||
// Interrupt vector as defined by Cortex-M, starting with the stack top.
|
|||
// On reset, SP is initialized with *0x0 and PC is loaded with *0x4, loading
|
|||
// _stack_top and Reset_Handler.
|
|||
.long _stack_top |
|||
.long Reset_Handler |
|||
.long NMI_Handler |
|||
.long HardFault_Handler |
|||
.long MemoryManagement_Handler |
|||
.long BusFault_Handler |
|||
.long UsageFault_Handler |
|||
.long 0 |
|||
.long 0 |
|||
.long 0 |
|||
.long 0 |
|||
.long SVC_Handler |
|||
.long DebugMon_Handler |
|||
.long 0 |
|||
.long PendSV_Handler |
|||
.long SysTick_Handler |
|||
|
|||
// Extra interrupts for peripherals defined by the hardware vendor.
|
|||
`)) |
|||
err = t.Execute(w, device.metadata) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
num := 0 |
|||
for _, intr := range device.interrupts { |
|||
if intr.Value == num-1 { |
|||
continue |
|||
} |
|||
if intr.Value < num { |
|||
panic("interrupt numbers are not sorted") |
|||
} |
|||
for intr.Value > num { |
|||
w.WriteString(" .long 0\n") |
|||
num++ |
|||
} |
|||
num++ |
|||
fmt.Fprintf(w, " .long %s_IRQHandler\n", intr.Name) |
|||
} |
|||
|
|||
w.WriteString(` |
|||
// Define default implementations for interrupts, redirecting to
|
|||
// Default_Handler when not implemented.
|
|||
IRQ NMI_Handler |
|||
IRQ HardFault_Handler |
|||
IRQ MemoryManagement_Handler |
|||
IRQ BusFault_Handler |
|||
IRQ UsageFault_Handler |
|||
IRQ SVC_Handler |
|||
IRQ DebugMon_Handler |
|||
IRQ PendSV_Handler |
|||
IRQ SysTick_Handler |
|||
`) |
|||
for _, intr := range device.interrupts { |
|||
fmt.Fprintf(w, " IRQ %s_IRQHandler\n", intr.Name) |
|||
} |
|||
return w.Flush() |
|||
} |
|||
|
|||
func generate(indir, outdir, sourceURL string) error { |
|||
if _, err := os.Stat(indir); os.IsNotExist(err) { |
|||
fmt.Fprintln(os.Stderr, "cannot find input directory:", indir) |
|||
os.Exit(1) |
|||
} |
|||
os.MkdirAll(outdir, 0777) |
|||
|
|||
infiles, err := filepath.Glob(filepath.Join(indir, "*.svd")) |
|||
if err != nil { |
|||
fmt.Fprintln(os.Stderr, "could not read .svd files:", err) |
|||
os.Exit(1) |
|||
} |
|||
sort.Strings(infiles) |
|||
for _, infile := range infiles { |
|||
fmt.Println(infile) |
|||
device, err := readSVD(infile, sourceURL) |
|||
if err != nil { |
|||
return fmt.Errorf("failed to read: %w", err) |
|||
} |
|||
err = writeGo(outdir, device) |
|||
if err != nil { |
|||
return fmt.Errorf("failed to write Go file: %w", err) |
|||
} |
|||
err = writeAsm(outdir, device) |
|||
if err != nil { |
|||
return fmt.Errorf("failed to write assembly file: %w", err) |
|||
} |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func main() { |
|||
sourceURL := flag.String("source", "<unknown>", "source SVD file") |
|||
flag.Parse() |
|||
if flag.NArg() != 2 { |
|||
fmt.Fprintln(os.Stderr, "provide exactly two arguments: input directory (with .svd files) and output directory for generated files") |
|||
flag.PrintDefaults() |
|||
return |
|||
} |
|||
indir := flag.Arg(0) |
|||
outdir := flag.Arg(1) |
|||
err := generate(indir, outdir, *sourceURL) |
|||
if err != nil { |
|||
fmt.Fprintln(os.Stderr, err) |
|||
os.Exit(1) |
|||
} |
|||
} |
Loading…
Reference in new issue