Files
rpicarrierboard/PORT_STATUS.md
noise fb19caa95d Add GPS + PPS distribution sheet (all rev-A blocks captured)
- New carrier/GPS_PPS.kicad_sch via tools/build_gps.py: J_GPS 5-pin
  (PPS + UART2 NMEA + 3V3_AUX + GND) per Pinout_BOM section 6
- PPS chain: GPS_PPS_RAW -> PESD3V3L1BA TVS -> 74LVC1G17 Schmitt
  (+3V3_AUX) -> PPS_CLEAN -> 33R series -> SYNC_OUT (CM5 pin 18 + J2.6),
  plus J_PPS1 (UWB) / J_PPS2 (scope) 2-pin taps; board is PPS sink only
- NMEA on UART2 (GPIO4/GPIO5) per correction C2; debug console keeps UART0
- Converted SYNC_OUT/GPIO4/GPIO5 local labels on CM5_GPIO to global
  (names unchanged) to reach the new sheet; root sheet-symbol needs no pins
- J_GPS pins 1/2 typed output (GPS drives PPS/TX) for driver-aware checks
- Verify: ERC 133->133 zero new; analyzer +73/-0 zero regressions; netlist
  confirms all cross-sheet nets; SPICE 24 pass / 2 benign warn / 0 fail

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 07:22:16 -04:00

