Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 166 additions & 105 deletions svdmmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,58 @@
Copyright 2019 Adam Greig.
Licensed under the MIT and Apache 2.0 licenses. See LICENSE files for details.

Since hacked up a bit by Mitchell Johnson, 2019
Find the original here:
https://github.com/stm32-rs/stm32-rs/blob/master/scripts/svdmmap.py
Updated using the latest mmap.py, see https://raw.githubusercontent.com/stm32-rs/svdtools/master/svdtools/mmap.py
"""

import sys
import copy
import xml.etree.ElementTree as ET
import lxml.etree as ET


def get_field_offset_width(ftag):
"""
Return the offset and width of a field, parsing either bitOffset+bitWidth,
or a bitRange tag, or lsb and msb tags.
"""
if ftag.findtext("bitOffset") is not None:
offset = int(ftag.findtext("bitOffset"), 0)
width = int(ftag.findtext("bitWidth"), 0)
elif ftag.findtext("bitRange") is not None:
msb, lsb = ftag.findtext("bitRange")[1:-1].split(":")
offset = int(lsb, 0)
width = int(msb, 0) - offset + 1
elif ftag.findtext("lsb") is not None:
lsb = int(ftag.findtext("lsb"), 0)
msb = int(ftag.findtext("msb"), 0)
offset = lsb
fwidth = msb - lsb + 1
return offset, width


def iter_clusters(ptag):
registers = ptag.find('registers')
registers = ptag.find("registers")
if registers is None:
return []
else:
return registers.findall('cluster')
return registers.findall("cluster")


def iter_registers(ptag):
registers = ptag.find('registers')
registers = ptag.find("registers")
if registers is None:
return []
else:
return registers.findall('register')
return registers.findall("register")


def iter_fields(rtag):
fields = rtag.find('fields')
fields = rtag.find("fields")
if fields is None:
return []
else:
return fields.findall('field')
return fields.findall("field")


ACCESS = {
"read-only": "ro",
"read-write": "rw",
"write-only": "wo",
}
ACCESS = {"read-only": "ro", "read-write": "rw", "write-only": "wo"}


def get_access(tag):
Expand All @@ -51,7 +64,7 @@ def get_access(tag):
If possible it is shortened to ro/rw/wo, and then
returned inside brackets with a leading space.
"""
access = get_string(tag, 'access')
access = get_string(tag, "access")
if access is not None:
return " (" + ACCESS.get(access, access) + ")"
else:
Expand Down Expand Up @@ -82,36 +95,51 @@ def get_int(node, tag, default=None):
return int(text, 10)


def expand_dim(node):
def derived_str(dname):
if dname is None:
return ""
else:
return f" (={dname})"


def expand_dim(node, field=False):
"""
Given a node (a cluster or a register) which may have a `dim` child,
Given a node (a cluster, a register or a field) which may have a `dim` child,
returns an expanded list of all such nodes with '%s' in the name replaced
by the appropriate index. If there is no `dim` child, a list containing
just the original node is returned.
"""
dim = node.findtext('dim')
dim = node.findtext("dim")
if dim is None:
return [node]
inc = get_int(node, 'dimIncrement')
idxs = get_string(node, 'dimIndex')
inc = get_int(node, "dimIncrement")
idxs = get_string(node, "dimIndex")
if idxs is None:
idxs = list(range(dim))
idxs = list(range(int(dim, 0)))
else:
if "," in idxs:
if len(idxs) == 1:
pass
elif "," in idxs:
idxs = idxs.split(",")
elif "-" in idxs:
li, ri = idxs.split("-")
idxs = list(range(int(li), int(ri)+1))
idxs = list(range(int(li), int(ri) + 1))
else:
raise ValueError("Unknown dimIndex: '{idxs}'".format(**locals()))
raise ValueError(f"Unknown dimIndex: '{idxs}'")
nodes = []
for cnt, idx in enumerate(idxs):
name = get_string(node, 'name').replace("%s", str(idx))
name = get_string(node, "name").replace("%s", str(idx))
# description = get_string(node, "description").replace("%s", str(idx))
dim_node = copy.deepcopy(node)
dim_node.find('name').text = name
addr = get_int(dim_node, 'addressOffset') + cnt * inc
dim_node.find('addressOffset').text = "0x{addr:08x}".format(**locals())
dim_node.attrib['dim_index'] = idx
dim_node.find("name").text = name
# dim_node.find("description").text = description
if field:
offset = get_int(dim_node, "bitOffset") + cnt * inc
dim_node.find("bitOffset").text = f"{offset}"
else:
addr = get_int(dim_node, "addressOffset") + cnt * inc
dim_node.find("addressOffset").text = f"0x{addr:08x}"
dim_node.attrib["dim_index"] = str(idx)
nodes.append(dim_node)
return nodes

