fix(ecad): the SI receipt was measuring copper that is not a route#693
Merged
Conversation
…b copper Re-measured the full CM5 SI receipt on e1a5d90 (post-#684) to check whether the two broken claims had moved. They moved, and the run also invalidated the reading of the skew claim, so this supersedes M7's "two blocker groups". Three of four claims now break: vias_per_si_net regressed through its bound (2.766 -> 3.128). worst_intra_pair_skew is 38.521mm, and it is not detour skew — seven pair legs are not routed. Measured copper length against each net's own straight pad-to-pad span, a ratio needing no connectivity model: human board 0 of 86 legs below 50%, ours 7 of 84, minimum 0.01 (/USB3-0.TX_P carries 0.23mm for an 18.36mm span). /MIPI1.D1_P is four disjoint fragments, the largest a floating 4.234mm stub, beside a complete 38.57mm twin. Two gaps let that reach the receipt: si_claims gates "measured" on net_routed_length > 0, and coupled_fraction normalizes by P alone, so a stub beside a full twin scores 1.000 — the metric rewards the failure. The DRC misses it too: each starved leg is bridged by copper it is SHORTED to, and the connectivity walk crosses the short. Also recorded, with the human-board control M7 lacked: MinDrill/AnnularRing/ MinTraceWidth fire on nearly every via and thin trace on the human board too, so Clearance is the attributable rule — stripped 23 -> ours 173, i.e. +150. What did not work: descend_board tunes 0 of 41 pairs on a full board (all rejected by its fail-closed check) despite reaching ~0.006mm in isolation; phase compensation fixes 3 pairs and leaves 38 over tolerance; and coupling degrades after construction (47 coupled, 10 below 0.5 by the end) because negotiation re-routes pair legs independently. No bounds were touched. The human board re-verifies ALL HOLD, unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…osed on unrouted legs Three findings from re-measuring the full-board CM5 SI receipt, each fixed or staged with the measurement that motivates it. No bound was changed; the human board re-verifies ALL HOLD with every original number identical. 1. The pruner was defeated by shorts. `dangling_copper_mask` is documented as dropping copper whose island touches no pad "of their net", but it judged anchoring over the board-global touch graph and accepted ANY pad in the component — so a dead island of net X that merely brushed net Y saw Y's pads and survived. On a board with 845 shorts that is not a corner case. Judging over same-net connectivity (build_same_net_dsu, built from the contact list the global pass already produces, so no extra broadphase; net ties still connect) removes 393 dead traces and 52 dead vias from the routed CM5 while UnconnectedNet holds at 253 — nothing lost a connection. worst_group_skew falls 8.397mm -> 3.579mm, Clearance 173 -> 150. UnconnectedNet gets the same graph, for the same reason: a short is not a substitute for a route. 2. The receipt measured legs that are not routes. si_claims gated "measured" on net_routed_length > 0, which sums any copper carrying the net name, so a pad escape qualified — /USB3-0.TX_P was scored as a routed pair with 0.23mm of copper across an 18.36mm pad span, while the DRC was already reporting it UnconnectedNet. coupled_fraction then normalizes by P alone, so a stub beside a full twin scores 1.000: /MIPI1.D3_P read coupled 1.000 at 38.52mm of skew. The metric rewarded the failure. Now a pair is excluded when either leg holds less copper than its own pad span requires, and the exclusions are counted and bounded by a new si_pairs_incomplete claim — excluding silently would be worse than the original bug, since a board could pass by leaving the hard pairs as stubs. DRC continuity was tried as the gate first and is disqualifying: the fixture is reverse-engineered, and continuity calls 25 of the HUMAN board's 49 pairs discontinuous. The length test reads only copper length and pad positions. Threshold 0.5 is calibrated to the anchor's thinnest leg (0.522). 3. Spurs, staged not shipped. Dead-end branches hanging off live islands are invisible to an island-level pruner and large here: /USB3-1.DP carried 72.86mm whose shortest pad-to-pad path is 36.60mm, so 36.26mm was stub — a real unterminated stub on a USB3 pair, not just bad bookkeeping. spur_copper_mask implements iterated leaf removal and is sound (it changes neither board's UnconnectedNet count) and recovers vias_per_si_net 3.106 -> 2.626, but it is NOT wired into route_all: for a partially-reached net every piece of copper is a dead end, so it reclassifies those nets as unrouted and moves routability. That contract change needs its own full-board before/after. Remaining: worst_intra_pair_skew is still 38.521mm. /MIPI1.D3_N holds 77.03mm for a 34.54mm span, continuous with 2/2 pads and all of it on pad-to-pad paths — a redundant cycle, the signature of a re-route that never removed the path it replaced. Compensation is the wrong tool for it either way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…es strand route_all prunes dangling copper as its last word, but si_finish runs after it, and polish_pairs / descend_board / meander_pair_skew all rip copper and re-route it. So the board si_finish handed back was dirtier than the one it was given: measured on the full CM5, 231 dead traces and 52 dead vias, which net_routed_length counts as routed copper. Caught by the end-to-end run and invisible without it. The same-net anchoring fix was working inside route_all — the fresh route's prune dropped 2030 dead traces where re-pruning the saved board found 393 — yet the final receipt came out byte-identical to the unfixed baseline, because everything route_all cleaned up was replaced afterwards. Fresh full route, 874.5s, routability 0.994: worst_group_skew 9.297mm -> 3.579mm, Clearance 173 -> 158 (route-attributable +150 -> +135), NetIslands 93 -> 17. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SiBounds gained fields on both sides (max_incomplete_pairs here, the impedance pair from #691) — additive, both kept, and max_incomplete_pairs takes #[serde(default)] to match the convention #691 established for new bounds. Human anchor re-verified after the merge: ALL HOLD, six claims, every pre-existing number unchanged. 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. |
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.
Re-measures the full-board CM5
vcad.si-claims/1receipt after #684 and fixesthree fail-closed gaps it exposed. It does not reach all-claims-HOLDS, and
the reason is the point of the PR.
What the re-measurement found
The premise going in was that two claims needed closing. Measured on a fresh
full route, three of four were broken —
vias_per_si_nethad regressed throughits bound — and the headline skew turned out not to be skew at all:
/USB3-0.TX_Pwas being scored as a routed differential pair with 0.23mm ofcopper across an 18.36mm pad span, while the DRC was already reporting it
UnconnectedNet. Comparing every leg's copper against its own straightpad-to-pad distance — a ratio needing no connectivity model, so it cannot be an
artifact of how one defines an island:
The reported 38mm "skew" was essentially the length of whichever leg did
route. Compensation was never going to close it — there is no second leg to
match.
Three fixes
1. The pruner was defeated by shorts.
dangling_copper_maskis documentedas dropping copper whose island touches no pad "of their net", but it judged
anchoring over the board-global touch graph and accepted any pad in the
component — so a dead island of net X that merely brushed net Y saw Y's pads and
survived. On a board with 845 shorts that is not a corner case. Now judged over
same-net connectivity, reusing the contact list the global pass already builds
(no extra broadphase); net ties still connect.
2. The receipt measured legs that are not routes.
si_claimsgated"measured" on
net_routed_length > 0, which sums any copper carrying the netname. And
coupled_fractionnormalizes by P alone, so a stub beside a full twinscores 1.000 —
/MIPI1.D3_Preadcoupled 1.000while carrying 38.52mm ofskew. The metric rewarded the failure. A pair is now excluded when either leg
holds less copper than its own pad span requires, and the exclusions are counted
and bounded by a new
si_pairs_incompleteclaim, because excluding silentlywould be worse than the original bug: a board could pass by leaving the hard
pairs as stubs.
DRC continuity was tried as the gate first and is disqualifying — the
fixture is reverse-engineered, and continuity calls 25 of the human board's 49
pairs discontinuous, which would break the calibration anchor. The threshold
(0.5) is calibrated to the anchor's own thinnest leg, 0.522.
3.
si_finishhanded back a dirtier board than it was given.route_allprunes as its last word, but
si_finishruns after it and every stage rips andre-routes copper: 231 dead traces and 52 dead vias left stranded and counted as
routed length. This one was invisible without the end-to-end run — the fix in
(1) was working inside
route_all(2030 dead traces pruned vs 393 whenre-pruning the saved board) yet the final receipt came out byte-identical,
because everything cleaned up was replaced afterwards.
Results — fresh full route, 874.5s, routability 0.994
worst_group_skewworst_intra_pair_skewsi_pairs_incompletemin_pair_coupled_fractionvias_per_si_netRoute-attributable
Clearance+150 → +135,NetIslands93 → 17.MinDrill/AnnularRing/MinTraceWidthfire on nearly every via and thin traceon the human board too, so they are imported-rule artifacts and
Clearanceisthe rule that attributes.
What is left
worst_intra_pair_skewis unchanged at 38.521mm./MIPI1.D3_Nholds 77.03mm ofcopper for a 34.54mm span,
continuouswith 2/2 pads and all of it onpad-to-pad paths — a redundant cycle, the signature of a re-route that never
removed the path it replaced. Leaf-pruning cannot touch it (every node has
degree ≥ 2), and compensation would add copper to a net that already has twice
what it needs.
Also staged but deliberately not wired in:
spur_copper_mask/prune_spur_copper, which strips dead-end branches off live islands(
/USB3-1.DPcarried 72.86mm whose shortest pad-to-pad path is 36.60mm — a~34mm unterminated stub on a USB3 pair). It is sound — it changes neither
board's
UnconnectedNetcount — and it recoversvias_per_si_netto 2.626, butfor a partially-reached net every piece of copper is a dead end, so enabling it
reclassifies those nets as unrouted and moves
routability. That contractchange needs its own full-board before/after.
Guarantees
every commit and after the merge, with every pre-existing number identical
(9.756 / 1.074 / 0.857 / 2.265).
Clearance— the connectivity fixes do not move it.-D warningsclean,cargo fmtclean. The newbehaviour is pinned by
dead_copper_shorted_to_a_live_net_is_still_pruned,verified to fail without the fix.
packages/kernel-wasmartifacts touched.Full measurement record, including what did not work (
descend_boardtunes 0 of41 pairs on a full board; phase compensation reaches 3 of 41; construction
couples 47 of 49 but negotiation un-couples down to 10 below 0.5), is in
docs/gpu-router-m7-pair-si.md.🤖 Generated with Claude Code