Files
rpicarrierboard/PORT_STATUS.md
noise c469238dd5 Power sheet stage 1: 15V input protection (ideal-diode + TVS)
First stage of the power tree. New carrier/Power.kicad_sch (generated by
tools/build_power.py): J_PWR 15V inlet -> D_REV LM74700-Q1 ideal-diode
controller + Q_REV N-FET (reverse-polarity / reverse-current) -> VIN_PROT,
with SMBJ18A input TVS and 100uF/25V || 10uF/50V bulk + VCAP cap.

Part-selection note: the spec's buck candidates miss its own >=20V Vin rule
(TPS54424=17V, TPS54560=async); the 3 sync bucks (stages 2-4) will use
>=30V synchronous parts, datasheet-verified, feeding +5V/+3V3_RF/+3V3_AUX.

tools/build_power.py generalizes the build_ekey approach: generic place()
attaches a net label / GND symbol per pin (by-name connectivity, grid-snapped);
PWR_FLAGs co-located on real pin coords.

Verify: ERC 133->133 (zero new); analyzer +16 added, 0 regressions; netlist
confirms the ideal-diode topology (+15V_IN/VIN_PROT/GATE_REV/VCAP_REV/GND).

Confirm vs LM74700-Q1 datasheet: C_VCAP value, EN tie, FET Vds/SOA, gate R.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 16:41:22 -04:00

111 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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), then
> `CM5_Carrier_Design.md` (frozen rev-A spec).
## What this repo is
Two intertwined goals:
1. **Improve Claude's EDA tooling** — the [`kicad-happy`](https://github.com/aklofas/kicad-happy)
skill 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.
2. **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 export
- `poppler` ✅ (pdftotext) — datasheet page selection
- `ngspice` ✅ — `spice` skill; full verify loop validated on the reference (8 subcircuits pass)
- Python ≥3.10 (Homebrew 3.14)
## The verify loop (run after EVERY schematic edit)
```bash
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 netlist` before/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 transform
`abs=(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_*`, NOT `MIPI*`.** The connectors J5/J16 are camera FFCs
bundling DPHY + cross-sheet I2C0 (`SCL0/SDA0`) + `CAM_GPIO0/1` — a 3-sheet hierarchical block.
- **`R8` is a camera pull-up, not USB** (the Explore-agent inventory was wrong). Confirmed via
`kicad-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
1. ~~Drop the PCIe-M2 sheet entirely~~ **DONE** (see strip table). Confirmed E-key-only (no M-key SSD).
2. ~~Re-baseline~~ **DONE**`analysis/baseline/carrier_stripped.json` is the add-phase diff anchor.
3. **M.2 E-key sheet****DONE** (`carrier/M2_Ekey.kicad_sch`, generated by `tools/build_ekey.py`).
J_E = KiCad `Connector:Bus_M.2_Socket_E`, fp `Connector_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) CM5 `PCIE_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 route `WIFI_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).
4. **Power sheet** (`carrier/Power.kicad_sch`, generated by `tools/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-Q1` ideal-diode + Q_REV N-FET (reverse-polarity/-current) → `VIN_PROT`; `SMBJ18A` TVS + 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→5V 5A → `+5V`. **Stage 3** U_BUCK33R 15→3.3V 4A → `+3V3_RF` (then REMOVE the E-key temp PWR_FLAG; wire buck EN ← CM5 `PCIE_PWR_EN`/106). **Stage 4** U_BUCK33A 15→3.3V 1A → `+3V3_AUX`. SPICE-validate each feedback divider (like the reference U8/AP3441).
5. **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.