Expand All @@ -123,17 +151,17 @@ def expand_cluster(node):
offsets updated to include the cluster address offset.
The returned register nodes are as though they were never in a cluster.
"""
cluster_name = get_string(node, 'name')
if node.attrib.get('dim_index') is not None:
cluster_name = cluster_name.replace('[%s]', node.attrib['dim_index'])
cluster_addr = get_int(node, 'addressOffset')
if node.attrib.get("dim_index") is None:
raise ValueError("Can't process cluster without dim_index")
cluster_idx = node.attrib["dim_index"]
cluster_addr = get_int(node, "addressOffset")
nodes = []
for rtag in node.findall('register'):
addr = cluster_addr + get_int(rtag, 'addressOffset')
name = str(cluster_name) + get_string(rtag, 'name')
for rtag in node.findall("register"):
addr = cluster_addr + get_int(rtag, "addressOffset")
name = get_string(rtag, "name") + str(cluster_idx)
new_rtag = copy.deepcopy(rtag)
new_rtag.find('addressOffset').text = "0x{addr:08x}".format(**locals())
new_rtag.find('name').text = name
new_rtag.find("addressOffset").text = f"0x{addr:08x}"
new_rtag.find("name").text = name
nodes.append(new_rtag)
return nodes

Expand All @@ -143,22 +171,36 @@ def parse_register(rtag):
Extract register and field information from a register node into a dict.
"""
fields = {}
rname = get_string(rtag, 'name')
rdesc = get_string(rtag, 'description')
rname = get_string(rtag, "name")
rdesc = get_string(rtag, "description")
raccess = get_access(rtag)
roffset = get_int(rtag, 'addressOffset')
roffset = get_int(rtag, "addressOffset")
r_derived = derived_str(rtag.attrib.get("derivedFrom", None))
rsize = get_int(rtag, 'size')
for ftag in iter_fields(rtag):
fname = get_string(ftag, 'name')
foffset = get_int(ftag, 'bitOffset')
fwidth = get_int(ftag, 'bitWidth')
fdesc = get_string(ftag, 'description')
faccess = get_access(ftag)
fields[fname] = {"name": fname, "offset": foffset,
"width": fwidth, "description": fdesc,
"access": faccess}
return {"name": rname, "offset": roffset, "description": rdesc,
"access": raccess, "fields": fields, "size": rsize}
for ftag in expand_dim(ftag, field=True):
fname = get_string(ftag, "name")
foffset, fwidth = get_field_offset_width(ftag)
fdesc = get_string(ftag, "description")
faccess = get_access(ftag)
f_derived = derived_str(ftag.attrib.get("derivedFrom", None))
fields[fname] = {
"name": fname,
"offset": foffset,
"width": fwidth,
"description": fdesc,
"access": faccess,
"derived": f_derived,
}
return {
"name": rname,
"offset": roffset,
"description": rdesc,
"access": raccess,
"fields": fields,
"derived": r_derived,
"size": rsize,
}


