modm_data.svd

 1# Copyright 2022, Niklas Hauser
 2# SPDX-License-Identifier: MPL-2.0
 3
 4from . import stmicro
 5from .model import Device, PeripheralType, Peripheral, Register, BitField, compare_device_trees
 6from .write import format_svd, write_svd
 7from .read import read_svd
 8
 9__all__ = [
10    "stmicro",
11    "Device",
12    "PeripheralType",
13    "Peripheral",
14    "Register",
15    "BitField",
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
def compare_device_trees(left, right):
129def compare_device_trees(left, right):
130    assert isinstance(left, Device) and isinstance(right, Device)
131    if len(left.children) != len(right.children):
132        return False
133    if not left.children:
134        return True
135    return all(_compare_trees(left, right) for left, right in zip(left.children, right.children))
def format_svd(register_tree):
80def format_svd(register_tree):
81    device = etree.Element("device")
82    device.set("schemaVersion", "1.1")
83    # device.set("xmlns:xs", "http://www.w3.org/2001/XMLSchema-instance")
84    # device.set("xs:noNamespaceSchemaLocation", "CMSIS-SVD_Schema_1_1.xsd")
85
86    _format_svd(device, register_tree)
87
88    svd = etree.ElementTree(device)
89    return svd
def write_svd(svd, path, pretty=True):
92def write_svd(svd, path, pretty=True):
93    with open(path, "wb") as file:
94        svd.write(file, pretty_print=pretty, doctype='<?xml version="1.0" encoding="utf-8" standalone="no"?>')
def read_svd(path) -> Device:
 8def read_svd(path) -> Device:
 9    from cmsis_svd.parser import SVDParser
10
11    parser = SVDParser.for_xml_file(path)
12    pdev = parser.get_device()
13    device = Device(pdev.name, compatible=pdev.description.split(","))
14
15    for peripheral in pdev.peripherals:
16        ptype = peripheral.get_derived_from()
17        if ptype is not None:
18            ptype = ptype.name
19        nper = Peripheral(peripheral.name, ptype, peripheral.base_address, parent=device)
20        for register in peripheral.registers:
21            nreg = Register(register.name, register.address_offset, register.size // 8, parent=nper)
22            for field in register.fields:
23                BitField(field.name, field.bit_offset, field.bit_width, parent=nreg)
24
25    return device