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_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': (lambda ch: [parse_field(xch) for xch in ch]) }) 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 def parse_peripheral(xd) -> SvdPeripheral: return parse_generic(xd, SvdPeripheral, 'peripheral', True, True, False, { 'registers': parse_registers, # FIXME }, { 'interrupt': parse_interrupt, 'addressBlock': parse_addressBlock }) def parse_device(xd) -> SvdDevice: return parse_generic(xd, SvdDevice, 'device', True, 2, False, { 'cpu': parse_cpu, 'peripherals': (lambda ch: [parse_peripheral(xch) for xch in ch]), '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