- Second LM61460-Q1 (U_BUCK33R), FB 23.2k/10.0k -> 3.32V (SPICE: vfb 0.994V, inrush settles 3.32V); shared BOM with stage 2 (L 4.7uH/7A, RT 31.6k ~420kHz) - BIAS -> +3V3_RF (datasheet-valid >=3.1V, falls back to VIN below) - EN <- PCIE_PWR_EN: Module1 pin 106 NC swapped for a global label on CM5_HighSpeed + 100k pull-down (mirrors reference R14) so the Wi-Fi rail stays off until CM5 asserts it; netlist-verified 3-node net - Removed the E-key temp PWR_FLAG (#FLG33RF); buck PWR_FLAG on L33.2 is now the sole +3V3_RF driver - Verify: ERC 133->133 zero new; analyzer +49/-0 zero regressions; SPICE 17 pass / 2 benign warn / 0 fail Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
13 KiB
CM5 Carrier Port — Status & Handoff
Handoff doc for whoever (human or LLM) continues this work. Read this first, then
.claude/skills/kicad-port/SKILL.md(the mandatory workflow/verify-loop), thenCM5_Carrier_Design.md(frozen rev-A spec).
▶ RESUME HERE (session handoff, 2026-07-01)
State: branch port/strip-phase. Sheets in carrier/: root CM5IO, CM5_GPIO,
CM5_HighSpeed (reference, stripped), M2_Ekey (E-key, done), Power (stages 1–3 done).
Baseline anchor: analysis/baseline/carrier_stripped.json.
Done: strip phase (microSD/HDMI/MIPI/PCIe-M2); M.2 E-key sheet (PCIe ×1 to CM5); Power
stage 1 (LM74700 ideal-diode input protection → VIN_PROT); Power stage 2 (LM61460-Q1
15→+5v 5 A sync buck, SPICE-verified 5.02 V); Power stage 3 (LM61460-Q1 15→+3V3_RF 4 A,
SPICE-verified 3.32 V; EN ← CM5 PCIE_PWR_EN/106 + 100k pull-down like reference R14; E-key
temp PWR_FLAG removed — buck is the sole +3V3_RF driver). Current whole-design ERC = 133
(all pre-existing types; a good edit adds zero new).
NEXT ACTION → Power Stage 4: U_BUCK33A 15→+3V3_AUX ~1 A. Candidate part TPS54202
(28 V sync) — datasheet-verify first (KiCad has Regulator_Switching:TPS54202DDC). One check
that MUST pass: the SMBJ18A clamp (~29 V at rated Ipp) vs the part's VIN abs-max — if the
datasheet says <30 V abs-max, pick a 36 V-class sync part instead (LMR51430/TPS54360-class)
or justify why the surge case is acceptable. FB divider per that part's VREF; SPICE-verify.
+3V3_AUX is a NEW net → global label. Then the GPS + PPS sheet, then power-budget review.
How to build/verify (every change):
- Sheets are generated wholesale — edit
tools/build_ekey.py/tools/build_power.py, thenpython3 tools/build_power.py --apply. Custom IC symbols go incarrier/CM5IO.kicad_sym(bare name) AND embed in the sheet asCM5IO:<name>— else ERClib_symbol_issues. - Verify loop (judge by the integrated root run, not standalone sub-sheets):
kicad-cli sch erc --format json -o /tmp/e.json carrier/CM5IO.kicad_sch(track delta vs 133, zero new types) →analyze_schematic.py carrier/CM5IO.kicad_sch+diff_analysis.py analysis/baseline/carrier_stripped.json …(zero regressions) → for bucks,simulate_subcircuits.py. - Gotchas (full list in memory
port-progress): grid-snap all coords to 1.27 mm or labels don't bind; PWR_FLAG/GND must sit on a real pin coord; pre-existing+5vis hierarchical (not global); type feedback pinspassive; connector/module pin types may need remodeling for cross-part ERC.
What this repo is
Two intertwined goals:
- Improve Claude's EDA tooling — the
kicad-happyskill suite (kicad,datasheets,spice, …), wired in as Claude Code skills via~/.claude/skills/{kicad,datasheets,spice} -> kicad-happy/skills/*symlinks. The toolkit clones (kicad-happy/,kicad-claude-toolkit/,MCP-KiCad/) are gitignored — re-clone if missing. - Port the Raspberry Pi CM5IO reference into a custom CM5 carrier board — the real test bed (PTP/timing instrumentation node: 15V→5V front end, GbE magjack, M.2 E-key Wi-Fi (AW7915/MT7915), 2×USB-A, USB-C programming, RTC, GPS-PPS distribution).
Method is diff-from-reference: refs/ holds the untouched CM5IO reference; carrier/ is the
working copy. Every edit is verified against analysis/baseline/cm5io.json.
Environment / tooling state (macOS, Homebrew)
kicad-cli✅ (/opt/homebrew/bin/kicad-cli) — ERC + netlist exportpoppler✅ (pdftotext) — datasheet page selectionngspice✅ —spiceskill; full verify loop validated on the reference (8 subcircuits pass)- Python ≥3.10 (Homebrew 3.14)
The verify loop (run after EVERY schematic edit)
SCH=carrier/CM5IO.kicad_sch ; KSCR=~/.claude/skills/kicad/scripts
kicad-cli sch erc --exit-code-violations --format json -o /tmp/erc.json "$SCH" # hard gate
python3 $KSCR/analyze_schematic.py "$SCH" -o /tmp/head.json # structural
python3 $KSCR/diff_analysis.py analysis/baseline/cm5io.json /tmp/head.json --text # vs baseline
- Track the ERC DELTA, not the absolute — the reference already has 182 pre-existing
violations (mostly
unconnected_wire_endpoint). A good edit introduces zero new types. - The analyzer diff is the semantic gate (names broken design intent); ERC is the hygiene gate.
- For kept-block integrity, also diff
kicad-cli sch export netlistbefore/after — it caught a mis-inventoried part (see R8 below).
Datasheet extraction (done)
CM5 module extracted to datasheets/extracted/SC1466_43a9ec.json (200 pins, score 8.7/10,
queryable via ~/.claude/skills/datasheets/scripts/datasheet_features.py). Drove the NC pin
lists for every strip (e.g. HDMI Module1 pins = all pins whose datasheet name starts HDMI).
The strip tool: tools/retire_block.py
General block-retirement for these s-expr sheets. Capabilities:
- excise symbols by Reference; retire nets by name-glob (all label types: local / hierarchical / global)
- anchor-aware wire removal (union-find): removes a wire only if its connected component reaches no kept anchor (kept non-power pin or kept label) AND is seeded by this edit — so shared nets survive and pre-existing dangles are untouched
- place
(no_connect)on exposed Module1 pins (pin coords via a validated transformabs=(px+rot(lx,ly).x, py-rot(lx,ly).y), rot0/no-mirror; refuses other orientations) - clean orphaned power flags, dangling NCs, orphaned labels
--sheet-ports NAME,...removes root-sheet hierarchical pins + connecting wires
Usage: python3 tools/retire_block.py <sheet> --symbols J7,U18 --nets 'SD_*' --nc-pins 57,61 [--sheet-ports ...] [--apply] (dry-run without --apply).
Known gap (next improvement): does NOT auto-prune orphaned power-label/wire stubs left after a
connector is removed (e.g. leftover +3.3v/HDMI_5v labels). Those were cleaned manually — see git history.
Strip phase — DONE (microSD + HDMI + MIPI + PCIe-M2), ERC 182 → 133, 0 new violations, 0 regressions
| Block | Sheet(s) | Command(s) (run with --apply) |
|---|---|---|
| microSD | CM5_GPIO | --symbols J7,U18,C5 --nets 'SD_*' --nc-pins 57,61,62,63,67,69,75 |
| HDMI | CM5_HighSpeed | --symbols J22,J10 --nets 'HDMI0_*,HDMI1_*,HDMI_5v' --nc-pins 143,145,146,147,148,149,151,152,153,154,158,160,164,166,170,172,176,178,182,184,188,190,199,200 |
| MIPI/camera | CM5_HighSpeed, CM5_GPIO, CM5IO(root) | (1) HighSpeed --symbols J5,J16 --nets 'DPHY0_*,DPHY1_*,SCL1,SDA1' --nc-pins 115,117,121,123,127,129,133,135,139,141,175,177,181,183,187,189,193,194,195,196 (2) GPIO --nc-pins 80,82,97,100 (3) root --sheet-ports SCL0,SDA0,CAM_GPIO0,CAM_GPIO1 (4) HighSpeed --symbols R8 --nets 'SCL0,SDA0' (5) manual: remove 3 orphaned +3.3v labels + 2 wire stubs |
| PCIe-M2 (M-key/NVMe) | CM5_HighSpeed, CM5IO(root), .kicad_pro |
(1) HighSpeed --nets 'PCIE_*' --nc-pins 102,104,106,109,110,112,116,118,122,124 (2) root --drop-sheet PCIe-M2.kicad_sch (3) root --sheet-ports PCIE_CLK_P,PCIE_CLK_N,PCIE_TX_P,PCIE_TX_N,PCIE_nRST,PCIE_RX_P,PCIE_RX_N,PCIE_nCLKREQ,PCIE_PWR_EN,PCIE_nWAKE (strip dangling HighSpeed sheet-symbol pins) (4) git rm carrier/PCIe-M2.kicad_sch (5) manual: remove PCIe-M2 entry from .kicad_pro sheets array + its ERC exclusion. Design confirmed: 1× M.2 E-key (Wi-Fi) only, 0× M-key SSD — the single CM5 PCIe ×1 lane is reserved for the E-key; the freed PCIE_* lanes (now NC on Module1 unit 2) will be rewired to the fresh E-key sheet in the add phase. |
retire_block.py gained --drop-sheet FILE: removes the whole (sheet …) block by Sheetfile + its sheet_instances path (run on the parent sheet). .kicad_pro sheets-array + ERC-exclusion cleanup is still manual (next tool gap).
Notes that bit us (don't re-learn the hard way):
- MIPI lanes are labeled
DPHY0_*/DPHY1_*, NOTMIPI*. The connectors J5/J16 are camera FFCs bundling DPHY + cross-sheet I2C0 (SCL0/SDA0) +CAM_GPIO0/1— a 3-sheet hierarchical block. R8is a camera pull-up, not USB (the Explore-agent inventory was wrong). Confirmed viakicad-cli sch export netlist(R8.2 → J16.17). Always verify inventory against the netlist.SCL0/SDA0(I2C0) are camera-only 2-node nets (netlist-proven). NC'd to reserve I2C0 for a future external I2C RTC (CM5's own RTC is on-module via VBAT). If you add an external RTC, wire it here.
Next steps
Drop the PCIe-M2 sheet entirelyDONE (see strip table). Confirmed E-key-only (no M-key SSD).Re-baselineDONE —analysis/baseline/carrier_stripped.jsonis the add-phase diff anchor.- M.2 E-key sheet ✅ DONE (
carrier/M2_Ekey.kicad_sch, generated bytools/build_ekey.py). J_E = KiCadConnector:Bus_M.2_Socket_E, fpConnector_PCBEdge:M.2_2230-xx-E. PCIe link-0 wired to the 9 freed CM5 pins via global labels (PCIE_TX/RX/CLK ±, nRST, nCLKREQ, nWAKE — netlist-confirmed J_E↔Module1);+3V3_RF(4 pins) + 10µF∥2×0.1µF decoupling + temp PWR_FLAG; 11 GND; W_DISABLE1/2 10k pull-ups to +3V3_RF; ~41 NC (USB/BT, PCM/I2S, SDIO, UART, COEX, I2C, link-1). RX not coupled on carrier (card-side per M.2; BOM C_PCIE removed). Verify: ERC 133→133 (zero new); analyzer diff +7 added, 0 regressions. Connector-symbol pin types remodeled for cross-connector ERC: lines CM5 drives →passive; lines the card drives into CM5 inputs (RX pair + CLKREQ#/WAKE#) →output. Open items (review/next): (a) CM5PCIE_PWR_EN(pin 106) still NC — wire to the +3V3_RF buck EN in the power sheet; (b) the temp PWR_FLAG on +3V3_RF must be REMOVED once the buck drives it (else dual-driver ERC); (c) optionally routeWIFI_DISABLE#to a CM5 GPIO for SW radio control; (d) analyzer CG-AUD 5:1 sig/gnd on J_E is inherent to the M.2 connector (INFO, not fixable). New tool:tools/build_ekey.py(place connector + auto-NC + labels). - Power sheet (
carrier/Power.kicad_sch, generated bytools/build_power.py) — staged, sync + datasheet-built parts (user choice). Spec rating fix: the spec's buck candidates miss its own ≥20V-Vin rule (TPS54424=17V, TPS54560=async) — selecting synchronous ≥30V parts instead (LM61460-class 6A for +5V & +3V3_RF/4A; TPS54202 28V for +3V3_AUX), each verified vs datasheet before commit.- Stage 1 — input protection ✅ DONE. J_PWR(15V) → D_REV
LM74700-Q1ideal-diode + Q_REV N-FET (reverse-polarity/-current) →VIN_PROT;SMBJ18ATVS + 100µF/25V ∥ 10µF/50V bulk; C_VCAP charge-pump cap. Netlist-verified topology; ERC 133→133 (zero new); analyzer 0 regressions. Confirm vs LM74700-Q1 datasheet: C_VCAP value, EN-to-+15V_IN, FET pick (Vds≥30V, SOA/inrush) + optional gate R. - Stage 2 — U_BUCK5 15→
+5v5A ✅ DONE.LM61460-Q1(6A, 42V absmax sync; custom symbol built from the datasheet →carrier/CM5IO.kicad_symasCM5IO:LM61460-Q1, fpPackage_SO:Texas_HTSSOP-14-1EP…ThermalVias). VREF=1.0V → FB divider R_FBT5 40.2k / R_FBB5 10.0k = 5.02V (SPICE-confirmed vfb=1.0V). L5 4.7µH, Cout 2×47µF/16V, Cin 2×10µF/50V, Cboot 100n+Rboot 4.7Ω, RT 31.6k(~400kHz), BIAS→+5v, EN→VIN_PROT, PGOOD 100k→+5v.+5vkept HIERARCHICAL (pre-existing carrier net; global would collidesame_local_global_label) — Power sheet exposes a+5vsheet pin, tied into the root+5vnet; netlist-verified it drives Module1 + GPIO + USB. ERC 133→133 (zero new); analyzer +32 added 0 regressions; SPICE 12/13 pass, 1 benign warn (L5/Cboot mis-detected as LC filter). Confirm vs datasheet: RT freq, L sat ≥7A, EP=pad15, optional EN UVLO divider. - Stage 3 — U_BUCK33R 15→
+3V3_RF4A ✅ DONE. SecondLM61460-Q1(shared BOM with stage 2: L 4.7µH/7A ripple ~1.3A=33%, Cin 2×10µ/50V, Cout 2×47µ/16V, RT 31.6k ~420kHz, Cboot 100n+Rboot 4.7). FB divider R_FBT33 23.2k / R_FBB33 10.0k = 3.32V (SPICE: vfb 0.994V, inrush settles 3.32V). BIAS→+3V3_RF(datasheet: valid ≥3.1V, auto-falls back to VIN below). EN ← CM5PCIE_PWR_EN(Module1 pin 106: NC swapped for a global label onCM5_HighSpeed) + R_EN33 100k pull-down (mirrors reference R14 — netlist-verifiedModule1.106 + U_BUCK33R.7 + R_EN33.1) so the Wi-Fi rail is OFF until the CM5 asserts it. E-key temp#FLG33RFPWR_FLAG removed (build_ekey); the buck's PWR_FLAG on L33.2 is now the sole+3V3_RFdriver — netlist shows buck + all 4 J_E power pins + pull-ups + decoupling on one net. Verify: ERC 133→133 zero new; analyzer +49 added 0 regressions; SPICE 17 pass / 2 benign warn (the L/C_BOOT "LC filter" mis-detect, once per buck) / 0 fail. (Note:kicad-clinetlist export prints a pre-existing "annotation errors" warning — un-numbered generated refdeses likeJ_E/J_PWR; present atfd627f5too, cosmetic.) - Stage 4 U_BUCK33A 15→3.3V ~1A →
+3V3_AUX. SPICE-validate the feedback divider (see NEXT ACTION above for the part-selection gate).
- Stage 1 — input protection ✅ DONE. J_PWR(15V) → D_REV
- GPS + PPS distribution: GPS connector (PPS+UART+3V3+GND); PPS → TVS → Schmitt (LVC1G17) →
fan-out: CM5
Ethernet_SYNC_OUT(J2 pin-6 net, 3.3V, no translator) + 2 header taps. SPICE/timing check.
Out-of-repo notes
Richer running notes live in this machine's Claude memory:
~/.claude/projects/-Users-noise-Documents-obsidian-rpiboard/memory/ (project-cm5-eda,
port-progress, eda-tooling-gaps, tooling-installs-cm5). This doc is the portable subset.