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):
114def format_svd(register_tree):
115    device = etree.Element("device")
116    device.set("schemaVersion", "1.1")
117    # device.set("xmlns:xs", "http://www.w3.org/2001/XMLSchema-instance")
118    # device.set("xs:noNamespaceSchemaLocation", "CMSIS-SVD_Schema_1_1.xsd")
119
120    _format_svd(device, register_tree)
121
122    svd = etree.ElementTree(device)
123    return svd
def write_svd(svd, path, pretty=True):
126def write_svd(svd, path, pretty=True):
127    with open(path, "wb") as file:
128        svd.write(file, pretty_print=pretty, doctype='<?xml version="1.0" encoding="utf-8" standalone="no"?>')
def read_svd(path) -> Device:
25def read_svd(path) -> Device:
26    """
27    Reads the peripherals, registers and bit fields of a CMSIS-SVD file.
28    Derived peripherals are resolved, register arrays are not expanded.
29
30    :param path: path to the SVD file.
31    :return: the SVD device tree.
32    """
33    root = etree.parse(str(path)).getroot()
34    device = Device(root.findtext("name"), compatible=(root.findtext("description") or "").split(","))
35
36    registers = {}
37    peripherals = []
38    for node in root.iter("peripheral"):
39        name = node.findtext("name")
40        registers[name] = node.find("registers")
41        peripherals.append((name, node.get("derivedFrom"), _int(node, "baseAddress")))
42
43    for name, derived_from, address in peripherals:
44        peripheral = Peripheral(name, derived_from, address, parent=device)
45        if derived_from:
46            peripheral.derived_from = derived_from
47        if (nodes := registers[name]) is None and derived_from:
48            nodes = registers.get(derived_from)
49        if nodes is None:
50            continue
51        for rnode in nodes.iter("register"):
52            size = _int(rnode, "size", 32) // 8
53            register = Register(rnode.findtext("name"), _int(rnode, "addressOffset"), size, parent=peripheral)
54            for fnode in rnode.iter("field"):
55                BitField(fnode.findtext("name"), *_bit_range(fnode), parent=register)
56
57    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.