Skip to content

Commit 4c25810

Browse files
committed
[st] Add register map query
1 parent 972949b commit 4c25810

File tree

3 files changed

+77
-42
lines changed

3 files changed

+77
-42
lines changed

ext/st/cmsis.lb

Lines changed: 64 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# -*- coding: utf-8 -*-
33
#
44
# Copyright (c) 2016, Fabian Greif
5-
# Copyright (c) 2017, 2024, Niklas Hauser
5+
# Copyright (c) 2017, 2024, 2025, Niklas Hauser
66
#
77
# This file is part of the modm project.
88
#
@@ -14,6 +14,8 @@
1414
import re
1515
from pathlib import Path
1616
from collections import defaultdict
17+
import subprocess
18+
1719

1820
def getDefineForDevice(device_id, familyDefines):
1921
"""
@@ -61,45 +63,67 @@ def getDefineForDevice(device_id, familyDefines):
6163

6264
return None
6365

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+
64107
bprops = {}
65-
def common_rcc_map(env):
108+
def common_register_map(env):
66109
"""
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.
69111
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.
75113
"""
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)
103127

104128

105129
def common_header_file(env):
@@ -127,7 +151,7 @@ def common_header_file(env):
127151
define = None
128152

129153
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)
131155
define = getDefineForDevice(device.identifier, match)
132156
if define is None or match is None:
133157
raise ValidateException("No device define found for '{}'!".format(device.partname))
@@ -198,17 +222,17 @@ def prepare(module, options):
198222
return False
199223

200224
module.add_query(
201-
EnvironmentQuery(name="rcc-map", factory=common_rcc_map))
225+
EnvironmentQuery(name="headers", factory=common_header_file))
202226
module.add_query(
203227
EnvironmentQuery(name="peripherals", factory=common_peripherals))
204228
module.add_query(
205-
EnvironmentQuery(name="headers", factory=common_header_file))
229+
EnvironmentQuery(name="registers", factory=common_register_map))
206230

207231
module.depends(":cmsis:core")
208232
return True
209233

210234
def validate(env):
211-
env.query("rcc-map")
235+
env.query("headers")
212236
env.query("peripherals")
213237

214238
def build(env):

ext/st/ll.lb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,15 @@ def prepare(module, options):
4141
return False
4242

4343
folder = f"stm32{target.family}xx"
44+
headers = []
4445
for header in Path(localpath(f"cube-hal/{folder}/Inc")).glob("*_ll_*.h"):
45-
module.add_submodule(Header(header))
46+
module.add_submodule(header := Header(header))
47+
headers.append(header)
48+
49+
module.add_query(
50+
EnvironmentQuery(name="__headers", factory=lambda env:
51+
[header.header for header in headers
52+
if env.has_module(f":cmsis:ll:{header.name}")]))
4653

4754
module.depends(":cmsis:device")
4855
return True

src/modm/platform/clock/stm32/module.lb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
1313
# -----------------------------------------------------------------------------
1414

15+
from collections import defaultdict
1516

1617
def init(module):
1718
module.name = ":platform:rcc"
@@ -29,6 +30,7 @@ def prepare(module, options):
2930
def build(env):
3031
device = env[":target"]
3132
driver = device.get_driver("rcc")
33+
regs = env.query(":cmsis:device:registers")
3234

3335
properties = {}
3436
properties["target"] = target = device.identifier
@@ -143,7 +145,9 @@ def build(env):
143145
env.template("rcc.hpp.in")
144146

145147
all_peripherals = env.query(":cmsis:device:peripherals")
146-
rcc_map = env.query(":cmsis:device:rcc-map")
148+
rcc_map = defaultdict(dict)
149+
for (reg, per, typ) in regs.findall(r"RCC_([A-Z0-9]*?)_([A-Z0-9]+?)(EN|RST)"):
150+
rcc_map[per][typ] = reg
147151
rcc_enable = {}
148152
rcc_reset = {}
149153

0 commit comments

Comments
 (0)