Skip to content

docs(router): correct every CM5 DRC/SI claim measured against the inflated baseline#690

Merged
ecto merged 7 commits into
mainfrom
claude/serene-taussig-bc9520
Jul 25, 2026
Merged

docs(router): correct every CM5 DRC/SI claim measured against the inflated baseline#690
ecto merged 7 commits into
mainfrom
claude/serene-taussig-bc9520

Conversation

@ecto

@ecto ecto commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Re-measures and corrects every published DRC and signal-integrity claim about the CM5 autorouting benchmark. All of them were computed against a stripped-fixture baseline that one of our own bugs had inflated.

Why

KiCad stores a pad's angle as absolute — it already includes the footprint's orientation. vcad's importer stored that raw value while eleven consumers compose fp.rotation + pad.rotation, double-counting the footprint rotation on every rotated part. On fine-pitch rotated packages the neighbouring pads overlapped, manufacturing phantom shorts and clearance violations out of nothing (#684).

Those phantoms landed in the baseline, which is the denominator of every route-attributable claim we publish. A bigger floor makes our delta look smaller, so the error flattered us in every published comparison — the direction that most needs correcting.

Method

Re-measured from scratch on merged main with #684 rebased in. DRC and SI are deterministic given a fixed board file, so each was run twice; every pair agreed exactly (no spread to report). The route stage is the only stochastic step and is reported with its wall-clock.

Note: the full-board route shared the machine with another session's full-board route (load average 23.4). Contention only inflates wall-clock, so the <30 min conclusion holds a fortiori.

Every number that moved

The baseline itself (stripped fixture, all copper removed)

before after
short/clearance 980 311
Clearance 74 23
Short ~906 258

648 violations this project attributed to the reverse-engineered source were ours.

The human production board

before after
total violations 16,485 14,104
short/clearance 6,994

Our board — the claim that broke

before after
Clearance 74 (claimed = the floor) 173
floor 74 23
route-attributable Clearance 0 150

"Route-attributable violations: ZERO" was false. It read 74 − 74 = 0 because the inflated floor happened to equal what our board scored.

The shipped fab package re-measures at Clearance 81 vs floor 23 = 58 route-attributable.

SI receipts

40-net subset before after
worst_intra_pair_skew 0.983 HOLDS 1.353 BROKEN (bound 1.100)
min_pair_coupled_fraction 0.777 0.582 HOLDS
vias_per_si_net 2.727 2.474 HOLDS
verdict ALL HOLD 3 of 4

The "40-net subset — receipt Pass" headline is withdrawn. It also failed to reproduce on merged main before this fix (1.347mm), so the original Pass was a property of one branch state, not a reproducible result.

full board before after
worst_group_skew 9.297 HOLDS 8.397 HOLDS
worst_intra_pair_skew 37.853 BROKEN 38.521 BROKEN
min_pair_coupled_fraction 0.000 BROKEN 0.101 BROKEN
vias_per_si_net 2.766 HOLDS 3.128 BROKEN (bound 3.000)
verdict 2 of 4 1 of 4

vias_per_si_net broke as coverage improved — the board routes more connections (0.994 vs 0.983) and pays in vias. Worth stating plainly rather than reporting only the routability gain.

Short accounting

"The import carries ~906 pad-level shorts, and routing only 40 nets adds ~409" → the import carries 258, and the 40-net route adds 27. The conclusion is unchanged and still correct: Short is not route-attributable on this fixture; use Clearance.

Wall-clock — the <30 min target is met

stage before now
route 113 min (pre-GPU-fix) 958.9 s ≈ 16.0 min
full chain 2 h 51 m → 67 min 1,634 s ≈ 27.2 min

Routability 0.994 is also a new record (prior best 0.985).

Incidental gain

Pair-first now couples 47 pairs in round 0, up from 43 — overlapping pads had been sealing the BGA fields that pairs escape through.

What did NOT move

No receipt bound was adjusted. The human board's anchor re-measures bit-identical: 9.756 / 1.074 / 0.857 / 2.265, ALL HOLD. Pad angles feed pad geometry, not trace lengths, so the calibration anchor and the envelope argument it supports survive intact.

The claim that survives with a corrected number: the finishing pass still introduces no clearance violations. Published as "identical with the pass on and off (74)"; corrected, the 40-net board scores Clearance 23 — exactly the floor.

