3"""Product matrix and pin-list table shape for the RA8D2 and RA8P1 groups.
5Everything here is transcribed from the two datasheets' own front matter --
6the part-numbering scheme figure, the product list, the package dimensions
7appendix and the Function Comparison table -- and nothing here parses. It is
8the vocabulary the parser and the renderer share, kept in one file so a
9product-matrix fact has exactly one home.
11Used by ``gen_pinouts.py``; see that file for the whole pipeline.
14from __future__
import annotations
17from dataclasses
import dataclass
18from pathlib
import Path
20REPO_ROOT = Path(__file__).resolve().parents[2]
21OUT_DIR = REPO_ROOT /
"docs" /
"pinouts"
28class ParseError(RuntimeError):
29 """The datasheet did not have the shape this parser requires."""
39 "A": (
"single",
False),
40 "B": (
"single",
True),
48 "R":
"5 MB (1 MB MRAM + 4 MB flash)",
49 "S":
"9 MB (1 MB MRAM + 8 MB flash)",
58@dataclass(frozen=True)
60 """One package option from the part-number scheme."""
75 "P-LFBGA224-11x11-0.65",
76 "11 mm x 11 mm, 0.65 mm pitch",
83 "P-LFBGA289-12x12-0.65",
84 "12 mm x 12 mm, 0.65 mm pitch",
91 "P-LFBGA303-15x15-0.80",
92 "15 mm x 15 mm, 0.80 mm pitch",
98@dataclass(frozen=True)
100 """One MCU group and the datasheet that defines its pin lists."""
115 pdf=REPO_ROOT /
"docs" /
"reference" /
"ra8d2-datasheet.pdf",
116 doc_id=
"R01DS0493EJ",
119 tagline=
"Arm Cortex-M85 @ 1 GHz (+ Cortex-M33 @ 250 MHz on dual-core "
120 "feature sets), graphics and Ethernet MCU",
125 pdf=REPO_ROOT /
"docs" /
"reference" /
"ra8p1-datasheet.pdf",
126 doc_id=
"R01DS0439EJ",
129 tagline=
"RA8D2 plus an Arm Ethos-U55 NPU; pin-compatible with the RA8D2 in every package",
139FIELDS = (
"power",
"port",
"exbus",
"irq",
"comms",
"timer",
"analog",
"video")
143 "power":
"Power, system, clock, debug, CAC",
144 "exbus":
"External bus / SDRAM",
145 "irq":
"External interrupt",
146 "comms":
"SCI/IIC/I3C/SPI/CANFD/USBFS/USBHS/OSPI/SSIE/SDHI/MMC/ESWM(GMII,RGMII,MII,RMII)/PDMIF",
147 "timer":
"GPT/AGT/ULPT/RTC",
148 "analog":
"ADC16H/DAC12/ACMPHS",
149 "video":
"MIPI/GLCDC/CEU",
154COLUMN_ORDER = (
"port",
"power",
"exbus",
"irq",
"comms",
"timer",
"analog",
"video")
159 "standard": ((
"AC",
True), (
"AC",
False), (
"AB",
True), (
"AB",
False)),
160 "sip": ((
"AJ",
True), (
"AJ",
False)),
166FIGURE_RE = re.compile(
r"^\s*Figure 1\.[3-8]\s+Pin assignment for (.+?)\s*$")
170 "BGA 289-pin": (
"AC",
True),
171 "without_MIPI_BGA 289-pin": (
"AC",
False),
172 "BGA 224-pin": (
"AB",
True),
173 "without_MIPI_BGA 224-pin": (
"AB",
False),
174 "BGA 303-pin": (
"AJ",
True),
175 "without_MIPI_BGA 303-pin": (
"AJ",
False),
178PORT_RE = re.compile(
r"\bP(?:[0-9A-D])\d{2}\b")
192TABLE_RE = re.compile(
193 r"^Table\s+(\d+\.\d+)\s+Pin list for the (Standard|SiP) product\s+"
194 r"\((\d+) of (\d+)\)\s*$"
197BALL_RE = re.compile(
r"^[A-Z]{1,2}\d{1,2}$")
198ROW_START_RE = re.compile(
r"^(?:[A-Z]{1,2}\d{1,2}|" + DASH +
r")(?:\s|$)")
201@dataclass(frozen=True)
203 """One orderable part number, decoded from its own characters."""
214 def cores(self) -> str:
215 """Return the core-count class encoded by this part's feature set."""
216 return FEATURE_SETS[self.feature][0]
219 def mipi(self) -> bool:
220 """Return whether this part bonds out the MIPI DSI/CSI pins."""
221 return FEATURE_SETS[self.feature][1]
224def decode_part(number: str) -> Part:
225 """Decode a part number per the datasheet part-numbering scheme."""
226 match = re.fullmatch(
227 r"R7(?P<mem>[KJ])A8(?P<grp>D2|P1)(?P<feat>[ABJK])(?P<mram>[DFRS])"
228 r"(?P<temp>[LD])(?P<qual>[CS])(?P<pkg>A[BCJ])",
232 msg = f
"unparsable part number: {number}"
233 raise ParseError(msg)
236 group=f
"RA8{match['grp']}",
237 feature=match[
"feat"],
240 quality=match[
"qual"],
241 package=match[
"pkg"],
245 sip = PACKAGES[part.package].sip
246 if (match[
"mem"] ==
"J") != sip
or (part.quality ==
"S") != sip:
247 msg = f
"inconsistent SiP encoding in {number}"
248 raise ParseError(msg)
252def variant_slug(package: str, mipi: bool) -> str:
253 """Return the stable generated-file slug for one package variant."""
254 pkg = PACKAGES[package]
255 kind =
"mipi" if mipi
else "nomipi"
256 tail =
"_sip" if pkg.sip
else ""
257 return f
"bga{pkg.balls}{tail}_{kind}"
260def ball_key(ball: str) -> tuple:
261 """Sort key putting balls in printed order: row letter, then number."""
262 match = re.fullmatch(
r"([A-Z]{1,2})(\d{1,2})", ball)
263 row, num = match.group(1), int(match.group(2))
264 return (len(row), row, num)
267def port_key(port: str) -> tuple:
268 """Return a numeric sort key for a port name, after known port names."""
269 match = re.fullmatch(
r"P([0-9A-F])(\d{2})", port)
272 return (0, match.group(1), int(match.group(2)))
275def sram_for(part: Part) -> str:
276 """SRAM size per the Function Comparison table: dual core costs 128 KB."""
277 return "1664 KB" if part.cores ==
"dual" else "1792 KB"