modm_data.svd

 1# Copyright 2022, Niklas Hauser
 2# SPDX-License-Identifier: MPL-2.0
 3
 4from .model import Device, PeripheralType, Peripheral, Register, BitField, EnumeratedValue, compare_device_trees
 5from .write import format_svd, write_svd
 6from .read import read_svd
 7
 8__all__ = [
 9    "stmicro",
10    "Device",
11    "PeripheralType",
12    "Peripheral",
13    "Register",
14    "BitField",
15    "EnumeratedValue",
16    "compare_device_trees",
17    "format_svd",
18    "write_svd",
19    "read_svd",
20]
class Device(anytree.node.node.Node):
 8class Device(Node):
 9    def __init__(self, name, **kw):
10        if "compatible" not in kw:
11            kw["compatible"] = [name]
12        super().__init__(name, **kw)
13
14    def __hash__(self) -> int:
15        value = 0
16        for peripheral in self.children:
17            value += hash(peripheral)
18            for register in peripheral.children:
19                value += hash(register)
20                for bitfield in register.children:
21                    value += hash(bitfield)
22        return value
23
24    def __eq__(self, other) -> bool:
25        if isinstance(other, self.__class__):
26            return compare_device_trees(self, other)
27        else:
28            return NotImplemented

A simple tree node with a name and any kwargs.

Args: name: A name or any other object this node can reference to as identifier.

Keyword Args: parent: Reference to parent node. children: Iterable with child nodes. *: Any other given attribute is just stored as object attribute.

Other than :any:AnyNode this class has at least the name attribute, to distinguish between different instances.

The parent attribute refers the parent node:

>>> from anytree import Node, RenderTree
>>> root = Node("root")
>>> s0 = Node("sub0", parent=root)
>>> s0b = Node("sub0B", parent=s0, foo=4, bar=109)
>>> s0a = Node("sub0A", parent=s0)
>>> s1 = Node("sub1", parent=root)
>>> s1a = Node("sub1A", parent=s1)
>>> s1b = Node("sub1B", parent=s1, bar=8)
>>> s1c = Node("sub1C", parent=s1)
>>> s1ca = Node("sub1Ca", parent=s1c)
>>> print(RenderTree(root))
Node('/root')
├── Node('/root/sub0')
│   ├── Node('/root/sub0/sub0B', bar=109, foo=4)
│   └── Node('/root/sub0/sub0A')
└── Node('/root/sub1')
    ├── Node('/root/sub1/sub1A')
    ├── Node('/root/sub1/sub1B', bar=8)
    └── Node('/root/sub1/sub1C')
        └── Node('/root/sub1/sub1C/sub1Ca')

The same tree can be constructed by using the children attribute:

>>> root = Node("root", children=[
...     Node("sub0", children=[
...         Node("sub0B", bar=109, foo=4),
...         Node("sub0A", children=None),
...     ]),
...     Node("sub1", children=[
...         Node("sub1A"),
...         Node("sub1B", bar=8, children=[]),
...         Node("sub1C", children=[
...             Node("sub1Ca"),
...         ]),
...     ]),
... ])
>>> print(RenderTree(root))
Node('/root')
├── Node('/root/sub0')
│   ├── Node('/root/sub0/sub0B', bar=109, foo=4)
│   └── Node('/root/sub0/sub0A')
└── Node('/root/sub1')
    ├── Node('/root/sub1/sub1A')
    ├── Node('/root/sub1/sub1B', bar=8)
    └── Node('/root/sub1/sub1C')
        └── Node('/root/sub1/sub1C/sub1Ca')
Device(name, **kw)
 9    def __init__(self, name, **kw):
