|
2 | 2 | # -*- coding: utf-8 -*- |
3 | 3 | # |
4 | 4 | # Copyright (c) 2016, Fabian Greif |
5 | | -# Copyright (c) 2017, 2024, Niklas Hauser |
| 5 | +# Copyright (c) 2017, 2024, 2025, Niklas Hauser |
6 | 6 | # |
7 | 7 | # This file is part of the modm project. |
8 | 8 | # |
|
14 | 14 | import re |
15 | 15 | from pathlib import Path |
16 | 16 | from collections import defaultdict |
| 17 | +import subprocess |
| 18 | + |
17 | 19 |
|
18 | 20 | def getDefineForDevice(device_id, familyDefines): |
19 | 21 | """ |
@@ -61,45 +63,67 @@ def getDefineForDevice(device_id, familyDefines): |
61 | 63 |
|
62 | 64 | return None |
63 | 65 |
|
| 66 | + |
| 67 | +class RegisterMap: |
| 68 | + def __init__(self, defines, logger): |
| 69 | + self._defs = defines |
| 70 | + self._log = logger |
| 71 | + self.result = None |
| 72 | + |
| 73 | + def _result(self, query, value, ll): |
| 74 | + self.result = value |
| 75 | + self._log(f"{query} -{ll}-> {self.result}") |
| 76 | + return self.result |
| 77 | + |
| 78 | + def findall(self, query, default=None): |
| 79 | + if matches := re.findall(f"#define (?:{query}) ", self._defs): |
| 80 | + return self._result(query, matches, "fn") |
| 81 | + return self._result(query, default or [], "fd") |
| 82 | + |
| 83 | + def search(self, query, default=None): |
| 84 | + if (match := re.search(f"#define (?:{query}) ", self._defs)) is not None: |
| 85 | + if not (groups := match.groups()): |
| 86 | + return self._result(query, match.group(0)[7:-1], "s0") |
| 87 | + if len(groups) == 1: |
| 88 | + return self._result(query, groups[0], "s1") |
| 89 | + return self._result(query, groups, "sn") |
| 90 | + return self._result(query, default, "sd") |
| 91 | + |
| 92 | + def _ops(self, re_pers, re_regs, re_bits, bit_fmt): |
| 93 | + reg_bits = defaultdict(list) |
| 94 | + matches = re.findall(f"#define (({re_pers})_({re_regs})_(?:{re_bits})) ", self._defs) |
| 95 | + for whole, per, reg in matches: |
| 96 | + reg_bits[f"{per}->{reg}"].append(whole) |
| 97 | + statements = [f"{reg}{bit_fmt(' | '.join(bits))};" for reg, bits in reg_bits.items()] |
| 98 | + return self._result((re_pers, re_regs, re_bits), "\n".join(statements), "r") |
| 99 | + |
| 100 | + def set(self, pers, regs, bits): |
| 101 | + return self._ops(pers, regs, bits, lambda bits: f" |= {bits}") |
| 102 | + |
| 103 | + def clear(self, pers, regs, bits): |
| 104 | + return self._ops(pers, regs, bits, lambda bits: f" &= ~({bits})") |
| 105 | + |
| 106 | + |
64 | 107 | bprops = {} |
65 | | -def common_rcc_map(env): |
| 108 | +def common_register_map(env): |
66 | 109 | """ |
67 | | - Finds all CMSIS bit fields related to enabling and resetting peripherals |
68 | | - in the RCC of the format `RCC_(REGISTER)_(PERIPHERAL)_(TYPE)` where: |
| 110 | + Finds all register and bit names in the CMSIS header file. |
69 | 111 |
|
70 | | - - REGISTER: a variation of `(BUS)(ID?)(ENR|RSTR)`, e.g. `AHB1ENR` |
71 | | - - PERIPHERAL: typical peripheral name, e.g. `GPIOA` |
72 | | - - TYPE: either `EN` or `RST`. |
73 | | -
|
74 | | - :returns: a 2D-dictionary: `map[PERIPHERAL][TYPE] = REGISTER` |
| 112 | + :returns: a RegisterMap object that allows regex-ing for register names. |
75 | 113 | """ |
76 | | - headers = env.query("headers") |
77 | | - core_header = repopath("ext/arm/cmsis/CMSIS/Core/Include", headers["core_header"]) |
78 | | - |
79 | | - content = "" |
80 | | - for header_path in [core_header, localpath(bprops["folder"], headers["device_header"])]: |
81 | | - content += Path(header_path).read_text(encoding="utf-8", errors="replace") |
82 | | - |
83 | | - # find mpu and fpu features |
84 | | - features = re.findall(r"#define +__([MF]PU)_PRESENT +([01])", content) |
85 | | - core_features = {f[0]:bool(int(f[1])) for f in features} |
86 | | - # find all peripherals |
87 | | - mperipherals = re.findall(r"#define +(.*?) +\(\((.*?_Type(?:Def)?)", content) |
88 | | - # We only care about the absolute peripheral addresses |
89 | | - peripherals = [(p[0],p[1]) for p in mperipherals] |
90 | | - # filter out MPU and/or FPU if required |
91 | | - peripherals = filter(lambda p: p[0] not in core_features or core_features[p[0]], peripherals) |
92 | | - peripherals = sorted(peripherals, key=lambda p: p[0]) |
93 | | - # print("\n".join([s+" -> "+hex(a) for (s,k,a) in peripherals])) |
94 | | - |
95 | | - # Find all RCC enable and reset definitions |
96 | | - match = re.findall(r"RCC_([A-Z0-9]*?)_([A-Z0-9]+?)(EN|RST) ", content) |
97 | | - rcc_map = defaultdict(dict) |
98 | | - for (reg, per, typ) in match: |
99 | | - rcc_map[per][typ] = reg |
100 | | - |
101 | | - bprops["peripherals"] = peripherals |
102 | | - return rcc_map |
| 114 | + cmsis = env.query(":cmsis:device:headers") |
| 115 | + include_paths = [repopath("ext/arm/cmsis/CMSIS/Core/Include"), localpath(bprops["folder"])] |
| 116 | + headers = [Path(localpath(bprops["folder"], cmsis["device_header"]))] |
| 117 | + headers += env.query(":cmsis:ll:__headers", []) |
| 118 | + |
| 119 | + cmd = "arm-none-eabi-gcc" |
| 120 | + cmd += " -dM -E -mcpu=" + cmsis["core_header"][:-2].replace("core_c", "cortex-") |
| 121 | + cmd += " -D " + cmsis["define"] |
| 122 | + for p in include_paths: cmd += f" -I {p}" |
| 123 | + for h in headers: cmd += f" {h}" |
| 124 | + output = subprocess.run(cmd, shell=True, capture_output=True) |
| 125 | + |
| 126 | + return RegisterMap(output.stdout.decode("utf-8"), env.log.debug) |
103 | 127 |
|
104 | 128 |
|
105 | 129 | def common_header_file(env): |
@@ -127,7 +151,7 @@ def common_header_file(env): |
127 | 151 | define = None |
128 | 152 |
|
129 | 153 | content = Path(localpath(folder, family_header)).read_text(encoding="utf-8", errors="replace") |
130 | | - match = re.findall(r"if defined\((?P<define>STM32[CFGHLU][\w\d]+)\)", content) |
| 154 | + match = re.findall(r"if defined\((STM32[A-Z][\w\d]+)\)", content) |
131 | 155 | define = getDefineForDevice(device.identifier, match) |
132 | 156 | if define is None or match is None: |
133 | 157 | raise ValidateException("No device define found for '{}'!".format(device.partname)) |
@@ -198,17 +222,17 @@ def prepare(module, options): |
198 | 222 | return False |
199 | 223 |
|
200 | 224 | module.add_query( |
201 | | - EnvironmentQuery(name="rcc-map", factory=common_rcc_map)) |
| 225 | + EnvironmentQuery(name="headers", factory=common_header_file)) |
202 | 226 | module.add_query( |
203 | 227 | EnvironmentQuery(name="peripherals", factory=common_peripherals)) |
204 | 228 | module.add_query( |
205 | | - EnvironmentQuery(name="headers", factory=common_header_file)) |
| 229 | + EnvironmentQuery(name="registers", factory=common_register_map)) |
206 | 230 |
|
207 | 231 | module.depends(":cmsis:core") |
208 | 232 | return True |
209 | 233 |
|
210 | 234 | def validate(env): |
211 | | - env.query("rcc-map") |
| 235 | + env.query("headers") |
212 | 236 | env.query("peripherals") |
213 | 237 |
|
214 | 238 | def build(env): |
|
0 commit comments