feat(router): universal pair coupling and the SI receipt (M7)#681
Merged
Conversation
… CM5 The SI receipt's two pair claims (min_pair_coupled_fraction, worst_intra_pair_skew) are worst-case over every routed pair, so they are gated by the pairs that fail to route coupled at all. A bail census over the CM5's 49 inferred pairs found three causes, in order of size: * The phantom centerline was pinned to start and end on the OUTER layer. Inside a BGA, the outer layer is solid pads, so no fat capsule can ever sit there and the search failed outright — 28 of 32 census bails and 48 of 56 in a full route. Pairs escape a BGA the way the human board does: down a via, coupled on an inner layer under the field. Letting the centerline begin anywhere in the stack took the census from 26 to 41 coupled and made it 9x faster (failed searches no longer burn the whole retreat ladder). * `pair_partner` did not know the USB DP/DM convention that `classes.rs` uses to build the pair list, so the classifier declared USB pairs and this stage immediately reported "no partner name" for them. The two now share one source of truth. * The neck-down retreat was a fixed 15-rung table of guesses. It is now measured: probe outward from each end for the first point where a fat capsule is actually legal, and start the ladder there. Letting legs begin on inner layers exposed a latent connectivity bug: pad connectors searched from ALL copper layers, so a connector could start at a pad's XY on a layer the pad is not on and never touch it electrically — and nothing catches that, because the copper is same-net and the clearance probe is happy. Connectors now leave a pad only on that pad's own layers, with a regression test. Bail reasons are typed (`PairBail`) rather than log-only, with `census_pairs` and an `si_census` example, so the census is a measurement instead of a grep. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…air skew Adds `router::si_finish`, run at the end of the CM5 pipeline, which works the tail the detailed router leaves behind. The two pair claims are worst-case over EVERY routed pair, so one bad pair breaks them however good the rest is. Three stages, each non-regressive and gated on the exact oracle: 1. `polish_pairs` rips still-uncoupled pairs off the settled board and re-routes them coupled (reroute-then-descend: descent is a local optimizer, so a pair that started far off-target must be re-routed before descending it is worth anything). 2. `descend_board` — the si_descent driver moved into the library so the router and the example cannot drift — drives out residual skew. 3. Phase compensation adds length back to the short leg. Why stage 3 exists: a coupled pair's skew is structural, not sloppy routing. Both legs are offsets of ONE centerline, so at every bend the outer leg takes the long way and the inner the short way, and the difference is `(w + gap) · Σ 2·tan(θ/2)` over signed turns — ~0.9mm for a single net right-angle turn at this board's geometry, against a 1.1mm bound. It cannot be shifted away: a lateral shift moves both legs equally and their difference depends only on the separation. Length has to be added back. A generic meander cannot do it (its bends go into the twin one gap away — "meanders do not fit" refused 4 of 7 pairs), so bumps bulge AWAY from the twin, tiled along the run because one bump on a 17mm straight adds 0.19mm. The two claims genuinely trade against each other — length added to a coupled leg moves it away from its twin, which costs coupled fraction. Measured: at full amplitude, coupling fell 0.777 -> 0.252. Amplitude is therefore capped by the coupling window on leg-width runs, breakout copper keeps the generous amplitude, and a floor keeps every pair well clear of the 0.5 bound. The bounds themselves are untouched. Also fixes a real defect this work uncovered: the descent's fail-closed check dropped BOTH nets' copper before probing, so it could not see a leg drifting into its own partner — the gap spring is a soft term, not a guarantee. Each leg is now checked against the twin at its DESCENDED position (checking new-against-old rejects everything, since the pair moves as a unit), and the gap spring gets the same +20um cushion the clearance hinge already used, so its equilibrium sits outside the hard rule. On the subset board this took clearance violations from 4404 to 77 (7061 -> 2733 total): the shipped descent had been pinching pairs to 0.000mm and no check caught it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rtcut Two paths in the pair stage committed copper the oracle never saw, and both show up as intra-pair clearance violations on a polished board: * `polish_pairs` assembles a board from several separately-probed pieces — the pair's legs, its four breakout connectors, and every displaced single re-routed around it — and nothing re-checked the assembly. Worse, the incremental session probe and the DRC disagree at mitred pair corners: the probe passed legs 0.191mm apart that the DRC rejects against its 0.245mm pair-gap rule. Since the DRC is the standard the receipt is judged by, the gate now runs `check_drc_in_region` over the pair's own bounding box and reverts the pair unless hard clearance/short errors are no worse than before. * The connector's straight-hop shortcut returned copper without probing it at all — the one path in this stage that bypassed the oracle outright. Measured on the CM5 40-net subset: the routed board carried 0 intra-pair clearance violations and the polished board carried 3 (down to 0.017mm against a 0.080mm base clearance). With both fixes the polished board carries 0, at a cost of two pairs that no longer polish (35 -> 33) — the correct trade, and the "strictly non-regressive" contract now holds in DRC terms rather than only in coupling terms. Total board clearance violations are identical with the finishing pass on and off (74), i.e. it introduces none. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ending it The length a run can absorb turns out to be independent of bump amplitude: gain per cell is 2·(hypot(cell/3, h) − cell/3), which at cell = 3h is 0.83·h spread over 3h, so any amplitude yields ~0.28mm per mm of run. Small bumps are therefore strictly better — same capacity, less coupled fraction spent, less chance of hitting a neighbour — so the ladder now starts at 0.06mm and climbs only when a rung cannot reach the deficit. Subset board: 8 -> 17 pairs compensated, coupled fraction 0.730 -> 0.777, worst skew 1.336 -> 1.312mm, still zero intra-pair DRC violations. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ALL HOLD Tiling per segment capped the achievable length at one small cell per segment. A routed leg is a simplified maze staircase — many ~0.45mm segments — so that came to ~0.03mm of added length each, and a 10.66mm run reported "cannot reach 1.312mm" while sitting on ample copper. Resampling the path at a fixed arclength step decouples the bump pattern from the original vertices and restores the true capacity: 2·(√2 − 1)/3 ≈ 0.276mm added per mm of run at cell = 3·amplitude, independent of amplitude. Only the prefix needed to absorb the deficit is reworked, so trace count stays bounded. On the CM5 40-net subset this closes the last claim: worst_group_skew 0.000 mm <= 10.0 HOLDS worst_intra_pair_skew 0.983 mm <= 1.1 HOLDS (was 1.499) min_pair_coupled_fraction 0.777 >= 0.5 HOLDS vias_per_si_net 2.727 <= 3.0 HOLDS verdict: ALL HOLD with zero intra-pair DRC violations and a board clearance count identical to the same board routed with the finishing pass disabled (74), i.e. the pass introduces none. Bounds are untouched — the human CM5 still scores Pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The per-segment tiling bug was invisible to every existing test because a synthetic straight run has few vertices and plenty of room. These pin the property that actually decides whether the skew claim can close: the same 20mm of copper must absorb the same deficit whether it arrives as two segments or forty, and every bump must go away from the twin. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Records what the M6 doc's named levers actually bought, including where they did not reach: coupled construction 17 -> 39 of 49 pairs (the phantom centerline had been pinned to the outer layer, which inside a BGA is solid pads — 28 of 32 census bails), a 40-net board reaching receipt Pass end to end, and the full board still at 2/4 pinned by two specific groups. Also states the closed forms the finishing work turned on: pair skew is structural at (w + gap)·Sum 2·tan(theta/2), and compensation capacity is 2(sqrt2 - 1)/3 ~= 0.276mm per mm of run, independent of amplitude. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Choji has used up today's reviewsYour 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. |
A 250-net board reproduces the full board's failure exactly (2/4, same two blocker groups, /ETH.3_P worst on both at 37.5mm and 37.9mm). That stops the 40-net Pass from being over-read: it reflects an uncongested board where polish can rip and re-route freely, not a solved pipeline. Polish's success rate under congestion is the real scaling limit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI was failing on `phyz-md = ^0.1` resolving against phyz 0.3.1. That is unrelated to this branch: main fixed it in #675, which landed after this branch was cut. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Drives
vcad.si-claims/1toward Pass. Coupled construction goes from 17 to 39 of 49 pairs on the CM5 (22 → 43 in a live full route), a 40-net board now reaches receipt Pass end to end, and three real defects surfaced along the way. The full board is still 2/4 — see Honest gaps.The bounds were not touched. The human CM5 still scores Pass with identical numbers (9.756 / 1.074 / 0.857 / 2.265), which is what makes the receipt an envelope argument rather than a target.
Lever 1 — coupled construction, 17 → 39 of 49
Method: typed bail reasons (
PairBail) pluscensus_pairs/si_census, which runs the pair stage alone on a copper-stripped board — ~15s versus ~2.7h for a full route, and it isolates geometry/logic failures from congestion.centerline-search28 of 32DP/DMinpair_partnercenterline-search28centerline-search23centerline-search22centerline-search6centerline-search5The decisive row: the phantom centerline was pinned to start and end on the outer layer. Inside a BGA that is solid pads, so no fat capsule can ever sit there and the search failed outright — 28 of 32 census bails, 48 of 56 in a full route. Pairs escape a BGA the way the human board does: down a via, coupled on an inner layer under the field. This also made the census 9× faster, since failed searches stopped burning the whole retreat ladder.
The last row is a deliberate loss for correctness — see defect 1.
Lever 2 —
router::si_finishThree non-regressive, oracle-gated stages:
polish_pairs(rip and re-route coupled) →descend_board(differentiable skew descent, moved out of the example so router and example cannot drift) → phase compensation.Shaped by two closed forms worth stating:
skew = (w + gap)·Σ 2·tan(θ/2)— ~0.9mm per net right-angle turn at this geometry, against a 1.1mm bound. A lateral shift cannot fix it (it moves both legs equally). Length must be added back.2(√2−1)/3 ≈ 0.276mm per mm of run, independent of amplitude. So small bumps are strictly better (same capacity, less coupling spent), the amplitude ladder climbs, and bumps must be tiled by arclength not per segment — per-segment tiling capped capacity at ~0.03mm/segment, which is why a 10.66mm run reported "cannot reach 1.312mm" while sitting on ample copper.The two pair claims genuinely trade against each other: at full amplitude, coupling fell 0.777 → 0.252. Amplitude is capped by the coupling window on leg-width runs, with a floor keeping pairs clear of the 0.5 bound.
Three defects this surfaced
polish_pairscommitted unchecked assemblies, and the incremental probe disagrees with the DRC at mitred pair corners (probe passed 0.191mm where DRC requires 0.245mm). Gated oncheck_drc_in_region: intra-pair violations 3 → 0.Results
40-net subset — Pass, but see the control below:
Zero intra-pair DRC violations; total
Clearanceidentical with the pass on and off (74).250-net board — 2/4, and the control that matters. Routability 0.957, 36 pairs, 17 of 37 re-coupled — and the same two blocker groups as the full board, with
/ETH.3_Pworst on both (37.5mm / 37.9mm).Full board (2h41m, routability 0.983): still 2/4. Coupling is pinned at 0.000 by the short
/HS.*pairs (~1.8mm nets whose legs land on different layers, which the metric scores as exactly zero); skew is pinned at 37.9mm by four detour pairs where polish fails under congestion.The 40-net Pass reflects an uncongested board, not a solved pipeline.
polish_pairscan rip and re-route a pair freely when there is space; its success rate falls as congestion rises. The finishing pass is sound and non-regressive at every size — what does not yet scale is its ability to land a re-route on a full board. That, not the compensation maths, is the real remaining work.Honest gaps
docs/gpu-router-m7-pair-si.md;/ETH.3_Preproduces at 250 nets, so it is a stable target rather than a full-board-only artifact.Shortcounts are not route-attributable on this fixture: the import carries ~906 pad-level shorts and every routed trace merges copper clusters (routing 40 nets adds ~409). UseClearance.Verification
256 tests pass,
clippy --features gpu -- -D warningsclean,cargo fmt --checkclean, changelog entry validated.Note: another in-flight branch (#677, "pad-layer-pinned stubs", "opt-in strict pair coupling") overlaps the pad-layer fix here and may conflict at merge.
🤖 Generated with Claude Code