10        if "compatible" not in kw:
11            kw["compatible"] = [name]
12        super().__init__(name, **kw)
Inherited Members
anytree.node.node.Node
name
parent
anytree.node.nodemixin.NodeMixin
separator
children
path
iter_path_reverse
ancestors
anchestors
descendants
root
siblings
leaves
is_leaf
is_root
height
depth
size
class PeripheralType(anytree.node.node.Node):
31class PeripheralType(Node):
32    def __init__(self, name, **kw):
33        super().__init__(name, **kw)
34
35    def __hash__(self) -> int:
36        value = hash(self.name)
37        if hasattr(self, "filters") and self.filters:
38            value += hash(str(self.filters))
39        return value
40
41    def __eq__(self, other) -> bool:
42        if isinstance(other, self.__class__):
43            return self.name == other.name and self.filters == other.filters
44        else:
45            return NotImplemented

A simple tree node with a name and any kwargs.

Args: name: A name or any other object this node can reference to as identifier.

Keyword Args: parent: Reference to parent node. children: Iterable with child nodes. *: Any other given attribute is just stored as object attribute.

Other than :any:AnyNode this class has at least the name attribute, to distinguish between different instances.

The parent attribute refers the parent node:

>>> from anytree import Node, RenderTree
>>> root = Node("root")
>>> s0 = Node("sub0", parent=root)
>>> s0b = Node("sub0B", parent=s0, foo=4, bar=109)
>>> s0a = Node("sub0A", parent=s0)
>>> s1 = Node("sub1", parent=root)
>>> s1a = Node("sub1A", parent=s1)
>>> s1b = Node("sub1B", parent=s1, bar=8)
>>> s1c = Node("sub1C", parent=s1)
>>> s1ca = Node("sub1Ca", parent=s1c)
>>> print(RenderTree(root))
Node('/root')
├── Node('/root/sub0')
│   ├── Node('/root/sub0/sub0B', bar=109, foo=4)
│   └── Node('/root/sub0/sub0A')
└── Node('/root/sub1')
    ├── Node('/root/sub1/sub1A')
    ├── Node('/root/sub1/sub1B', bar=8)
    └── Node('/root/sub1/sub1C')
        └── Node('/root/sub1/sub1C/sub1Ca')

The same tree can be constructed by using the children attribute:

>>> root = Node("root", children=[
...     Node("sub0", children=[
...         Node("sub0B", bar=109, foo=4),
...         Node("sub0A", children=None),
...     ]),
...     Node("sub1", children=[
...         Node("sub1A"),
...         Node("sub1B", bar=8, children=[]),
...         Node("sub1C", children=[
...             Node("sub1Ca"),
...         ]),
...     ]),
... ])
>>> print(RenderTree(root))
Node('/root')
├── Node('/root/sub0')
│   ├── Node('/root/sub0/sub0B', bar=109, foo=4)
│   └── Node('/root/sub0/sub0A')
└── Node('/root/sub1')
    ├── Node('/root/sub1/sub1A')
    ├── Node('/root/sub1/sub1B', bar=8)
    └── Node('/root/sub1/sub1C')
        └── Node('/root/sub1/sub1C/sub1Ca')
PeripheralType(name, **kw)
32    def __init__(self, name, **kw):
33        super().__init__(name, **kw)
Inherited Members
anytree.node.node.Node
name
parent
anytree.node.nodemixin.NodeMixin
separator
children
path
iter_path_reverse
ancestors
anchestors
descendants
root
siblings
leaves
is_leaf
is_root
height
depth
size
class Peripheral(anytree.node.node.Node):
48class Peripheral(Node):
49    def __init__(self, name, type, address, **kw):
50        super().__init__(name, type=type, address=address, **kw)
51
52    def __hash__(self) -> int:
53        value = hash(f"{self.name} {self.address} {self.type}")
54        if hasattr(self, "filters") and self.filters:
55            value += hash(str(self.filters))
56        return value
57
58    def __eq__(self, other) -> bool:
59        if isinstance(other, self.__class__):
60            return self.name == other.name and self.address == other.address
61        else:
62            return NotImplemented

A simple tree node with a name and any kwargs.

Args: name: A name or any other object this node can reference to as identifier.

Keyword Args: parent: Reference to parent node. children: Iterable with child nodes. *: Any other given attribute is just stored as object attribute.

Other than :any:AnyNode this class has at least the name attribute, to distinguish between different instances.

The parent attribute refers the parent node:

