|
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,68 @@ 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 | + # print(self._defs) |
| 73 | + |
| 74 | + def _result(self, query, value, ll): |
| 75 | + self.result = value |
| 76 | + self._log(f"{query} -{ll}-> {self.result}") |
| 77 | + return self.result |
| 78 | + |
| 79 | + def findall(self, query, default=None): |
| 80 | + if matches := re.findall(f"#define (?:{query}) ", self._defs): |
| 81 | + return self._result(query, matches, "fn") |
| 82 | + return self._result(query, default or [], "fd") |
| 83 | + |
| 84 | + def search(self, query, default=None): |
| 85 | + if (match := re.search(f"#define (?:{query}) ", self._defs)) is not None: |
| 86 | + if not (groups := match.groups()): |
| 87 | + return self._result(query, match.group(0)[7:-1], "s0") |
| 88 | + if len(groups) == 1: |
| 89 | + return self._result(query, groups[0], "s1") |
| 90 | + return self._result(query, groups, "sn") |
| 91 | + return self._result(query, default, "sd") |
| 92 | + |
| 93 | + def _ops(self, re_pers, re_regs, re_bits, bit_fmt): |
| 94 | + reg_bits = defaultdict(list) |
| 95 | + matches = re.findall(f"#define (({re_pers})_({re_regs})_(?:{re_bits})) ", self._defs) |
| 96 | + for whole, per, reg in matches: |
| 97 | + reg_bits[f"{per}->{reg}"].append(whole) |
| 98 | + statements = [f"{reg}{bit_fmt(' | '.join(bits))};" for reg, bits in reg_bits.items()] |
| 99 | + return self._result((re_pers, re_regs, re_bits), "\n".join(statements), "r") |
| 100 | + |
| 101 | + def set(self, pers, regs, bits): |
| 102 | + return self._ops(pers, regs, bits, lambda bits: f" |= {bits}") |
| 103 | + |
| 104 | + def clear(self, pers, regs, bits): |
| 105 | + return self._ops(pers, regs, bits, lambda bits: f" &= ~({bits})") |
| 106 | + |
| 107 | + |
64 | 108 | bprops = {}
|
65 |
| -def common_rcc_map(env): |
| 109 | +def common_register_map(env): |
66 | 110 | """
|
67 |
| - Finds all CMSIS bit fields related to enabling and resetting peripherals |
68 |
| - in the RCC of the format `RCC_(REGISTER)_(PERIPHERAL)_(TYPE)` where: |
| 111 | + Finds all register and bit names in the CMSIS header file. |
69 | 112 |
|
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` |
| 113 | + :returns: a RegisterMap object that allows regex-ing for register names. |
75 | 114 | """
|
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 |
| 115 | + cmsis = env.query(":cmsis:device:headers") |
| 116 | + include_paths = [repopath("ext/arm/cmsis/CMSIS/Core/Include"), localpath(bprops["folder"])] |
| 117 | + headers = [Path(localpath(bprops["folder"], cmsis["device_header"]))] |
| 118 | + headers += env.query(":cmsis:ll:__headers", []) |
| 119 | + |
| 120 | + cmd = "arm-none-eabi-gcc" |
| 121 | + cmd += " -dM -E -mcpu=" + cmsis["core_header"][:-2].replace("core_c", "cortex-") |
| 122 | + cmd += " -D " + cmsis["define"] |
| 123 | + for p in include_paths: cmd += f" -I {p}" |
| 124 | + for h in headers: cmd += f" {h}" |
| 125 | + output = subprocess.run(cmd, shell=True, capture_output=True) |
| 126 | + |
| 127 | + return RegisterMap(output.stdout.decode("utf-8"), env.log.debug) |
103 | 128 |
|
104 | 129 |
|
105 | 130 | def common_header_file(env):
|
@@ -127,7 +152,7 @@ def common_header_file(env):
|
127 | 152 | define = None
|
128 | 153 |
|
129 | 154 | 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) |
| 155 | + match = re.findall(r"if defined\((STM32[A-Z][\w\d]+)\)", content) |
131 | 156 | define = getDefineForDevice(device.identifier, match)
|
132 | 157 | if define is None or match is None:
|
133 | 158 | raise ValidateException("No device define found for '{}'!".format(device.partname))
|
@@ -198,17 +223,17 @@ def prepare(module, options):
|
198 | 223 | return False
|
199 | 224 |
|
200 | 225 | module.add_query(
|
201 |
| - EnvironmentQuery(name="rcc-map", factory=common_rcc_map)) |
| 226 | + EnvironmentQuery(name="headers", factory=common_header_file)) |
202 | 227 | module.add_query(
|
203 | 228 | EnvironmentQuery(name="peripherals", factory=common_peripherals))
|
204 | 229 | module.add_query(
|
205 |
| - EnvironmentQuery(name="headers", factory=common_header_file)) |
| 230 | + EnvironmentQuery(name="registers", factory=common_register_map)) |
206 | 231 |
|
207 | 232 | module.depends(":cmsis:core")
|
208 | 233 | return True
|
209 | 234 |
|
210 | 235 | def validate(env):
|
211 |
| - env.query("rcc-map") |
| 236 | + env.query("headers") |
212 | 237 | env.query("peripherals")
|
213 | 238 |
|
214 | 239 | def build(env):
|
|
0 commit comments