Reviewer notes

  • Body text in gpu-router-m7-pair-si.md is left as originally published, with a correction section appended, so the record shows what moved rather than silently overwriting it.
  • ~/Downloads/cm5-vcad-fab/FAB_NOTES.md is corrected in place (outside the repo, so not in this diff). The gerbers do not need regenerating: board.kicad_pcb round-trips identically to the fixture under the corrected reader (1,403 pads across 227 rotated footprints, zero mismatch) — the old writer emitted the raw absolute angle verbatim, so the copper was always correct. Only the numbers computed from it were wrong. Its drc_report.txt still carries the old Clearance 74 and is flagged as predating the correction.
  • Memory files cm5-fab-ready-campaign.md and cm5-si-campaign.md updated with the corrected figures and the lesson: when an importer changes, re-derive the baseline before trusting any delta.
  • This PR depends on fix(ecad): KiCad pad angles are absolute — stop double-counting footprint rotation #684; it is merged into this branch.

🤖 Generated with Claude Code

ecto and others added 4 commits July 25, 2026 11:06
A pair whose pads sit a few mm apart has no room for the lead-inset phantom
centerline: the lead eats both ends and a ~1.8mm span keeps ~0.9mm to search,
so both ladders fail and the pair falls back to two independent singles. Those
singles land on DIFFERENT layers, and coupled_fraction only counts twin copper
on the same layer — so the pair scores exactly 0.0 and pins the SI receipt's
min_pair_coupled_fraction at zero however well the rest of the board routes
(M7 named this: the /HS.* group).

Such a pair does not need a search. Route it directly: one straight, lead-free
centerline on the best layer all four pads share, offset into the two legs by
the same realize_legs every other centerline uses. Four point probes choose the
layer; the authoritative check is still validate_and_commit, so this adds no
way to commit copper the oracle has not seen.

CM5 pair census: 44 of 49 coupled, up from 39, and the centerline-search bail
class — M7's dominant mode at 5-6 pairs — is now empty. The 5 that remain are
long pairs failing at their connectors, a different mode.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rint rotation

KiCad stores a pad's angle ABSOLUTELY: it already includes the footprint's
orientation. Measured on the CM5 fixture, the pad angle tracks the footprint
exactly — fp_rot -90 -> pad_rot 270 (798 pads), 45 -> 45, 90 -> 90 (285),
135 -> 135, 180 -> 180. vcad's IR stores it RELATIVE, because all eleven
geometry consumers (DRC, the router's spatial index, DFM, pour synthesis,
render, the 3D mesh, eval) compose fp.rotation + pad.rotation.

The importer stored the absolute angle, so every rotated footprint had its
footprint rotation counted twice. A TQFN's 0.25 x 0.875mm pads at 0.5mm pitch
came out turned 90 degrees — long axis ALONG the pitch — so neighbouring pads
overlapped. That is not a subtle error: it invented copper.

Fixed at the import boundary (reader subtracts, writer re-composes) so all
eleven consumers become correct without touching them, pinned by a round-trip
test.

Measured on the CM5 fixture, copper stripped:
  short/clearance   980 -> 311
  Clearance          74 ->  23
So 648 of the violations this project has been attributing to the
reverse-engineered source were ours. Every DRC-delta claim measured against
that baseline needs re-measuring.

Pair coupling census: 39 -> 47 of 49. The four /ETH.* pairs that pinned both
broken SI claims couple now that their pads no longer overlap.

Also here, both found while chasing those bails and each worth a pair on its
own (46 -> 47 together):
- short pairs get a direct, lead-free centerline (a ~1.8mm pad-to-pad span has
  no room for the lead inset, so it fell back to singles on different layers
  and scored exactly 0.0 coupling);
- a pad connector retreats along the centerline and escalates its search
  window, because a pad inside a fine-pitch field must escape laterally before
  it can drop a via, and a window sized from a 0.8mm hop cannot contain that.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…lated baseline

