Skip to content

Commit 303463d

Browse files
committed
add minimal SVD->MM register map converter script
1 parent 2355d8c commit 303463d

3 files changed

Lines changed: 116 additions & 0 deletions

File tree

bitfilled/bitfilled/macros.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,14 @@
2929
/// @param ... Custom bitfield operations when desired (e.g., bitband<PERIPH_BASE>).
3030
#define BF_MMREG(TYPE, ...) public ::bitfilled::mmreg<TYPE, ::bitfilled::access::__VA_ARGS__>
3131

32+
#define BF_CONCAT_IMPL(X, Y) X##Y
33+
#define BF_CONCAT(X, Y) BF_CONCAT_IMPL(X, Y)
34+
#define BF_UNIQUE_NAME(NAME) BF_CONCAT(NAME, __LINE__)
35+
36+
#define BF_MMREG_RESERVED(WIDTH, SIZE) \
37+
private: \
38+
const ::std::array<::bitfilled::sized_unsigned_t<WIDTH>, SIZE> BF_UNIQUE_NAME(_reserved_); \
39+
\
40+
public:
41+
3242
#endif // __BITFILLED_MACROS_HPP__

tools/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cmsis-svd==0.6

tools/svd2mmregmap.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#!/usr/bin/env python3
2+
import argparse
3+
import pathlib
4+
from cmsis_svd import SVDParser
5+
from cmsis_svd.model import SVDAccessType, SVDCPUNameType
6+
7+
def instance_to_type(name):
8+
return f"{name}_t"
9+
10+
def sized_int(bit_size):
11+
return f"::bitfilled::sized_unsigned_t<{bit_size // 8}>"
12+
13+
def is_bitband_range(address):
14+
return address >= 0x40000000 and address < 0x42000000
15+
16+
def convert_access(svd_access):
17+
match svd_access:
18+
case SVDAccessType.READ_ONLY:
19+
return "r"
20+
case SVDAccessType.WRITE_ONLY | SVDAccessType.WRITE_ONCE:
21+
return "w"
22+
case SVDAccessType.READ_WRITE | SVDAccessType.READ_WRITE_ONCE | _:
23+
return "rw"
24+
25+
def generate_peripheral(peripheral, bitband):
26+
peripheral_name = peripheral.name
27+
if len(peripheral.group_name):
28+
peripheral_name = peripheral.group_name
29+
30+
parts = []
31+
parts.append(
32+
f"struct {instance_to_type(peripheral_name)} {{")
33+
# TODO: only use bitband if all peripherals of the chip are in bitband range
34+
parts.append(
35+
" using mmr_ops = ::bitfilled::bitband<PERIPH_BASE>;" if bitband and is_bitband_range(peripheral.base_address) else
36+
" using mmr_ops = ::bitfilled::base;")
37+
38+
# TODO: in the first round of iteration, generate enum types where enumeratedValues is provided
39+
# also de-duplicate enum types across registers
40+
41+
nametrim = peripheral.name + "_"
42+
offset = 0
43+
for register in peripheral.get_registers():
44+
# filling gaps in the register map with reserved
45+
if (offset < register.address_offset):
46+
reserved_size = (register.address_offset - offset) * 8 // register.size
47+
parts.append(
48+
f" BF_MMREG_RESERVED({register.size // 8}, {reserved_size})")
49+
50+
# define the register
51+
regname = register.name.removeprefix(nametrim)
52+
regnametype = instance_to_type(regname)
53+
parts.append(
54+
f" struct {regnametype} : BF_MMREG({sized_int(register.size)}, {convert_access(register.access)}, mmr_ops) {{\n"
55+
f" BF_COPY_SUPERCLASS({regnametype});")
56+
57+
# define register fields
58+
for field in register.get_fields():
59+
# TODO: group the fields into bitfieldset, if they are contiguous, share properties and are named accordingly
60+
61+
access = field.access if field.access else register.access
62+
lsb = field.bit_offset
63+
msb = field.bit_offset + field.bit_width - 1
64+
parts.append(
65+
f" BF_MMREGBITS({sized_int(register.size)}, {convert_access(access)}, {lsb}, {msb}) {field.name};")
66+
67+
parts.append(
68+
f" }} {regname};")
69+
offset = register.address_offset + register.size // 8
70+
71+
parts.append(
72+
"};")
73+
return "\n".join(parts)
74+
75+
if __name__ == "__main__":
76+
parser = argparse.ArgumentParser(
77+
description="Parse an SVD file and generate bitfilled register map for a peripheral type."
78+
)
79+
parser.add_argument(
80+
"path",
81+
type=pathlib.Path,
82+
help="Path to the input SVD file"
83+
)
84+
parser.add_argument(
85+
"peripheral",
86+
type=str,
87+
help="Name of the peripheral or peripheral group for code generation"
88+
)
89+
90+
args = parser.parse_args()
91+
92+
parser = SVDParser.for_xml_file(str(args.path))
93+
device = parser.get_device()
94+
bitband_support = device.cpu.name == SVDCPUNameType.CM3 or device.cpu.name == SVDCPUNameType.CM4
95+
96+
peripherals = device.get_peripherals()
97+
if args.peripheral == None:
98+
raise ValueError("Peripheral name must be provided")
99+
100+
for peripheral in peripherals:
101+
if peripheral.name == args.peripheral or peripheral.group_name == args.peripheral:
102+
print(generate_peripheral(peripheral, bitband_support))
103+
exit(0)
104+
105+
raise ValueError(f"Peripheral {args.peripheral} not found in the SVD file")

0 commit comments

Comments
 (0)