>>> from anytree import Node, RenderTree
>>> root = Node("root")
>>> s0 = Node("sub0", parent=root)
>>> s0b = Node("sub0B", parent=s0, foo=4, bar=109)
>>> s0a = Node("sub0A", parent=s0)
>>> s1 = Node("sub1", parent=root)
>>> s1a = Node("sub1A", parent=s1)
>>> s1b = Node("sub1B", parent=s1, bar=8)
>>> s1c = Node("sub1C", parent=s1)
>>> s1ca = Node("sub1Ca", parent=s1c)
>>> print(RenderTree(root))
Node('/root')
├── Node('/root/sub0')
│   ├── Node('/root/sub0/sub0B', bar=109, foo=4)
│   └── Node('/root/sub0/sub0A')
└── Node('/root/sub1')
    ├── Node('/root/sub1/sub1A')
    ├── Node('/root/sub1/sub1B', bar=8)
    └── Node('/root/sub1/sub1C')
        └── Node('/root/sub1/sub1C/sub1Ca')

The same tree can be constructed by using the children attribute:

>>> root = Node("root", children=[
...     Node("sub0", children=[
...         Node("sub0B", bar=109, foo=4),
...         Node("sub0A", children=None),
...     ]),
...     Node("sub1", children=[
...         Node("sub1A"),
...         Node("sub1B", bar=8, children=[]),
...         Node("sub1C", children=[
...             Node("sub1Ca"),
...         ]),
...     ]),
... ])
>>> print(RenderTree(root))
Node('/root')
├── Node('/root/sub0')
│   ├── Node('/root/sub0/sub0B', bar=109, foo=4)
│   └── Node('/root/sub0/sub0A')
└── Node('/root/sub1')
    ├── Node('/root/sub1/sub1A')
    ├── Node('/root/sub1/sub1B', bar=8)
    └── Node('/root/sub1/sub1C')
        └── Node('/root/sub1/sub1C/sub1Ca')
Peripheral(name, type, address, **kw)
49    def __init__(self, name, type, address, **kw):
50        super().__init__(name, type=type, address=address, **kw)
Inherited Members
anytree.node.node.Node
name
parent
anytree.node.nodemixin.NodeMixin
separator
children
path
iter_path_reverse
ancestors
anchestors
descendants
root
siblings
leaves
is_leaf
is_root
height
depth
size
class Register(anytree.node.node.Node):
65class Register(Node):
66    def __init__(self, name, offset, width=None, **kw):
67        super().__init__(name, offset=offset, width=width or 4, **kw)
68
69    @property
70    def address(self) -> int:
71        return self.parent.address + self.offset
72
73    @property
74    def addresses(self) -> int:
75        for ii in range(self.width):
76            yield self.parent.address + self.offset + ii
77
78    def __hash__(self) -> int:
79        value = hash(f"{self.name} {self.offset} {self.width}")
80        if hasattr(self, "filters") and self.filters:
81            value += hash(str(self.filters))
82        return value
83
84    def __eq__(self, other) -> bool:
85        if isinstance(other, self.__class__):
86            return self.name == other.name and self.offset == other.offset
87        else:
88            return NotImplemented

A simple tree node with a name and any kwargs.

Args: name: A name or any other object this node can reference to as identifier.

Keyword Args: parent: Reference to parent node. children: Iterable with child nodes. *: Any other given attribute is just stored as object attribute.

Other than :any:AnyNode this class has at least the name attribute, to distinguish between different instances.

The parent attribute refers the parent node:

>>> from anytree import Node, RenderTree
>>> root = Node("root")
>>> s0 = Node("sub0", parent=root)
>>> s0b = Node("sub0B", parent=s0, foo=4, bar=109)
>>> s0a = Node("sub0A", parent=s0)
>>> s1 = Node("sub1", parent=root)
>>> s1a = Node("sub1A", parent=s1)
>>> s1b = Node("sub1B", parent=s1, bar=8)
>>> s1c = Node("sub1C", parent=s1)
>>> s1ca = Node("sub1Ca", parent=s1c)
>>> print(RenderTree(root))
Node('/root')
├── Node('/root/sub0')
│   ├── Node('/root/sub0/sub0B', bar=109, foo=4)
│   └── Node('/root/sub0/sub0A')
└── Node('/root/sub1')
    ├── Node('/root/sub1/sub1A')
    ├── Node('/root/sub1/sub1B', bar=8)
    └── Node('/root/sub1/sub1C')
        └── Node('/root/sub1/sub1C/sub1Ca')

