mmiogrok/svd/svdparse.py

233 lines
7.6 KiB
Python

import xml, xml.etree
import xml.etree.ElementTree as ET
import typing
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):
k, v = ch.tag, ch
else:
k, v = ch
field = get_type_hints(t)[k]
known_types = {
SvdDevice: parse_device,
SvdCpu: parse_cpu,
SvdPeripheral: parse_peripheral,
SvdRegister: parse_register,
SvdField: parse_field,
SvdEnumeratedValues: parse_enumeratedValues,
SvdCluster: parse_cluster,
SvdSauRegionsConfig: parse_sauRegionsConfig,
SvdWriteConstraint: parse_writeConstraint,
SvdEnumeratedValue: parse_enumeratedValue,
SvdWriteConstraintRange: parse_writeConstraintRange,
SvdInterrupt: parse_interrupt,
SvdAddressBlock: parse_addressBlock,
SvdSauRegion: parse_sauRegion,
}
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 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': # 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:
return v # good enough for now
else:
assert False, ("unknown type %s for field %s" % (field, k))
REG_PROP_NAMES = {'size', 'access', 'protection', 'resetValue', 'resetMask'}
DIM_ELEM_NAMES = {'dim','dimIncrement','dimIndex','dimName','dimArrayIndex'}
def try_parse_registerProperties(xd, part, dimElem) -> SvdRegisterProperties:
d = dict((n, None) for n in SvdRegisterProperties._fields)
for ch in xd:
if ch.tag in d:
d[ch.tag] = parse_one(ch, SvdRegisterProperties)
else:
assert ch.tag in part._fields or (dimElem and ch.tag in DIM_ELEM_NAMES), \
("unknown child %s" % repr(ch))
return SvdRegisterProperties(**d)
def try_parse_dimElement(xd, part, regProp) -> SvdDimElement:
d = dict((n, None) for n in SvdDimElement._fields)
for ch in xd:
if ch.tag == 'dimArrayIndex':
d[ch.tag] = parse_dimArrayIndex(ch)
elif ch.tag in d:
d[ch.tag] = parse_one(ch, SvdDimElement)
else:
assert ch.tag in part._fields or (regProp and ch.tag in REG_PROP_NAMES), \
("unknown child %s" % repr(ch))
return SvdDimElement(**d)
def parse_generic(xd, T: type, tag: str,
hasRegProps: bool = False, hasAttrib: bool = False,
hasDimElem: bool = False, specials: Dict[str, Any] = None,
lists: Dict[str, Any] = None):
assert xd.tag == tag, ("expected %s, got %s" % (tag, xd.tag))
d = dict((n, None) for n in T._fields)
if hasRegProps:
d['registerProperties'] = try_parse_registerProperties(xd, T, hasDimElem)
if hasDimElem:
d['dimElement'] = try_parse_dimElement(xd, T, hasRegProps)
if hasAttrib == True:
for k, v in xd.attrib.items():
if k in d:
d[k] = v
else:
assert False, ("unknown attrib %s" % repr((k, v)))
elif hasAttrib == False:
assert len(xd.attrib) == 0
for ch in xd:
if lists and ch.tag in lists:
if d[ch.tag] == None: d[ch.tag] = []
d[ch.tag].append(lists[ch.tag](ch))
elif specials and ch.tag in specials:
d[ch.tag] = specials[ch.tag](ch)
elif ch.tag in d:
d[ch.tag] = parse_one(ch, T)
else:
assert (hasRegProps and ch.tag in REG_PROP_NAMES) \
or (hasDimElem and ch.tag in DIM_ELEM_NAMES), \
("unknown child %s" % repr(ch))
return T(**d)
def parse_list_inner(f, l):
if l is None or len(l) == 0: return None
return [f(x) for x in l]
def parse_list(f):
return (lambda l: parse_list_inner(f, l))
def parse_sauRegion(xd): return parse_generic(xd, SvdSauRegion, 'region')
def parse_addressBlock(xd): return parse_generic(xd, SvdAddressBlock, 'addressBlock')
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_generic(xd, SvdWriteConstraint, 'writeConstraint')
def parse_dimArrayIndex(xd): return parse_generic(xd, SvdDimArrayIndex, 'dimArrayIndex')
def parse_sauRegionsConfig(xd):
return parse_generic(xd, SvdSauRegionsConfig, 'sauRegionsConfig', lists={
'regions': parse_sauRegion,
})
def parse_cluster(xd) -> SvdCluster:
return parse_generic(xd, SvdCluster, 'cluster', True, True, True, lists={
'register': parse_register,
'cluster': parse_cluster,
})
def parse_enumeratedValues(xd) -> SvdEnumeratedValues:
return parse_generic(xd, SvdEnumeratedValues, 'enumeratedValues', hasAttrib=True, lists={
'enumeratedValue': parse_enumeratedValue,
})
def parse_cpu(xd): return parse_generic(xd, SvdCpu, 'cpu')
def parse_field(xd): return parse_generic(xd, SvdField, 'field', False, True, True)
def parse_register(xd) -> SvdRegister:
return parse_generic(xd, SvdRegister, 'register', True, True, True, {
'writeConstraint': parse_writeConstraint,
'fields': parse_list(parse_field), #(lambda ch: [parse_field(xch) for xch in ch]) # FIXME
})
def parse_registers(xd) -> Sequence[Union[SvdCluster, SvdRegister]]:
rr = []
for ch in xd:
if ch.tag == 'cluster':
rr.append(parse_cluster(ch))
elif ch.tag == 'register':
rr.append(parse_register(ch))
else:
assert False, ("expected cluster or register, got %s"%ch)
return rr if len(rr) > 0 else None
def parse_peripheral(xd) -> SvdPeripheral:
return parse_generic(xd, SvdPeripheral, 'peripheral', True, True, False, {
'registers': parse_registers, # FIXME # UUUH what was to be fixed again?
}, {
'interrupt': parse_interrupt,
'addressBlock': parse_addressBlock
})
def parse_device(xd) -> SvdDevice:
return parse_generic(xd, SvdDevice, 'device', True, 2, False, {
'cpu': parse_cpu,
'peripherals': parse_list(parse_peripheral), #(lambda ch: [parse_peripheral(xch) for xch in ch]), # FIXME
'vendorExtensions': (lambda ch: ch)
})
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()
#print("a->b:", b - a)
#print("b->c:", c - b)
# for NXP/MIMXRT1062: XML parsing 2s, deserialization 8s, welp
# oh well
return r