many eda additions
This commit is contained in:
144
.claude/skills/kicad-build/SKILL.md
Normal file
144
.claude/skills/kicad-build/SKILL.md
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
# kicad-build — PCB generation from opencode
|
||||||
|
|
||||||
|
Use this skill when building PCBs with the kicad-claude-toolkit inside opencode.
|
||||||
|
It wires KiCad's pcbnew (only available in KiCad's bundled Python) into opencode's
|
||||||
|
bash toolchain so you can generate .kicad_sch, .kicad_pcb, run ERC/DRC, and export
|
||||||
|
gerbers — all from opencode.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- KiCad.app installed at `/Applications/KiCad/KiCad.app`
|
||||||
|
- `kicad-cli` on PATH (`brew install kicad`)
|
||||||
|
- `ngspice` on PATH for simulation (`brew install ngspice`)
|
||||||
|
- `netlistsvg` on PATH for schematic SVG (`npm install -g netlistsvg`)
|
||||||
|
- Run `tools/install-kicad-toolkit` once to install circuit_toolkit into KiCad's Python
|
||||||
|
|
||||||
|
## Key paths
|
||||||
|
|
||||||
|
| What | Path |
|
||||||
|
|------|------|
|
||||||
|
| KiCad Python (has pcbnew) | `/Applications/KiCad/KiCad.app/Contents/Frameworks/Python.framework/Versions/3.9/bin/python3` |
|
||||||
|
| Launcher script | `tools/kicad-python` |
|
||||||
|
| Build orchestrator | `tools/kicad-build` |
|
||||||
|
| Install script | `tools/install-kicad-toolkit` |
|
||||||
|
| circuit_toolkit source | `kicad-claude-toolkit/python/circuit_toolkit` |
|
||||||
|
| S-expression editor | `tools/retire_block.py` |
|
||||||
|
| KiCad CLI | `/opt/homebrew/bin/kicad-cli` (or `kicad-cli` on PATH) |
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
The toolkit has two layers:
|
||||||
|
|
||||||
|
1. **circuit_toolkit** (Python) — describes circuit topology (components, nets) and
|
||||||
|
generates `.kicad_sch` / `.kicad_pcb` files. The PCB builder (`builders/pcb.py`)
|
||||||
|
calls `pcbnew` directly, so it MUST run inside KiCad's Python.
|
||||||
|
2. **kicad-cli** (binary) — runs ERC, DRC, exports gerbers/BOM/PDF/netlist. Runs
|
||||||
|
anywhere on PATH.
|
||||||
|
|
||||||
|
### The launcher
|
||||||
|
|
||||||
|
`tools/kicad-python` finds KiCad's Python and execs it. Use it for any script that
|
||||||
|
imports `pcbnew` or `circuit_toolkit.builders.pcb`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./tools/kicad-python -c "from circuit_toolkit.builders.pcb import build_pcb"
|
||||||
|
./tools/kicad-python my_script.py
|
||||||
|
./tools/kicad-python -m circuit_toolkit.build board_dir/
|
||||||
|
```
|
||||||
|
|
||||||
|
### The build orchestrator
|
||||||
|
|
||||||
|
`tools/kicad-build <board-dir> [--all]` runs the full pipeline:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. circuit_toolkit build → .kicad_sch + .kicad_pcb
|
||||||
|
2. kicad-cli sch erc → ERC report
|
||||||
|
3. kicad-cli pcb drc → DRC report
|
||||||
|
4. kicad-cli pcb export → gerber + drill + position
|
||||||
|
5. kicad-cli sch export → BOM + PDF + netlist
|
||||||
|
6. ngspice -b → SPICE simulations (if sim/ present)
|
||||||
|
```
|
||||||
|
|
||||||
|
Output lands in `<board-dir>/output/`.
|
||||||
|
|
||||||
|
## Writing a board
|
||||||
|
|
||||||
|
A board is a Python script using `circuit_toolkit.blocks`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# my-board/build.py
|
||||||
|
from circuit_toolkit import Board
|
||||||
|
from circuit_toolkit.blocks import usbc_power, ams1117_ldo, led_indicator, pin_header, m2_mounting_hole
|
||||||
|
|
||||||
|
board = Board("my-board", size=(48, 30))
|
||||||
|
vbus, gnd, cc1, cc2 = usbc_power(board, ref="J1", cc_pulldowns="5.1k")
|
||||||
|
v3v3 = ams1117_ldo(board, ref="U1", vin=vbus, gnd=gnd, output_voltage=3.3)
|
||||||
|
led_indicator(board, ref_led="D1", ref_resistor="R3", vin=v3v3, gnd=gnd, color="red")
|
||||||
|
pin_header(board, ref="J2", pins=2, label="3V3_OUT", nets=[v3v3, gnd])
|
||||||
|
for ref in ("H1", "H2", "H3", "H4"):
|
||||||
|
m2_mounting_hole(board, ref=ref)
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run:
|
||||||
|
```bash
|
||||||
|
./tools/kicad-python build.py
|
||||||
|
```
|
||||||
|
|
||||||
|
A separate `layout.py` provides component positions + tracks + vias + zones, passed
|
||||||
|
to `build_pcb()`.
|
||||||
|
|
||||||
|
## Editing KiCad s-expressions directly
|
||||||
|
|
||||||
|
For surgical edits to `.kicad_sch` or `.kicad_pcb` s-expressions (removing blocks,
|
||||||
|
flooded net cleanup, no-connect markers), use `tools/retire_block.py`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/retire_block.py carrier/CM5IO.kicad_sch \
|
||||||
|
--symbols J7,U18 --nets 'SD_*' \
|
||||||
|
--apply
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common opencode patterns
|
||||||
|
|
||||||
|
### Generate a PCB from a board definition
|
||||||
|
```bash
|
||||||
|
cd <board-dir>
|
||||||
|
./tools/kicad-python build.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run ERC after schematic changes
|
||||||
|
```bash
|
||||||
|
kicad-cli sch erc *.kicad_sch --output output/erc/erc_report.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run DRC after layout changes
|
||||||
|
```bash
|
||||||
|
kicad-cli pcb drc *.kicad_pcb --output output/drc/drc_report.html
|
||||||
|
```
|
||||||
|
|
||||||
|
### Export gerbers for fab
|
||||||
|
```bash
|
||||||
|
kicad-cli pcb export gerbers *.kicad_pcb --output output/fab/gerber
|
||||||
|
kicad-cli pcb export drill *.kicad_pcb --output output/fab/gerber
|
||||||
|
kicad-cli pcb export pos *.kicad_pcb --output output/fab/positions.csv
|
||||||
|
```
|
||||||
|
|
||||||
|
### Render 3D PCB view
|
||||||
|
```bash
|
||||||
|
kicad-cli pcb render *.kicad_pcb -o output/3d.png
|
||||||
|
```
|
||||||
|
|
||||||
|
### SPICE simulation
|
||||||
|
```bash
|
||||||
|
ngspice -b -r output/sim/run.raw sim/circuit.cir
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- **"pcbnew not found"**: use `tools/kicad-python` instead of system python3
|
||||||
|
- **"kicad-cli not found"**: `brew install kicad`
|
||||||
|
- **"ngspice not found"**: `brew install ngspice`
|
||||||
|
- **KiCad updates**: re-run `tools/install-kicad-toolkit` after KiCad updates
|
||||||
|
- **Python 3.9 vs 3.10**: KiCad ships 3.9; toolkit pyproject says >=3.10. The
|
||||||
|
`--no-build-isolation` flag in install-kicad-toolkit bypasses this (code is
|
||||||
|
compatible with 3.9).
|
||||||
93
.claude/skills/kicad-port/SKILL.md
Normal file
93
.claude/skills/kicad-port/SKILL.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
---
|
||||||
|
name: kicad-port
|
||||||
|
description: "Project skill for porting the Raspberry Pi CM5IO reference design into this custom CM5 carrier board. Use this skill on EVERY schematic/PCB task in this repo: editing .kicad_sch, defining or changing pinouts and nets, adding/removing circuit blocks, or verifying a design change. It encodes the source-of-truth for CM5 pins, the s-expression editing rules, and the MANDATORY verify loop (kicad-cli ERC + kicad-happy analyzer + diff-vs-reference). Always consult this skill before editing any KiCad file here, and run the verify loop after."
|
||||||
|
---
|
||||||
|
|
||||||
|
# CM5 Carrier — Port Workflow
|
||||||
|
|
||||||
|
We are building a custom Raspberry Pi **CM5 carrier board** by porting the official
|
||||||
|
**CM5IO reference design**. Work is currently in the **schematic / pinout phase**.
|
||||||
|
|
||||||
|
This is a **diff-from-reference** project: every change starts from a known-good reference
|
||||||
|
and must stay electrically defensible against it. Claude is the *editor* (surgical s-expr
|
||||||
|
text edits); the tooling below is the *verifier*. Never edit without verifying.
|
||||||
|
|
||||||
|
## Source of truth (consult before any pin/net decision)
|
||||||
|
|
||||||
|
1. `cm5-datasheet.pdf` — authoritative CM5 module pinout, power sequencing, pin reservations.
|
||||||
|
2. `CM5_Carrier_Pinout_BOM.md` / `CM5_Carrier_Design.md` — this board's intended pin map and BOM.
|
||||||
|
3. `refs/CM5IO.kicad_sch` (+ `CM5_GPIO`, `CM5_HighSpeed`, `PCIe-M2` sub-sheets) — the reference
|
||||||
|
schematic we port FROM. Hierarchical design; sub-sheets are referenced by UUID.
|
||||||
|
|
||||||
|
If a pin assignment disagrees between the datasheet and any other doc, **the datasheet wins** —
|
||||||
|
flag the discrepancy, don't silently pick one.
|
||||||
|
|
||||||
|
## S-expression editing rules (footguns)
|
||||||
|
|
||||||
|
KiCad `.kicad_sch` / `.kicad_pcb` files are s-expression text — readable and Edit-able.
|
||||||
|
But:
|
||||||
|
|
||||||
|
- **NEVER hand-edit or hand-invent UUIDs.** Copy-pasting a symbol/sheet block as text and
|
||||||
|
reusing its UUID corrupts the schematic (KiCad treats duplicates as the same object).
|
||||||
|
If you need a new instance, generate a fresh UUID:
|
||||||
|
`python3 -c "import uuid; print(uuid.uuid4())"`
|
||||||
|
- **Hierarchical nets:** a net can carry multiple labels across sheets (the analyzer's `LB-001`
|
||||||
|
finding). Prefer one canonical label style per cross-sheet net; don't rename one half.
|
||||||
|
- **Power nets need a driver:** every power rail needs a power symbol or `PWR_FLAG`, or ERC fails.
|
||||||
|
- After ANY structural edit, re-run the verify loop below. A passing Edit is not a passing design.
|
||||||
|
- Coordinate edits through git. Another (context-free) agent may touch these files; commit small,
|
||||||
|
review diffs, never blind-overwrite a sheet you didn't just read.
|
||||||
|
|
||||||
|
## The verify loop (MANDATORY after every schematic change)
|
||||||
|
|
||||||
|
`python3` here is Homebrew 3.14 (the kicad-happy scripts require Python ≥ 3.10).
|
||||||
|
Scripts live under `~/.claude/skills/kicad/scripts/` (symlinked from `kicad-happy/`).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SCH=path/to/your.kicad_sch # the file you changed
|
||||||
|
KSCR=~/.claude/skills/kicad/scripts
|
||||||
|
|
||||||
|
# 1. KiCad's own ERC (the hard gate — must be clean before commit)
|
||||||
|
kicad-cli sch erc --exit-code-violations --output /tmp/erc.json --format json "$SCH"
|
||||||
|
# non-zero exit => fix or revert before proceeding.
|
||||||
|
|
||||||
|
# 2. kicad-happy structural review (catches what ERC won't: decoupling gaps,
|
||||||
|
# connector ground ratios, protocol/voltage mismatches, multi-label nets)
|
||||||
|
python3 $KSCR/analyze_schematic.py "$SCH" --text # human-readable
|
||||||
|
python3 $KSCR/analyze_schematic.py "$SCH" -o /tmp/head.json # machine-readable
|
||||||
|
|
||||||
|
# 3. Diff against the committed reference baseline to see what your change moved
|
||||||
|
python3 $KSCR/diff_analysis.py analysis/baseline/cm5io.json /tmp/head.json --text
|
||||||
|
```
|
||||||
|
|
||||||
|
Treat new WARN/ERROR findings in step 2/3 as regressions to justify or fix — not noise.
|
||||||
|
|
||||||
|
## Optional deeper checks
|
||||||
|
|
||||||
|
- **SPICE** (`spice` skill): validates analog subcircuits (regulator feedback dividers, RC/LC
|
||||||
|
filters, crystal load caps). Requires a simulator on PATH — **`ngspice` IS installed**
|
||||||
|
(`/opt/homebrew/bin/ngspice`) and the loop is validated end-to-end on the reference
|
||||||
|
(8 subcircuits pass, incl. the 5V→3.3V buck feedback divider). Run after the analyzer:
|
||||||
|
`python3 ~/.claude/skills/spice/scripts/simulate_subcircuits.py <analysis.json> -o sim.json`.
|
||||||
|
- **Datasheets** (`datasheets` skill): extract CM5 / IC specs from PDFs so analyzer findings are
|
||||||
|
`datasheet-backed` rather than `heuristic`. Start with `cm5-datasheet.pdf`.
|
||||||
|
|
||||||
|
## Schematic hygiene checklist (before declaring a sheet done)
|
||||||
|
|
||||||
|
- [ ] All nets named — no auto-generated `Net-(R1-Pad1)` names in the final design
|
||||||
|
- [ ] Power rails ALL_CAPS (`+3V3`, `+5V`, `GND`); active-low uses `n` prefix (`nRESET`, `nCS`)
|
||||||
|
- [ ] `PWR_FLAG` on every power net with no explicit driver
|
||||||
|
- [ ] No-connect markers on all intentionally unconnected pins
|
||||||
|
- [ ] Hierarchical port names match the nets they carry
|
||||||
|
- [ ] Reference designators follow convention (U/R/C/L/D/Q/J/SW/F/TP/BT), annotated by block
|
||||||
|
- [ ] Title block filled (project, rev, date, author, one-line description)
|
||||||
|
- [ ] Design notes added for non-obvious choices (pull-up values, protection ratings, placement)
|
||||||
|
- [ ] **ERC passes with zero errors** (step 1 above)
|
||||||
|
|
||||||
|
## Reference shelf (not wired into the loop)
|
||||||
|
|
||||||
|
- `MCP-KiCad/` — MCP server over `pcbnew` (SWIG). PCB/fabrication only, Linux/flatpak-first.
|
||||||
|
Ignore until layout phase, and even then prefer `kicad-cli pcb` over it.
|
||||||
|
- `kicad-claude-toolkit/` — greenfield "circuit-as-Python → PCB" generator + IPC bridge.
|
||||||
|
Built for new designs, not porting. Only its `schematic-hygiene` guidance (folded in above)
|
||||||
|
is relevant now.
|
||||||
36
.gitignore
vendored
Normal file
36
.gitignore
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# --- KiCad transient / generated ---
|
||||||
|
*.lck
|
||||||
|
~*.lck
|
||||||
|
*.kicad_prl
|
||||||
|
*-backups/
|
||||||
|
fp-info-cache
|
||||||
|
*.bak
|
||||||
|
*.kicad_sch-bak
|
||||||
|
_autosave-*
|
||||||
|
|
||||||
|
# --- macOS ---
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# --- editor / MCP local artifacts ---
|
||||||
|
.history/
|
||||||
|
.playwright-mcp/
|
||||||
|
|
||||||
|
# --- External tooling clones (kept locally, not vendored into this repo) ---
|
||||||
|
# These are git clones with their own history; the kicad-port skill references
|
||||||
|
# kicad-happy via ~/.claude/skills symlinks. Re-clone if missing.
|
||||||
|
/kicad-happy/
|
||||||
|
/kicad-claude-toolkit/
|
||||||
|
/MCP-KiCad/
|
||||||
|
|
||||||
|
# --- Analysis cache (timestamped runs); committed baselines live in analysis/baseline/ ---
|
||||||
|
.kicad-happy/
|
||||||
|
analysis/runs/
|
||||||
|
|
||||||
|
# --- kicad-build toolchain output ---
|
||||||
|
output/
|
||||||
|
*.raw
|
||||||
|
*.xml
|
||||||
|
|
||||||
|
# --- python cache ---
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
95
PORT_STATUS.md
Normal file
95
PORT_STATUS.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# 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), ERC 182 → 136, 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 |
|
||||||
|
|
||||||
|
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** (NVMe M-key — not used; the M.2 **E-key** Wi-Fi will be a
|
||||||
|
FRESH sheet in the add phase, per design decision). Use `--sheet-ports` for its root hierarchical pins.
|
||||||
|
2. **Re-baseline**: capture a new analyzer baseline of `carrier/` so the add phase diffs against the
|
||||||
|
stripped design, not the original reference.
|
||||||
|
3. **Add phase**: 15V→5V buck front-end (reverse-protection + TVS), M.2 E-key (PCIe Gen2 ×1 +
|
||||||
|
CLKREQ/PERST/RF_KILL + 3.3V), GPS connector, PPS distribution (GPS PPS → TVS → Schmitt buffer →
|
||||||
|
fan-out: CM5 `Ethernet_SYNC_OUT` + 2 header taps). SPICE-validate the buck divider + PPS network.
|
||||||
|
|
||||||
|
## 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.
|
||||||
220
analysis/baseline/cm5io_sim.json
Normal file
220
analysis/baseline/cm5io_sim.json
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
{
|
||||||
|
"analyzer_type": "spice",
|
||||||
|
"schema_version": "1.3.0",
|
||||||
|
"summary": {
|
||||||
|
"total": 8,
|
||||||
|
"total_findings": 0,
|
||||||
|
"pass": 8,
|
||||||
|
"warn": 0,
|
||||||
|
"fail": 0,
|
||||||
|
"skip": 0,
|
||||||
|
"by_severity": {
|
||||||
|
"error": 0,
|
||||||
|
"warning": 0,
|
||||||
|
"info": 8
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"findings": [],
|
||||||
|
"simulation_results": [
|
||||||
|
{
|
||||||
|
"subcircuit_type": "voltage_divider",
|
||||||
|
"components": [
|
||||||
|
"R15",
|
||||||
|
"R16"
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"ratio": 0.180328,
|
||||||
|
"vout_V": 0.5950823999999999
|
||||||
|
},
|
||||||
|
"simulated": {
|
||||||
|
"vout_V": 0.595082
|
||||||
|
},
|
||||||
|
"delta": {
|
||||||
|
"vout_error_pct": 0.0
|
||||||
|
},
|
||||||
|
"status": "pass",
|
||||||
|
"elapsed_s": 1.214,
|
||||||
|
"reference": "R15/R16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subcircuit_type": "voltage_divider",
|
||||||
|
"components": [
|
||||||
|
"R15",
|
||||||
|
"R16"
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"ratio": 0.180328,
|
||||||
|
"vout_V": 0.5950823999999999
|
||||||
|
},
|
||||||
|
"simulated": {
|
||||||
|
"vout_V": 0.595082
|
||||||
|
},
|
||||||
|
"delta": {
|
||||||
|
"vout_error_pct": 0.0
|
||||||
|
},
|
||||||
|
"status": "pass",
|
||||||
|
"elapsed_s": 0.021,
|
||||||
|
"reference": "R15/R16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subcircuit_type": "decoupling",
|
||||||
|
"components": [
|
||||||
|
"C18",
|
||||||
|
"C9",
|
||||||
|
"C12",
|
||||||
|
"C6",
|
||||||
|
"C4"
|
||||||
|
],
|
||||||
|
"rail": "/00000000-0000-0000-0000-00005ed4bb5b/+5v",
|
||||||
|
"cap_count": 6,
|
||||||
|
"expected": {},
|
||||||
|
"simulated": {
|
||||||
|
"z_min_ohms": 0.0078,
|
||||||
|
"z_at_1MHz_ohms": 0.0093,
|
||||||
|
"z_at_100kHz_ohms": 0.0781
|
||||||
|
},
|
||||||
|
"delta": {},
|
||||||
|
"model_note": "generic ESR + parasitic L from self-resonant frequency",
|
||||||
|
"status": "pass",
|
||||||
|
"note": "Z=0.009\u03a9 at 1MHz",
|
||||||
|
"elapsed_s": 0.016,
|
||||||
|
"reference": "C18/C9/C12/C6/C4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subcircuit_type": "decoupling",
|
||||||
|
"components": [
|
||||||
|
"C2"
|
||||||
|
],
|
||||||
|
"rail": "+5v",
|
||||||
|
"cap_count": 1,
|
||||||
|
"expected": {},
|
||||||
|
"simulated": {
|
||||||
|
"z_min_ohms": 0.0158,
|
||||||
|
"f_at_zmin_hz": 1678740.0,
|
||||||
|
"z_at_1MHz_ohms": 0.0188,
|
||||||
|
"z_at_100kHz_ohms": 0.1594
|
||||||
|
},
|
||||||
|
"delta": {},
|
||||||
|
"model_note": "generic ESR + parasitic L from self-resonant frequency",
|
||||||
|
"status": "pass",
|
||||||
|
"note": "Z=0.019\u03a9 at 1MHz",
|
||||||
|
"elapsed_s": 0.013,
|
||||||
|
"reference": "C2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subcircuit_type": "decoupling",
|
||||||
|
"components": [
|
||||||
|
"C7",
|
||||||
|
"C3",
|
||||||
|
"C8"
|
||||||
|
],
|
||||||
|
"rail": "VBUS",
|
||||||
|
"cap_count": 3,
|
||||||
|
"expected": {},
|
||||||
|
"simulated": {
|
||||||
|
"z_min_ohms": 0.0075,
|
||||||
|
"z_at_1MHz_ohms": 0.009,
|
||||||
|
"z_at_100kHz_ohms": 0.0569
|
||||||
|
},
|
||||||
|
"delta": {},
|
||||||
|
"model_note": "generic ESR + parasitic L from self-resonant frequency",
|
||||||
|
"status": "pass",
|
||||||
|
"note": "Z=0.009\u03a9 at 1MHz",
|
||||||
|
"elapsed_s": 0.013,
|
||||||
|
"reference": "C7/C3/C8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subcircuit_type": "regulator_feedback",
|
||||||
|
"components": [
|
||||||
|
"R15",
|
||||||
|
"R16"
|
||||||
|
],
|
||||||
|
"regulator": "U8",
|
||||||
|
"expected": {
|
||||||
|
"ratio": 0.180328,
|
||||||
|
"vref_V": 0.6,
|
||||||
|
"vfb_V": 0.599951256,
|
||||||
|
"vin_V": 3.327
|
||||||
|
},
|
||||||
|
"simulated": {
|
||||||
|
"vfb_V": 0.6
|
||||||
|
},
|
||||||
|
"delta": {
|
||||||
|
"vfb_error_pct": 0.0
|
||||||
|
},
|
||||||
|
"status": "pass",
|
||||||
|
"elapsed_s": 0.01,
|
||||||
|
"reference": "R15/R16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subcircuit_type": "inrush",
|
||||||
|
"components": [
|
||||||
|
"C7",
|
||||||
|
"C3",
|
||||||
|
"C8"
|
||||||
|
],
|
||||||
|
"regulator": "U6",
|
||||||
|
"expected": {
|
||||||
|
"v_target_V": 5.0,
|
||||||
|
"estimated_inrush_A": 1.2
|
||||||
|
},
|
||||||
|
"simulated": {
|
||||||
|
"v_settled_V": 5.0
|
||||||
|
},
|
||||||
|
"delta": {
|
||||||
|
"v_settle_error_pct": 0.0
|
||||||
|
},
|
||||||
|
"status": "pass",
|
||||||
|
"note": "Transient simulated",
|
||||||
|
"elapsed_s": 0.013,
|
||||||
|
"reference": "C7/C3/C8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subcircuit_type": "inrush",
|
||||||
|
"components": [
|
||||||
|
"C16",
|
||||||
|
"C19",
|
||||||
|
"C14",
|
||||||
|
"C15",
|
||||||
|
"C17"
|
||||||
|
],
|
||||||
|
"regulator": "U8",
|
||||||
|
"expected": {
|
||||||
|
"v_target_V": 3.327,
|
||||||
|
"estimated_inrush_A": 0.133
|
||||||
|
},
|
||||||
|
"simulated": {
|
||||||
|
"v_settled_V": 3.327
|
||||||
|
},
|
||||||
|
"delta": {
|
||||||
|
"v_settle_error_pct": 0.0
|
||||||
|
},
|
||||||
|
"status": "pass",
|
||||||
|
"note": "Transient simulated",
|
||||||
|
"elapsed_s": 0.011,
|
||||||
|
"reference": "C16/C19/C14/C15/C17"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"trust_summary": {
|
||||||
|
"total_findings": 0,
|
||||||
|
"trust_level": "high",
|
||||||
|
"by_confidence": {
|
||||||
|
"deterministic": 0,
|
||||||
|
"heuristic": 0,
|
||||||
|
"datasheet-backed": 0
|
||||||
|
},
|
||||||
|
"by_evidence_source": {
|
||||||
|
"datasheet": 0,
|
||||||
|
"topology": 0,
|
||||||
|
"heuristic_rule": 0,
|
||||||
|
"symbol_footprint": 0,
|
||||||
|
"bom": 0,
|
||||||
|
"geometry": 0,
|
||||||
|
"api_lookup": 0
|
||||||
|
},
|
||||||
|
"provenance_coverage_pct": null
|
||||||
|
},
|
||||||
|
"total_elapsed_s": 1.31,
|
||||||
|
"simulator": "/opt/homebrew/bin/ngspice",
|
||||||
|
"ngspice": "/opt/homebrew/bin/ngspice"
|
||||||
|
}
|
||||||
38
analysis/spice_runs/decoupling_C18_C9_C12_C6_C4.cir
Normal file
38
analysis/spice_runs/decoupling_C18_C9_C12_C6_C4.cir
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
* Auto-generated testbench for decoupling analysis: /00000000-0000-0000-0000-00005ed4bb5b/+5v rail
|
||||||
|
* 6 capacitor(s) in parallel, series R-L-C model
|
||||||
|
* Model: ESR/ESL from pdn_impedance (package-based)
|
||||||
|
|
||||||
|
R_esr_C18 rail n_esr_0 948.7m
|
||||||
|
L_par_C18 n_esr_0 n_lpar_0 500p
|
||||||
|
C18 n_lpar_0 0 100n
|
||||||
|
R_esr_C9 rail n_esr_1 948.7m
|
||||||
|
L_par_C9 n_esr_1 n_lpar_1 500p
|
||||||
|
C9 n_lpar_1 0 100n
|
||||||
|
R_esr_C12 rail n_esr_2 948.7m
|
||||||
|
L_par_C12 n_esr_2 n_lpar_2 500p
|
||||||
|
C12 n_lpar_2 0 100n
|
||||||
|
R_esr_C6 rail n_esr_3 15.8m
|
||||||
|
L_par_C6 n_esr_3 n_lpar_3 900p
|
||||||
|
C6 n_lpar_3 0 10u
|
||||||
|
R_esr_C4 rail n_esr_4 15.8m
|
||||||
|
L_par_C4 n_esr_4 n_lpar_4 900p
|
||||||
|
C4 n_lpar_4 0 10u
|
||||||
|
R_esr_C10 rail n_esr_5 948.7m
|
||||||
|
L_par_C10 n_esr_5 n_lpar_5 500p
|
||||||
|
C10 n_lpar_5 0 100n
|
||||||
|
|
||||||
|
* 1A AC current source into the rail node — V(rail) = Z(f)
|
||||||
|
IAC 0 rail DC 0 AC 1
|
||||||
|
|
||||||
|
.control
|
||||||
|
ac dec 200 100 100Meg
|
||||||
|
let z_mag = vm(rail)
|
||||||
|
meas ac z_min min z_mag
|
||||||
|
meas ac f_at_zmin when z_mag=z_min
|
||||||
|
meas ac z_at_1M find vm(rail) at=1Meg
|
||||||
|
meas ac z_at_100k find vm(rail) at=100k
|
||||||
|
echo "z_min=$&z_min f_at_zmin=$&f_at_zmin z_at_1M=$&z_at_1M z_at_100k=$&z_at_100k n_caps=6" > analysis/spice_runs/decoupling_C18_C9_C12_C6_C4.out
|
||||||
|
quit
|
||||||
|
.endc
|
||||||
|
|
||||||
|
.end
|
||||||
24
analysis/spice_runs/decoupling_C18_C9_C12_C6_C4.log
Normal file
24
analysis/spice_runs/decoupling_C18_C9_C12_C6_C4.log
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
=== stdout ===
|
||||||
|
|
||||||
|
Note: No compatibility mode selected!
|
||||||
|
|
||||||
|
|
||||||
|
Circuit: * auto-generated testbench for decoupling analysis: /00000000-0000-0000-0000-00005ed4bb5b/+5v rail
|
||||||
|
|
||||||
|
Doing analysis at TEMP = 27.000000 and TNOM = 27.000000
|
||||||
|
|
||||||
|
Using SPARSE 1.3 as Direct Linear Solver
|
||||||
|
|
||||||
|
No. of Data Rows : 1201
|
||||||
|
z_min = 7.76885e-03 at= 1.67880e+06
|
||||||
|
meas ac f_at_zmin when z_mag=7.768850e-03 failed!
|
||||||
|
|
||||||
|
z_at_1m = 9.25020e-03
|
||||||
|
z_at_100k = 7.81266e-02
|
||||||
|
ngspice-46 done
|
||||||
|
|
||||||
|
=== stderr ===
|
||||||
|
|
||||||
|
Error: measure f_at_zmin when(WHEN) : out of interval
|
||||||
|
Error: &f_at_zmin: no such variable.
|
||||||
|
|
||||||
1
analysis/spice_runs/decoupling_C18_C9_C12_C6_C4.out
Normal file
1
analysis/spice_runs/decoupling_C18_C9_C12_C6_C4.out
Normal file
@@ -0,0 +1 @@
|
|||||||
|
z_min=0.00776885 f_at_zmin= z_at_1M=0.0092502 z_at_100k=0.0781266 n_caps=6
|
||||||
23
analysis/spice_runs/decoupling_C2.cir
Normal file
23
analysis/spice_runs/decoupling_C2.cir
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
* Auto-generated testbench for decoupling analysis: +5v rail
|
||||||
|
* 1 capacitor(s) in parallel, series R-L-C model
|
||||||
|
* Model: ESR/ESL from pdn_impedance (package-based)
|
||||||
|
|
||||||
|
R_esr_C2 rail n_esr_0 15.8m
|
||||||
|
L_par_C2 n_esr_0 n_lpar_0 900p
|
||||||
|
C2 n_lpar_0 0 10u
|
||||||
|
|
||||||
|
* 1A AC current source into the rail node — V(rail) = Z(f)
|
||||||
|
IAC 0 rail DC 0 AC 1
|
||||||
|
|
||||||
|
.control
|
||||||
|
ac dec 200 100 100Meg
|
||||||
|
let z_mag = vm(rail)
|
||||||
|
meas ac z_min min z_mag
|
||||||
|
meas ac f_at_zmin when z_mag=z_min
|
||||||
|
meas ac z_at_1M find vm(rail) at=1Meg
|
||||||
|
meas ac z_at_100k find vm(rail) at=100k
|
||||||
|
echo "z_min=$&z_min f_at_zmin=$&f_at_zmin z_at_1M=$&z_at_1M z_at_100k=$&z_at_100k n_caps=1" > analysis/spice_runs/decoupling_C2.out
|
||||||
|
quit
|
||||||
|
.endc
|
||||||
|
|
||||||
|
.end
|
||||||
40
analysis/spice_runs/decoupling_C2.log
Normal file
40
analysis/spice_runs/decoupling_C2.log
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
=== stdout ===
|
||||||
|
|
||||||
|
Note: No compatibility mode selected!
|
||||||
|
|
||||||
|
|
||||||
|
Circuit: * auto-generated testbench for decoupling analysis: +5v rail
|
||||||
|
|
||||||
|
Doing analysis at TEMP = 27.000000 and TNOM = 27.000000
|
||||||
|
|
||||||
|
Using SPARSE 1.3 as Direct Linear Solver
|
||||||
|
|
||||||
|
No. of Data Rows : 1201
|
||||||
|
z_min = 1.58000e-02 at= 1.67880e+06
|
||||||
|
f_at_zmin = 1.67874e+06
|
||||||
|
z_at_1m = 1.88393e-02
|
||||||
|
z_at_100k = 1.59375e-01
|
||||||
|
ngspice-46 done
|
||||||
|
|
||||||
|
=== stderr ===
|
||||||
|
Warning: singular matrix: check node n_lpar_0
|
||||||
|
|
||||||
|
Note: Starting dynamic gmin stepping
|
||||||
|
Warning: singular matrix: check node n_lpar_0
|
||||||
|
|
||||||
|
Warning: Dynamic gmin stepping failed
|
||||||
|
Note: Starting true gmin stepping
|
||||||
|
Warning: singular matrix: check node n_lpar_0
|
||||||
|
|
||||||
|
Warning: singular matrix: check node n_lpar_0
|
||||||
|
|
||||||
|
Warning: singular matrix: check node n_lpar_0
|
||||||
|
|
||||||
|
Warning: singular matrix: check node n_lpar_0
|
||||||
|
|
||||||
|
Warning: True gmin stepping failed
|
||||||
|
Note: Starting source stepping
|
||||||
|
Warning: source stepping failed
|
||||||
|
Note: Transient op started
|
||||||
|
Note: Transient op finished successfully
|
||||||
|
|
||||||
1
analysis/spice_runs/decoupling_C2.out
Normal file
1
analysis/spice_runs/decoupling_C2.out
Normal file
@@ -0,0 +1 @@
|
|||||||
|
z_min=0.0158 f_at_zmin=1.67874E+06 z_at_1M=0.0188393 z_at_100k=0.159375 n_caps=1
|
||||||
29
analysis/spice_runs/decoupling_C7_C3_C8.cir
Normal file
29
analysis/spice_runs/decoupling_C7_C3_C8.cir
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
* Auto-generated testbench for decoupling analysis: VBUS rail
|
||||||
|
* 3 capacitor(s) in parallel, series R-L-C model
|
||||||
|
* Model: ESR/ESL from pdn_impedance (package-based)
|
||||||
|
|
||||||
|
R_esr_C7 rail n_esr_0 15.8m
|
||||||
|
L_par_C7 n_esr_0 n_lpar_0 900p
|
||||||
|
C7 n_lpar_0 0 10u
|
||||||
|
R_esr_C3 rail n_esr_1 100m
|
||||||
|
L_par_C3 n_esr_1 n_lpar_1 7.5n
|
||||||
|
C3 n_lpar_1 0 100u
|
||||||
|
R_esr_C8 rail n_esr_2 15.8m
|
||||||
|
L_par_C8 n_esr_2 n_lpar_2 900p
|
||||||
|
C8 n_lpar_2 0 10u
|
||||||
|
|
||||||
|
* 1A AC current source into the rail node — V(rail) = Z(f)
|
||||||
|
IAC 0 rail DC 0 AC 1
|
||||||
|
|
||||||
|
.control
|
||||||
|
ac dec 200 100 100Meg
|
||||||
|
let z_mag = vm(rail)
|
||||||
|
meas ac z_min min z_mag
|
||||||
|
meas ac f_at_zmin when z_mag=z_min
|
||||||
|
meas ac z_at_1M find vm(rail) at=1Meg
|
||||||
|
meas ac z_at_100k find vm(rail) at=100k
|
||||||
|
echo "z_min=$&z_min f_at_zmin=$&f_at_zmin z_at_1M=$&z_at_1M z_at_100k=$&z_at_100k n_caps=3" > analysis/spice_runs/decoupling_C7_C3_C8.out
|
||||||
|
quit
|
||||||
|
.endc
|
||||||
|
|
||||||
|
.end
|
||||||
44
analysis/spice_runs/decoupling_C7_C3_C8.log
Normal file
44
analysis/spice_runs/decoupling_C7_C3_C8.log
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
=== stdout ===
|
||||||
|
|
||||||
|
Note: No compatibility mode selected!
|
||||||
|
|
||||||
|
|
||||||
|
Circuit: * auto-generated testbench for decoupling analysis: vbus rail
|
||||||
|
|
||||||
|
Doing analysis at TEMP = 27.000000 and TNOM = 27.000000
|
||||||
|
|
||||||
|
Using SPARSE 1.3 as Direct Linear Solver
|
||||||
|
|
||||||
|
No. of Data Rows : 1201
|
||||||
|
z_min = 7.52546e-03 at= 1.67880e+06
|
||||||
|
meas ac f_at_zmin when z_mag=7.525463e-03 failed!
|
||||||
|
|
||||||
|
z_at_1m = 8.98325e-03
|
||||||
|
z_at_100k = 5.69392e-02
|
||||||
|
ngspice-46 done
|
||||||
|
|
||||||
|
=== stderr ===
|
||||||
|
Warning: singular matrix: check node n_lpar_1
|
||||||
|
|
||||||
|
Note: Starting dynamic gmin stepping
|
||||||
|
Warning: singular matrix: check node rail
|
||||||
|
|
||||||
|
Warning: Dynamic gmin stepping failed
|
||||||
|
Note: Starting true gmin stepping
|
||||||
|
Warning: singular matrix: check node rail
|
||||||
|
|
||||||
|
Warning: singular matrix: check node rail
|
||||||
|
|
||||||
|
Warning: singular matrix: check node rail
|
||||||
|
|
||||||
|
Warning: singular matrix: check node rail
|
||||||
|
|
||||||
|
Warning: True gmin stepping failed
|
||||||
|
Note: Starting source stepping
|
||||||
|
Warning: source stepping failed
|
||||||
|
Note: Transient op started
|
||||||
|
Note: Transient op finished successfully
|
||||||
|
|
||||||
|
Error: measure f_at_zmin when(WHEN) : out of interval
|
||||||
|
Error: &f_at_zmin: no such variable.
|
||||||
|
|
||||||
1
analysis/spice_runs/decoupling_C7_C3_C8.out
Normal file
1
analysis/spice_runs/decoupling_C7_C3_C8.out
Normal file
@@ -0,0 +1 @@
|
|||||||
|
z_min=0.00752546 f_at_zmin= z_at_1M=0.00898326 z_at_100k=0.0569392 n_caps=3
|
||||||
25
analysis/spice_runs/inrush_idx0.cir
Normal file
25
analysis/spice_runs/inrush_idx0.cir
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
* Auto-generated testbench for inrush analysis: U6 (LDO)
|
||||||
|
* Output: 5.0V, 120.0µF total capacitance
|
||||||
|
* Soft-start: 0.5ms ramp
|
||||||
|
|
||||||
|
* Voltage source with linear ramp (soft-start) — 10us delay before ramp
|
||||||
|
Vreg ramp 0 PWL(0 0 10u 0 510u 5.0)
|
||||||
|
* Regulator output impedance
|
||||||
|
Rout ramp out 100m
|
||||||
|
|
||||||
|
* Output capacitors
|
||||||
|
C7 out 0 10u
|
||||||
|
C3 out 0 100u
|
||||||
|
C8 out 0 10u
|
||||||
|
|
||||||
|
.control
|
||||||
|
tran 1u 1.5m
|
||||||
|
let i_out = abs(i(Rout))
|
||||||
|
let i_peak = vecmax(i_out)
|
||||||
|
let t_peak = i_out[vecmin(abs(i_out - i_peak))]
|
||||||
|
meas tran v_settled find v(out) at=1.35m
|
||||||
|
echo "i_peak=$&i_peak t_peak=$&t_peak v_settled=$&v_settled v_target=5.0 r_out=0.1" > analysis/spice_runs/inrush_idx0.out
|
||||||
|
quit
|
||||||
|
.endc
|
||||||
|
|
||||||
|
.end
|
||||||
37
analysis/spice_runs/inrush_idx0.log
Normal file
37
analysis/spice_runs/inrush_idx0.log
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
=== stdout ===
|
||||||
|
|
||||||
|
Note: No compatibility mode selected!
|
||||||
|
|
||||||
|
|
||||||
|
Circuit: * auto-generated testbench for inrush analysis: u6 (ldo)
|
||||||
|
|
||||||
|
Doing analysis at TEMP = 27.000000 and TNOM = 27.000000
|
||||||
|
|
||||||
|
Using SPARSE 1.3 as Direct Linear Solver
|
||||||
|
|
||||||
|
Initial Transient Solution
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Node Voltage
|
||||||
|
---- -------
|
||||||
|
ramp 0
|
||||||
|
out 0
|
||||||
|
vreg#branch 0
|
||||||
|
|
||||||
|
|
||||||
|
No. of Data Rows : 1517
|
||||||
|
v_settled = 5.00000e+00
|
||||||
|
ngspice-46 done
|
||||||
|
|
||||||
|
=== stderr ===
|
||||||
|
|
||||||
|
Error: no such function as i,
|
||||||
|
or i(rout) is not available.
|
||||||
|
Error: RHS "abs(i(rout))" invalid
|
||||||
|
Warning from checkvalid: vector i_out is not available or has zero length.
|
||||||
|
Error: RHS "vecmax(i_out)" invalid
|
||||||
|
Warning from checkvalid: vector i_out is not available or has zero length.
|
||||||
|
Error: RHS "i_out[vecmin(abs(i_out - i_peak))]" invalid
|
||||||
|
Error: &i_peak: no such variable.
|
||||||
|
Error: &t_peak: no such variable.
|
||||||
|
|
||||||
1
analysis/spice_runs/inrush_idx0.out
Normal file
1
analysis/spice_runs/inrush_idx0.out
Normal file
@@ -0,0 +1 @@
|
|||||||
|
i_peak= t_peak= v_settled=5 v_target=5.0 r_out=0.1
|
||||||
27
analysis/spice_runs/inrush_idx1.cir
Normal file
27
analysis/spice_runs/inrush_idx1.cir
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
* Auto-generated testbench for inrush analysis: U8 (switching)
|
||||||
|
* Output: 3.327V, 40.1µF total capacitance
|
||||||
|
* Soft-start: 1.0ms ramp
|
||||||
|
|
||||||
|
* Voltage source with linear ramp (soft-start) — 10us delay before ramp
|
||||||
|
Vreg ramp 0 PWL(0 0 10u 0 1.01m 3.327)
|
||||||
|
* Regulator output impedance
|
||||||
|
Rout ramp out 10m
|
||||||
|
|
||||||
|
* Output capacitors
|
||||||
|
C16 out 0 10u
|
||||||
|
C19 out 0 100n
|
||||||
|
C14 out 0 10u
|
||||||
|
C15 out 0 10u
|
||||||
|
C17 out 0 10u
|
||||||
|
|
||||||
|
.control
|
||||||
|
tran 2u 3m
|
||||||
|
let i_out = abs(i(Rout))
|
||||||
|
let i_peak = vecmax(i_out)
|
||||||
|
let t_peak = i_out[vecmin(abs(i_out - i_peak))]
|
||||||
|
meas tran v_settled find v(out) at=2.7m
|
||||||
|
echo "i_peak=$&i_peak t_peak=$&t_peak v_settled=$&v_settled v_target=3.327 r_out=0.01" > analysis/spice_runs/inrush_idx1.out
|
||||||
|
quit
|
||||||
|
.endc
|
||||||
|
|
||||||
|
.end
|
||||||
37
analysis/spice_runs/inrush_idx1.log
Normal file
37
analysis/spice_runs/inrush_idx1.log
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
=== stdout ===
|
||||||
|
|
||||||
|
Note: No compatibility mode selected!
|
||||||
|
|
||||||
|
|
||||||
|
Circuit: * auto-generated testbench for inrush analysis: u8 (switching)
|
||||||
|
|
||||||
|
Doing analysis at TEMP = 27.000000 and TNOM = 27.000000
|
||||||
|
|
||||||
|
Using SPARSE 1.3 as Direct Linear Solver
|
||||||
|
|
||||||
|
Initial Transient Solution
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Node Voltage
|
||||||
|
---- -------
|
||||||
|
ramp 0
|
||||||
|
out 0
|
||||||
|
vreg#branch 0
|
||||||
|
|
||||||
|
|
||||||
|
No. of Data Rows : 1518
|
||||||
|
v_settled = 3.32700e+00
|
||||||
|
ngspice-46 done
|
||||||
|
|
||||||
|
=== stderr ===
|
||||||
|
|
||||||
|
Error: no such function as i,
|
||||||
|
or i(rout) is not available.
|
||||||
|
Error: RHS "abs(i(rout))" invalid
|
||||||
|
Warning from checkvalid: vector i_out is not available or has zero length.
|
||||||
|
Error: RHS "vecmax(i_out)" invalid
|
||||||
|
Warning from checkvalid: vector i_out is not available or has zero length.
|
||||||
|
Error: RHS "i_out[vecmin(abs(i_out - i_peak))]" invalid
|
||||||
|
Error: &i_peak: no such variable.
|
||||||
|
Error: &t_peak: no such variable.
|
||||||
|
|
||||||
1
analysis/spice_runs/inrush_idx1.out
Normal file
1
analysis/spice_runs/inrush_idx1.out
Normal file
@@ -0,0 +1 @@
|
|||||||
|
i_peak= t_peak= v_settled=3.327 v_target=3.327 r_out=0.01
|
||||||
20
analysis/spice_runs/regulator-feedback_U8_R15_R16.cir
Normal file
20
analysis/spice_runs/regulator-feedback_U8_R15_R16.cir
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
* Auto-generated testbench for regulator feedback: U8
|
||||||
|
* Divider: R15/R16, ratio=0.1803
|
||||||
|
* Expected Vout=3.327V, Vfb=0.6000V (Vref=0.6V)
|
||||||
|
* Topology: standard
|
||||||
|
* Model: ideal passives (exact for DC divider)
|
||||||
|
|
||||||
|
VIN out_rail 0 DC 3.327
|
||||||
|
R15 out_rail fb_net 10k
|
||||||
|
R16 fb_net 0 2.2k
|
||||||
|
|
||||||
|
.control
|
||||||
|
op
|
||||||
|
let vfb_sim = v(fb_net)
|
||||||
|
let expected = 0.599951256
|
||||||
|
let error_pct = abs(vfb_sim - expected) / expected * 100
|
||||||
|
echo "vfb_sim=$&vfb_sim error_pct=$&error_pct expected=0.599951256 vin=3.327 vref=0.6" > analysis/spice_runs/regulator-feedback_U8_R15_R16.out
|
||||||
|
quit
|
||||||
|
.endc
|
||||||
|
|
||||||
|
.end
|
||||||
16
analysis/spice_runs/regulator-feedback_U8_R15_R16.log
Normal file
16
analysis/spice_runs/regulator-feedback_U8_R15_R16.log
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
=== stdout ===
|
||||||
|
|
||||||
|
Note: No compatibility mode selected!
|
||||||
|
|
||||||
|
|
||||||
|
Circuit: * auto-generated testbench for regulator feedback: u8
|
||||||
|
|
||||||
|
Doing analysis at TEMP = 27.000000 and TNOM = 27.000000
|
||||||
|
|
||||||
|
Using SPARSE 1.3 as Direct Linear Solver
|
||||||
|
|
||||||
|
No. of Data Rows : 1
|
||||||
|
ngspice-46 done
|
||||||
|
|
||||||
|
=== stderr ===
|
||||||
|
|
||||||
1
analysis/spice_runs/regulator-feedback_U8_R15_R16.out
Normal file
1
analysis/spice_runs/regulator-feedback_U8_R15_R16.out
Normal file
@@ -0,0 +1 @@
|
|||||||
|
vfb_sim=0.599951 error_pct=7.27272E-05 expected=0.599951256 vin=3.327 vref=0.6
|
||||||
17
analysis/spice_runs/voltage-divider_R15_R16.cir
Normal file
17
analysis/spice_runs/voltage-divider_R15_R16.cir
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
* Auto-generated testbench for voltage divider: R15/R16
|
||||||
|
* Expected Vout = 3.3 * 0.180328 = 0.5951 V
|
||||||
|
* Model: ideal passives (exact, unloaded)
|
||||||
|
|
||||||
|
VIN M2_3v3 0 DC 3.3
|
||||||
|
R15 M2_3v3 FB 10k
|
||||||
|
R16 FB 0 2.2k
|
||||||
|
|
||||||
|
.control
|
||||||
|
op
|
||||||
|
let vout_sim = v(FB)
|
||||||
|
let error_pct = abs(vout_sim - 0.5950823999999999) / 0.5950823999999999 * 100
|
||||||
|
echo "vout_sim=$&vout_sim error_pct=$&error_pct expected=0.5950823999999999" > analysis/spice_runs/voltage-divider_R15_R16.out
|
||||||
|
quit
|
||||||
|
.endc
|
||||||
|
|
||||||
|
.end
|
||||||
16
analysis/spice_runs/voltage-divider_R15_R16.log
Normal file
16
analysis/spice_runs/voltage-divider_R15_R16.log
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
=== stdout ===
|
||||||
|
|
||||||
|
Note: No compatibility mode selected!
|
||||||
|
|
||||||
|
|
||||||
|
Circuit: * auto-generated testbench for voltage divider: r15/r16
|
||||||
|
|
||||||
|
Doing analysis at TEMP = 27.000000 and TNOM = 27.000000
|
||||||
|
|
||||||
|
Using SPARSE 1.3 as Direct Linear Solver
|
||||||
|
|
||||||
|
No. of Data Rows : 1
|
||||||
|
ngspice-46 done
|
||||||
|
|
||||||
|
=== stderr ===
|
||||||
|
|
||||||
1
analysis/spice_runs/voltage-divider_R15_R16.out
Normal file
1
analysis/spice_runs/voltage-divider_R15_R16.out
Normal file
@@ -0,0 +1 @@
|
|||||||
|
vout_sim=0.595082 error_pct=7.27272E-05 expected=0.5950823999999999
|
||||||
844
carrier/CM5IO.kicad_pro
Normal file
844
carrier/CM5IO.kicad_pro
Normal file
@@ -0,0 +1,844 @@
|
|||||||
|
{
|
||||||
|
"board": {
|
||||||
|
"3dviewports": [],
|
||||||
|
"design_settings": {
|
||||||
|
"defaults": {
|
||||||
|
"apply_defaults_to_fp_fields": false,
|
||||||
|
"apply_defaults_to_fp_shapes": false,
|
||||||
|
"apply_defaults_to_fp_text": false,
|
||||||
|
"board_outline_line_width": 0.05,
|
||||||
|
"copper_line_width": 0.2,
|
||||||
|
"copper_text_italic": false,
|
||||||
|
"copper_text_size_h": 1.5,
|
||||||
|
"copper_text_size_v": 1.5,
|
||||||
|
"copper_text_thickness": 0.3,
|
||||||
|
"copper_text_upright": false,
|
||||||
|
"courtyard_line_width": 0.05,
|
||||||
|
"dimension_precision": 1,
|
||||||
|
"dimension_units": 2,
|
||||||
|
"dimensions": {
|
||||||
|
"arrow_length": 1270000,
|
||||||
|
"extension_offset": 500000,
|
||||||
|
"keep_text_aligned": true,
|
||||||
|
"suppress_zeroes": false,
|
||||||
|
"text_position": 0,
|
||||||
|
"units_format": 1
|
||||||
|
},
|
||||||
|
"fab_line_width": 0.1,
|
||||||
|
"fab_text_italic": false,
|
||||||
|
"fab_text_size_h": 1.0,
|
||||||
|
"fab_text_size_v": 1.0,
|
||||||
|
"fab_text_thickness": 0.15,
|
||||||
|
"fab_text_upright": false,
|
||||||
|
"other_line_width": 0.1,
|
||||||
|
"other_text_italic": false,
|
||||||
|
"other_text_size_h": 1.0,
|
||||||
|
"other_text_size_v": 1.0,
|
||||||
|
"other_text_thickness": 0.15,
|
||||||
|
"other_text_upright": false,
|
||||||
|
"pads": {
|
||||||
|
"drill": 2.7,
|
||||||
|
"height": 2.7,
|
||||||
|
"width": 2.7
|
||||||
|
},
|
||||||
|
"silk_line_width": 0.12,
|
||||||
|
"silk_text_italic": false,
|
||||||
|
"silk_text_size_h": 1.0,
|
||||||
|
"silk_text_size_v": 1.0,
|
||||||
|
"silk_text_thickness": 0.15,
|
||||||
|
"silk_text_upright": false,
|
||||||
|
"zones": {
|
||||||
|
"45_degree_only": false,
|
||||||
|
"min_clearance": 0.39
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"diff_pair_dimensions": [
|
||||||
|
{
|
||||||
|
"gap": 0.0,
|
||||||
|
"via_gap": 0.0,
|
||||||
|
"width": 0.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"drc_exclusions": [
|
||||||
|
"clearance|139230000|150500000|44050c2e-137a-4ee5-aab6-af6e82cb550c|c1309054-b3ec-4f1e-9334-00cba6baa146",
|
||||||
|
"clearance|140500000|149230000|fcebfa8a-eb3d-4196-9404-532dc9ddde9f|c1309054-b3ec-4f1e-9334-00cba6baa146",
|
||||||
|
"clearance|140920000|151770000|40ece557-24f2-47a4-bf57-058580957d62|44050c2e-137a-4ee5-aab6-af6e82cb550c",
|
||||||
|
"clearance|141770000|150920000|40ece557-24f2-47a4-bf57-058580957d62|fcebfa8a-eb3d-4196-9404-532dc9ddde9f",
|
||||||
|
"clearance|146205000|147060000|cf9a04ac-e742-4cc4-b820-74b94e875632|fd267553-ab4f-45b2-b9de-3fd8c755208d",
|
||||||
|
"clearance|159795000|147060000|386d8066-f8d7-448e-8535-fb964d94eab0|49b00fb2-fbf7-4862-ac04-916c407cd0f2",
|
||||||
|
"clearance|159975000|145410000|029ebbb9-b02f-45bd-96ed-01f4d0ed11f9|8cfd9587-a6c9-4a80-b337-75a1568e78eb",
|
||||||
|
"clearance|163003858|146776424|a2c51ef8-4a45-4b19-8a83-e2fa4ed59648|49b00fb2-fbf7-4862-ac04-916c407cd0f2",
|
||||||
|
"courtyards_overlap|124749999|104420001|00000000-0000-0000-0000-00005d2e9306|00000000-0000-0000-0000-00005e42b6ed",
|
||||||
|
"footprint_type_mismatch|187000000|157525000|00000000-0000-0000-0000-00005d2fec1a|00000000-0000-0000-0000-000000000000"
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"rule_severities": {
|
||||||
|
"annular_width": "error",
|
||||||
|
"clearance": "warning",
|
||||||
|
"connection_width": "warning",
|
||||||
|
"copper_edge_clearance": "error",
|
||||||
|
"copper_sliver": "warning",
|
||||||
|
"courtyards_overlap": "error",
|
||||||
|
"diff_pair_gap_out_of_range": "warning",
|
||||||
|
"diff_pair_uncoupled_length_too_long": "error",
|
||||||
|
"drill_out_of_range": "error",
|
||||||
|
"duplicate_footprints": "warning",
|
||||||
|
"extra_footprint": "warning",
|
||||||
|
"footprint": "error",
|
||||||
|
"footprint_symbol_mismatch": "warning",
|
||||||
|
"footprint_type_mismatch": "error",
|
||||||
|
"hole_clearance": "error",
|
||||||
|
"hole_near_hole": "error",
|
||||||
|
"holes_co_located": "warning",
|
||||||
|
"invalid_outline": "error",
|
||||||
|
"isolated_copper": "warning",
|
||||||
|
"item_on_disabled_layer": "error",
|
||||||
|
"items_not_allowed": "error",
|
||||||
|
"length_out_of_range": "error",
|
||||||
|
"lib_footprint_issues": "warning",
|
||||||
|
"lib_footprint_mismatch": "warning",
|
||||||
|
"malformed_courtyard": "error",
|
||||||
|
"microvia_drill_out_of_range": "error",
|
||||||
|
"missing_courtyard": "ignore",
|
||||||
|
"missing_footprint": "warning",
|
||||||
|
"net_conflict": "warning",
|
||||||
|
"npth_inside_courtyard": "warning",
|
||||||
|
"padstack": "error",
|
||||||
|
"pth_inside_courtyard": "warning",
|
||||||
|
"shorting_items": "error",
|
||||||
|
"silk_edge_clearance": "ignore",
|
||||||
|
"silk_over_copper": "error",
|
||||||
|
"silk_overlap": "error",
|
||||||
|
"skew_out_of_range": "error",
|
||||||
|
"solder_mask_bridge": "ignore",
|
||||||
|
"starved_thermal": "ignore",
|
||||||
|
"text_height": "warning",
|
||||||
|
"text_thickness": "warning",
|
||||||
|
"through_hole_pad_without_hole": "error",
|
||||||
|
"too_many_vias": "error",
|
||||||
|
"track_dangling": "warning",
|
||||||
|
"track_width": "error",
|
||||||
|
"tracks_crossing": "error",
|
||||||
|
"unconnected_items": "error",
|
||||||
|
"unresolved_variable": "error",
|
||||||
|
"via_dangling": "warning",
|
||||||
|
"zones_intersect": "error"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"allow_blind_buried_vias": false,
|
||||||
|
"allow_microvias": false,
|
||||||
|
"max_error": 0.005,
|
||||||
|
"min_clearance": 0.125,
|
||||||
|
"min_connection": 0.0,
|
||||||
|
"min_copper_edge_clearance": 0.01,
|
||||||
|
"min_hole_clearance": 0.19,
|
||||||
|
"min_hole_to_hole": 0.24,
|
||||||
|
"min_microvia_diameter": 0.2,
|
||||||
|
"min_microvia_drill": 0.1,
|
||||||
|
"min_resolved_spokes": 2,
|
||||||
|
"min_silk_clearance": 0.0,
|
||||||
|
"min_text_height": 0.8,
|
||||||
|
"min_text_thickness": 0.08,
|
||||||
|
"min_through_hole_diameter": 0.2,
|
||||||
|
"min_track_width": 0.1,
|
||||||
|
"min_via_annular_width": 0.1,
|
||||||
|
"min_via_annulus": 0.049999999999999996,
|
||||||
|
"min_via_diameter": 0.45,
|
||||||
|
"solder_mask_to_copper_clearance": 0.0,
|
||||||
|
"use_height_for_length_calcs": true
|
||||||
|
},
|
||||||
|
"teardrop_options": [
|
||||||
|
{
|
||||||
|
"td_onpadsmd": true,
|
||||||
|
"td_onroundshapesonly": false,
|
||||||
|
"td_ontrackend": false,
|
||||||
|
"td_onviapad": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"teardrop_parameters": [
|
||||||
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
|
"td_target_name": "td_round_shape",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
|
"td_target_name": "td_rect_shape",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
|
"td_target_name": "td_track_end",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"track_widths": [
|
||||||
|
0.0,
|
||||||
|
0.127,
|
||||||
|
0.13,
|
||||||
|
0.147,
|
||||||
|
0.2,
|
||||||
|
0.23,
|
||||||
|
0.3,
|
||||||
|
0.5,
|
||||||
|
1.0,
|
||||||
|
2.0,
|
||||||
|
3.0
|
||||||
|
],
|
||||||
|
"tuning_pattern_settings": {
|
||||||
|
"diff_pair_defaults": {
|
||||||
|
"corner_radius_percentage": 100,
|
||||||
|
"corner_style": 1,
|
||||||
|
"max_amplitude": 1.0,
|
||||||
|
"min_amplitude": 0.1,
|
||||||
|
"single_sided": false,
|
||||||
|
"spacing": 0.6
|
||||||
|
},
|
||||||
|
"diff_pair_skew_defaults": {
|
||||||
|
"corner_radius_percentage": 100,
|
||||||
|
"corner_style": 1,
|
||||||
|
"max_amplitude": 1.0,
|
||||||
|
"min_amplitude": 0.1,
|
||||||
|
"single_sided": false,
|
||||||
|
"spacing": 0.6
|
||||||
|
},
|
||||||
|
"single_track_defaults": {
|
||||||
|
"corner_radius_percentage": 100,
|
||||||
|
"corner_style": 1,
|
||||||
|
"max_amplitude": 1.0,
|
||||||
|
"min_amplitude": 0.1,
|
||||||
|
"single_sided": false,
|
||||||
|
"spacing": 0.6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"via_dimensions": [
|
||||||
|
{
|
||||||
|
"diameter": 0.0,
|
||||||
|
"drill": 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"diameter": 0.45,
|
||||||
|
"drill": 0.2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"zones_allow_external_fillets": false,
|
||||||
|
"zones_use_no_outline": false
|
||||||
|
},
|
||||||
|
"ipc2581": {
|
||||||
|
"dist": "",
|
||||||
|
"distpn": "",
|
||||||
|
"internal_id": "",
|
||||||
|
"mfg": "",
|
||||||
|
"mpn": ""
|
||||||
|
},
|
||||||
|
"layer_presets": [],
|
||||||
|
"viewports": []
|
||||||
|
},
|
||||||
|
"boards": [],
|
||||||
|
"cvpcb": {
|
||||||
|
"equivalence_files": []
|
||||||
|
},
|
||||||
|
"erc": {
|
||||||
|
"erc_exclusions": [
|
||||||
|
"power_pin_not_driven|1003300|1181100|66cf9899-100d-45fb-9b9f-8f866de8a3fe|00000000-0000-0000-0000-000000000000|/e63e39d7-6ac0-4ffd-8aa3-1841a4541b55/00000000-0000-0000-0000-00005cff706a|/e63e39d7-6ac0-4ffd-8aa3-1841a4541b55/00000000-0000-0000-0000-00005cff706a|",
|
||||||
|
"power_pin_not_driven|1524000|317500|2cecda96-8fc5-40d2-a9f4-ae1444adbd06|00000000-0000-0000-0000-000000000000|/e63e39d7-6ac0-4ffd-8aa3-1841a4541b55/00000000-0000-0000-0000-00005ed4bb5b|/e63e39d7-6ac0-4ffd-8aa3-1841a4541b55/00000000-0000-0000-0000-00005ed4bb5b|",
|
||||||
|
"power_pin_not_driven|393700|1181100|0fc4267c-2119-444e-b3b2-d8a7bd88ec8a|00000000-0000-0000-0000-000000000000|/e63e39d7-6ac0-4ffd-8aa3-1841a4541b55/00000000-0000-0000-0000-00005cff706a|/e63e39d7-6ac0-4ffd-8aa3-1841a4541b55/00000000-0000-0000-0000-00005cff706a|"
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"version": 0
|
||||||
|
},
|
||||||
|
"pin_map": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"rule_severities": {
|
||||||
|
"bus_definition_conflict": "error",
|
||||||
|
"bus_entry_needed": "error",
|
||||||
|
"bus_to_bus_conflict": "error",
|
||||||
|
"bus_to_net_conflict": "error",
|
||||||
|
"conflicting_netclasses": "error",
|
||||||
|
"different_unit_footprint": "error",
|
||||||
|
"different_unit_net": "error",
|
||||||
|
"duplicate_reference": "error",
|
||||||
|
"duplicate_sheet_names": "error",
|
||||||
|
"endpoint_off_grid": "warning",
|
||||||
|
"extra_units": "error",
|
||||||
|
"global_label_dangling": "error",
|
||||||
|
"hier_label_mismatch": "error",
|
||||||
|
"label_dangling": "error",
|
||||||
|
"lib_symbol_issues": "warning",
|
||||||
|
"missing_bidi_pin": "warning",
|
||||||
|
"missing_input_pin": "warning",
|
||||||
|
"missing_power_pin": "error",
|
||||||
|
"missing_unit": "warning",
|
||||||
|
"multiple_net_names": "error",
|
||||||
|
"net_not_bus_member": "error",
|
||||||
|
"no_connect_connected": "error",
|
||||||
|
"no_connect_dangling": "error",
|
||||||
|
"pin_not_connected": "error",
|
||||||
|
"pin_not_driven": "error",
|
||||||
|
"pin_to_pin": "error",
|
||||||
|
"power_pin_not_driven": "error",
|
||||||
|
"similar_labels": "error",
|
||||||
|
"simulation_model_issue": "ignore",
|
||||||
|
"unannotated": "error",
|
||||||
|
"unit_value_mismatch": "error",
|
||||||
|
"unresolved_variable": "error",
|
||||||
|
"wire_dangling": "error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"pinned_footprint_libs": [],
|
||||||
|
"pinned_symbol_libs": []
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"filename": "CM5IO.kicad_pro",
|
||||||
|
"version": 1
|
||||||
|
},
|
||||||
|
"net_settings": {
|
||||||
|
"classes": [
|
||||||
|
{
|
||||||
|
"bus_width": 12,
|
||||||
|
"clearance": 0.125,
|
||||||
|
"diff_pair_gap": 0.25,
|
||||||
|
"diff_pair_via_gap": 0.25,
|
||||||
|
"diff_pair_width": 0.13,
|
||||||
|
"line_style": 0,
|
||||||
|
"microvia_diameter": 0.3,
|
||||||
|
"microvia_drill": 0.1,
|
||||||
|
"name": "Default",
|
||||||
|
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"track_width": 0.13,
|
||||||
|
"via_diameter": 0.45,
|
||||||
|
"via_drill": 0.2,
|
||||||
|
"wire_width": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bus_width": 12,
|
||||||
|
"clearance": 0.13,
|
||||||
|
"diff_pair_gap": 0.253,
|
||||||
|
"diff_pair_via_gap": 0.25,
|
||||||
|
"diff_pair_width": 0.127,
|
||||||
|
"line_style": 0,
|
||||||
|
"microvia_diameter": 0.3,
|
||||||
|
"microvia_drill": 0.1,
|
||||||
|
"name": "100R",
|
||||||
|
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"track_width": 0.127,
|
||||||
|
"via_diameter": 0.45,
|
||||||
|
"via_drill": 0.2,
|
||||||
|
"wire_width": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bus_width": 12,
|
||||||
|
"clearance": 0.13,
|
||||||
|
"diff_pair_gap": 0.25,
|
||||||
|
"diff_pair_via_gap": 0.25,
|
||||||
|
"diff_pair_width": 0.178,
|
||||||
|
"line_style": 0,
|
||||||
|
"microvia_diameter": 0.3,
|
||||||
|
"microvia_drill": 0.1,
|
||||||
|
"name": "50R",
|
||||||
|
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"track_width": 0.13,
|
||||||
|
"via_diameter": 0.45,
|
||||||
|
"via_drill": 0.2,
|
||||||
|
"wire_width": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bus_width": 12,
|
||||||
|
"clearance": 0.13,
|
||||||
|
"diff_pair_gap": 0.25,
|
||||||
|
"diff_pair_via_gap": 0.25,
|
||||||
|
"diff_pair_width": 0.13,
|
||||||
|
"line_style": 0,
|
||||||
|
"microvia_diameter": 0.3,
|
||||||
|
"microvia_drill": 0.1,
|
||||||
|
"name": "75R",
|
||||||
|
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"track_width": 0.13,
|
||||||
|
"via_diameter": 0.45,
|
||||||
|
"via_drill": 0.2,
|
||||||
|
"wire_width": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bus_width": 12,
|
||||||
|
"clearance": 0.13,
|
||||||
|
"diff_pair_gap": 0.253,
|
||||||
|
"diff_pair_via_gap": 0.25,
|
||||||
|
"diff_pair_width": 0.147,
|
||||||
|
"line_style": 0,
|
||||||
|
"microvia_diameter": 0.3,
|
||||||
|
"microvia_drill": 0.1,
|
||||||
|
"name": "90R",
|
||||||
|
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"track_width": 0.147,
|
||||||
|
"via_diameter": 0.45,
|
||||||
|
"via_drill": 0.2,
|
||||||
|
"wire_width": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bus_width": 12,
|
||||||
|
"clearance": 0.13,
|
||||||
|
"diff_pair_gap": 0.253,
|
||||||
|
"diff_pair_via_gap": 0.25,
|
||||||
|
"diff_pair_width": 0.127,
|
||||||
|
"line_style": 0,
|
||||||
|
"microvia_diameter": 0.3,
|
||||||
|
"microvia_drill": 0.1,
|
||||||
|
"name": "HDMI",
|
||||||
|
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"track_width": 0.127,
|
||||||
|
"via_diameter": 0.45,
|
||||||
|
"via_drill": 0.2,
|
||||||
|
"wire_width": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bus_width": 12,
|
||||||
|
"clearance": 1.0,
|
||||||
|
"diff_pair_gap": 0.25,
|
||||||
|
"diff_pair_via_gap": 0.25,
|
||||||
|
"diff_pair_width": 0.13,
|
||||||
|
"line_style": 0,
|
||||||
|
"microvia_diameter": 0.3,
|
||||||
|
"microvia_drill": 0.1,
|
||||||
|
"name": "POE TAPS",
|
||||||
|
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"track_width": 0.3,
|
||||||
|
"via_diameter": 0.45,
|
||||||
|
"via_drill": 0.2,
|
||||||
|
"wire_width": 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"net_colors": null,
|
||||||
|
"netclass_assignments": null,
|
||||||
|
"netclass_patterns": [
|
||||||
|
{
|
||||||
|
"netclass": "90R",
|
||||||
|
"pattern": "/CM5_HighSpeed/USB3*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"netclass": "100R",
|
||||||
|
"pattern": "/CM5_HighSpeed/DPHY*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"netclass": "HDMI",
|
||||||
|
"pattern": "/CM5_HighSpeed/HDMI*_D*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"netclass": "HDMI",
|
||||||
|
"pattern": "/CM5_HighSpeed/HDMI*_CK*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"netclass": "POE TAPS",
|
||||||
|
"pattern": "/CM5_GPIO ( Ethernet, GPIO, SDCARD)/TR*_TAP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"netclass": "100R",
|
||||||
|
"pattern": "/CM5_GPIO ( Ethernet, GPIO, SDCARD)/TRD*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"netclass": "90R",
|
||||||
|
"pattern": "/CM5_HighSpeed/PCIE_*X_*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"netclass": "90R",
|
||||||
|
"pattern": "/CM5_HighSpeed/PCIE_*CLK_*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"netclass": "90R",
|
||||||
|
"pattern": "/USB2*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pcbnew": {
|
||||||
|
"last_paths": {
|
||||||
|
"gencad": "CM5IOUSB3.cad",
|
||||||
|
"idf": "",
|
||||||
|
"netlist": "CM5IO.net",
|
||||||
|
"plot": "./",
|
||||||
|
"pos_files": "RPI-CM5IO-Gerber200924/",
|
||||||
|
"specctra_dsn": "",
|
||||||
|
"step": "",
|
||||||
|
"svg": "SVG/",
|
||||||
|
"vmrl": "",
|
||||||
|
"vrml": ""
|
||||||
|
},
|
||||||
|
"page_layout_descr_file": ""
|
||||||
|
},
|
||||||
|
"schematic": {
|
||||||
|
"annotate_start_num": 0,
|
||||||
|
"bom_export_filename": "",
|
||||||
|
"bom_fmt_presets": [],
|
||||||
|
"bom_fmt_settings": {
|
||||||
|
"field_delimiter": ",",
|
||||||
|
"keep_line_breaks": false,
|
||||||
|
"keep_tabs": false,
|
||||||
|
"name": "CSV",
|
||||||
|
"ref_delimiter": ",",
|
||||||
|
"ref_range_delimiter": "",
|
||||||
|
"string_delimiter": "\""
|
||||||
|
},
|
||||||
|
"bom_presets": [],
|
||||||
|
"bom_settings": {
|
||||||
|
"exclude_dnp": false,
|
||||||
|
"fields_ordered": [
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Reference",
|
||||||
|
"name": "Reference",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": true,
|
||||||
|
"label": "Value",
|
||||||
|
"name": "Value",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Datasheet",
|
||||||
|
"name": "Datasheet",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": true,
|
||||||
|
"label": "Footprint",
|
||||||
|
"name": "Footprint",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Qty",
|
||||||
|
"name": "${QUANTITY}",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": true,
|
||||||
|
"label": "DNP",
|
||||||
|
"name": "${DNP}",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "#",
|
||||||
|
"name": "${ITEM_NUMBER}",
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Field4",
|
||||||
|
"name": "Field4",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Field5",
|
||||||
|
"name": "Field5",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Field6",
|
||||||
|
"name": "Field6",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Field7",
|
||||||
|
"name": "Field7",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Field8",
|
||||||
|
"name": "Field8",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Part Description",
|
||||||
|
"name": "Part Description",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group_by": false,
|
||||||
|
"label": "Description",
|
||||||
|
"name": "Description",
|
||||||
|
"show": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filter_string": "",
|
||||||
|
"group_symbols": true,
|
||||||
|
"name": "",
|
||||||
|
"sort_asc": true,
|
||||||
|
"sort_field": "Reference"
|
||||||
|
},
|
||||||
|
"connection_grid_size": 50.0,
|
||||||
|
"drawing": {
|
||||||
|
"dashed_lines_dash_length_ratio": 12.0,
|
||||||
|
"dashed_lines_gap_length_ratio": 3.0,
|
||||||
|
"default_bus_thickness": 12.0,
|
||||||
|
"default_junction_size": 40.0,
|
||||||
|
"default_line_thickness": 6.0,
|
||||||
|
"default_text_size": 50.0,
|
||||||
|
"default_wire_thickness": 6.0,
|
||||||
|
"field_names": [],
|
||||||
|
"intersheets_ref_own_page": false,
|
||||||
|
"intersheets_ref_prefix": "",
|
||||||
|
"intersheets_ref_short": false,
|
||||||
|
"intersheets_ref_show": false,
|
||||||
|
"intersheets_ref_suffix": "",
|
||||||
|
"junction_size_choice": 3,
|
||||||
|
"label_size_ratio": 0.3,
|
||||||
|
"operating_point_overlay_i_precision": 3,
|
||||||
|
"operating_point_overlay_i_range": "~A",
|
||||||
|
"operating_point_overlay_v_precision": 3,
|
||||||
|
"operating_point_overlay_v_range": "~V",
|
||||||
|
"overbar_offset_ratio": 1.23,
|
||||||
|
"pin_symbol_size": 25.0,
|
||||||
|
"text_offset_ratio": 0.3
|
||||||
|
},
|
||||||
|
"legacy_lib_dir": "",
|
||||||
|
"legacy_lib_list": [],
|
||||||
|
"meta": {
|
||||||
|
"version": 1
|
||||||
|
},
|
||||||
|
"net_format_name": "",
|
||||||
|
"ngspice": {
|
||||||
|
"fix_include_paths": true,
|
||||||
|
"fix_passive_vals": false,
|
||||||
|
"meta": {
|
||||||
|
"version": 0
|
||||||
|
},
|
||||||
|
"model_mode": 0,
|
||||||
|
"workbook_filename": ""
|
||||||
|
},
|
||||||
|
"page_layout_descr_file": "",
|
||||||
|
"plot_directory": "./SVG",
|
||||||
|
"spice_adjust_passive_values": false,
|
||||||
|
"spice_current_sheet_as_root": false,
|
||||||
|
"spice_external_command": "spice \"%I\"",
|
||||||
|
"spice_model_current_sheet_as_root": true,
|
||||||
|
"spice_save_all_currents": false,
|
||||||
|
"spice_save_all_dissipations": false,
|
||||||
|
"spice_save_all_voltages": false,
|
||||||
|
"subpart_first_id": 65,
|
||||||
|
"subpart_id_separator": 0
|
||||||
|
},
|
||||||
|
"sheets": [
|
||||||
|
[
|
||||||
|
"e63e39d7-6ac0-4ffd-8aa3-1841a4541b55",
|
||||||
|
"Root"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"00000000-0000-0000-0000-00005cff70b1",
|
||||||
|
"CM5_HighSpeed"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"00000000-0000-0000-0000-00005cff706a",
|
||||||
|
"CM5_GPIO ( Ethernet, GPIO, SDCARD)"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"00000000-0000-0000-0000-00005ed4bb5b",
|
||||||
|
"PCIe-M2"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"text_variables": {}
|
||||||
|
}
|
||||||
3816
carrier/CM5IO.kicad_sch
Normal file
3816
carrier/CM5IO.kicad_sch
Normal file
File diff suppressed because it is too large
Load Diff
10220
carrier/CM5IO.kicad_sym
Normal file
10220
carrier/CM5IO.kicad_sym
Normal file
File diff suppressed because it is too large
Load Diff
19656
carrier/CM5_GPIO.kicad_sch
Normal file
19656
carrier/CM5_GPIO.kicad_sch
Normal file
File diff suppressed because it is too large
Load Diff
12907
carrier/CM5_HighSpeed.kicad_sch
Normal file
12907
carrier/CM5_HighSpeed.kicad_sch
Normal file
File diff suppressed because it is too large
Load Diff
7933
carrier/PCIe-M2.kicad_sch
Normal file
7933
carrier/PCIe-M2.kicad_sch
Normal file
File diff suppressed because it is too large
Load Diff
4
carrier/fp-lib-table
Normal file
4
carrier/fp-lib-table
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
(fp_lib_table
|
||||||
|
(version 7)
|
||||||
|
(lib (name "CM5IO")(type "KiCad")(uri "${KIPRJMOD}/CM5IO.pretty")(options "")(descr ""))
|
||||||
|
)
|
||||||
4
carrier/sym-lib-table
Normal file
4
carrier/sym-lib-table
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
(sym_lib_table
|
||||||
|
(version 7)
|
||||||
|
(lib (name "CM5IO")(type "KiCad")(uri "${KIPRJMOD}/CM5IO.kicad_sym")(options "")(descr ""))
|
||||||
|
)
|
||||||
1807
datasheets/extracted/SC1466_43a9ec.json
Normal file
1807
datasheets/extracted/SC1466_43a9ec.json
Normal file
File diff suppressed because it is too large
Load Diff
17
datasheets/extracted/manifest.json
Normal file
17
datasheets/extracted/manifest.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"last_updated": "2026-06-24T12:39:26-04:00",
|
||||||
|
"extractions": {
|
||||||
|
"SC1466_43a9ec": {
|
||||||
|
"file": "SC1466_43a9ec.json",
|
||||||
|
"mpn": "SC1466",
|
||||||
|
"category": "module",
|
||||||
|
"source_pdf": "cm5-datasheet.pdf",
|
||||||
|
"source_pdf_hash": "sha256:80070fefd8db6e8abc6e146c8b7b5fb318ba129cc1e28826936d547fde79c863",
|
||||||
|
"extraction_date": "2026-06-24T16:39:26.264105+00:00",
|
||||||
|
"extraction_score": 8.7,
|
||||||
|
"extraction_version": 2,
|
||||||
|
"pin_count": 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Submodule refs/.history updated: ab8c8d6b26...a29df2ece6
64
tools/install-kicad-toolkit
Executable file
64
tools/install-kicad-toolkit
Executable file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# install-kicad-toolkit — install circuit_toolkit into KiCad's bundled Python
|
||||||
|
# so pcbnew-dependent builders (pcb.py) can import it.
|
||||||
|
#
|
||||||
|
# Usage: ./install-kicad-toolkit.sh
|
||||||
|
#
|
||||||
|
# This is a one-time setup. After KiCad updates, re-run to pick up changes.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
TOOLKIT_SRC="$PROJECT_ROOT/kicad-claude-toolkit/python/circuit_toolkit"
|
||||||
|
|
||||||
|
# Find KiCad Python
|
||||||
|
KICAD_APP="/Applications/KiCad/KiCad.app"
|
||||||
|
KICAD_PYTHON="${KICAD_APP}/Contents/Frameworks/Python.framework/Versions/3.9/bin/python3"
|
||||||
|
|
||||||
|
if [ ! -x "$KICAD_PYTHON" ]; then
|
||||||
|
echo "ERROR: KiCad Python not found at $KICAD_PYTHON" >&2
|
||||||
|
echo "Install KiCad from kicad.org, then re-run." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "KiCad Python: $KICAD_PYTHON"
|
||||||
|
echo "circuit_toolkit source: $TOOLKIT_SRC"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Verify pcbnew works
|
||||||
|
echo "Checking pcbnew..."
|
||||||
|
if ! "$KICAD_PYTHON" -c "import pcbnew" 2>/dev/null; then
|
||||||
|
echo "ERROR: pcbnew not importable. Run KiCad.app at least once." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " pcbnew OK"
|
||||||
|
|
||||||
|
# Install circuit_toolkit in development mode
|
||||||
|
echo ""
|
||||||
|
echo "Installing circuit_toolkit (editable)..."
|
||||||
|
pushd "$TOOLKIT_SRC" >/dev/null
|
||||||
|
# KiCad ships Python 3.9; toolkit pyproject requires >=3.10. Use --no-build-isolation
|
||||||
|
# to bypass the version check (the code only needs 3.9 features).
|
||||||
|
"$KICAD_PYTHON" -m pip install --no-build-isolation -e . 2>&1
|
||||||
|
echo " installed"
|
||||||
|
|
||||||
|
# Also install optional sim deps
|
||||||
|
echo ""
|
||||||
|
echo "Installing optional sim deps (matplotlib, numpy)..."
|
||||||
|
"$KICAD_PYTHON" -m pip install matplotlib numpy 2>&1 || echo " (non-fatal: sim deps skipped)"
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
echo ""
|
||||||
|
echo "Verifying..."
|
||||||
|
"$KICAD_PYTHON" -c "
|
||||||
|
import pcbnew
|
||||||
|
import circuit_toolkit
|
||||||
|
from circuit_toolkit.core.board import Board
|
||||||
|
from circuit_toolkit.builders.pcb import build_pcb
|
||||||
|
print(f' circuit_toolkit {circuit_toolkit.__version__} OK (pcbnew {pcbnew.__file__})')
|
||||||
|
"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Done. circuit_toolkit is now available inside KiCad's Python."
|
||||||
|
echo "Use ./tools/kicad-python to run scripts with pcbnew access."
|
||||||
256
tools/kicad-build
Executable file
256
tools/kicad-build
Executable file
@@ -0,0 +1,256 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# kicad-build — orchestrator for circuit_toolkit + kicad-cli.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./kicad-build <board-dir> [--erc] [--drc] [--gerber] [--bom] [--schematic] [--sim] [--all]
|
||||||
|
#
|
||||||
|
# Steps (all are optional, --all runs everything):
|
||||||
|
# 1. circuit_toolkit build (generates .kicad_sch + .kicad_pcb)
|
||||||
|
# 2. kicad-cli sch erc (electrical rules check)
|
||||||
|
# 3. kicad-cli pcb drc (design rules check)
|
||||||
|
# 4. kicad-cli pcb export gerber/drill/position
|
||||||
|
# 5. kicad-cli sch export bom/pdf/netlist
|
||||||
|
# 6. SPICE simulation (if sim/ present)
|
||||||
|
#
|
||||||
|
# Requires:
|
||||||
|
# - KiCad.app installed (for pcbnew)
|
||||||
|
# - kicad-cli on PATH (homebrew: brew install kicad)
|
||||||
|
# - ngspice on PATH (for --sim, brew install ngspice)
|
||||||
|
# - netlistsvg on PATH (for --schematic SVG, npm install -g netlistsvg)
|
||||||
|
# - circuit_toolkit installed into KiCad's Python (run install-kicad-toolkit.sh)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
KICAD_PYTHON="$SCRIPT_DIR/kicad-python"
|
||||||
|
KICAD_CLI=$(command -v kicad-cli 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
# circuit_toolkit paths
|
||||||
|
TOOLKIT_SRC="$PROJECT_ROOT/kicad-claude-toolkit/python/circuit_toolkit"
|
||||||
|
TOOLKIT_PY="$TOOLKIT_SRC/circuit_toolkit"
|
||||||
|
|
||||||
|
# Output directory (relative to board dir)
|
||||||
|
OUTPUT_DIR=""
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: $0 <board-dir> [--erc] [--drc] [--gerber] [--bom] [--schematic] [--sim] [--all]"
|
||||||
|
echo ""
|
||||||
|
echo " --erc Run ERC on generated schematic"
|
||||||
|
echo " --drc Run DRC on generated PCB"
|
||||||
|
echo " --gerber Export Gerber + drill + position files"
|
||||||
|
echo " --bom Generate BOM (flat + JLCPCB)"
|
||||||
|
echo " --schematic Generate schematic SVG via netlistsvg"
|
||||||
|
echo " --sim Run SPICE simulations (if sim/ present)"
|
||||||
|
echo " --all Run all steps (default)"
|
||||||
|
echo " --dry-run Print planned commands without executing"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse args
|
||||||
|
BOARD_DIR=""
|
||||||
|
DO_ERC=false
|
||||||
|
DO_DRC=false
|
||||||
|
DO_GERBER=false
|
||||||
|
DO_BOM=false
|
||||||
|
DO_SCHEMATIC=false
|
||||||
|
DO_SIM=false
|
||||||
|
DO_ALL=true
|
||||||
|
DRY_RUN=false
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--erc) DO_ERC=true; DO_ALL=false;;
|
||||||
|
--drc) DO_DRC=true; DO_ALL=false;;
|
||||||
|
--gerber) DO_GERBER=true; DO_ALL=false;;
|
||||||
|
--bom) DO_BOM=true; DO_ALL=false;;
|
||||||
|
--schematic) DO_SCHEMATIC=true; DO_ALL=false;;
|
||||||
|
--sim) DO_SIM=true; DO_ALL=false;;
|
||||||
|
--dry-run) DRY_RUN=true;;
|
||||||
|
-h|--help) usage;;
|
||||||
|
-*)
|
||||||
|
echo "Unknown option: $arg" >&2
|
||||||
|
usage;;
|
||||||
|
*)
|
||||||
|
if [ -z "$BOARD_DIR" ]; then
|
||||||
|
BOARD_DIR="$arg"
|
||||||
|
else
|
||||||
|
echo "Unexpected argument: $arg" >&2
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$BOARD_DIR" ]; then
|
||||||
|
echo "ERROR: board directory required" >&2
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
BOARD_DIR="$(cd "$BOARD_DIR" && pwd)"
|
||||||
|
OUTPUT_DIR="$BOARD_DIR/output"
|
||||||
|
|
||||||
|
if $DO_ALL; then
|
||||||
|
DO_ERC=true; DO_DRC=true; DO_GERBER=true; DO_BOM=true; DO_SCHEMATIC=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Pre-flight checks ──────────────────────────────────────────────────
|
||||||
|
check_kicad_python() {
|
||||||
|
if ! $DRY_RUN && ! bash "$KICAD_PYTHON" -c "import pcbnew" 2>/dev/null; then
|
||||||
|
echo "ERROR: pcbnew not available. Run KiCad.app at least once, then retry." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_kicad_cli() {
|
||||||
|
if [ -z "$KICAD_CLI" ]; then
|
||||||
|
echo "ERROR: kicad-cli not on PATH. Install via: brew install kicad" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Step 1: circuit_toolkit build ──────────────────────────────────────
|
||||||
|
step_build() {
|
||||||
|
local build_script="$BOARD_DIR/build.py"
|
||||||
|
if [ ! -f "$build_script" ]; then
|
||||||
|
echo "SKIP build: no build.py in $BOARD_DIR"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "=== Step 1: circuit_toolkit build ==="
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo " [dry-run] KiCad Python $KICAD_PYTHON"
|
||||||
|
echo " [dry-run] cd $BOARD_DIR && $KICAD_PYTHON build.py"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
pushd "$BOARD_DIR" >/dev/null
|
||||||
|
PYTHONPATH="$TOOLKIT_SRC:$PYTHONPATH" \
|
||||||
|
bash "$KICAD_PYTHON" build.py
|
||||||
|
popd >/dev/null
|
||||||
|
echo " -> $OUTPUT_DIR/"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Step 2: ERC ────────────────────────────────────────────────────────
|
||||||
|
step_erc() {
|
||||||
|
local sch="$BOARD_DIR/*.kicad_sch"
|
||||||
|
sch=$(ls $sch 2>/dev/null | head -1 || true)
|
||||||
|
if [ -z "$sch" ]; then
|
||||||
|
echo "SKIP ERC: no .kicad_sch found"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "=== Step 2: ERC ==="
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo " [dry-run] $KICAD_CLI sch erc $sch"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
mkdir -p "$OUTPUT_DIR/erc"
|
||||||
|
$KICAD_CLI sch erc "$sch" --output "$OUTPUT_DIR/erc/erc_report.txt" 2>&1 || true
|
||||||
|
echo " -> $OUTPUT_DIR/erc/"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Step 3: DRC ────────────────────────────────────────────────────────
|
||||||
|
step_drc() {
|
||||||
|
local pcb="$BOARD_DIR/*.kicad_pcb"
|
||||||
|
pcb=$(ls $pcb 2>/dev/null | head -1 || true)
|
||||||
|
if [ -z "$pcb" ]; then
|
||||||
|
echo "SKIP DRC: no .kicad_pcb found"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "=== Step 3: DRC ==="
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo " [dry-run] $KICAD_CLI pcb drc $pcb"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
mkdir -p "$OUTPUT_DIR/drc"
|
||||||
|
$KICAD_CLI pcb drc "$pcb" --output "$OUTPUT_DIR/drc/drc_report.html" 2>&1 || true
|
||||||
|
echo " -> $OUTPUT_DIR/drc/"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Step 4: Gerber/Drill/Position export ──────────────────────────────
|
||||||
|
step_gerber() {
|
||||||
|
local pcb="$BOARD_DIR/*.kicad_pcb"
|
||||||
|
pcb=$(ls $pcb 2>/dev/null | head -1 || true)
|
||||||
|
if [ -z "$pcb" ]; then
|
||||||
|
echo "SKIP gerber: no .kicad_pcb found"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "=== Step 4: Gerber + Drill + Position export ==="
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo " [dry-run] $KICAD_CLI pcb export gerbers $pcb --output $OUTPUT_DIR/fab/gerber"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
mkdir -p "$OUTPUT_DIR/fab/gerber"
|
||||||
|
$KICAD_CLI pcb export gerbers "$pcb" --output "$OUTPUT_DIR/fab/gerber" 2>&1
|
||||||
|
$KICAD_CLI pcb export drill "$pcb" --output "$OUTPUT_DIR/fab/gerber" 2>&1
|
||||||
|
$KICAD_CLI pcb export pos "$pcb" --output "$OUTPUT_DIR/fab/positions.csv" 2>&1
|
||||||
|
echo " -> $OUTPUT_DIR/fab/"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Step 5: BOM + schematic PDF + netlist ─────────────────────────────
|
||||||
|
step_bom() {
|
||||||
|
local sch="$BOARD_DIR/*.kicad_sch"
|
||||||
|
sch=$(ls $sch 2>/dev/null | head -1 || true)
|
||||||
|
if [ -z "$sch" ]; then
|
||||||
|
echo "SKIP bom: no .kicad_sch found"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "=== Step 5: BOM + schematic PDF + netlist ==="
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo " [dry-run] $KICAD_CLI sch export bom $sch --output $OUTPUT_DIR/fab"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
mkdir -p "$OUTPUT_DIR/fab"
|
||||||
|
$KICAD_CLI sch export bom "$sch" --output "$OUTPUT_DIR/fab" 2>&1
|
||||||
|
$KICAD_CLI sch export pdf "$sch" --output "$OUTPUT_DIR/schematic.pdf" 2>&1
|
||||||
|
$KICAD_CLI sch export "$sch" --format netlist --output "$OUTPUT_DIR/fab/netlist.xml" 2>&1
|
||||||
|
echo " -> $OUTPUT_DIR/fab/"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Step 6: SPICE simulation ──────────────────────────────────────────
|
||||||
|
step_sim() {
|
||||||
|
if [ ! -d "$BOARD_DIR/sim" ]; then
|
||||||
|
echo "SKIP sim: no sim/ directory"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "=== Step 6: SPICE simulation ==="
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo " [dry-run] ngspice -b $BOARD_DIR/sim/*.cir"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if ! command -v ngspice &>/dev/null; then
|
||||||
|
echo "SKIP sim: ngspice not on PATH (brew install ngspice)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
mkdir -p "$OUTPUT_DIR/sim"
|
||||||
|
for cir in "$BOARD_DIR"/sim/*.cir; do
|
||||||
|
echo " sim: $(basename "$cir")"
|
||||||
|
ngspice -b -r "$OUTPUT_DIR/sim/$(basename "$cir" .cir).raw" "$cir" 2>&1 || true
|
||||||
|
done
|
||||||
|
echo " -> $OUTPUT_DIR/sim/"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Run ────────────────────────────────────────────────────────────────
|
||||||
|
echo "kicad-build: $BOARD_DIR"
|
||||||
|
echo " KiCad Python: $KICAD_PYTHON"
|
||||||
|
echo " kicad-cli: $KICAD_CLI"
|
||||||
|
echo " output: $OUTPUT_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
check_kicad_python
|
||||||
|
if [ -n "$KICAD_CLI" ]; then
|
||||||
|
: # ok
|
||||||
|
else
|
||||||
|
echo "WARNING: kicad-cli not found (ERC/DRC/gerber/bom will be skipped)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
step_build
|
||||||
|
$DO_ERC && step_erc
|
||||||
|
$DO_DRC && step_drc
|
||||||
|
$DO_GERBER && step_gerber
|
||||||
|
$DO_BOM && step_bom
|
||||||
|
$DO_SIM && step_sim
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Done ==="
|
||||||
|
echo "Output: $OUTPUT_DIR/"
|
||||||
|
ls -la "$OUTPUT_DIR/" 2>/dev/null || true
|
||||||
44
tools/kicad-python
Executable file
44
tools/kicad-python
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# kicad-python — launcher for KiCad's bundled Python with pcbnew available.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./kicad-python <script.py> [args...]
|
||||||
|
# ./kicad-python -c "import pcbnew; ..."
|
||||||
|
# ./kicad-python -m circuit_toolkit.build my-board/
|
||||||
|
#
|
||||||
|
# Detects KiCad.app on macOS, KiCad on Linux, or KiCad Flatpak.
|
||||||
|
# Exits non-zero if KiCad Python is not found.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
KICAD_APP="/Applications/KiCad/KiCad.app"
|
||||||
|
KICAD_PYTHON="${KICAD_APP}/Contents/Frameworks/Python.framework/Versions/3.9/bin/python3"
|
||||||
|
|
||||||
|
# Try Flatpak install
|
||||||
|
if [ -x "/var/lib/flatpak/app/org.kicad.KiCad" ] || command -v flatpak &>/dev/null; then
|
||||||
|
FLATPAK_PYTHON=$(flatpak run --command=python3 org.kicad.KiCad 2>/dev/null || true)
|
||||||
|
if [ -n "$FLATPAK_PYTHON" ]; then
|
||||||
|
KICAD_PYTHON="$FLATPAK_PYTHON"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try Linux install
|
||||||
|
if [ ! -x "$KICAD_PYTHON" ]; then
|
||||||
|
for candidate in \
|
||||||
|
/opt/kicad/bin/python3 \
|
||||||
|
/usr/bin/kicad-python \
|
||||||
|
/usr/bin/python3; do
|
||||||
|
if "$candidate" -c "import pcbnew" 2>/dev/null; then
|
||||||
|
KICAD_PYTHON="$candidate"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x "$KICAD_PYTHON" ]; then
|
||||||
|
echo "ERROR: KiCad Python not found at $KICAD_PYTHON" >&2
|
||||||
|
echo "Install KiCad (kicad.org) or a Flatpak, then re-run." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$KICAD_PYTHON" "$@"
|
||||||
266
tools/retire_block.py
Normal file
266
tools/retire_block.py
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""retire_block.py — remove a circuit block from a CM5-carrier KiCad sheet cleanly.
|
||||||
|
|
||||||
|
For the diff-from-reference port (see .claude/skills/kicad-port). Given a sheet and a
|
||||||
|
set of reference designators to drop plus the net-name globs they own, this:
|
||||||
|
1. excises each named (symbol ...) block (paren-matched, by Reference property),
|
||||||
|
2. floods the retired nets from their (label ...) coords through (wire ...) segments,
|
||||||
|
3. removes those labels + every wire in the flooded nets,
|
||||||
|
4. places (no_connect ...) markers on the exposed module pins that sat on those nets,
|
||||||
|
5. writes back only if parentheses stay balanced.
|
||||||
|
|
||||||
|
Module pin coordinates are computed from the lib_symbol pin locals + the placement
|
||||||
|
transform (validated abs = origin + (lx, -ly) for rot0/no-mirror; other orientations
|
||||||
|
are rejected rather than guessed). Idempotent-ish: re-running finds nothing to do.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 tools/retire_block.py <sheet.kicad_sch> \
|
||||||
|
--symbols J7,U18 --nets 'SD_*' \
|
||||||
|
--module-lib CM5IO:ComputeModule5-CM5 [--apply]
|
||||||
|
Without --apply it's a dry run (prints the plan, writes nothing).
|
||||||
|
"""
|
||||||
|
import re, sys, argparse, fnmatch, math, uuid as uuidlib
|
||||||
|
|
||||||
|
S = r'\s*'
|
||||||
|
|
||||||
|
def xform(px, py, rot, mirror, lx, ly):
|
||||||
|
"""Schematic abs coord of a lib pin local (lx,ly) for a placement. Validated:
|
||||||
|
mirror, then CCW rotate, then lib-Y-up -> schematic-Y-down flip."""
|
||||||
|
X, Y = lx, ly
|
||||||
|
if mirror == 'y': X = -X
|
||||||
|
if mirror == 'x': Y = -Y
|
||||||
|
r = math.radians(rot)
|
||||||
|
rx = X * math.cos(r) - Y * math.sin(r)
|
||||||
|
ry = X * math.sin(r) + Y * math.cos(r)
|
||||||
|
return (round(px + rx, 2), round(py - ry, 2))
|
||||||
|
|
||||||
|
def paren_end(t, s):
|
||||||
|
d = 0
|
||||||
|
for i in range(s, len(t)):
|
||||||
|
if t[i] == '(': d += 1
|
||||||
|
elif t[i] == ')':
|
||||||
|
d -= 1
|
||||||
|
if d == 0: return i + 1
|
||||||
|
raise ValueError("unbalanced from offset %d" % s)
|
||||||
|
|
||||||
|
def fnum(x): return round(float(x), 2)
|
||||||
|
|
||||||
|
def find_symbol_blocks(t):
|
||||||
|
"""Yield (start,end,reference) for every placed (symbol (lib_id ...)) block."""
|
||||||
|
for m in re.finditer(r'\(symbol' + S + r'\(lib_id' + S + r'"', t):
|
||||||
|
s = m.start(); e = paren_end(t, s); seg = t[s:e]
|
||||||
|
ref = re.search(r'\(property' + S + r'"Reference"' + S + r'"([^"]+)"', seg)
|
||||||
|
if ref: yield (s, e, ref.group(1))
|
||||||
|
|
||||||
|
def module_pins_abs(t, module_lib):
|
||||||
|
"""Compute {pin_number:(x,y)} for the placed module instance on this sheet."""
|
||||||
|
inst = re.search(r'\(symbol' + S + r'\(lib_id' + S + r'"' + re.escape(module_lib) + r'"\)(.{0,500}?)\(unit' + S + r'(\d+)\)', t, re.S)
|
||||||
|
if not inst: return {}, None
|
||||||
|
body, unit = inst.group(1), inst.group(2)
|
||||||
|
at = re.search(r'\(at' + S + r'([\d.-]+)' + S + r'([\d.-]+)' + S + r'([\d.-]+)\)', body)
|
||||||
|
mir = re.search(r'\(mirror' + S + r'(\w+)\)', body)
|
||||||
|
px, py, rot = float(at.group(1)), float(at.group(2)), float(at.group(3))
|
||||||
|
if rot != 0 or mir:
|
||||||
|
sys.exit(f"REFUSING: module instance has rot={rot} mirror={mir and mir.group(1)} — transform only validated for rot0/no-mirror.")
|
||||||
|
libname = module_lib.split(':')[-1]
|
||||||
|
pins = {}
|
||||||
|
for pm in re.finditer(r'\(pin\b.*?\(at' + S + r'([\d.-]+)' + S + r'([\d.-]+)' + S + r'[\d.-]+\).*?\(number' + S + r'"([^"]+)"', t, re.S):
|
||||||
|
lx, ly, num = float(pm.group(1)), float(pm.group(2)), pm.group(3)
|
||||||
|
pins.setdefault(num, (round(px + lx, 2), round(py - ly, 2)))
|
||||||
|
return pins, unit
|
||||||
|
|
||||||
|
def collect(t, pat):
|
||||||
|
"""Return list of (start,end,coord,text) for top-level objects named `pat` token."""
|
||||||
|
out = []
|
||||||
|
for m in re.finditer(r'\(' + pat + r'\b', t):
|
||||||
|
s = m.start(); e = paren_end(t, s); seg = t[s:e]
|
||||||
|
at = re.search(r'\(at' + S + r'([\d.-]+)' + S + r'([\d.-]+)', seg)
|
||||||
|
out.append((s, e, (fnum(at.group(1)), fnum(at.group(2))) if at else None, seg))
|
||||||
|
return out
|
||||||
|
|
||||||
|
def wire_pts(seg):
|
||||||
|
return [(fnum(a), fnum(b)) for a, b in re.findall(r'\(xy' + S + r'([\d.-]+)' + S + r'([\d.-]+)\)', seg)]
|
||||||
|
|
||||||
|
def lib_pins(t, lib_id):
|
||||||
|
"""Local (lx,ly,angle) for every pin of a lib_symbol definition."""
|
||||||
|
name = lib_id.split(':')[-1]
|
||||||
|
lm = re.search(r'\(symbol' + S + r'"' + re.escape(lib_id) + r'"', t) or \
|
||||||
|
re.search(r'\(symbol' + S + r'"' + re.escape(name) + r'"', t)
|
||||||
|
if not lm: return []
|
||||||
|
seg = t[lm.start():paren_end(t, lm.start())]
|
||||||
|
return [(float(a), float(b), float(c)) for a, b, c in
|
||||||
|
re.findall(r'\(pin\b.*?\(at' + S + r'([\d.-]+)' + S + r'([\d.-]+)' + S + r'([\d.-]+)\)', seg, re.S)]
|
||||||
|
|
||||||
|
def placed_symbols(t):
|
||||||
|
"""Yield dict per placed symbol: start,end,ref,lib,at(px,py,rot),mirror."""
|
||||||
|
for m in re.finditer(r'\(symbol' + S + r'\(lib_id' + S + r'"([^"]+)"\)', t):
|
||||||
|
s = m.start(); e = paren_end(t, s); seg = t[s:e]
|
||||||
|
ref = re.search(r'\(property' + S + r'"Reference"' + S + r'"([^"]+)"', seg)
|
||||||
|
at = re.search(r'\(at' + S + r'([\d.-]+)' + S + r'([\d.-]+)' + S + r'([\d.-]+)\)', seg)
|
||||||
|
mir = re.search(r'\(mirror' + S + r'(\w+)\)', seg)
|
||||||
|
yield dict(s=s, e=e, ref=ref.group(1) if ref else None, lib=m.group(1),
|
||||||
|
at=(float(at.group(1)), float(at.group(2)), float(at.group(3))) if at else None,
|
||||||
|
mirror=mir.group(1) if mir else None)
|
||||||
|
|
||||||
|
def sym_pin_coords(t, sym):
|
||||||
|
if not sym['at']: return []
|
||||||
|
px, py, rot = sym['at']
|
||||||
|
return [xform(px, py, rot, sym['mirror'], lx, ly) for lx, ly, _ in lib_pins(t, sym['lib'])]
|
||||||
|
|
||||||
|
def sheet_port_spans(t, names):
|
||||||
|
"""On a ROOT sheet, find (pin "NAME" ...) hierarchical ports inside (sheet ...) blocks
|
||||||
|
whose name is in `names`. Returns (pin spans to remove, their connection coords)."""
|
||||||
|
spans, coords = [], set()
|
||||||
|
for m in re.finditer(r'\(sheet\b', t):
|
||||||
|
s = m.start(); e = paren_end(t, s)
|
||||||
|
for pm in re.finditer(r'\(pin' + S + r'"([^"]+)"', t[s:e]):
|
||||||
|
if pm.group(1) in names:
|
||||||
|
ps = s + pm.start(); pe = paren_end(t, ps)
|
||||||
|
at = re.search(r'\(at' + S + r'([\d.-]+)' + S + r'([\d.-]+)', t[ps:pe])
|
||||||
|
spans.append((ps, pe))
|
||||||
|
if at: coords.add((fnum(at.group(1)), fnum(at.group(2))))
|
||||||
|
return spans, coords
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument('sheet')
|
||||||
|
ap.add_argument('--symbols', default='')
|
||||||
|
ap.add_argument('--nets', default='')
|
||||||
|
ap.add_argument('--nc-pins', default='', help='module pin NUMBERS to no-connect (from analyzer regressions)')
|
||||||
|
ap.add_argument('--sheet-ports', default='', help='hierarchical net names: remove matching sheet-symbol pins + connecting root wires (run on the ROOT sheet)')
|
||||||
|
ap.add_argument('--module-lib', default='CM5IO:ComputeModule5-CM5')
|
||||||
|
ap.add_argument('--apply', action='store_true')
|
||||||
|
a = ap.parse_args()
|
||||||
|
drop_refs = set(filter(None, a.symbols.split(',')))
|
||||||
|
net_globs = list(filter(None, a.nets.split(',')))
|
||||||
|
nc_nums = list(filter(None, a.nc_pins.split(',')))
|
||||||
|
port_names = set(filter(None, a.sheet_ports.split(',')))
|
||||||
|
t = open(a.sheet).read()
|
||||||
|
|
||||||
|
modpins, unit = module_pins_abs(t, a.module_lib)
|
||||||
|
pin_at = {xy: n for n, xy in modpins.items()}
|
||||||
|
nc_pins = [(n, modpins[n]) for n in nc_nums if n in modpins]
|
||||||
|
missing_nc = [n for n in nc_nums if n not in modpins]
|
||||||
|
if missing_nc: sys.exit(f"NC pins not found on module: {missing_nc}")
|
||||||
|
|
||||||
|
# 1) symbols to remove + their abs pin coords (for stub cleanup)
|
||||||
|
placed = list(placed_symbols(t))
|
||||||
|
removed_syms = [sy for sy in placed if sy['ref'] in drop_refs]
|
||||||
|
found_refs = {sy['ref'] for sy in removed_syms}
|
||||||
|
sym_spans = [(sy['s'], sy['e']) for sy in removed_syms]
|
||||||
|
removed_pin_coords = set()
|
||||||
|
for sy in removed_syms:
|
||||||
|
removed_pin_coords |= set(sym_pin_coords(t, sy))
|
||||||
|
|
||||||
|
# 2) retired labels by glob — match ANY label type (local / hierarchical / global)
|
||||||
|
labels = collect(t, 'label') + collect(t, 'hierarchical_label') + collect(t, 'global_label')
|
||||||
|
retired_labels = [(s, e, c) for s, e, c, seg in labels
|
||||||
|
for nm in [re.search(r'"([^"]+)"', seg).group(1)]
|
||||||
|
if any(fnmatch.fnmatch(nm, g) for g in net_globs)]
|
||||||
|
retired_coords = {c for _, _, c in retired_labels}
|
||||||
|
|
||||||
|
# 3) anchor-aware wire removal via connected components.
|
||||||
|
# A wire is removed only if its component (a) is orphaned by THIS edit (touches a
|
||||||
|
# removed pin, retired label, or target NC pin) AND (b) reaches no KEPT anchor —
|
||||||
|
# a kept non-power component pin or a kept label. Shared nets (e.g. a camera FFC's
|
||||||
|
# I2C/power pins) stay anchored by the kept side, so they survive; pre-existing
|
||||||
|
# floating wires aren't seeded, so they're untouched.
|
||||||
|
wires = collect(t, 'wire')
|
||||||
|
wpts = [(s, e, wire_pts(seg)) for s, e, _, seg in wires]
|
||||||
|
parent = {}
|
||||||
|
def find(x):
|
||||||
|
parent.setdefault(x, x)
|
||||||
|
root = x
|
||||||
|
while parent[root] != root: root = parent[root]
|
||||||
|
while parent[x] != root: parent[x], x = root, parent[x]
|
||||||
|
return root
|
||||||
|
def union(a, b): parent[find(a)] = find(b)
|
||||||
|
for s, e, pts in wpts:
|
||||||
|
for p in pts[1:]:
|
||||||
|
union(pts[0], p)
|
||||||
|
|
||||||
|
nc_coords = {xy for _, xy in nc_pins}
|
||||||
|
kept_label_coords = {c for s, e, c, seg in labels
|
||||||
|
if not any(fnmatch.fnmatch(re.search(r'"([^"]+)"', seg).group(1), g)
|
||||||
|
for g in net_globs)}
|
||||||
|
kept_pin_coords = set()
|
||||||
|
for sy in placed:
|
||||||
|
if sy['ref'] in drop_refs or sy['lib'].startswith('power:'): continue
|
||||||
|
kept_pin_coords |= set(sym_pin_coords(t, sy))
|
||||||
|
kept_pin_coords -= nc_coords # pins we are NC'ing are being disconnected
|
||||||
|
anchors = kept_pin_coords | kept_label_coords
|
||||||
|
# sheet-symbol hierarchical ports to drop (root sheet) + their connection coords
|
||||||
|
port_spans, port_coords = sheet_port_spans(t, port_names) if port_names else ([], set())
|
||||||
|
seeds = set(removed_pin_coords) | set(retired_coords) | nc_coords | port_coords
|
||||||
|
|
||||||
|
anchored_roots, seeded_roots = set(), set()
|
||||||
|
for c in anchors:
|
||||||
|
if c in parent: anchored_roots.add(find(c))
|
||||||
|
for c in seeds:
|
||||||
|
if c in parent: seeded_roots.add(find(c))
|
||||||
|
removable_roots = seeded_roots - anchored_roots
|
||||||
|
wire_remove = [(s, e, pts) for s, e, pts in wpts if find(pts[0]) in removable_roots]
|
||||||
|
wire_remove_spans = {(s, e) for s, e, _ in wire_remove}
|
||||||
|
freed = set(removed_pin_coords)
|
||||||
|
for _, _, pts in wire_remove:
|
||||||
|
freed |= set(pts)
|
||||||
|
kept_wire_pts = set()
|
||||||
|
for s, e, pts in wpts:
|
||||||
|
if (s, e) not in wire_remove_spans:
|
||||||
|
kept_wire_pts |= set(pts)
|
||||||
|
flooded_wires = wire_remove # naming compat for the summary print
|
||||||
|
stub_wires = []
|
||||||
|
|
||||||
|
# 5) orphaned power flags (power:*): pin freed by my edit, no kept wire left on it
|
||||||
|
power_spans = []
|
||||||
|
for sy in placed:
|
||||||
|
if sy['ref'] in drop_refs or not sy['lib'].startswith('power:'): continue
|
||||||
|
pcs = sym_pin_coords(t, sy)
|
||||||
|
if pcs and all(pc in freed and pc not in kept_wire_pts for pc in pcs):
|
||||||
|
power_spans.append((sy['s'], sy['e']))
|
||||||
|
|
||||||
|
# 6) dangling no_connects sitting on a removed symbol's pin
|
||||||
|
dangling_nc = [(s, e) for s, e, c, _ in collect(t, 'no_connect') if c in removed_pin_coords]
|
||||||
|
|
||||||
|
# 7) labels orphaned by my edit (any label type): coord freed, no kept wire left,
|
||||||
|
# not already retired. Covers local (label), and cross-sheet (hierarchical_label /
|
||||||
|
# global_label) that only fed a removed connector (e.g. a camera FFC's SCL0/CAM_GPIO).
|
||||||
|
retired_label_spans = {(s, e) for s, e, _ in retired_labels}
|
||||||
|
all_label_objs = collect(t, 'label') + collect(t, 'hierarchical_label') + collect(t, 'global_label')
|
||||||
|
orphan_labels = [(s, e) for s, e, c, _ in all_label_objs
|
||||||
|
if (s, e) not in retired_label_spans and c in freed and c not in kept_wire_pts]
|
||||||
|
|
||||||
|
nc_pins = sorted(nc_pins, key=lambda kv: int(re.sub(r'\D', '', kv[0]) or 0))
|
||||||
|
print(f"sheet: {a.sheet} (module unit {unit}, {len(modpins)} pins)")
|
||||||
|
print(f"symbols to remove: {sorted(found_refs)} (requested {sorted(drop_refs)}; missing {sorted(drop_refs-found_refs)})")
|
||||||
|
print(f"retired-net labels: {len(retired_labels)} | wires removed (anchor-aware): {len(wire_remove)}")
|
||||||
|
print(f"orphaned power flags: {len(power_spans)} | dangling NC removed: {len(dangling_nc)} | orphaned labels: {len(orphan_labels)}")
|
||||||
|
if port_names: print(f"sheet-symbol ports removed: {len(port_spans)} ({sorted(port_names)})")
|
||||||
|
print(f"module pins to NC ({len(nc_pins)}): " + ", ".join(f"{n}@{xy}" for n, xy in nc_pins))
|
||||||
|
|
||||||
|
# build new text: remove all spans, then append fresh no_connects on exposed module pins
|
||||||
|
spans = sorted(set(sym_spans) | {(s, e) for s, e, _ in retired_labels}
|
||||||
|
| wire_remove_spans | set(power_spans) | set(dangling_nc) | set(orphan_labels)
|
||||||
|
| set(port_spans), key=lambda x: -x[0])
|
||||||
|
nt = t
|
||||||
|
for s, e in spans:
|
||||||
|
p = s
|
||||||
|
while p > 0 and nt[p-1] in '\t ': p -= 1
|
||||||
|
if p > 0 and nt[p-1] == '\n': p -= 1
|
||||||
|
nt = nt[:p] + nt[e:]
|
||||||
|
# insert no_connect markers before the final closing paren of the sheet
|
||||||
|
nc_block = "".join(
|
||||||
|
f'\t(no_connect\n\t\t(at {xy[0]} {xy[1]})\n\t\t(uuid "{uuidlib.uuid5(uuidlib.NAMESPACE_DNS, a.sheet+n)}")\n\t)\n'
|
||||||
|
for n, xy in nc_pins)
|
||||||
|
last = nt.rstrip().rfind(')')
|
||||||
|
nt = nt[:last] + nc_block + nt[last:]
|
||||||
|
assert nt.count('(') == nt.count(')'), "UNBALANCED — aborting"
|
||||||
|
|
||||||
|
if a.apply:
|
||||||
|
open(a.sheet, 'w').write(nt)
|
||||||
|
print(f"APPLIED. parens balanced ({nt.count('(')}). +{len(nc_pins)} no_connect, -{len(spans)} objects.")
|
||||||
|
else:
|
||||||
|
print(f"DRY RUN ok. parens would balance ({nt.count('(')}). Re-run with --apply to write.")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user