svd generator (+ fix parser, works now)
This commit is contained in:
parent
303da594c4
commit
4a9db32d33
45
svd/svd.py
45
svd/svd.py
|
@ -23,16 +23,16 @@ SvdAccess = Literal["read-only", "write-only", "read-write", "writeOnce", "read-
|
||||||
|
|
||||||
|
|
||||||
class SvdSauRegion(NamedTuple):
|
class SvdSauRegion(NamedTuple):
|
||||||
enabled: bool
|
enabled: bool # attribute
|
||||||
name: str
|
name: str # attribute
|
||||||
base: int
|
base: int
|
||||||
limit: int
|
limit: int
|
||||||
access: SvdSauAccess
|
access: SvdSauAccess
|
||||||
|
|
||||||
|
|
||||||
class SvdSauRegionsConfig(NamedTuple):
|
class SvdSauRegionsConfig(NamedTuple):
|
||||||
enabled: bool
|
enabled: bool # attribute
|
||||||
protectionWhenDisabled: SvdSauAccess
|
protectionWhenDisabled: SvdSauAccess # attribute
|
||||||
regions: Sequence[SvdSauRegion]
|
regions: Sequence[SvdSauRegion]
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ class SvdEnumeratedValue(NamedTuple):
|
||||||
|
|
||||||
|
|
||||||
class SvdEnumeratedValues(NamedTuple):
|
class SvdEnumeratedValues(NamedTuple):
|
||||||
derivedFrom: str
|
derivedFrom: str # attribute
|
||||||
name: str
|
name: str
|
||||||
usage: SvdUsage
|
usage: SvdUsage
|
||||||
enumeratedValue: Sequence[SvdEnumeratedValue]
|
enumeratedValue: Sequence[SvdEnumeratedValue]
|
||||||
|
@ -86,7 +86,7 @@ class SvdDimArrayIndex(NamedTuple):
|
||||||
class SvdDimElement(NamedTuple):
|
class SvdDimElement(NamedTuple):
|
||||||
dim: int
|
dim: int
|
||||||
dimIncrement: int
|
dimIncrement: int
|
||||||
dimIndex: int
|
dimIndex: str
|
||||||
dimName: str
|
dimName: str
|
||||||
dimArrayIndex: SvdDimArrayIndex
|
dimArrayIndex: SvdDimArrayIndex
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ class SvdInterrupt(NamedTuple):
|
||||||
|
|
||||||
|
|
||||||
class SvdCluster(NamedTuple):
|
class SvdCluster(NamedTuple):
|
||||||
derivedFrom: str
|
derivedFrom: str # attribute
|
||||||
dimElement: SvdDimElement
|
dimElement: SvdDimElement
|
||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
|
@ -118,8 +118,8 @@ class SvdCluster(NamedTuple):
|
||||||
|
|
||||||
|
|
||||||
class SvdWriteConstraintRange(NamedTuple):
|
class SvdWriteConstraintRange(NamedTuple):
|
||||||
min: int
|
minimum: int
|
||||||
max: int
|
maximum: int
|
||||||
|
|
||||||
class SvdWriteConstraint(NamedTuple):
|
class SvdWriteConstraint(NamedTuple):
|
||||||
writeAsRead: bool
|
writeAsRead: bool
|
||||||
|
@ -127,23 +127,16 @@ class SvdWriteConstraint(NamedTuple):
|
||||||
range: SvdWriteConstraintRange
|
range: SvdWriteConstraintRange
|
||||||
|
|
||||||
|
|
||||||
class SvdBitRangeLsbMsb(NamedTuple):
|
|
||||||
lsb: int
|
|
||||||
msb: int
|
|
||||||
class SvdBitRangeOffsetWidth(NamedTuple):
|
|
||||||
bitOffset: int
|
|
||||||
bitWidth: int
|
|
||||||
|
|
||||||
|
|
||||||
SvdBitRangePattern = str
|
|
||||||
|
|
||||||
|
|
||||||
class SvdField(NamedTuple):
|
class SvdField(NamedTuple):
|
||||||
derivedFrom: str
|
derivedFrom: str # attribute
|
||||||
dimElement: SvdDimElement
|
dimElement: SvdDimElement
|
||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
bitRange: Union[SvdBitRangeLsbMsb, SvdBitRangeOffsetWidth, SvdBitRangePattern]
|
bitOffset: int
|
||||||
|
bitWidth: int
|
||||||
|
lsb: int
|
||||||
|
msb: int
|
||||||
|
bitRange: str
|
||||||
access: SvdAccess
|
access: SvdAccess
|
||||||
modifiedWriteValues: SvdModifiedWriteValues
|
modifiedWriteValues: SvdModifiedWriteValues
|
||||||
writeConstraint: SvdWriteConstraint
|
writeConstraint: SvdWriteConstraint
|
||||||
|
@ -152,7 +145,7 @@ class SvdField(NamedTuple):
|
||||||
|
|
||||||
|
|
||||||
class SvdRegister(NamedTuple):
|
class SvdRegister(NamedTuple):
|
||||||
derivedFrom: str
|
derivedFrom: str # attribute
|
||||||
dimElement: SvdDimElement
|
dimElement: SvdDimElement
|
||||||
name: str
|
name: str
|
||||||
displayName: str
|
displayName: str
|
||||||
|
@ -166,10 +159,12 @@ class SvdRegister(NamedTuple):
|
||||||
writeConstraint: SvdWriteConstraint
|
writeConstraint: SvdWriteConstraint
|
||||||
readAction: SvdReadAction
|
readAction: SvdReadAction
|
||||||
fields: Sequence[SvdField]
|
fields: Sequence[SvdField]
|
||||||
|
# ignoring for now because this SVD is VERY broken
|
||||||
|
#enumeratedValues: SvdEnumeratedValues # HACK: SiFive e310x, Kendryte k210
|
||||||
|
|
||||||
|
|
||||||
class SvdPeripheral(NamedTuple):
|
class SvdPeripheral(NamedTuple):
|
||||||
derivedFrom: str
|
derivedFrom: str # attribute
|
||||||
name: str
|
name: str
|
||||||
version: str
|
version: str
|
||||||
description: str
|
description: str
|
||||||
|
@ -203,5 +198,5 @@ class SvdDevice(NamedTuple):
|
||||||
peripherals: Sequence[SvdPeripheral]
|
peripherals: Sequence[SvdPeripheral]
|
||||||
vendorExtensions: Any
|
vendorExtensions: Any
|
||||||
|
|
||||||
# TODO: functions for expanding registerproperties, dimelements and derivedfrom?
|
# TODO: functions for expanding/flattening registerProperties, dimElements and derivedFrom?
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
|
||||||
|
import typing, collections.abc
|
||||||
|
|
||||||
|
from svd import *
|
||||||
|
from typing import get_type_hints
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME:
|
||||||
|
# * peripheral registers not emitting
|
||||||
|
|
||||||
|
|
||||||
|
def ser_one(v) -> str:
|
||||||
|
assert v is not None
|
||||||
|
if isinstance(v, bool): return "true" if v else "false"
|
||||||
|
elif isinstance(v, int): return hex(v)
|
||||||
|
elif isinstance(v, str): return v
|
||||||
|
elif type(v) == tuple:
|
||||||
|
return "r%dp%d" % (v[0], v[1])
|
||||||
|
else: return None
|
||||||
|
|
||||||
|
|
||||||
|
type2attr = {
|
||||||
|
SvdSauRegion: {'enabled','name'},
|
||||||
|
SvdSauRegionsConfig: {'enabled','protectionWhenDisabled'},
|
||||||
|
SvdEnumeratedValues: {'derivedFrom'},
|
||||||
|
SvdCluster: {'derivedFrom'},
|
||||||
|
SvdField: {'derivedFrom'},
|
||||||
|
SvdRegister: {'derivedFrom'},
|
||||||
|
SvdPeripheral: {'derivedFrom'},
|
||||||
|
}
|
||||||
|
type2tag = {
|
||||||
|
SvdSauRegion: 'region',
|
||||||
|
SvdSauRegionsConfig: 'sauRegionsConfig',
|
||||||
|
SvdCpu: 'cpu',
|
||||||
|
SvdRegisterProperties: 'registerProperties',
|
||||||
|
SvdEnumeratedValue: 'enumeratedValue',
|
||||||
|
SvdEnumeratedValues: 'enumeratedValues',
|
||||||
|
SvdDimArrayIndex: 'dimArrayIndex',
|
||||||
|
SvdDimElement: 'dimElement',
|
||||||
|
SvdAddressBlock: 'addressBlock',
|
||||||
|
SvdInterrupt: 'interrupt',
|
||||||
|
SvdCluster: 'cluster',
|
||||||
|
SvdWriteConstraintRange: 'range',
|
||||||
|
SvdWriteConstraint: 'writeConstraint',
|
||||||
|
SvdField: 'field',
|
||||||
|
SvdRegister: 'register',
|
||||||
|
SvdPeripheral: 'peripheral',
|
||||||
|
SvdDevice: 'device',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def gen_generic(f, svdelem, T, tag, attrs=(), extraattr="", no_outer=False):
|
||||||
|
atdict={}
|
||||||
|
childdict={}
|
||||||
|
|
||||||
|
d = dict((n, svdelem.__getattribute__(n)) for n in T._fields)
|
||||||
|
for k, v in d.items():
|
||||||
|
if v is None: continue
|
||||||
|
if k in attrs:
|
||||||
|
atdict[k] = v
|
||||||
|
else:
|
||||||
|
childdict[k] = v
|
||||||
|
|
||||||
|
atstr = ' '.join('%s="%s"' % (k, ser_one(v)) for k, v in atdict.items())
|
||||||
|
|
||||||
|
if not no_outer: f.write("<%s%s%s%s>\n" % (tag, (' ' if len(atstr)+len(extraattr)>0 else ''), atstr, extraattr))
|
||||||
|
|
||||||
|
types = get_type_hints(T)
|
||||||
|
for k, v in childdict.items():
|
||||||
|
if v is None: continue
|
||||||
|
typ = types[k]
|
||||||
|
x = ser_one(v)
|
||||||
|
if x is None: # TODO: optimize
|
||||||
|
if '__origin__' in typ.__dict__ and typ.__origin__ == collections.abc.Sequence:
|
||||||
|
if len(v) > 0:
|
||||||
|
f.write("<%s>\n" % k)
|
||||||
|
ttt = typ.__args__[0]
|
||||||
|
isunion = '__origin__' in ttt.__dict__ and ttt.__origin__ == typing.Union
|
||||||
|
for x in v:
|
||||||
|
if isunion: ttt = type(x)
|
||||||
|
gen_generic(f, x, ttt, type2tag[ttt], type2attr.get(ttt, ()))
|
||||||
|
f.write("</%s>\n" % k)
|
||||||
|
else:
|
||||||
|
noouter = typ in {SvdDimElement, SvdRegisterProperties}
|
||||||
|
gen_generic(f, v, typ, k, type2attr.get(typ, ()), no_outer=noouter)
|
||||||
|
else:
|
||||||
|
f.write("<%s>%s</%s>\n"%(k, x, k))
|
||||||
|
|
||||||
|
if not no_outer: f.write("</%s>\n" % tag)
|
||||||
|
|
||||||
|
|
||||||
|
def generate(f, dev: SvdDevice):
|
||||||
|
f.write("""<?xml version="1.0" encoding="utf-8"?>\n""")
|
||||||
|
gen_generic(f, dev, SvdDevice, 'device',
|
||||||
|
extraattr=""" schemaVersion="1.1" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd" """)
|
||||||
|
|
|
@ -7,6 +7,18 @@ from svd import *
|
||||||
from typing import get_type_hints
|
from typing import get_type_hints
|
||||||
|
|
||||||
|
|
||||||
|
def parse_int(s):
|
||||||
|
if len(s) == 0: raise ValueError()
|
||||||
|
if s.startswith('0b') or s[0] == '#':
|
||||||
|
if 'x' in s: return s # eh
|
||||||
|
elif s.startswith('0b'): return int(s[2:], 2)
|
||||||
|
else: return int(s[1:], 2)
|
||||||
|
elif s[0] == '0' and len(s) > 1 and not all(x == '0' for x in s):
|
||||||
|
# HACK: STMicro STM32F411
|
||||||
|
return int(s, 16)
|
||||||
|
else: return int(s, 0)
|
||||||
|
|
||||||
|
|
||||||
def parse_one(ch, t):
|
def parse_one(ch, t):
|
||||||
k, v = None, None
|
k, v = None, None
|
||||||
if isinstance(ch, ET.Element):
|
if isinstance(ch, ET.Element):
|
||||||
|
@ -15,7 +27,6 @@ def parse_one(ch, t):
|
||||||
k, v = ch
|
k, v = ch
|
||||||
|
|
||||||
field = get_type_hints(t)[k]
|
field = get_type_hints(t)[k]
|
||||||
#print("%s -> %s [%s]" %(k, v, repr(field)))
|
|
||||||
|
|
||||||
known_types = {
|
known_types = {
|
||||||
SvdDevice: parse_device,
|
SvdDevice: parse_device,
|
||||||
|
@ -36,20 +47,25 @@ def parse_one(ch, t):
|
||||||
|
|
||||||
vv = known_types.get(field)
|
vv = known_types.get(field)
|
||||||
if vv:
|
if vv:
|
||||||
|
#print("%s -> %s [%s]" %(k, v, repr(field)))
|
||||||
return vv(v)
|
return vv(v)
|
||||||
elif isinstance(ch, ET.Element):
|
elif isinstance(ch, ET.Element):
|
||||||
v = ch.text
|
v = ch.text
|
||||||
|
#print("%s -> %s [%s]" %(k, v, repr(field)))
|
||||||
|
|
||||||
if field == int:
|
if field == int:
|
||||||
return int(v, 0)
|
return parse_int(v)
|
||||||
elif field == bool:
|
elif field == bool:
|
||||||
assert v in {'true','false','1','0'}, ("invalid boolean value %s"%v)
|
assert v in {'true','false','1','0'}, ("invalid boolean value %s"%v)
|
||||||
return v in {'true','1'}
|
return v in {'true','1'}
|
||||||
elif field == str or field.__origin__ == typing.Literal:
|
elif field == str or field.__origin__ == typing.Literal:
|
||||||
return v
|
return v
|
||||||
elif field.__origin__ == tuple:
|
elif field.__origin__ == tuple:
|
||||||
if k == 'revision':
|
if k == 'revision': # good enough
|
||||||
return tuple(int(x) for x in v[1:].split('p'))
|
if len(v) > 0 and v[0] != 'r': # HACK: ESP32
|
||||||
|
return v
|
||||||
|
else:
|
||||||
|
return tuple(int(x) for x in v[1:].split('p'))
|
||||||
else:
|
else:
|
||||||
assert False, ("unknown Tuple type %s for field %s" % (field, k))
|
assert False, ("unknown Tuple type %s for field %s" % (field, k))
|
||||||
elif field.__origin__ == typing.Union:
|
elif field.__origin__ == typing.Union:
|
||||||
|
@ -127,8 +143,7 @@ def parse_addressBlock(xd): return parse_generic(xd, SvdAddressBlock, 'addressBl
|
||||||
def parse_interrupt(xd): return parse_generic(xd, SvdInterrupt, 'interrupt')
|
def parse_interrupt(xd): return parse_generic(xd, SvdInterrupt, 'interrupt')
|
||||||
def parse_writeConstraintRange(xd): return parse_generic(xd, SvdWriteConstraintRange, 'range')
|
def parse_writeConstraintRange(xd): return parse_generic(xd, SvdWriteConstraintRange, 'range')
|
||||||
def parse_enumeratedValue(xd): return parse_generic(xd, SvdEnumeratedValue, 'enumeratedValue')
|
def parse_enumeratedValue(xd): return parse_generic(xd, SvdEnumeratedValue, 'enumeratedValue')
|
||||||
def parse_writeConstraint(xd):
|
def parse_writeConstraint(xd): return parse_generic(xd, SvdWriteConstraint, 'writeConstraint')
|
||||||
return parse_writeConstraint(xd, SvdWriteConstraint, 'writeConstraint')
|
|
||||||
def parse_dimArrayIndex(xd): return parse_generic(xd, SvdDimArrayIndex, 'dimArrayIndex')
|
def parse_dimArrayIndex(xd): return parse_generic(xd, SvdDimArrayIndex, 'dimArrayIndex')
|
||||||
|
|
||||||
|
|
||||||
|
@ -171,6 +186,7 @@ def parse_registers(xd) -> Sequence[Union[SvdCluster, SvdRegister]]:
|
||||||
rr.append(parse_register(ch))
|
rr.append(parse_register(ch))
|
||||||
else:
|
else:
|
||||||
assert False, ("expected cluster or register, got %s"%ch)
|
assert False, ("expected cluster or register, got %s"%ch)
|
||||||
|
return rr
|
||||||
|
|
||||||
|
|
||||||
def parse_peripheral(xd) -> SvdPeripheral:
|
def parse_peripheral(xd) -> SvdPeripheral:
|
||||||
|
@ -188,7 +204,18 @@ def parse_device(xd) -> SvdDevice:
|
||||||
|
|
||||||
|
|
||||||
def parse(path) -> SvdDevice:
|
def parse(path) -> SvdDevice:
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
#a = datetime.datetime.now()
|
||||||
xmlroot = ET.parse(path).getroot()
|
xmlroot = ET.parse(path).getroot()
|
||||||
|
#b = datetime.datetime.now()
|
||||||
|
r = parse_device(xmlroot)
|
||||||
|
#c = datetime.datetime.now()
|
||||||
|
|
||||||
return parse_device(xmlroot)
|
#print("a->b:", b - a)
|
||||||
|
#print("b->c:", c - b)
|
||||||
|
# for NXP/MIMXRT1062: XML parsing 2s, deserialization 8s, welp
|
||||||
|
# oh well
|
||||||
|
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue