Contrary to what is defined in the CMSIS SVD standard, in svdconv
the inheritance and adjustment of register sizes
follow a specific algorithm based on implicit and explicit size definitions at different levels. The calculation of the
effective size involves both explicit definitions and inheritance from higher levels, following a
recursive approach to determine the maximum size within each level. Here's how the algorithm works:
For each peripheral, if clusters are present, they are resolved from the inside out, starting with the innermost
clusters (where children are only registers). Each cluster is examined by iterating through all of its children—either
registers or subclusters. The effective size for a register is either explicitly defined or
inherited from a higher level, such as from the cluster, peripheral, or ultimately the device level. If no
explicit size is defined, it inherits the size from the levels above, taking the first size that is found while
traversing upwards. If this traversal reaches the device level and no size is defined there either, the size is set to
32 bits by default.
For a given cluster, its size is determined by finding the maximum size among all of its children, including both
registers and subclusters.
This process is repeated recursively, starting with the innermost clusters and moving outward to determine the
size of the parent clusters. Finally, the peripheral's size is calculated by taking the maximum size among all of its
direct children, including clusters and registers. This approach ensures that the effective size of a peripheral is
determined based on the largest size found in its entire hierarchy.
Python code that demonstrates the algorithm (click to expand)
```python from dataclasses import dataclass, field
@dataclass(kw_only=True) class Device: size: None | int = None name: str peripherals: list["Peripheral"] =
field(default_factory=list)
@dataclass(kw_only=True) class Peripheral: size: None | int = None name: str registers_clusters: list["Register |
Cluster"] = field(default_factory=list) parent: Device
@dataclass(kw_only=True) class Cluster: size: None | int = None name: str registers_clusters: list["Register | Cluster"]
= field(default_factory=list) parent: "Cluster | Peripheral"
@dataclass(kw_only=True) class Register: size: None | int = None name: str parent: "Cluster | Peripheral"
def resolve_size_over_levels(peripheral: Peripheral) -> int: # Start resolving the size for the peripheral by examining
all its children inherited_size = resolve_inherited_size(peripheral) effective_size = inherited_size
for element in peripheral.registers_clusters: child_size = resolve_size(element, inherited_size) effective_size =
max(effective_size, child_size)
peripheral.size = effective_size return effective_size
def resolve_size(element: Register | Cluster, inherited_size: int) -> int: # Determine effective size for Register or
Cluster if isinstance(element, Register): return element.size if element.size is not None else inherited_size
if isinstance(element, Cluster): return resolve_cluster_size(element)
raise ValueError("Element must be either Register or Cluster")
def resolve_cluster_size(cluster: Cluster) -> int: # Start resolving the size for the cluster by examining all its
children cluster_inherited_size = resolve_inherited_size(cluster) effective_size = cluster_inherited_size
for child in cluster.registers_clusters: child_size = resolve_size(child, cluster_inherited_size) effective_size =
max(effective_size, child_size)
cluster.size = effective_size return effective_size
def resolve_inherited_size(element: Peripheral | Cluster | Register) -> int: # Traverse upwards to find the first
defined size, or return 32 if no size is defined current = element.parent while current is not None: if current.size is
not None: return current.size current = getattr(current, "parent", None) return 32 # Default to 32 if no size is found
in any parent level
# Example usage: def print_hierarchy(element: Peripheral | Cluster | Register, path: str = ""): if isinstance(element,
Peripheral): path += element.name print(f"{path} (size: {element.size})") for child in element.registers_clusters:
print_hierarchy(child, path + ".") elif isinstance(element, Cluster): path += element.name print(f"{path} (size:
{element.size})") for child in element.registers_clusters: print_hierarchy(child, path + ".") elif isinstance(element,
Register): path += element.name print(f"{path} (size: {element.size})") else: raise ValueError("Element must be either
Peripheral, Cluster or Register")
device = Device(name="DeviceA") peripheral_a = Peripheral(name="PeripheralA", parent=device)
cluster_a = Cluster(name="ClusterA", parent=peripheral_a) register_a = Register(name="RegisterA", parent=cluster_a)
register_b = Register(name="RegisterB", parent=cluster_a) cluster_b = Cluster(name="ClusterB", parent=cluster_a)
register_a_in_cluster_b = Register(name="RegisterA", parent=cluster_b) register_b_in_cluster_b =
Register(name="RegisterB", size=64, parent=cluster_b) cluster_b.registers_clusters.extend([register_a_in_cluster_b,
register_b_in_cluster_b]) cluster_a.registers_clusters.extend([register_a, register_b, cluster_b])
cluster_c = Cluster(name="ClusterC", parent=peripheral_a) register_a_in_cluster_c = Register(name="RegisterA",
parent=cluster_c) register_b_in_cluster_c = Register(name="RegisterB", parent=cluster_c)
cluster_c.registers_clusters.extend([register_a_in_cluster_c, register_b_in_cluster_c])
register_a_in_peripheral = Register(name="RegisterA", parent=peripheral_a)
peripheral_a.registers_clusters.extend([cluster_a, cluster_c, register_a_in_peripheral])
device.peripherals.append(peripheral_a)
# Resolve sizes and print hierarchy resolve_size_over_levels(peripheral_a) print_hierarchy(peripheral_a) ```
test_complex_size_adjustment
This test examines how the parser handles more intricate scenarios of size inheritance and adjustments across
clusters and registers within a peripheral. The SVD file contains clusters and registers with varying degrees
of explicit and implicit size definitions. The parser must determine the correct effective sizes based on size
inheritance rules and adjustments at different levels. In this case, the sizes within the clusters and
registers are adjusted, either explicitly or inherited, following a recursive calculation of the maximum
effective size.
Expected Outcome: The parser should process the file without errors, correctly inheriting and adjusting sizes
across clusters and registers. ClusterA, which has no explicit size defined, should have its size adjusted to
64 bits, inherited from ClusterB and its child registers. Both RegisterA
and RegisterB
within ClusterA
should also have effective sizes of 64 bits. Similarly, ClusterB should inherit a size of 64 bits from its
children, while ClusterC, which also lacks an explicit size, should have a final size of 32 bits, based on its
child registers, which inherit the size implicitly from device level (default value). Since the size
adjustment for ClusterC is executed before the final size adjustment for PeripheralA, at this point of time,
no size is set for PeripheralA. Finally, PeripheralA should adjust its size to 64 bits, reflecting the largest
size found in its children. The parser should handle all these size adjustments and inheritance steps
accurately, consistent with the behavior of svdconv
.
Processable with svdconv: yes
Source code in tests/test_process/test_size_inheritance_and_adjustment.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222 | @pytest.mark.filterwarnings("error::svdsuite.process.ProcessWarning")
def test_complex_size_adjustment(get_processed_device_from_testfile: Callable[[str], Device]):
"""
This test examines how the parser handles more intricate scenarios of size inheritance and adjustments across
clusters and registers within a peripheral. The SVD file contains clusters and registers with varying degrees
of explicit and implicit size definitions. The parser must determine the correct effective sizes based on size
inheritance rules and adjustments at different levels. In this case, the sizes within the clusters and
registers are adjusted, either explicitly or inherited, following a recursive calculation of the maximum
effective size.
**Expected Outcome:** The parser should process the file without errors, correctly inheriting and adjusting sizes
across clusters and registers. ClusterA, which has no explicit size defined, should have its size adjusted to
64 bits, inherited from ClusterB and its child registers. Both `RegisterA` and `RegisterB` within ClusterA
should also have effective sizes of 64 bits. Similarly, ClusterB should inherit a size of 64 bits from its
children, while ClusterC, which also lacks an explicit size, should have a final size of 32 bits, based on its
child registers, which inherit the size implicitly from device level (default value). Since the size
adjustment for ClusterC is executed before the final size adjustment for PeripheralA, at this point of time,
no size is set for PeripheralA. Finally, PeripheralA should adjust its size to 64 bits, reflecting the largest
size found in its children. The parser should handle all these size adjustments and inheritance steps
accurately, consistent with the behavior of `svdconv`.
**Processable with svdconv:** yes
"""
device = get_processed_device_from_testfile("size_inheritance_and_adjustment/complex_size_adjustment.svd")
assert len(device.peripherals) == 1
assert len(device.peripherals[0].registers_clusters) == 3
cluster_a = device.peripherals[0].registers_clusters[0]
assert isinstance(cluster_a, Cluster)
assert cluster_a.name == "ClusterA"
assert cluster_a.address_offset == 0x0
assert cluster_a.size == 64 # not set, size adjustment results in 64 from ClusterB
assert len(cluster_a.registers_clusters) == 3
assert isinstance(cluster_a.registers_clusters[0], Register)
assert cluster_a.registers_clusters[0].name == "RegisterA"
assert cluster_a.registers_clusters[0].address_offset == 0x0
assert cluster_a.registers_clusters[0].size == 64 # not set, effective size results in 64 from ClusterA
assert isinstance(cluster_a.registers_clusters[1], Register)
assert cluster_a.registers_clusters[1].name == "RegisterB"
assert cluster_a.registers_clusters[1].address_offset == 0x8
assert cluster_a.registers_clusters[1].size == 64 # not set, effective size results in 64 from ClusterA
cluster_b = cluster_a.registers_clusters[2]
assert isinstance(cluster_b, Cluster)
assert cluster_b.name == "ClusterB"
assert cluster_b.address_offset == 0x10
assert cluster_b.size == 64 # not set, size adjustment results in 64 from RegisterB
assert len(cluster_b.registers_clusters) == 2
assert isinstance(cluster_b.registers_clusters[0], Register)
assert cluster_b.registers_clusters[0].name == "RegisterA"
assert cluster_b.registers_clusters[0].address_offset == 0x0
assert cluster_b.registers_clusters[0].size == 64 # not set, effective size results in 64 from ClusterB
assert isinstance(cluster_b.registers_clusters[1], Register)
assert cluster_b.registers_clusters[1].name == "RegisterB"
assert cluster_b.registers_clusters[1].address_offset == 0x8
assert cluster_b.registers_clusters[1].size == 64 # explicitly set to 64
cluster_c = device.peripherals[0].registers_clusters[1]
assert isinstance(cluster_c, Cluster)
assert cluster_c.name == "ClusterC"
assert cluster_c.address_offset == 0x20
assert cluster_c.size == 32 # not set, size adjustment results implicit in 32 from RegisterA and RegisterB
assert len(cluster_c.registers_clusters) == 2
assert isinstance(cluster_c.registers_clusters[0], Register)
assert cluster_c.registers_clusters[0].name == "RegisterA"
assert cluster_c.registers_clusters[0].address_offset == 0x0
assert cluster_c.registers_clusters[0].size == 32 # not set, effective size results in 32 from ClusterC size
assert isinstance(cluster_c.registers_clusters[1], Register)
assert cluster_c.registers_clusters[1].name == "RegisterB"
assert cluster_c.registers_clusters[1].address_offset == 0x8
assert cluster_c.registers_clusters[1].size == 32 # not set, effective size results in 32 from ClusterC size
assert isinstance(device.peripherals[0].registers_clusters[2], Register)
assert device.peripherals[0].registers_clusters[2].name == "RegisterA"
assert device.peripherals[0].registers_clusters[2].address_offset == 0x30
assert device.peripherals[0].registers_clusters[2].size == 64 # not set, ef. size results in 64 from peripheral
|
SVD file: size_inheritance_and_adjustment/complex_size_adjustment.svd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 | <?xml version='1.0' encoding='utf-8'?>
<device xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd" schemaVersion="1.3">
<name>complex_size_adjustment</name>
<version>1.0</version>
<description>Test_Example device</description>
<cpu>
<name>CM0</name>
<revision>r0p0</revision>
<endian>little</endian>
<mpuPresent>false</mpuPresent>
<fpuPresent>false</fpuPresent>
<nvicPrioBits>4</nvicPrioBits>
<vendorSystickConfig>false</vendorSystickConfig>
</cpu>
<addressUnitBits>8</addressUnitBits>
<width>32</width>
<peripherals>
<peripheral>
<name>PeripheralA</name>
<baseAddress>0x40001000</baseAddress>
<addressBlock>
<offset>0x0</offset>
<size>0x1000</size>
<usage>registers</usage>
</addressBlock>
<registers>
<cluster>
<name>ClusterA</name>
<description>ClusterA description</description>
<addressOffset>0x0</addressOffset>
<register>
<name>RegisterA</name>
<addressOffset>0x0</addressOffset>
</register>
<register>
<name>RegisterB</name>
<addressOffset>0x8</addressOffset>
</register>
<cluster>
<name>ClusterB</name>
<description>ClusterB description</description>
<addressOffset>0x10</addressOffset>
<register>
<name>RegisterA</name>
<addressOffset>0x0</addressOffset>
</register>
<register>
<name>RegisterB</name>
<addressOffset>0x8</addressOffset>
<size>64</size>
</register>
</cluster>
</cluster>
<cluster>
<name>ClusterC</name>
<description>ClusterC description</description>
<addressOffset>0x20</addressOffset>
<register>
<name>RegisterA</name>
<addressOffset>0x0</addressOffset>
</register>
<register>
<name>RegisterB</name>
<addressOffset>0x8</addressOffset>
</register>
</cluster>
<register>
<name>RegisterA</name>
<addressOffset>0x30</addressOffset>
</register>
</registers>
</peripheral>
</peripherals>
</device>
|
test_overlap_due_to_size_adjustment
This test verifies how the parser handles size adjustments that lead to register overlaps. The SVD file
contains a peripheral where the size inheritance and adjustment algorithm causes an overlap between two
registers. Specifically, PeripheralA
starts with a defined size of 32 bits, but the effective size is
adjusted to 64 bits due to the size of RegisterB
. RegisterA
, which does not have an explicit size,
inherits its size from the peripheral, leading to an overlap with RegisterB
, which has an address offset of
0x4.
Expected Outcome: The parser should process the file and issue a warning about the overlap caused by the size
adjustment. PeripheralA
should have a final size of 64 bits, overriding the original size of 32 bits due to
the size of RegisterB
. RegisterA
, which does not have a size defined, should inherit the size from
PeripheralA
and have a final size of 64 bits. This leads to an overlap with RegisterB
, whose size is
explicitly set to 64 bits, starting at address offset 0x4. The parser should handle this case by adjusting the
sizes correctly and raising a warning due to the overlap, similar to the behavior in svdconv
.
Processable with svdconv: yes
Source code in tests/test_process/test_size_inheritance_and_adjustment.py
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262 | @pytest.mark.filterwarnings("error::svdsuite.process.ProcessWarning")
def test_overlap_due_to_size_adjustment(get_processed_device_from_testfile: Callable[[str], Device]):
"""
This test verifies how the parser handles size adjustments that lead to register overlaps. The SVD file
contains a peripheral where the size inheritance and adjustment algorithm causes an overlap between two
registers. Specifically, `PeripheralA` starts with a defined size of 32 bits, but the effective size is
adjusted to 64 bits due to the size of `RegisterB`. `RegisterA`, which does not have an explicit size,
inherits its size from the peripheral, leading to an overlap with `RegisterB`, which has an address offset of
0x4.
**Expected Outcome:** The parser should process the file and issue a warning about the overlap caused by the size
adjustment. `PeripheralA` should have a final size of 64 bits, overriding the original size of 32 bits due to
the size of `RegisterB`. `RegisterA`, which does not have a size defined, should inherit the size from
`PeripheralA` and have a final size of 64 bits. This leads to an overlap with `RegisterB`, whose size is
explicitly set to 64 bits, starting at address offset 0x4. The parser should handle this case by adjusting the
sizes correctly and raising a warning due to the overlap, similar to the behavior in `svdconv`.
**Processable with svdconv:** yes
"""
with pytest.warns(ProcessWarning):
device = get_processed_device_from_testfile(
"size_inheritance_and_adjustment/overlap_due_to_size_adjustment.svd"
)
assert len(device.peripherals) == 1
assert len(device.peripherals[0].registers_clusters) == 2
assert device.peripherals[0].size == 64 # explicitly set to 32, adjustment overwrite to 64 from RegisterB
assert isinstance(device.peripherals[0].registers_clusters[0], Register)
assert device.peripherals[0].registers_clusters[0].name == "RegisterA"
assert device.peripherals[0].registers_clusters[0].address_offset == 0x0
assert device.peripherals[0].registers_clusters[0].size == 64 # not set, ef. size results in 64 (PeripheralA)
assert isinstance(device.peripherals[0].registers_clusters[1], Register)
assert device.peripherals[0].registers_clusters[1].name == "RegisterB"
assert device.peripherals[0].registers_clusters[1].address_offset == 0x4
assert device.peripherals[0].registers_clusters[1].size == 64 # explicitly set to 64
|
SVD file: size_inheritance_and_adjustment/overlap_due_to_size_adjustment.svd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 | <?xml version='1.0' encoding='utf-8'?>
<device xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd" schemaVersion="1.3">
<name>overlap_due_to_size_adjustment</name>
<version>1.0</version>
<description>Test_Example device</description>
<cpu>
<name>CM0</name>
<revision>r0p0</revision>
<endian>little</endian>
<mpuPresent>false</mpuPresent>
<fpuPresent>false</fpuPresent>
<nvicPrioBits>4</nvicPrioBits>
<vendorSystickConfig>false</vendorSystickConfig>
</cpu>
<addressUnitBits>8</addressUnitBits>
<width>32</width>
<peripherals>
<peripheral>
<name>PeripheralA</name>
<baseAddress>0x40001000</baseAddress>
<size>32</size>
<addressBlock>
<offset>0x0</offset>
<size>0x1000</size>
<usage>registers</usage>
</addressBlock>
<registers>
<register>
<name>RegisterA</name>
<addressOffset>0x0</addressOffset>
</register>
<register>
<name>RegisterB</name>
<addressOffset>0x4</addressOffset>
<size>64</size>
</register>
</registers>
</peripheral>
</peripherals>
</device>
|
test_simple_size_adjustment
This test evaluates how the parser handles simple size inheritance and explicit size adjustments within a
peripheral. In this scenario, the peripheral has both implicit and explicit size definitions, and the parser
must adjust these sizes based on the rules described. Specifically, RegisterA
has no explicit size set, so
it should inherit the size from the peripheral level, while RegisterB
has an explicit size defined.
svdconv
processes this file correctly, applying size adjustments based on the highest level's explicit
settings.
Expected Outcome: The parser should correctly adjust the sizes, processing the SVD file without errors. The
peripheral should have two registers, RegisterA
and RegisterB
. The overall size of the peripheral should
be set to 64 bits, which overrides the explicit 16-bit setting due to size adjustments. RegisterA
, with no
explicit size set, should inherit the peripheral's size and be 64 bits. RegisterB
, which explicitly has a
size of 64 bits, should retain this size. The parser should process the inheritance and size adjustment
algorithm correctly, reflecting the behavior of svdconv
.
Processable with svdconv: yes
Source code in tests/test_process/test_size_inheritance_and_adjustment.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136 | @pytest.mark.filterwarnings("error::svdsuite.process.ProcessWarning")
def test_simple_size_adjustment(get_processed_device_from_testfile: Callable[[str], Device]):
"""
This test evaluates how the parser handles simple size inheritance and explicit size adjustments within a
peripheral. In this scenario, the peripheral has both implicit and explicit size definitions, and the parser
must adjust these sizes based on the rules described. Specifically, `RegisterA` has no explicit size set, so
it should inherit the size from the peripheral level, while `RegisterB` has an explicit size defined.
`svdconv` processes this file correctly, applying size adjustments based on the highest level's explicit
settings.
**Expected Outcome:** The parser should correctly adjust the sizes, processing the SVD file without errors. The
peripheral should have two registers, `RegisterA` and `RegisterB`. The overall size of the peripheral should
be set to 64 bits, which overrides the explicit 16-bit setting due to size adjustments. `RegisterA`, with no
explicit size set, should inherit the peripheral's size and be 64 bits. `RegisterB`, which explicitly has a
size of 64 bits, should retain this size. The parser should process the inheritance and size adjustment
algorithm correctly, reflecting the behavior of `svdconv`.
**Processable with svdconv:** yes
"""
device = get_processed_device_from_testfile("size_inheritance_and_adjustment/simple_size_adjustment.svd")
assert len(device.peripherals) == 1
assert len(device.peripherals[0].registers_clusters) == 2
assert device.peripherals[0].size == 64 # explicitly set to 16, adjustment overwrite to 64
assert isinstance(device.peripherals[0].registers_clusters[0], Register)
assert device.peripherals[0].registers_clusters[0].name == "RegisterA"
assert device.peripherals[0].registers_clusters[0].address_offset == 0x0
assert device.peripherals[0].registers_clusters[0].size == 64 # not set, effective size results in 64
assert isinstance(device.peripherals[0].registers_clusters[1], Register)
assert device.peripherals[0].registers_clusters[1].name == "RegisterB"
assert device.peripherals[0].registers_clusters[1].address_offset == 0x8
assert device.peripherals[0].registers_clusters[1].size == 64 # explicitly set to 64
|
SVD file: size_inheritance_and_adjustment/simple_size_adjustment.svd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 | <?xml version='1.0' encoding='utf-8'?>
<device xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd" schemaVersion="1.3">
<name>simple_size_adjustment</name>
<version>1.0</version>
<description>Test_Example device</description>
<cpu>
<name>CM0</name>
<revision>r0p0</revision>
<endian>little</endian>
<mpuPresent>false</mpuPresent>
<fpuPresent>false</fpuPresent>
<nvicPrioBits>4</nvicPrioBits>
<vendorSystickConfig>false</vendorSystickConfig>
</cpu>
<addressUnitBits>8</addressUnitBits>
<width>32</width>
<peripherals>
<peripheral>
<name>PeripheralA</name>
<baseAddress>0x40001000</baseAddress>
<size>16</size>
<addressBlock>
<offset>0x0</offset>
<size>0x1000</size>
<usage>registers</usage>
</addressBlock>
<registers>
<register>
<name>RegisterA</name>
<addressOffset>0x0</addressOffset>
</register>
<register>
<name>RegisterB</name>
<addressOffset>0x8</addressOffset>
<size>64</size>
</register>
</registers>
</peripheral>
</peripherals>
</device>
|