def parse(svdfile):
Expand All @@ -168,50 +210,65 @@ def parse(svdfile):
tree = ET.parse(svdfile)
peripherals = {}
device_interrupts = {}
for ptag in tree.find('peripherals').findall('peripheral'):
for ptag in tree.find("peripherals").findall("peripheral"):
interrupts = {}
registers = {}
clusters = {}
pname = get_string(ptag, 'name')
pbase = get_int(ptag, 'baseAddress')
pname = get_string(ptag, "name")
pbase = get_int(ptag, "baseAddress")
address_block = ptag.find('addressBlock')
psize = 0
if address_block is not None:
psize = get_int(address_block, 'size')
for itag in ptag.findall('interrupt'):
iname = get_string(itag, 'name')
idesc = get_string(itag, 'description')
ival = get_int(itag, 'value')
interrupt = {"name": iname, "description": idesc, "value": ival,
"pname": pname}
for itag in ptag.findall("interrupt"):
iname = get_string(itag, "name")
idesc = get_string(itag, "description")
ival = get_int(itag, "value")
interrupt = {
"name": iname,
"description": idesc,
"value": ival,
"pname": pname,
}
interrupts[iname] = device_interrupts[ival] = interrupt
for ctag in iter_clusters(ptag):
for ctag in expand_dim(ctag):
cname = get_string(ctag, 'name')
cdesc = get_string(ctag, 'description')
coff = get_int(ctag, 'addressOffset')
cname = get_string(ctag, "name")
cdesc = get_string(ctag, "description")
coff = get_int(ctag, "addressOffset")
c_derived = derived_str(ctag.attrib.get("derivedFrom", None))
for rtag in expand_cluster(ctag):
register = parse_register(rtag)
registers[register['name']] = register
clusters[cname] = {"name": cname, "description": cdesc,
"offset": coff}
registers[register["name"]] = register
clusters[cname] = {
"name": cname,
"description": cdesc,
"offset": coff,
"derived": c_derived,
}
for rtag in iter_registers(ptag):
for rtag in expand_dim(rtag):
register = parse_register(rtag)
registers[register['name']] = register
peripherals[pname] = {"name": pname, "base": pbase, "size": psize,
"interrupts": interrupts, "registers": registers,
"clusters": clusters}
if 'derivedFrom' in ptag.attrib:
registers[register["name"]] = register
peripherals[pname] = {
"name": pname,
"base": pbase,
"interrupts": interrupts,
"registers": registers,
"clusters": clusters,
"size": psize,
}
if "derivedFrom" in ptag.attrib:
peripherals[pname]["derives"] = ptag.attrib["derivedFrom"]
for pname, periph in list(peripherals.items()):
if 'derives' in periph:
peripherals[pname]['registers'] = \
peripherals[periph['derives']]['registers']
peripherals[pname]['size'] = \
peripherals[periph['derives']]['size']
return {"name": svdfile.split(b".")[0], "peripherals": peripherals,
"interrupts": device_interrupts}
if "derives" in periph:
peripherals[pname]["registers"] = peripherals[periph["derives"]]["registers"]
peripherals[pname]['size'] = peripherals[periph['derives']]['size']
return {
"name": svdfile.split(b".")[0],
"peripherals": peripherals,
"interrupts": device_interrupts,
}


def to_text(device):
Expand All @@ -220,31 +277,35 @@ def to_text(device):
in the device, such that automated diffing is possible.
"""
mmap = []
for i in device['interrupts'].values():
mmap.append("INTERRUPT {i['value']:03d}: ".format(**locals())
+ "{i['name']} ({i['pname']}): {i['description']}".format(**locals()))
for p in device['peripherals'].values():
mmap.append("0x{p['base']:08X} A PERIPHERAL {p['name']}".format(**locals()))
for c in p['clusters'].values():
addr = p['base'] + c['offset']
mmap.append("0x{addr:08X} B CLUSTER {c['name']}: ".format(**locals())
+ "{c['description']}".format(**locals()))
for r in p['registers'].values():
addr = p['base'] + r['offset']
mmap.append("0x{addr:08X} B REGISTER {r['name']}{r['access']}: ".format(**locals())
+ "{r['description']}".format(**locals()))
for f in r['fields'].values():
offset, width = f['offset'], f['width']
mmap.append("0x{addr:08X} C FIELD {offset:02d}w{width:02d} ".format(**locals())
+ "{f['name']}{f['access']}: ".format(**locals())
+ "{f['description']}".format(**locals()))
for i in device["interrupts"].values():
mmap.append(
f"INTERRUPT {i['value']:03d}: "
+ f"{i['name']} ({i['pname']}): {i['description']}"
)
for p in device["peripherals"].values():
mmap.append(f"0x{p['base']:08X} A PERIPHERAL {p['name']}")
for c in p["clusters"].values():
addr = p["base"] + c["offset"]
mmap.append(
f"0x{addr:08X} B CLUSTER {c['name']}{c['derived']}: "
+ f"{c['description']}"
)
for r in p["registers"].values():
addr = p["base"] + r["offset"]
mmap.append(
f"0x{addr:08X} B REGISTER {r['name']}{r['derived']}{r['access']}: "
+ f"{r['description']}"
)
for f in r["fields"].values():
offset, width = f["offset"], f["width"]
mmap.append(
f"0x{addr:08X} C FIELD {offset:02d}w{width:02d} "
+ f"{f['name']}{f['derived']}{f['access']}: "
+ f"{f['description']}"
)
return "\n".join(sorted(mmap))


def main():
device = parse(sys.argv[1])
print(to_text(device))


if __name__ == "__main__":
main()
def main(svd_file):
device = parse(svd_file)
return to_text(device)