| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- #!/usr/bin/env python
- """
- This file is part of PyCortexMDebug
- PyCortexMDebug is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- PyCortexMDebug is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with PyCortexMDebug. If not, see <http://www.gnu.org/licenses/>.
- """
- import sys
- from collections import OrderedDict
- import os
- import pickle
- import traceback
- import re
- import warnings
- import x2d
- class SmartDict:
- """
- Dictionary for search by case-insensitive lookup and/or prefix lookup
- """
- def __init__(self):
- self.od = OrderedDict()
- self.casemap = {}
- def __getitem__(self, key):
- if key in self.od:
- return self.od[key]
- if key.lower() in self.casemap:
- return self.od[self.casemap[key.lower()]]
- return self.od[self.prefix_match(key)]
- def is_ambiguous(self, key):
- return (
- key not in self.od
- and key not in self.casemap
- and len(list(self.prefix_match_iter(key))) > 1
- )
- def prefix_match_iter(self, key):
- name, number = re.match(r"^(.*?)([0-9]*)$", key.lower()).groups()
- for entry, od_key in self.casemap.items():
- if entry.startswith(name) and entry.endswith(number):
- yield od_key
- def prefix_match(self, key):
- for od_key in self.prefix_match_iter(key):
- return od_key
- return None
- def __setitem__(self, key, value):
- if key in self.od:
- warnings.warn("Duplicate entry %s", key)
- elif key.lower() in self.casemap:
- warnings.warn(
- "Entry %s differs from duplicate %s only in cAsE",
- key,
- self.casemap[key.lower()],
- )
- self.casemap[key.lower()] = key
- self.od[key] = value
- def __delitem__(self, key):
- if (
- self.casemap[key.lower()] == key
- ): # Check that we did not overwrite this entry
- del self.casemap[key.lower()]
- del self.od[key]
- def __contains__(self, key):
- return key in self.od or key.lower() in self.casemap or self.prefix_match(key)
- def __iter__(self):
- return iter(self.od)
- def __len__(self):
- return len(self.od)
- def items(self):
- return self.od.items()
- def keys(self):
- return self.od.keys()
- def values(self):
- return self.od.values()
- def __str__(self):
- return str(self.od)
- class SVDNonFatalError(Exception):
- """Exception class for non-fatal errors
- So far, these have related to quirks in some vendor SVD files which are reasonable to ignore
- """
- def __init__(self, m):
- self.m = m
- self.exc_info = sys.exc_info()
- def __str__(self):
- s = "Non-fatal: {}".format(self.m)
- s += "\n" + str("".join(traceback.format_exc())).strip()
- return s
- class SVDFile:
- """
- A parsed SVD file
- """
- def __init__(self, fname):
- """
- Args:
- fname: Filename for the SVD file
- """
- self.peripherals = SmartDict()
- self.base_address = 0
- xml_file_name = os.path.expanduser(fname)
- pickle_file_name = xml_file_name + ".pickle"
- root = None
- if os.path.exists(pickle_file_name):
- print("Loading pickled SVD")
- root = pickle.load(open(pickle_file_name, "rb"))
- else:
- print("Loading XML SVD and pickling it")
- root = x2d.parse(open(xml_file_name, "rb"))
- pickle.dump(root, open(pickle_file_name, "wb"), pickle.HIGHEST_PROTOCOL)
- print("Processing SVD tree")
- # XML elements
- for p in root["device"]["peripherals"]["peripheral"]:
- try:
- self.peripherals[p["name"]] = SVDPeripheral(p, self)
- except SVDNonFatalError as e:
- # print(e)
- pass
- print("SVD Ready")
- def add_register(parent, node):
- """
- Add a register node to a peripheral
- Args:
- parent: Parent SVDPeripheral object
- node: XML file node fot of the register
- """
- if hasattr(node, "dim"):
- dim = int(str(node.dim), 0)
- # dimension is not used, number of split indexes should be same
- incr = int(str(node.dimIncrement), 0)
- default_dim_index = ",".join((str(i) for i in range(dim)))
- dim_index = str(getattr(node, "dimIndex", default_dim_index))
- indices = dim_index.split(",")
- offset = 0
- for i in indices:
- name = str(node.name) % i
- reg = SVDPeripheralRegister(node, parent)
- reg.name = name
- reg.offset += offset
- parent.registers[name] = reg
- offset += incr
- else:
- try:
- reg = SVDPeripheralRegister(node, parent)
- name = str(node.name)
- if name not in parent.registers:
- parent.registers[name] = reg
- else:
- if hasattr(node, "alternateGroup"):
- print("Register %s has an alternate group", name)
- except SVDNonFatalError as e:
- print(e)
- def add_cluster(parent, node):
- """
- Add a register cluster to a peripheral
- """
- if hasattr(node, "dim"):
- dim = int(str(node.dim), 0)
- # dimension is not used, number of split indices should be same
- incr = int(str(node.dimIncrement), 0)
- default_dim_index = ",".join((str(i) for i in range(dim)))
- dim_index = str(getattr(node, "dimIndex", default_dim_index))
- indices = dim_index.split(",")
- offset = 0
- for i in indices:
- name = str(node.name) % i
- cluster = SVDRegisterCluster(node, parent)
- cluster.name = name
- cluster.address_offset += offset
- cluster.base_address += offset
- parent.clusters[name] = cluster
- offset += incr
- else:
- try:
- parent.clusters[str(node.name)] = SVDRegisterCluster(node, parent)
- except SVDNonFatalError as e:
- print(e)
- class SVDRegisterCluster:
- """
- Register cluster
- """
- def __init__(self, svd_elem, parent):
- """
- Args:
- svd_elem: XML element for the register cluster
- parent: Parent SVDPeripheral object
- """
- self.parent_base_address = parent.base_address
- self.parent_name = parent.name
- self.address_offset = int(str(svd_elem.addressOffset), 0)
- self.base_address = self.address_offset + self.parent_base_address
- # This doesn't inherit registers from anything
- children = svd_elem.getchildren()
- self.description = str(svd_elem.description)
- self.name = str(svd_elem.name)
- self.registers = SmartDict()
- self.clusters = SmartDict()
- for r in children:
- if r.tag == "register":
- add_register(self, r)
- def refactor_parent(self, parent):
- self.parent_base_address = parent.base_address
- self.parent_name = parent.name
- self.base_address = self.parent_base_address + self.address_offset
- values = self.registers.values()
- for r in values:
- r.refactor_parent(self)
- def __str__(self):
- return str(self.name)
- class SVDPeripheral:
- """
- This is a peripheral as defined in the SVD file
- """
- def __init__(self, svd_elem, parent):
- """
- Args:
- svd_elem: XML element for the peripheral
- parent: Parent SVDFile object
- """
- self.parent_base_address = parent.base_address
- # Look for a base address, as it is required
- if "baseAddress" not in svd_elem:
- raise SVDNonFatalError("Periph without base address")
- self.base_address = int(str(svd_elem.baseAddress), 0)
- if "@derivedFrom" in svd_elem:
- derived_from = svd_elem["@derivedFrom"]
- try:
- self.name = str(svd_elem.name)
- except AttributeError:
- self.name = parent.peripherals[derived_from].name
- try:
- self.description = str(svd_elem.description)
- except AttributeError:
- self.description = parent.peripherals[derived_from].description
- # pickle is faster than deepcopy by up to 50% on svd files with a
- # lot of derivedFrom definitions
- def copier(a):
- return pickle.loads(pickle.dumps(a))
- self.registers = copier(parent.peripherals[derived_from].registers)
- self.clusters = copier(parent.peripherals[derived_from].clusters)
- self.refactor_parent(parent)
- else:
- # This doesn't inherit registers from anything
- self.description = str(svd_elem.description)
- self.name = str(svd_elem.name)
- self.registers = SmartDict()
- self.clusters = SmartDict()
- if hasattr(svd_elem, "registers"):
- if "register" in svd_elem.registers:
- for r in svd_elem.registers.register:
- if isinstance(r, x2d.ObjectDict):
- add_register(self, r)
- if "cluster" in svd_elem.registers:
- for c in svd_elem.registers.cluster:
- if isinstance(c, x2d.ObjectDict):
- add_cluster(self, c)
- def refactor_parent(self, parent):
- self.parent_base_address = parent.base_address
- values = self.registers.values()
- for r in values:
- r.refactor_parent(self)
- for c in self.clusters.values():
- c.refactor_parent(self)
- def __str__(self):
- return str(self.name)
- class SVDPeripheralRegister:
- """
- A register within a peripheral
- """
- def __init__(self, svd_elem, parent):
- self.parent_base_address = parent.base_address
- self.name = str(svd_elem.name)
- self.description = str(svd_elem.description)
- self.offset = int(str(svd_elem.addressOffset), 0)
- if hasattr(svd_elem, "access"):
- self.access = str(svd_elem.access)
- else:
- self.access = "read-write"
- if hasattr(svd_elem, "size"):
- self.size = int(str(svd_elem.size), 0)
- else:
- self.size = 0x20
- self.fields = SmartDict()
- if "fields" in svd_elem:
- # Filter fields to only consider those of tag "field"
- for f in svd_elem.fields.field:
- if isinstance(f, x2d.ObjectDict):
- self.fields[str(f.name)] = SVDPeripheralRegisterField(f, self)
- def refactor_parent(self, parent):
- self.parent_base_address = parent.base_address
- def address(self):
- return self.parent_base_address + self.offset
- def readable(self):
- return self.access in ["read-only", "read-write", "read-writeOnce"]
- def writable(self):
- return self.access in [
- "write-only",
- "read-write",
- "writeOnce",
- "read-writeOnce",
- ]
- def __str__(self):
- return str(self.name)
- class SVDPeripheralRegisterField:
- """
- Field within a register
- """
- def __init__(self, svd_elem, parent):
- self.name = str(svd_elem.name)
- self.description = str(getattr(svd_elem, "description", ""))
- # Try to extract a bit range (offset and width) from the available fields
- if hasattr(svd_elem, "bitOffset") and hasattr(svd_elem, "bitWidth"):
- self.offset = int(str(svd_elem.bitOffset))
- self.width = int(str(svd_elem.bitWidth))
- elif hasattr(svd_elem, "bitRange"):
- bitrange = list(map(int, str(svd_elem.bitRange).strip()[1:-1].split(":")))
- self.offset = bitrange[1]
- self.width = 1 + bitrange[0] - bitrange[1]
- else:
- assert hasattr(svd_elem, "lsb") and hasattr(
- svd_elem, "msb"
- ), "Range not found for field {} in register {}".format(self.name, parent)
- lsb = int(str(svd_elem.lsb))
- msb = int(str(svd_elem.msb))
- self.offset = lsb
- self.width = 1 + msb - lsb
- self.access = str(getattr(svd_elem, "access", parent.access))
- self.enum = {}
- if hasattr(svd_elem, "enumeratedValues"):
- values = [
- v
- for v in svd_elem.enumeratedValues.getchildren()
- if v.tag == "enumeratedValue"
- ]
- for v in values:
- # Skip the "name" tag and any entries that don't have a value
- if v.tag == "name" or not hasattr(v, "value"):
- continue
- # Some Kinetis parts have values with # instead of 0x...
- value = str(v.value).replace("#", "0x")
- description = str(v.description) if hasattr(v, "description") else ""
- try:
- index = int(value, 0)
- self.enum[int(value, 0)] = (str(v.name), description)
- except ValueError:
- # If the value couldn't be converted as a single integer, skip it
- pass
- def readable(self):
- return self.access in ["read-only", "read-write", "read-writeOnce"]
- def writable(self):
- return self.access in [
- "write-only",
- "read-write",
- "writeOnce",
- "read-writeOnce",
- ]
- def __str__(self):
- return str(self.name)
- def _main():
- """
- Basic test to parse a file and do some things
- """
- for f in sys.argv[1:]:
- print("Testing file: {}".format(f))
- svd = SVDFile(f)
- print(svd.peripherals)
- key = list(svd.peripherals)[0]
- print("Registers in peripheral '{}':".format(key))
- print(svd.peripherals[key].registers)
- print("Done testing file: {}".format(f))
- if __name__ == "__main__":
- _main()
|