The same tree can be constructed by using the children attribute:

>>> root = Node("root", children=[
...     Node("sub0", children=[
...         Node("sub0B", bar=109, foo=4),
...         Node("sub0A", children=None),
...     ]),
...     Node("sub1", children=[
...         Node("sub1A"),
...         Node("sub1B", bar=8, children=[]),
...         Node("sub1C", children=[
...             Node("sub1Ca"),
...         ]),
...     ]),
... ])
>>> print(RenderTree(root))
Node('/root')
├── Node('/root/sub0')
│   ├── Node('/root/sub0/sub0B', bar=109, foo=4)
│   └── Node('/root/sub0/sub0A')
└── Node('/root/sub1')
    ├── Node('/root/sub1/sub1A')
    ├── Node('/root/sub1/sub1B', bar=8)
    └── Node('/root/sub1/sub1C')
        └── Node('/root/sub1/sub1C/sub1Ca')
Register(name, offset, width=None, **kw)
66    def __init__(self, name, offset, width=None, **kw):
67        super().__init__(name, offset=offset, width=width or 4, **kw)
address: int
69    @property
70    def address(self) -> int:
71        return self.parent.address + self.offset
addresses: int
73    @property
74    def addresses(self) -> int:
75        for ii in range(self.width):
76            yield self.parent.address + self.offset + ii
Inherited Members
anytree.node.node.Node
name
parent
anytree.node.nodemixin.NodeMixin
separator
children
path
iter_path_reverse
ancestors
anchestors
descendants
root
siblings
leaves
is_leaf
is_root
height
depth
size
class BitField(anytree.node.node.Node):
 91class BitField(Node):
 92    def __init__(self, name, position, width=None, **kw):
 93        super().__init__(name, position=position, width=width or 1, **kw)
 94
 95    @property
 96    def bit_address(self) -> int:
 97        return self.parent.address * 8 + self.position
 98
 99    @property
100    def bit_addresses(self) -> int:
101        for ii in range(self.width):
102            yield self.parent.address * 8 + self.position + ii
103
104    def __hash__(self) -> int:
105        return hash(f"{self.name} {self.position} {self.width}")
106
107    def __eq__(self, other) -> bool:
108        if isinstance(other, self.__class__):
109            return self.name == other.name and self.position == other.position
110        else:
111            return NotImplemented

A simple tree node with a name and any kwargs.

Args: name: A name or any other object this node can reference to as identifier.

Keyword Args: parent: Reference to parent node. children: Iterable with child nodes. *: Any other given attribute is just stored as object attribute.

Other than :any:AnyNode this class has at least the name attribute, to distinguish between different instances.

The parent attribute refers the parent node:

>>> from anytree import Node, RenderTree
>>> root = Node("root")
>>> s0 = Node("sub0", parent=root)
>>> s0b = Node("sub0B", parent=s0, foo=4, bar=109)
>>> s0a = Node("sub0A", parent=s0)
>>> s1 = Node("sub1", parent=root)
>>> s1a = Node("sub1A", parent=s1)
>>> s1b = Node("sub1B", parent=s1, bar=8)
>>> s1c = Node("sub1C", parent=s1)
>>> s1ca = Node("sub1Ca", parent=s1c)
>>> print(RenderTree(root))
Node('/root')
├── Node('/root/sub0')
│   ├── Node('/root/sub0/sub0B', bar=109, foo=4)
│   └── Node('/root/sub0/sub0A')
└── Node('/root/sub1')
    ├── Node('/root/sub1/sub1A')
    ├── Node('/root/sub1/sub1B', bar=8)
    └── Node('/root/sub1/sub1C')
        └── Node('/root/sub1/sub1C/sub1Ca')

