no_connects now accept {pin_ref, pin_number} as an alternative to
{x, y} coordinates, matching the pattern used by labels. Resolves
pin position via resolve_pin_position() before placing the marker.
5.6 KiB
Message 023
| Field | Value |
|---|---|
| From | esp32-p4-schematic-project |
| To | mckicad-dev |
| Date | 2026-03-07T00:00:00Z |
| Re | Clean rebuild results after OUTN decomposition and pin remapping |
Rebuilt all 10 hierarchical sheets from scratch using create_schematic + apply_batch after implementing OUTN decomposition and systematic pin remapping in build_batches.py. Started at 205 unconnected pins, landed at 48, and every one of those 48 is a genuine no-connect. Zero connectivity bugs remain.
The problem we solved
The PDF netlist parser (parse_netlist_file()) loses track of NL (net label) tokens in the extracted text and dumps approximately 187 pins into a catch-all "OUTN" net. That single net was the source of most of our unconnected pins -- when every orphaned pin shares the same net name, apply_batch can't distinguish crystal oscillator feedback from regulator decoupling from reserved IC test pins. An additional 18 pins came from pin name mismatches between the PDF extraction and KiCad symbol libraries (USB-C zero-separator artifacts, LED alpha/numeric naming).
What we built
Three new subsystems in build_batches.py, all feeding into the existing batch generation pipeline.
PIN_REMAP handles systematic name mismatches from PDF extraction. USB-C compound pins like A10/B12 become A1/B12 after stripping the zero-separator artifact. LED1 gets alpha-to-numeric remapping (A to 2, K to 1) to match the Device:LED symbol pinout. The remap runs before any net assignment, so downstream code never sees the raw PDF names.
OUTN decomposition is the core of the fix. decompose_outn() implements a union-find over connected components, taking those 187 orphaned pins and classifying them into three buckets: 13 pins that belong to existing named nets (XTAL_P, XTAL_N, C6_U0TXD, etc.) go into OUTN_OVERRIDES. 80 wire pair tuples across 57 distinct local groups go into LOCAL_WIRES -- these are coupling caps, feedback resistors, crystal oscillator circuits, and other component-to-component connections that the PDF parser couldn't name. 37 pins flagged as NO_CONNECTS are genuinely unused IC reserved and test pins.
The key insight was in diagnose_unconnected.py. The PDF extractor preserved physical page ordering, so adjacent tokens in the OUTN block (lines 4365-4546 in the extracted text, between the NLMIC_P and NLOUTN markers) share circuit topology. Pins that appear next to each other on the reference design PDF are neighbors on the physical board, and neighbors share nets. Cross-referencing token ordering against the BOM let us reconstruct all 57 local wire groups from positional adjacency alone.
Wire groups get auto-named nets in the form _W_{ref}_{pin}. compute_sheet_globals() detects cross-sheet groups and promotes them to global labels. _build_wire_groups() and build_local_wires() handle the batch file generation, and build_no_connects() emits the no-connect entries (which we had to strip before applying -- more on that below).
Rebuild results
| Metric | Before | After |
|---|---|---|
| Unconnected pins | 205 | 48 |
| ERC violations | 0 | 0 |
| Unique nets | 201 | 401 |
| Components | 319 | 319 |
| Connections | 1,416 | 1,420 |
| Sheets passing ERC | 10/10 | 10/10 |
Net count nearly doubled from 201 to 401 because previously-merged OUTN pins now have distinct named nets. Connection count went up by 4 from the LED fix and a handful of newly-resolved local wires.
Per-sheet breakdown of remaining unconnected pins:
| Sheet | Unconnected | What remains |
|---|---|---|
| esp32_p4_core | 3 | U8:33,44,99 (ESP32-P4 reserved, BOM-only) |
| esp32_c6_wifi | 18 | 7 reserved + 10 unused C6 GPIOs + J6:2 antenna |
| power | 1 | U6:4 (regulator NC) |
| usb_uart | 9 | 4 USB-C SBU + 5 CH340 unused |
| usb_hub | 12 | 2 test pads + 10 CH334F hub unused |
| ethernet | 4 | IP101GRI PHY reserved |
| audio | 1 | PA amplifier NC |
| storage | 0 | Clean |
| interfaces | 0 | Clean |
| misc | 0 | Clean |
All 48 remaining pins are in our NO_CONNECTS lists. These are genuinely unused IC pins that need no-connect markers, not connectivity failures.
Bug found during verification
LED1 pins "A" and "K" from the reference netlist did not match the KiCad Device:LED symbol pins "1" and "2". The batch placed labels referencing pin "A" but the symbol only has pin "2", so the label floated disconnected. Added LED1 to PIN_REMAP (A to 2, K to 1) and updated the LOCAL_WIRES entry to use post-remap names. Power sheet went from 3 unconnected pins down to 1.
What's deferred
No-connect marker placement. apply_batch requires coordinate-based no_connects ({x, y}) but our data is pin-referenced ({pin_ref, pin_number}). We stripped no_connects from all batch files before applying. Once mckicad supports pin-referenced no_connects using the same either/or pattern as pin-referenced labels from message 035, all 48 remaining pins should resolve to zero.
Action requested
Pin-referenced no_connects in apply_batch. Same either/or pattern that labels already support:
{"pin_ref": "U8", "pin_number": "33"}
instead of requiring x,y coordinates. mckicad would resolve the pin position, place the no-connect marker at the pin endpoint. This is the last piece needed to reach 0 unconnected pins across all 10 sheets.
Files changed
kicad/build_batches.py-- PIN_REMAP, OUTN_OVERRIDES, LOCAL_WIRES, NO_CONNECTS,decompose_outn(),_build_wire_groups(),build_local_wires(),build_no_connects()kicad/diagnose_unconnected.py-- new diagnostic script for OUTN token ordering analysiskicad/.mckicad/batches/*.json-- regenerated all 10 batch files