3"""Find the register SYMBOLS first-party C claims exist, and where it claims it.
5Two claim sites matter, because the invented-register defects landed through
8* a **citation** that names a register -- ``/* HUM Ch 32.3 "EASCR : Security
9 Configuration" p 1697 */``. The symbol is right there in the quoted section
10 name, together with the chapter and page the author believed it lived on.
11* a **register window** in a ``*_regs.h`` -- the struct members and the
12 ``k_..._off_...`` offset enum. #498's ``ra8_ptp_regs.h`` declared thirteen
13 registers this way while its citations named no symbol at all, so a
14 citation-only scan would have missed it entirely.
16This module only reports what the source CLAIMS. Deciding whether the manual
17agrees is ``check_hum_register_map.py``'s job, and the manual's side comes
18from ``hum_regmap.py``.
21from __future__
import annotations
24from dataclasses
import dataclass
31 (?:\.(?P<sub>\d{1,3}(?:\.\d{1,3})*))?
35 (?:(?:Table|Figure)\s+\d{1,3}(?:\.\d{1,3})*\s*)?
38 (?:\s*-\s*(?P<end>\d{1,5}))?
48CITE_SYMBOL_RE = re.compile(
r"^\s*(?P<symbol>[A-Z][A-Z0-9_]{1,23}[a-z]?)\s*(?::|$)")
52MEMBER_RE = re.compile(
53 r"^\s*volatile\s+u?int(?:8|16|32|64)_t\s+"
54 r"(?P<name>[A-Za-z_][A-Za-z0-9_]*)\s*(?:\[[^\]]*\])?\s*;"
59OFFSET_RE = re.compile(
60 r"^\s*k_(?:ra8_)?(?P<module>[a-z0-9_]+?)_off(?:set)?_(?P<symbol>[a-z0-9_]+)\s*=\s*"
61 r"(?P<value>0[xX][0-9A-Fa-f]+)[uUlL]*\s*,"
66LOOSE_CHAPTER_RE = re.compile(
r"HUM\s+Ch\s+(\d{1,2})\b")
69RESERVED_PREFIXES = (
"reserved",
"rsv",
"pad",
"dummy",
"unused")
72@dataclass(frozen=True)
74 """One place in first-party source that claims a register exists.
77 path: Repo-relative file the claim was made in.
78 line: 1-based line number of the claim.
79 symbol: Register symbol as the source spells it.
80 chapter: HUM chapter the claim is attributed to, or None when the
81 claim site carries no chapter of its own (a struct member relies
82 on the chapters its file cites).
83 page: Page the citation points at, or None for a struct claim.
84 page_end: End of a cited page range, or None.
85 offset: Byte offset the source declares, or None.
86 kind: ``cite``, ``member`` or ``offset``.
99def _line_of(text: str, position: int) -> int:
100 """Return the 1-based line number containing byte offset `position`."""
101 return text.count(
"\n", 0, position) + 1
104def cite_claims(path: str, text: str) -> list[SymbolClaim]:
105 """Every citation in `text` that names a register symbol."""
107 for match
in CITE_RE.finditer(text):
108 symbol_match = CITE_SYMBOL_RE.match(match.group(
"section"))
109 if symbol_match
is None:
111 start = int(match.group(
"start"))
112 end = int(match.group(
"end"))
if match.group(
"end")
else start
116 line=_line_of(text, match.start()),
117 symbol=symbol_match.group(
"symbol"),
118 chapter=int(match.group(
"chapter")),
128def is_reserved(name: str) -> bool:
129 """True when a struct member is padding rather than a register."""
130 lowered = name.lower()
131 return any(lowered.startswith(prefix)
for prefix
in RESERVED_PREFIXES)
134def window_claims(path: str, text: str) -> list[SymbolClaim]:
135 """Every register-window struct member and offset enumerator in `text`."""
137 for number, line
in enumerate(text.split(
"\n"), start=1):
138 member = MEMBER_RE.match(line)
139 if member
is not None:
140 name = member.group(
"name")
141 if not is_reserved(name)
and name.isupper():
142 claims.append(SymbolClaim(path, number, name,
None,
None,
None,
None,
"member"))
144 offset = OFFSET_RE.match(line)
145 if offset
is not None:
146 symbol = offset.group(
"symbol").upper()
155 int(offset.group(
"value"), 16),
162def cited_chapters(text: str) -> set[int]:
163 """Every HUM chapter number `text` names anywhere.
165 A register window is attributed to the chapters its own file names. That
166 is the attribution the #540 issue proposed, and it needs no new
167 annotation: a header that declares an MMIO window and names no chapter at
168 all cannot be checked, and is skipped rather than guessed at.
170 This uses the LOOSE pattern, not ``CITE_RE``, and deliberately so. A
171 header often states its chapter only in the ``@details`` prose of its
172 file-level Doxygen block -- ``ra8_rsip_regs_offsets.h`` says "HUM Ch 52
173 'Renesas Secure IP (RSIP-E50D)' p 3302" there and carries no standalone
174 ``/* HUM ... */`` comment at all. Requiring the strict form left that
175 whole header unattributed and therefore unchecked, which is the opposite
176 of what this gate is for. Attribution only has to know which chapters a
177 file is ABOUT; whether an individual citation is well-formed is
178 cite_check.py's question.
180 return {int(match.group(1))
for match
in LOOSE_CHAPTER_RE.finditer(text)}