The same tree can be constructed by using the children attribute:

>>> root = Node("root", children=[
...     Node("sub0", children=[
...         Node("sub0B", bar=109, foo=4),
...         Node("sub0A", children=None),
...     ]),
...     Node("sub1", children=[
...         Node("sub1A"),
...         Node("sub1B", bar=8, children=[]),
...         Node("sub1C", children=[
...             Node("sub1Ca"),
...         ]),
...     ]),
... ])
>>> print(RenderTree(root))
Node('/root')
├── Node('/root/sub0')
│   ├── Node('/root/sub0/sub0B', bar=109, foo=4)
│   └── Node('/root/sub0/sub0A')
└── Node('/root/sub1')
    ├── Node('/root/sub1/sub1A')
    ├── Node('/root/sub1/sub1B', bar=8)
    └── Node('/root/sub1/sub1C')
        └── Node('/root/sub1/sub1C/sub1Ca')
BitField(name, position, width=None, **kw)
92    def __init__(self, name, position, width=None, **kw):
93        super().__init__(name, position=position, width=width or 1, **kw)
bit_address: int
95    @property
96    def bit_address(self) -> int:
97        return self.parent.address * 8 + self.position
bit_addresses: int
 99    @property
100    def bit_addresses(self) -> int:
101        for ii in range(self.width):
102            yield self.parent.address * 8 + self.position + ii
Inherited Members
anytree.node.node.Node
name
parent
anytree.node.nodemixin.NodeMixin
separator
children
path
iter_path_reverse
ancestors
anchestors
descendants
root
siblings
leaves
is_leaf
is_root
height
depth
size
class EnumeratedValue(anytree.node.node.Node):
114class EnumeratedValue(Node):
115    def __init__(self, name, value, **kw):
116        super().__init__(name, value=value, **kw)
117
118    def __hash__(self) -> int:
119        return hash(f"{self.name} {self.value}")
120
121    def __eq__(self, other) -> bool:
122        if isinstance(other, self.__class__):
123            return self.name == other.name and self.value == other.value
124        else:
125            return NotImplemented

A simple tree node with a name and any kwargs.

Args: name: A name or any other object this node can reference to as identifier.

Keyword Args: parent: Reference to parent node. children: Iterable with child nodes. *: Any other given attribute is just stored as object attribute.

Other than :any:AnyNode this class has at least the name attribute, to distinguish between different instances.

The parent attribute refers the parent node:

>>> from anytree import Node, RenderTree
>>> root = Node("root")
>>> s0 = Node("sub0", parent=root)
>>> s0b = Node("sub0B", parent=s0, foo=4, bar=109)
>>> s0a = Node("sub0A", parent=s0)
>>> s1 = Node("sub1", parent=root)
>>> s1a = Node("sub1A", parent=s1)
>>> s1b = Node("sub1B", parent=s1, bar=8)
>>> s1c = Node("sub1C", parent=s1)
>>> s1ca = Node("sub1Ca", parent=s1c)
>>> print(RenderTree(root))
Node('/root')
├── Node('/root/sub0')
│   ├── Node('/root/sub0/sub0B', bar=109, foo=4)
│   └── Node('/root/sub0/sub0A')
└── Node('/root/sub1')
    ├── Node('/root/sub1/sub1A')
    ├── Node('/root/sub1/sub1B', bar=8)
    └── Node('/root/sub1/sub1C')
        └── Node('/root/sub1/sub1C/sub1Ca')

The same tree can be constructed by using the children attribute:

>>> root = Node("root", children=[
...     Node("sub0", children=[
...         Node("sub0B", bar=109, foo=4),
...         Node("sub0A", children=None),
...     ]),
...     Node("sub1", children=[
...         Node("sub1A"),
...         Node("sub1B", bar=8, children=[]),
...         Node("sub1C", children=[
...             Node("sub1Ca"),
...         ]),
...     ]),
... ])
>>> print(RenderTree(root))
Node('/root')
├── Node('/root/sub0')
│   ├── Node('/root/sub0/sub0B', bar=109, foo=4)
│   └── Node('/root/sub0/sub0A')
└── Node('/root/sub1')
    ├── Node('/root/sub1/sub1A')
    ├── Node('/root/sub1/sub1B', bar=8)
    └── Node('/root/sub1/sub1C')
        └── Node('/root/sub1/sub1C/sub1Ca')
