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):
|
||||
enabled: bool
|
||||
name: str
|
||||
enabled: bool # attribute
|
||||
name: str # attribute
|
||||
base: int
|
||||
limit: int
|
||||
access: SvdSauAccess
|
||||
|
||||
|
||||
class SvdSauRegionsConfig(NamedTuple):
|
||||
enabled: bool
|
||||
protectionWhenDisabled: SvdSauAccess
|
||||
enabled: bool # attribute
|
||||
protectionWhenDisabled: SvdSauAccess # attribute
|
||||
regions: Sequence[SvdSauRegion]
|
||||
|
||||
|
||||
|
@ -72,7 +72,7 @@ class SvdEnumeratedValue(NamedTuple):
|
|||
|
||||
|
||||
class SvdEnumeratedValues(NamedTuple):
|
||||
derivedFrom: str
|
||||
derivedFrom: str # attribute
|
||||
name: str
|
||||
usage: SvdUsage
|
||||
enumeratedValue: Sequence[SvdEnumeratedValue]
|
||||
|
@ -86,7 +86,7 @@ class SvdDimArrayIndex(NamedTuple):
|
|||
class SvdDimElement(NamedTuple):
|
||||
dim: int
|
||||
dimIncrement: int
|
||||
dimIndex: int
|
||||
dimIndex: str
|
||||
dimName: str
|
||||
dimArrayIndex: SvdDimArrayIndex
|
||||
|
||||
|
@ -105,7 +105,7 @@ class SvdInterrupt(NamedTuple):
|
|||
|
||||
|
||||
class SvdCluster(NamedTuple):
|
||||
derivedFrom: str
|
||||
derivedFrom: str # attribute
|
||||
dimElement: SvdDimElement
|
||||
name: str
|
||||
description: str
|
||||
|
@ -118,8 +118,8 @@ class SvdCluster(NamedTuple):
|
|||
|
||||
|
||||
class SvdWriteConstraintRange(NamedTuple):
|
||||
min: int
|
||||
max: int
|
||||
minimum: int
|
||||
maximum: int
|
||||
|
||||
class SvdWriteConstraint(NamedTuple):
|
||||
writeAsRead: bool
|
||||
|
@ -127,23 +127,16 @@ class SvdWriteConstraint(NamedTuple):
|
|||
range: SvdWriteConstraintRange
|
||||
|
||||
|
||||
class SvdBitRangeLsbMsb(NamedTuple):
|
||||
lsb: int
|
||||
msb: int
|
||||
class SvdBitRangeOffsetWidth(NamedTuple):
|
||||
bitOffset: int
|
||||
bitWidth: int
|
||||
|
||||
|
||||
SvdBitRangePattern = str
|
||||
|
||||
|
||||
class SvdField(NamedTuple):
|
||||
derivedFrom: str
|
||||
derivedFrom: str # attribute
|
||||
dimElement: SvdDimElement
|
||||
name: str
|
||||
description: str
|
||||
bitRange: Union[SvdBitRangeLsbMsb, SvdBitRangeOffsetWidth, SvdBitRangePattern]
|
||||
bitOffset: int
|
||||
bitWidth: int
|
||||
lsb: int
|
||||
msb: int
|
||||
bitRange: str
|
||||
access: SvdAccess
|
||||
modifiedWriteValues: SvdModifiedWriteValues
|
||||
writeConstraint: SvdWriteConstraint
|
||||
|
@ -152,7 +145,7 @@ class SvdField(NamedTuple):
|
|||
|
||||
|
||||
class SvdRegister(NamedTuple):
|
||||
derivedFrom: str
|
||||
derivedFrom: str # attribute
|
||||
dimElement: SvdDimElement
|
||||
name: str
|
||||
displayName: str
|
||||
|
@ -166,10 +159,12 @@ class SvdRegister(NamedTuple):
|
|||
writeConstraint: SvdWriteConstraint
|
||||
readAction: SvdReadAction
|
||||
fields: Sequence[SvdField]
|
||||
# ignoring for now because this SVD is VERY broken
|
||||
#enumeratedValues: SvdEnumeratedValues # HACK: SiFive e310x, Kendryte k210
|
||||
|
||||
|
||||
class SvdPeripheral(NamedTuple):
|
||||
derivedFrom: str
|
||||
derivedFrom: str # attribute
|
||||
name: str
|
||||
version: str
|
||||
description: str
|
||||
|
@ -203,5 +198,5 @@ class SvdDevice(NamedTuple):
|
|||
peripherals: Sequence[SvdPeripheral]
|
||||
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
|
||||
|
||||
|
||||
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):
|
||||
k, v = None, None
|
||||
if isinstance(ch, ET.Element):
|
||||
|
@ -15,7 +27,6 @@ def parse_one(ch, t):
|
|||
k, v = ch
|
||||
|
||||
field = get_type_hints(t)[k]
|
||||
#print("%s -> %s [%s]" %(k, v, repr(field)))
|
||||
|
||||
known_types = {
|
||||
SvdDevice: parse_device,
|
||||
|
@ -36,20 +47,25 @@ def parse_one(ch, t):
|
|||
|
||||
vv = known_types.get(field)
|
||||
if vv:
|
||||
#print("%s -> %s [%s]" %(k, v, repr(field)))
|
||||
return vv(v)
|
||||
elif isinstance(ch, ET.Element):
|
||||
v = ch.text
|
||||
#print("%s -> %s [%s]" %(k, v, repr(field)))
|
||||
|
||||
if field == int:
|
||||
return int(v, 0)
|
||||
return parse_int(v)
|
||||
elif field == bool:
|
||||
assert v in {'true','false','1','0'}, ("invalid boolean value %s"%v)
|
||||
return v in {'true','1'}
|
||||
elif field == str or field.__origin__ == typing.Literal:
|
||||
return v
|
||||
elif field.__origin__ == tuple:
|
||||
if k == 'revision':
|
||||
return tuple(int(x) for x in v[1:].split('p'))
|
||||
if k == 'revision': # good enough
|
||||
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:
|
||||
assert False, ("unknown Tuple type %s for field %s" % (field, k))
|
||||
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_writeConstraintRange(xd): return parse_generic(xd, SvdWriteConstraintRange, 'range')
|
||||
def parse_enumeratedValue(xd): return parse_generic(xd, SvdEnumeratedValue, 'enumeratedValue')
|
||||
def parse_writeConstraint(xd):
|
||||
return parse_writeConstraint(xd, SvdWriteConstraint, 'writeConstraint')
|
||||
def parse_writeConstraint(xd): return parse_generic(xd, SvdWriteConstraint, 'writeConstraint')
|
||||
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))
|
||||
else:
|
||||
assert False, ("expected cluster or register, got %s"%ch)
|
||||
return rr
|
||||
|
||||
|
||||
def parse_peripheral(xd) -> SvdPeripheral:
|
||||
|
@ -188,7 +204,18 @@ def parse_device(xd) -> SvdDevice:
|
|||
|
||||
|
||||
def parse(path) -> SvdDevice:
|
||||
import datetime
|
||||
|
||||
#a = datetime.datetime.now()
|
||||
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