svd.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. #!/usr/bin/env python
  2. """
  3. This file is part of PyCortexMDebug
  4. PyCortexMDebug is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. PyCortexMDebug is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with PyCortexMDebug. If not, see <http://www.gnu.org/licenses/>.
  14. """
  15. import sys
  16. from collections import OrderedDict
  17. import os
  18. import pickle
  19. import traceback
  20. import re
  21. import warnings
  22. import x2d
  23. class SmartDict:
  24. """
  25. Dictionary for search by case-insensitive lookup and/or prefix lookup
  26. """
  27. def __init__(self):
  28. self.od = OrderedDict()
  29. self.casemap = {}
  30. def __getitem__(self, key):
  31. if key in self.od:
  32. return self.od[key]
  33. if key.lower() in self.casemap:
  34. return self.od[self.casemap[key.lower()]]
  35. return self.od[self.prefix_match(key)]
  36. def is_ambiguous(self, key):
  37. return (
  38. key not in self.od
  39. and key not in self.casemap
  40. and len(list(self.prefix_match_iter(key))) > 1
  41. )
  42. def prefix_match_iter(self, key):
  43. name, number = re.match(r"^(.*?)([0-9]*)$", key.lower()).groups()
  44. for entry, od_key in self.casemap.items():
  45. if entry.startswith(name) and entry.endswith(number):
  46. yield od_key
  47. def prefix_match(self, key):
  48. for od_key in self.prefix_match_iter(key):
  49. return od_key
  50. return None
  51. def __setitem__(self, key, value):
  52. if key in self.od:
  53. warnings.warn("Duplicate entry %s", key)
  54. elif key.lower() in self.casemap:
  55. warnings.warn(
  56. "Entry %s differs from duplicate %s only in cAsE",
  57. key,
  58. self.casemap[key.lower()],
  59. )
  60. self.casemap[key.lower()] = key
  61. self.od[key] = value
  62. def __delitem__(self, key):
  63. if (
  64. self.casemap[key.lower()] == key
  65. ): # Check that we did not overwrite this entry
  66. del self.casemap[key.lower()]
  67. del self.od[key]
  68. def __contains__(self, key):
  69. return key in self.od or key.lower() in self.casemap or self.prefix_match(key)
  70. def __iter__(self):
  71. return iter(self.od)
  72. def __len__(self):
  73. return len(self.od)
  74. def items(self):
  75. return self.od.items()
  76. def keys(self):
  77. return self.od.keys()
  78. def values(self):
  79. return self.od.values()
  80. def __str__(self):
  81. return str(self.od)
  82. class SVDNonFatalError(Exception):
  83. """Exception class for non-fatal errors
  84. So far, these have related to quirks in some vendor SVD files which are reasonable to ignore
  85. """
  86. def __init__(self, m):
  87. self.m = m
  88. self.exc_info = sys.exc_info()
  89. def __str__(self):
  90. s = "Non-fatal: {}".format(self.m)
  91. s += "\n" + str("".join(traceback.format_exc())).strip()
  92. return s
  93. class SVDFile:
  94. """
  95. A parsed SVD file
  96. """
  97. def __init__(self, fname):
  98. """
  99. Args:
  100. fname: Filename for the SVD file
  101. """
  102. self.peripherals = SmartDict()
  103. self.base_address = 0
  104. xml_file_name = os.path.expanduser(fname)
  105. pickle_file_name = xml_file_name + ".pickle"
  106. root = None
  107. if os.path.exists(pickle_file_name):
  108. print("Loading pickled SVD")
  109. root = pickle.load(open(pickle_file_name, "rb"))
  110. else:
  111. print("Loading XML SVD and pickling it")
  112. root = x2d.parse(open(xml_file_name, "rb"))
  113. pickle.dump(root, open(pickle_file_name, "wb"), pickle.HIGHEST_PROTOCOL)
  114. print("Processing SVD tree")
  115. # XML elements
  116. for p in root["device"]["peripherals"]["peripheral"]:
  117. try:
  118. self.peripherals[p["name"]] = SVDPeripheral(p, self)
  119. except SVDNonFatalError as e:
  120. # print(e)
  121. pass
  122. print("SVD Ready")
  123. def add_register(parent, node):
  124. """
  125. Add a register node to a peripheral
  126. Args:
  127. parent: Parent SVDPeripheral object
  128. node: XML file node fot of the register
  129. """
  130. if hasattr(node, "dim"):
  131. dim = int(str(node.dim), 0)
  132. # dimension is not used, number of split indexes should be same
  133. incr = int(str(node.dimIncrement), 0)
  134. default_dim_index = ",".join((str(i) for i in range(dim)))
  135. dim_index = str(getattr(node, "dimIndex", default_dim_index))
  136. indices = dim_index.split(",")
  137. offset = 0
  138. for i in indices:
  139. name = str(node.name) % i
  140. reg = SVDPeripheralRegister(node, parent)
  141. reg.name = name
  142. reg.offset += offset
  143. parent.registers[name] = reg
  144. offset += incr
  145. else:
  146. try:
  147. reg = SVDPeripheralRegister(node, parent)
  148. name = str(node.name)
  149. if name not in parent.registers:
  150. parent.registers[name] = reg
  151. else:
  152. if hasattr(node, "alternateGroup"):
  153. print("Register %s has an alternate group", name)
  154. except SVDNonFatalError as e:
  155. print(e)
  156. def add_cluster(parent, node):
  157. """
  158. Add a register cluster to a peripheral
  159. """
  160. if hasattr(node, "dim"):
  161. dim = int(str(node.dim), 0)
  162. # dimension is not used, number of split indices should be same
  163. incr = int(str(node.dimIncrement), 0)
  164. default_dim_index = ",".join((str(i) for i in range(dim)))
  165. dim_index = str(getattr(node, "dimIndex", default_dim_index))
  166. indices = dim_index.split(",")
  167. offset = 0
  168. for i in indices:
  169. name = str(node.name) % i
  170. cluster = SVDRegisterCluster(node, parent)
  171. cluster.name = name
  172. cluster.address_offset += offset
  173. cluster.base_address += offset
  174. parent.clusters[name] = cluster
  175. offset += incr
  176. else:
  177. try:
  178. parent.clusters[str(node.name)] = SVDRegisterCluster(node, parent)
  179. except SVDNonFatalError as e:
  180. print(e)
  181. class SVDRegisterCluster:
  182. """
  183. Register cluster
  184. """
  185. def __init__(self, svd_elem, parent):
  186. """
  187. Args:
  188. svd_elem: XML element for the register cluster
  189. parent: Parent SVDPeripheral object
  190. """
  191. self.parent_base_address = parent.base_address
  192. self.parent_name = parent.name
  193. self.address_offset = int(str(svd_elem.addressOffset), 0)
  194. self.base_address = self.address_offset + self.parent_base_address
  195. # This doesn't inherit registers from anything
  196. children = svd_elem.getchildren()
  197. self.description = str(svd_elem.description)
  198. self.name = str(svd_elem.name)
  199. self.registers = SmartDict()
  200. self.clusters = SmartDict()
  201. for r in children:
  202. if r.tag == "register":
  203. add_register(self, r)
  204. def refactor_parent(self, parent):
  205. self.parent_base_address = parent.base_address
  206. self.parent_name = parent.name
  207. self.base_address = self.parent_base_address + self.address_offset
  208. values = self.registers.values()
  209. for r in values:
  210. r.refactor_parent(self)
  211. def __str__(self):
  212. return str(self.name)
  213. class SVDPeripheral:
  214. """
  215. This is a peripheral as defined in the SVD file
  216. """
  217. def __init__(self, svd_elem, parent):
  218. """
  219. Args:
  220. svd_elem: XML element for the peripheral
  221. parent: Parent SVDFile object
  222. """
  223. self.parent_base_address = parent.base_address
  224. # Look for a base address, as it is required
  225. if "baseAddress" not in svd_elem:
  226. raise SVDNonFatalError("Periph without base address")
  227. self.base_address = int(str(svd_elem.baseAddress), 0)
  228. if "@derivedFrom" in svd_elem:
  229. derived_from = svd_elem["@derivedFrom"]
  230. try:
  231. self.name = str(svd_elem.name)
  232. except AttributeError:
  233. self.name = parent.peripherals[derived_from].name
  234. try:
  235. self.description = str(svd_elem.description)
  236. except AttributeError:
  237. self.description = parent.peripherals[derived_from].description
  238. # pickle is faster than deepcopy by up to 50% on svd files with a
  239. # lot of derivedFrom definitions
  240. def copier(a):
  241. return pickle.loads(pickle.dumps(a))
  242. self.registers = copier(parent.peripherals[derived_from].registers)
  243. self.clusters = copier(parent.peripherals[derived_from].clusters)
  244. self.refactor_parent(parent)
  245. else:
  246. # This doesn't inherit registers from anything
  247. self.description = str(svd_elem.description)
  248. self.name = str(svd_elem.name)
  249. self.registers = SmartDict()
  250. self.clusters = SmartDict()
  251. if hasattr(svd_elem, "registers"):
  252. if "register" in svd_elem.registers:
  253. for r in svd_elem.registers.register:
  254. if isinstance(r, x2d.ObjectDict):
  255. add_register(self, r)
  256. if "cluster" in svd_elem.registers:
  257. for c in svd_elem.registers.cluster:
  258. if isinstance(c, x2d.ObjectDict):
  259. add_cluster(self, c)
  260. def refactor_parent(self, parent):
  261. self.parent_base_address = parent.base_address
  262. values = self.registers.values()
  263. for r in values:
  264. r.refactor_parent(self)
  265. for c in self.clusters.values():
  266. c.refactor_parent(self)
  267. def __str__(self):
  268. return str(self.name)
  269. class SVDPeripheralRegister:
  270. """
  271. A register within a peripheral
  272. """
  273. def __init__(self, svd_elem, parent):
  274. self.parent_base_address = parent.base_address
  275. self.name = str(svd_elem.name)
  276. self.description = str(svd_elem.description)
  277. self.offset = int(str(svd_elem.addressOffset), 0)
  278. if hasattr(svd_elem, "access"):
  279. self.access = str(svd_elem.access)
  280. else:
  281. self.access = "read-write"
  282. if hasattr(svd_elem, "size"):
  283. self.size = int(str(svd_elem.size), 0)
  284. else:
  285. self.size = 0x20
  286. self.fields = SmartDict()
  287. if "fields" in svd_elem:
  288. # Filter fields to only consider those of tag "field"
  289. for f in svd_elem.fields.field:
  290. if isinstance(f, x2d.ObjectDict):
  291. self.fields[str(f.name)] = SVDPeripheralRegisterField(f, self)
  292. def refactor_parent(self, parent):
  293. self.parent_base_address = parent.base_address
  294. def address(self):
  295. return self.parent_base_address + self.offset
  296. def readable(self):
  297. return self.access in ["read-only", "read-write", "read-writeOnce"]
  298. def writable(self):
  299. return self.access in [
  300. "write-only",
  301. "read-write",
  302. "writeOnce",
  303. "read-writeOnce",
  304. ]
  305. def __str__(self):
  306. return str(self.name)
  307. class SVDPeripheralRegisterField:
  308. """
  309. Field within a register
  310. """
  311. def __init__(self, svd_elem, parent):
  312. self.name = str(svd_elem.name)
  313. self.description = str(getattr(svd_elem, "description", ""))
  314. # Try to extract a bit range (offset and width) from the available fields
  315. if hasattr(svd_elem, "bitOffset") and hasattr(svd_elem, "bitWidth"):
  316. self.offset = int(str(svd_elem.bitOffset))
  317. self.width = int(str(svd_elem.bitWidth))
  318. elif hasattr(svd_elem, "bitRange"):
  319. bitrange = list(map(int, str(svd_elem.bitRange).strip()[1:-1].split(":")))
  320. self.offset = bitrange[1]
  321. self.width = 1 + bitrange[0] - bitrange[1]
  322. else:
  323. assert hasattr(svd_elem, "lsb") and hasattr(
  324. svd_elem, "msb"
  325. ), "Range not found for field {} in register {}".format(self.name, parent)
  326. lsb = int(str(svd_elem.lsb))
  327. msb = int(str(svd_elem.msb))
  328. self.offset = lsb
  329. self.width = 1 + msb - lsb
  330. self.access = str(getattr(svd_elem, "access", parent.access))
  331. self.enum = {}
  332. if hasattr(svd_elem, "enumeratedValues"):
  333. values = [
  334. v
  335. for v in svd_elem.enumeratedValues.getchildren()
  336. if v.tag == "enumeratedValue"
  337. ]
  338. for v in values:
  339. # Skip the "name" tag and any entries that don't have a value
  340. if v.tag == "name" or not hasattr(v, "value"):
  341. continue
  342. # Some Kinetis parts have values with # instead of 0x...
  343. value = str(v.value).replace("#", "0x")
  344. description = str(v.description) if hasattr(v, "description") else ""
  345. try:
  346. index = int(value, 0)
  347. self.enum[int(value, 0)] = (str(v.name), description)
  348. except ValueError:
  349. # If the value couldn't be converted as a single integer, skip it
  350. pass
  351. def readable(self):
  352. return self.access in ["read-only", "read-write", "read-writeOnce"]
  353. def writable(self):
  354. return self.access in [
  355. "write-only",
  356. "read-write",
  357. "writeOnce",
  358. "read-writeOnce",
  359. ]
  360. def __str__(self):
  361. return str(self.name)
  362. def _main():
  363. """
  364. Basic test to parse a file and do some things
  365. """
  366. for f in sys.argv[1:]:
  367. print("Testing file: {}".format(f))
  368. svd = SVDFile(f)
  369. print(svd.peripherals)
  370. key = list(svd.peripherals)[0]
  371. print("Registers in peripheral '{}':".format(key))
  372. print(svd.peripherals[key].registers)
  373. print("Done testing file: {}".format(f))
  374. if __name__ == "__main__":
  375. _main()