161 lines
16 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).
## ▶ 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`, `Power` (stages 14), `GPS_PPS`. **ALL
rev-A circuit blocks are now captured.** Baseline anchor: `analysis/baseline/carrier_stripped.json`.
**Done:** strip phase (microSD/HDMI/MIPI/PCIe-M2); M.2 E-key sheet (PCIe ×1); Power stage 1
(LM74700 → `VIN_PROT`), stage 2 (LM61460-Q1 → `+5v` 5 A, SPICE 5.02 V), stage 3 (LM61460-Q1 →
`+3V3_RF` 4 A, SPICE 3.32 V, EN ← `PCIE_PWR_EN`), stage 4 (TPS54202 → `+3V3_AUX` ~1 A, SPICE
3.315 V); **GPS + PPS sheet** (`carrier/GPS_PPS.kicad_sch` via `tools/build_gps.py`: J_GPS
5-pin per Pinout_BOM §6, PPS → PESD3V3 TVS → 74LVC1G17 @ +3V3_AUX → `PPS_CLEAN` → Rs 33Ω →
`SYNC_OUT` (CM5 pin 18 + J2.6) + J_PPS1/J_PPS2 taps; NMEA on **UART2 GPIO4/GPIO5** per
correction C2 — netlist: GPIO4 = Module1.54+J8.7+J_GPS.3, GPIO5 = Module1.34+J8.29+J_GPS.2).
The `SYNC_OUT`/`GPIO4`/`GPIO5` local labels on CM5_GPIO were converted to **global** labels
(names unchanged, nothing else used them) to reach the new sheet without root plumbing.
Current whole-design **ERC = 133** (all pre-existing types; a good edit adds **zero new**).
**NEXT ACTION → design-review + close-out passes** (no new blocks):
1. **Power-budget review** of the whole tree (loads vs ratings; the kicad skill's power-tree
check) + re-read PORT_STATUS "Confirm vs datasheet" leftovers (stage-1 C_VCAP/FET pick,
stage-2/3 L saturation ≥ Ipk, RT freq).
2. **Hygiene pass** (checklist in kicad-port skill): refdes numbering (un-numbered generated
refs `J_E`/`J_PWR`/`U_BUCK5`… trip kicad-cli "annotation errors" — decide whether to number
them), title blocks, netlist-vs-BOM sanity, design notes.
3. Then merge `port/strip-phase` → main and start layout prep (DF40 placement copy from ref).
**How to build/verify (every change):**
- Sheets are generated wholesale — edit `tools/build_ekey.py` / `tools/build_power.py`, then
`python3 tools/build_power.py --apply`. Custom IC symbols go in `carrier/CM5IO.kicad_sym`
(bare name) AND embed in the sheet as `CM5IO:<name>` — else ERC `lib_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 `+5v` is hierarchical (not global);
type feedback pins `passive`; connector/module pin types may need remodeling for cross-part ERC.
## 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 ✅ DONE.** `LM61460-Q1` (6A, 42V absmax sync; **custom symbol built from the datasheet**`carrier/CM5IO.kicad_sym` as `CM5IO:LM61460-Q1`, fp `Package_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. **`+5v` kept HIERARCHICAL** (pre-existing carrier net; global would collide `same_local_global_label`) — Power sheet exposes a `+5v` sheet pin, tied into the root `+5v` net; 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_RF` 4A ✅ DONE.** Second `LM61460-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 ← CM5 `PCIE_PWR_EN`** (Module1 pin 106: NC swapped for a global label on `CM5_HighSpeed`) + **R_EN33 100k pull-down** (mirrors reference R14 — netlist-verified `Module1.106 + U_BUCK33R.7 + R_EN33.1`) so the Wi-Fi rail is OFF until the CM5 asserts it. **E-key temp `#FLG33RF` PWR_FLAG removed** (build_ekey); the buck's PWR_FLAG on L33.2 is now the sole `+3V3_RF` driver — 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-cli` netlist export prints a pre-existing "annotation errors" warning — un-numbered generated refdeses like `J_E`/`J_PWR`; present at fd627f5 too, cosmetic.)
- **Stage 4 — U_BUCK33A 15→`+3V3_AUX` ~1A ✅ DONE (power sheet complete).** `TPS54202` (SOT-23-6, sync, fixed 500kHz, VFB=0.596V). **Abs-max gate PASSED but thin:** VIN abs-max **30V** vs SMBJ18A clamp ≤29.2V at full rated 600W surge → 0.8V transient margin (operating max 28V is not the surge number). Custom symbol `CM5IO:TPS54202` (the KiCad lib symbol `extends TPS54302` — no own pins, can't embed standalone) + bare copy in `carrier/CM5IO.kicad_sym`. FB per TI's 3.3V table row: **100k/22.1k + Cff 56pF → 3.29V (SPICE: vfb 0.597V, inrush settles 3.315V)**. L 10µH/2.5A (ΔI~0.52A), Cout 2×22µF/16V, Cboot 100n, Cin 10µF/50V+100n. **EN abs-max is only 7V → NO direct VIN tie** (unlike LM61460): UVLO divider **866k/110k** (solved from Ip=0.7µA/Ih=1.55µA, VEN 1.21/1.19V) → **start 10.13V / stop 8.61V**, EN ~1.9V at 15V in (~3.5V at 29V clamp — safe). FB+EN pins typed `passive` (divider-only nets). Verify: ERC 133→133 zero new; analyzer +65/0 zero regressions (warn/error set identical to stage 3); SPICE 23 pass / 2 benign warn / 0 fail / 0 skip. **New tooling gap found:** `simulate_subcircuits.py` lowercases refdeses into SPICE names — `C_IN33A` vs stage-3 `C_IN33a` collided ("device already exists") and silently SKIPPED the whole VIN_PROT decoupling group → renamed to `C_IN33Aa`. Rule: **never create refdeses differing only by case.**
5. **GPS + PPS distribution ✅ DONE** (`carrier/GPS_PPS.kicad_sch`, generated by `tools/build_gps.py`).
Per Pinout_BOM §6 (the authority — it CORRECTS the design doc): J_GPS 5-pin (1=PPS, 2=NMEA_RX←GPS TX,
3=NMEA_TX→GPS RX, 4=+3V3_AUX, 5=GND); `GPS_PPS_RAW` → TVS_PPS `PESD3V3L1BA` → U_PPS `74LVC1G17`
(SOT-23-5, +3V3_AUX, pin 1 true-NC) → `PPS_CLEAN` → R_PPS 33Ω → `SYNC_OUT` + J_PPS1 (UWB) / J_PPS2
(scope), 2-pin taps. **NMEA = UART2 on GPIO4/GPIO5** (correction C2; UART0/GPIO14-15 stays debug).
Cross-sheet wiring: converted the 6 local labels (`SYNC_OUT`×2, `GPIO4`×2, `GPIO5`×2 on CM5_GPIO) to
**global** labels — net names unchanged so no multi-label (LB-001) findings, ERC-neutral (133→133);
GPS sheet uses the same global names, root sheet-symbol has NO pins. J_GPS pins 1/2 re-typed `output`
(GPS module drives PPS + TX) so the analyzer's no_driver gate passes — same trick as the E-key socket.
**Verify:** ERC 133→133 zero new; analyzer +73/0 **0 regressions** (only the pre-existing MPN-coverage
counter moved 50→57 parts); netlist confirms all 6 cross-sheet nets (SYNC_OUT = Module1.18+J2.6+R_PPS.2);
SPICE 24 pass / 2 benign warn / 0 fail / 0 skip.
## 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.