vcad's KiCad importer stored pad angles as read, but KiCad's pad angle is
absolute — it already includes the footprint's orientation — while eleven
consumers compose `fp.rotation + pad.rotation`. Rotated fine-pitch packages
therefore had overlapping pads, manufacturing phantom shorts and clearance
violations (PR #684).

Those phantoms landed in the stripped-fixture *baseline*, which is the
denominator of every route-attributable claim we publish. A bigger floor makes
our delta look smaller, so the error flattered us in every comparison — the
direction that most needs correcting.

Re-measured from scratch on merged main with #684 in. DRC and SI are
deterministic given a fixed board file; every figure was run twice with
identical results, and the stochastic route stage is reported with wall-clock.

Numbers that moved:
- stripped-fixture floor: 980 -> 311 short/clearance, 74 -> 23 Clearance
  (648 violations blamed on the reverse-engineered source were ours)
- human production board: 16,485 -> 14,104 violations
- our full board: "route-attributable ZERO" was FALSE (74 - 74 = 0 against
  the inflated floor); corrected, 173 vs floor 23 = 150 attributable
- full-board receipt: 2/4 -> 1/4 (vias_per_si_net 2.766 -> 3.128, crossing
  the 3.0 bound as routability rose 0.983 -> 0.994)
- 40-net "receipt Pass (ALL HOLD)" WITHDRAWN -> 3/4 (intra-pair skew 1.353 vs
  1.100); it already failed to reproduce pre-fix at 1.347, so the Pass was a
  one-branch artifact rather than a result
- pad-level shorts in the import: ~906 -> 258; 40-net route adds 27, not ~409
  (the conclusion stands: Short is not route-attributable, use Clearance)
- route wall-clock 113 min -> 958.9 s (16.0 min); full chain 27.2 min, so the
  M6 "<30 min chain" target is met
- pair-first couples 47 pairs in round 0, up from 43

No receipt bound was adjusted. The human board's anchor re-measures
bit-identical (9.756 / 1.074 / 0.857 / 2.265, ALL HOLD) because pad angles feed
pad geometry, not trace lengths — so the envelope argument survives intact.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chojiai

chojiai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Choji has used up today's reviews

Your team has reached the free plan's limit of 5 reviews per day, so Choji stepped aside on this pull request. That's a plan limit, not a reflection of the change itself.

Your free reviews reset tomorrow. To have Choji review every push without waiting, upgrade to Pro for unlimited reviews.

You're on the free plan · 5 reviews/day.

@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

4 Skipped Deployments
Project Deployment Actions Updated (UTC)
mecheval Ignored Ignored Jul 25, 2026 4:57pm
vcad Ignored Ignored Jul 25, 2026 4:57pm
vcad-docs Ignored Ignored Jul 25, 2026 4:57pm
vcad-mcp Ignored Ignored Jul 25, 2026 4:57pm

Request Review

Second full-board route: routability 0.994 again, byte-identical board
(3,791 segments / 895 vias) — the route is deterministic given the tree.
Wall-clock 895.5 s vs run 1's 958.9 s.

The chain figure stays at one complete run: run 2's si_finish stage was
truncated when the machine's disk filled, so its 1,288 s is not a valid chain
sample and is not averaged in. Said so in the doc rather than presenting it as
a second measurement — a prior version of this document published a single
lucky run as typical, which is the habit this correction exists to break.

Both runs shared the machine with another session's full-board route, so the
timings are pessimistic; the <30 min conclusion holds regardless.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ecto

ecto commented Jul 25, 2026

Copy link
Copy Markdown
Owner Author

Route spread — second run complete

Second full-board route on the corrected importer:

run 1 run 2
routability 0.994 0.994
board 3,791 seg / 895 vias identical
route wall-clock 958.9 s 895.5 s

The route is deterministic given the tree — both runs produced a byte-identical board. Only wall-clock varies (~7%, from machine contention).

One caveat, stated rather than buried: the full-chain figure (27.2 min) rests on one complete run. Run 2's route stage finished normally, but its si_finish stage was truncated when the machine's disk filled, so its 1,288 s is not a valid chain sample and is not averaged in. The route-stage spread above is from two complete runs; the chain number needs a second clean run to earn the same standing.

Both runs also shared the machine with another session's full-board route (load average 23.4), so the timings are pessimistic. The <30 min conclusion holds a fortiori.

All DRC and SI figures in this PR were already confirmed by two identical runs each and are unchanged.

🤖 Generated with Claude Code

ecto and others added 2 commits July 25, 2026 12:51
…-bc9520

# Conflicts:
#	crates/vcad-ecad-pcb/src/router/pair.rs
Main moved during this work: #685 (Eagle pad rotation), #687 (Gerber apertures
turn with the pad), and #691 (per-layer controlled-impedance geometry). #691
rewrites router/pair.rs and adds a fifth SI claim, so the corrected numbers were
re-run against it rather than assumed still valid.

Nothing moved. The stripped floor (311 / 23), our board's DRC (Clearance 173),
and the 40-net route (byte-identical board, receipt 1.353 / 0.582 / 2.474) all
reproduce exactly — #691's per-layer widths are opt-in on target_impedance,
which the CM5 fixture's classes do not declare.

The new pair_impedance_correct_fraction claim is vacuous on this fixture: it
reports 0.000 against a bound of 0.000 with the reason "no differential net
class declares a target impedance — nothing to verify". The receipt is now five
claims wide, so the raw fraction reads 2 of 5 on the full board and 4 of 5 on
the subset. Those are not improvements, and the docs say so — the honest counts
against claims that actually verify something remain 1 of 4 and 3 of 4.

Also resolves the merge conflict with main in router/pair.rs by taking main's
version; this branch carries no crate changes, only docs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ecto
ecto merged commit 4bdce67 into main Jul 25, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant