1"""
2Utility script to generate offset definitions for a peripheral from an SVD file.
3
4Usage:
5
6    python gen_offsets.py \
7        --svd <svd_file> \
8        --output <output_file> \
9        --peripheral <peripheral_name> \
10        --guard <header_guard> \
11        [--prefix <prefix>]
12
13Copyright (c) 2024 Nordic Semiconductor ASA
14SPDX-License-Identifier: Apache-2.0
15"""
16
17import argparse
18from datetime import datetime
19from pathlib import Path
20import sys
21import xml.etree.ElementTree as ET
22
23
24HEADER = """/*
25 * Copyright (c) {} Nordic Semiconductor ASA
26 * SPDX-License-Identifier: Apache-2.0
27 */
28
29/* autogenerated using Nordic HAL utils/gen_offsets.py script */
30"""
31
32
33def parse_offsets(offsets, tree, base_name, base_offset):
34    if base_name:
35        base_name += "_"
36
37    entries = tree.findall("cluster") + tree.findall("register")
38    for entry in entries:
39        is_cluster = entry.tag == "cluster"
40        name = base_name + entry.find("name").text
41        offset = base_offset + int(entry.find("addressOffset").text, 0)
42
43        if "[%s]" not in name:
44            if is_cluster:
45                parse_offsets(offsets, entry, name, offset)
46            else:
47                offsets.append((name, offset))
48        else:
49            name = name.replace("[%s]", "_{}")
50            dim = int(entry.find("dim").text, 0)
51            dim_inc = int(entry.find("dimIncrement").text, 0)
52            for i in range(dim):
53                if is_cluster:
54                    parse_offsets(offsets, entry, name.format(i), offset + i * dim_inc)
55                else:
56                    offsets.append((name.format(i), offset + i * dim_inc))
57
58
59def gen_offsets(svd, output, peripheral, guard, prefix):
60    root = ET.parse(svd).getroot()
61    registers = root.find(f".//peripheral[name='{peripheral}']/registers")
62    if not registers:
63        sys.exit(f"No registers found for {peripheral}")
64
65    offsets = []
66    parse_offsets(offsets, registers, "", 0)
67
68    with open(output, "w") as f:
69        f.write(HEADER.format(datetime.now().year))
70        f.write(f"\n#ifndef {guard}\n")
71        f.write(f"#define {guard}\n\n")
72
73        width = max(len(offset[0]) for offset in offsets)
74        for offset in offsets:
75            f.write(f"#define {prefix}{offset[0].ljust(width)} 0x{offset[1]:03X}U\n")
76
77        f.write(f"\n#endif /* {guard} */\n")
78
79
80if __name__ == "__main__":
81    parser = argparse.ArgumentParser(allow_abbrev=False)
82    parser.add_argument("--svd", type=Path, required=True, help="SVD file")
83    parser.add_argument("--output", type=Path, required=True, help="Output file")
84    parser.add_argument("--peripheral", type=str, required=True, help="Peripheral")
85    parser.add_argument("--guard", type=str, required=True, help="Header guard")
86    parser.add_argument("--prefix", type=str, default="", help="Prefix register names")
87    args = parser.parse_args()
88
89    gen_offsets(args.svd, args.output, args.peripheral, args.guard, args.prefix)
90