EnumeratedValue(name, value, **kw)
115    def __init__(self, name, value, **kw):
116        super().__init__(name, value=value, **kw)
Inherited Members
anytree.node.node.Node
name
parent
anytree.node.nodemixin.NodeMixin
separator
children
path
iter_path_reverse
ancestors
anchestors
descendants
root
siblings
leaves
is_leaf
is_root
height
depth
size
def compare_device_trees(left, right):
138def compare_device_trees(left, right):
139    assert isinstance(left, Device) and isinstance(right, Device)
140    if len(left.children) != len(right.children):
141        return False
142    if not left.children:
143        return True
144    return all(_compare_trees(left, right) for left, right in zip(left.children, right.children))
def format_svd(register_tree):
118def format_svd(register_tree):
119    device = etree.Element("device")
120    device.set("schemaVersion", "1.1")
121    # device.set("xmlns:xs", "http://www.w3.org/2001/XMLSchema-instance")
122    # device.set("xs:noNamespaceSchemaLocation", "CMSIS-SVD_Schema_1_1.xsd")
123
124    _format_svd(device, register_tree)
125
126    svd = etree.ElementTree(device)
127    return svd
def write_svd(svd, path, pretty=True):
130def write_svd(svd, path, pretty=True):
131    with open(path, "wb") as file:
132        svd.write(file, pretty_print=pretty, doctype='<?xml version="1.0" encoding="utf-8" standalone="no"?>')
def read_svd(path) -> Device:
42def read_svd(path) -> Device:
43    """
44    Reads the peripherals, registers and bit fields of a CMSIS-SVD file.
45    Derived peripherals are resolved, register arrays are not expanded.
46
47    :param path: path to the SVD file.
48    :return: the SVD device tree.
49    """
50    root = etree.parse(str(path)).getroot()
51    device = Device(root.findtext("name"), compatible=(root.findtext("description") or "").split(","))
52
53    registers = {}
54    peripherals = []
55    for node in root.iter("peripheral"):
56        name = node.findtext("name")
57        registers[name] = node.find("registers")
58        peripherals.append(node)
59
60    for node in peripherals:
61        name = node.findtext("name")
62        derived_from = node.get("derivedFrom")
63        peripheral = Peripheral(name, _text(node, "groupName"), _int(node, "baseAddress"), parent=device)
64        peripheral.group = _text(node, "groupName")
65        peripheral.description = _text(node, "description")
66        peripheral.alternate = _text(node, "alternatePeripheral")
67        peripheral.interrupts = [
68            (inode.findtext("name"), _int(inode, "value"), _text(inode, "description"))
69            for inode in node.iter("interrupt")
70        ]
71        if derived_from:
72            peripheral.derived_from = derived_from
73        if (nodes := registers[name]) is None and derived_from:
74            nodes = registers.get(derived_from)
75        if nodes is None:
76            continue
77        for rnode in nodes.iter("register"):
78            size = _int(rnode, "size", 32) // 8
79            rname = rnode.findtext("name")
80            register = Register(rname.replace("[%s]", ""), _int(rnode, "addressOffset"), size, parent=peripheral)
81            register.description = _text(rnode, "description")
82            register.alternate = _text(rnode, "alternateRegister")
83            register.access = _text(rnode, "access")
84            if (dim := _int(rnode, "dim")) is not None:
85                register.dim = dim
86                register.dim_increment = _int(rnode, "dimIncrement", size)
87            _add_bit_fields(rnode, register)
88
89    return device

Reads the peripherals, registers and bit fields of a CMSIS-SVD file. Derived peripherals are resolved, register arrays are not expanded.

Parameters
  • path: path to the SVD file.
Returns

the SVD device tree.