@tscircuit/fanout-solver 0.0.37 → 0.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -2
- package/lib/add-via-layer-metadata.ts +48 -0
- package/lib/complete-original-endpoints.ts +30 -2
- package/lib/fanout-solver.ts +721 -33
- package/lib/geometry.ts +21 -0
- package/lib/get-routed-trace-copper.ts +15 -6
- package/lib/index.ts +3 -0
- package/lib/layer-names.ts +40 -0
- package/lib/match-bus-lengths.ts +681 -0
- package/lib/match-component-dogbone-via-sites.ts +584 -0
- package/lib/prepare-buses.ts +26 -0
- package/lib/route-bus.ts +872 -112
- package/lib/route-via-minimal-winding.ts +390 -74
- package/lib/types.ts +39 -7
- package/lib/validate-fanout-solution.ts +62 -6
- package/lib/validate-routed-copper-drc.ts +47 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,6 +73,10 @@ and treats each bus-layer decision atomically.
|
|
|
73
73
|
visibly continuous pad connections.
|
|
74
74
|
- Chamfers orthogonal routing corners into 45° segments before validating and
|
|
75
75
|
emitting the fanout.
|
|
76
|
+
- Honors a boundary bus `maxLengthSkew` as a hard local-fanout constraint. It
|
|
77
|
+
adds straight/45° meanders only after the dense component escape, keeps the
|
|
78
|
+
original endpoints and vias, and atomically rejects an assignment when the
|
|
79
|
+
requested skew cannot fit inside that bus's shared boundary.
|
|
76
80
|
- Verifies oriented-pad, via, trace, and already-routed fanout clearance on
|
|
77
81
|
every complete candidate, independent of the routing strategy that produced
|
|
78
82
|
it.
|
|
@@ -82,6 +86,13 @@ and treats each bus-layer decision atomically.
|
|
|
82
86
|
- `allowSameNetMerges` lets grouped branches such as VCC or GND reuse connected
|
|
83
87
|
copper instead of reserving artificial clearance from one another. It is
|
|
84
88
|
opt-in; different electrical nets remain hard obstacles.
|
|
89
|
+
- `allowBlindAndBuriedVias` describes the host board's manufacturing rule. It
|
|
90
|
+
defaults to `true` for standalone compatibility; hosts that manufacture
|
|
91
|
+
through-all vias should pass `false`, which reserves every copper layer in
|
|
92
|
+
route planning and emitted-copper DRC while preserving each route's logical
|
|
93
|
+
layer transition.
|
|
94
|
+
- Via-in-pad remains opt-in through `SimpleRouteJson.allowViaInPad === true`.
|
|
95
|
+
Undefined or false uses an offset dogbone, including plane terminations.
|
|
85
96
|
- Audits route continuity, unique connection coverage, boundary exits, and
|
|
86
97
|
retained downstream endpoints before marking a solution complete.
|
|
87
98
|
- `completeOriginalEndpoints` adds a bounded fail-first completion stage after
|
|
@@ -189,6 +200,7 @@ The canonical bus input is the current `SimpleRouteJson` bus structure:
|
|
|
189
200
|
busId: "ddr",
|
|
190
201
|
connectionNames: ["BUS_DDR_01", "BUS_DDR_02", "BUS_DDR_03"],
|
|
191
202
|
preferredExit: "right",
|
|
203
|
+
maxLengthSkew: 0.25,
|
|
192
204
|
},
|
|
193
205
|
],
|
|
194
206
|
}
|
|
@@ -201,6 +213,13 @@ routed cleanly, the solver rejects that bus for the current layer assignment and
|
|
|
201
213
|
tries another combination. `busExitPreferences` provides the same override
|
|
202
214
|
without modifying the input object.
|
|
203
215
|
|
|
216
|
+
`maxLengthSkew` is measured in millimeters of planar routed copper within this
|
|
217
|
+
fanout phase. It is supported for multi-connection boundary buses. A loose or
|
|
218
|
+
omitted constraint leaves the routed geometry unchanged; an impossible
|
|
219
|
+
constraint fails instead of returning a fanout that violates the declared skew.
|
|
220
|
+
Plane-terminated buses reject `maxLengthSkew` because they do not have a
|
|
221
|
+
boundary tuning corridor.
|
|
222
|
+
|
|
204
223
|
`availableCornersAndSides` is a solver-wide hard constraint. Its directed
|
|
205
224
|
corner names distinguish the two edges meeting at a corner: `top_left` exits
|
|
206
225
|
through the top edge, while `left_top` exits through the left edge. The complete
|
|
@@ -473,5 +492,7 @@ label.
|
|
|
473
492
|
## Scope
|
|
474
493
|
|
|
475
494
|
This package owns the BGA pad-to-breakout prefix. It does not replace the
|
|
476
|
-
board-level autorouter
|
|
477
|
-
|
|
495
|
+
board-level autorouter or route arbitrary obstacles between the breakout
|
|
496
|
+
boundary and the final destination. Its `maxLengthSkew` matching applies to the
|
|
497
|
+
local fanout prefix; end-to-end delay matching across multiple routing phases
|
|
498
|
+
still belongs to a board-level coordinator.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { SimpleRouteJson } from "@tscircuit/capacity-autorouter"
|
|
2
|
+
import { getViaSpanLayers } from "./layer-names"
|
|
3
|
+
import type {
|
|
4
|
+
FanoutSimplifiedPcbTrace,
|
|
5
|
+
SimpleRouteJsonWithFanoutPlanes,
|
|
6
|
+
} from "./types"
|
|
7
|
+
|
|
8
|
+
export function addViaLayerMetadataToTrace(params: {
|
|
9
|
+
trace: FanoutSimplifiedPcbTrace
|
|
10
|
+
layerNames: string[]
|
|
11
|
+
allowBlindAndBuriedVias: boolean
|
|
12
|
+
}): FanoutSimplifiedPcbTrace {
|
|
13
|
+
const { trace, layerNames, allowBlindAndBuriedVias } = params
|
|
14
|
+
return {
|
|
15
|
+
...trace,
|
|
16
|
+
route: trace.route.map((routePoint) =>
|
|
17
|
+
routePoint.route_type === "via" && !allowBlindAndBuriedVias
|
|
18
|
+
? {
|
|
19
|
+
...routePoint,
|
|
20
|
+
layers: getViaSpanLayers({
|
|
21
|
+
fromLayer: routePoint.from_layer,
|
|
22
|
+
toLayer: routePoint.to_layer,
|
|
23
|
+
layerNames,
|
|
24
|
+
allowBlindAndBuriedVias,
|
|
25
|
+
}),
|
|
26
|
+
}
|
|
27
|
+
: { ...routePoint },
|
|
28
|
+
),
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function addViaLayerMetadataToSrj(params: {
|
|
33
|
+
srj: SimpleRouteJsonWithFanoutPlanes | SimpleRouteJson
|
|
34
|
+
layerNames: string[]
|
|
35
|
+
allowBlindAndBuriedVias: boolean
|
|
36
|
+
}): SimpleRouteJsonWithFanoutPlanes {
|
|
37
|
+
const { srj, layerNames, allowBlindAndBuriedVias } = params
|
|
38
|
+
return {
|
|
39
|
+
...srj,
|
|
40
|
+
traces: (srj.traces ?? []).map((trace) =>
|
|
41
|
+
addViaLayerMetadataToTrace({
|
|
42
|
+
trace,
|
|
43
|
+
layerNames,
|
|
44
|
+
allowBlindAndBuriedVias,
|
|
45
|
+
}),
|
|
46
|
+
),
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
} from "./geometry"
|
|
13
13
|
import { obstacleSharesElectricalNet } from "./net-identity"
|
|
14
14
|
import type {
|
|
15
|
-
FanoutEndpointCompletionReport,
|
|
16
15
|
FanoutDownstreamRouter,
|
|
16
|
+
FanoutEndpointCompletionReport,
|
|
17
17
|
FanoutRoutePlan,
|
|
18
18
|
Point2D,
|
|
19
19
|
} from "./types"
|
|
@@ -267,6 +267,7 @@ function findLocalBranch(params: {
|
|
|
267
267
|
viaDiameter: number
|
|
268
268
|
viaHoleDiameter: number
|
|
269
269
|
clearance: number
|
|
270
|
+
allowBlindAndBuriedVias: boolean
|
|
270
271
|
}): {
|
|
271
272
|
trace?: SimplifiedPcbTrace
|
|
272
273
|
blockingConnectionNames: string[]
|
|
@@ -280,6 +281,7 @@ function findLocalBranch(params: {
|
|
|
280
281
|
viaDiameter,
|
|
281
282
|
viaHoleDiameter,
|
|
282
283
|
clearance,
|
|
284
|
+
allowBlindAndBuriedVias,
|
|
283
285
|
} = params
|
|
284
286
|
const { outward, perpendicular } = getTerminalDirections({ inputSrj, plan })
|
|
285
287
|
const branchStarts: Array<Point2D & { layer: string }> = [
|
|
@@ -414,6 +416,7 @@ function findLocalBranch(params: {
|
|
|
414
416
|
inputSrj,
|
|
415
417
|
routedSrj: candidateSrj,
|
|
416
418
|
clearance,
|
|
419
|
+
allowBlindAndBuriedVias,
|
|
417
420
|
})
|
|
418
421
|
if (!drc.valid) {
|
|
419
422
|
if (drc.issues.length < bestIssueCount) {
|
|
@@ -457,6 +460,7 @@ function runLocalCompletionPass(params: {
|
|
|
457
460
|
viaDiameter: number
|
|
458
461
|
viaHoleDiameter: number
|
|
459
462
|
clearance: number
|
|
463
|
+
allowBlindAndBuriedVias: boolean
|
|
460
464
|
}): CompletionAttempt {
|
|
461
465
|
const {
|
|
462
466
|
inputSrj,
|
|
@@ -466,6 +470,7 @@ function runLocalCompletionPass(params: {
|
|
|
466
470
|
viaDiameter,
|
|
467
471
|
viaHoleDiameter,
|
|
468
472
|
clearance,
|
|
473
|
+
allowBlindAndBuriedVias,
|
|
469
474
|
} = params
|
|
470
475
|
const traces: SimplifiedPcbTrace[] = []
|
|
471
476
|
const failedConnectionNames: string[] = []
|
|
@@ -480,6 +485,7 @@ function runLocalCompletionPass(params: {
|
|
|
480
485
|
viaDiameter,
|
|
481
486
|
viaHoleDiameter,
|
|
482
487
|
clearance,
|
|
488
|
+
allowBlindAndBuriedVias,
|
|
483
489
|
})
|
|
484
490
|
if (result.trace) {
|
|
485
491
|
traces.push(result.trace)
|
|
@@ -506,6 +512,7 @@ function runLocalCompletionPass(params: {
|
|
|
506
512
|
inputSrj,
|
|
507
513
|
routedSrj: simpleRouteJson,
|
|
508
514
|
clearance,
|
|
515
|
+
allowBlindAndBuriedVias,
|
|
509
516
|
}),
|
|
510
517
|
}
|
|
511
518
|
}
|
|
@@ -598,8 +605,16 @@ function acceptDownstreamTraces(params: {
|
|
|
598
605
|
localTraces: SimplifiedPcbTrace[]
|
|
599
606
|
candidates: SimplifiedPcbTrace[]
|
|
600
607
|
clearance: number
|
|
608
|
+
allowBlindAndBuriedVias: boolean
|
|
601
609
|
}): SimplifiedPcbTrace[] {
|
|
602
|
-
const {
|
|
610
|
+
const {
|
|
611
|
+
inputSrj,
|
|
612
|
+
fanoutSrj,
|
|
613
|
+
localTraces,
|
|
614
|
+
candidates,
|
|
615
|
+
clearance,
|
|
616
|
+
allowBlindAndBuriedVias,
|
|
617
|
+
} = params
|
|
603
618
|
const baselineTraces = [...(fanoutSrj.traces ?? []), ...localTraces]
|
|
604
619
|
const baselineSrj = { ...fanoutSrj, traces: baselineTraces }
|
|
605
620
|
const baselineConnectivity = validateOriginalEndpointConnectivity({
|
|
@@ -638,6 +653,7 @@ function acceptDownstreamTraces(params: {
|
|
|
638
653
|
inputSrj,
|
|
639
654
|
routedSrj: combinedSrj,
|
|
640
655
|
clearance,
|
|
656
|
+
allowBlindAndBuriedVias,
|
|
641
657
|
}).valid
|
|
642
658
|
) {
|
|
643
659
|
return usefulCandidates
|
|
@@ -655,6 +671,7 @@ function acceptDownstreamTraces(params: {
|
|
|
655
671
|
inputSrj,
|
|
656
672
|
routedSrj: candidateSrj,
|
|
657
673
|
clearance,
|
|
674
|
+
allowBlindAndBuriedVias,
|
|
658
675
|
})
|
|
659
676
|
if (!drc.valid) continue
|
|
660
677
|
const before = validateOriginalEndpointConnectivity({
|
|
@@ -729,6 +746,7 @@ function findDownstreamTerminalBranch(params: {
|
|
|
729
746
|
viaDiameter: number
|
|
730
747
|
viaHoleDiameter: number
|
|
731
748
|
clearance: number
|
|
749
|
+
allowBlindAndBuriedVias: boolean
|
|
732
750
|
}): SimplifiedPcbTrace | undefined {
|
|
733
751
|
const {
|
|
734
752
|
inputSrj,
|
|
@@ -739,6 +757,7 @@ function findDownstreamTerminalBranch(params: {
|
|
|
739
757
|
viaDiameter,
|
|
740
758
|
viaHoleDiameter,
|
|
741
759
|
clearance,
|
|
760
|
+
allowBlindAndBuriedVias,
|
|
742
761
|
} = params
|
|
743
762
|
const branchStart = {
|
|
744
763
|
x: plan.exitPoint.x,
|
|
@@ -782,6 +801,7 @@ function findDownstreamTerminalBranch(params: {
|
|
|
782
801
|
inputSrj,
|
|
783
802
|
routedSrj: candidateSrj,
|
|
784
803
|
clearance,
|
|
804
|
+
allowBlindAndBuriedVias,
|
|
785
805
|
}).valid
|
|
786
806
|
) {
|
|
787
807
|
return undefined
|
|
@@ -880,6 +900,7 @@ export function completeOriginalEndpoints(params: {
|
|
|
880
900
|
viaDiameter: number
|
|
881
901
|
viaHoleDiameter: number
|
|
882
902
|
clearance: number
|
|
903
|
+
allowBlindAndBuriedVias?: boolean
|
|
883
904
|
effort?: number
|
|
884
905
|
routeDownstreamConnections?: FanoutDownstreamRouter
|
|
885
906
|
}): CompleteOriginalEndpointsResult {
|
|
@@ -891,6 +912,7 @@ export function completeOriginalEndpoints(params: {
|
|
|
891
912
|
viaDiameter,
|
|
892
913
|
viaHoleDiameter,
|
|
893
914
|
clearance,
|
|
915
|
+
allowBlindAndBuriedVias = true,
|
|
894
916
|
effort = 1,
|
|
895
917
|
routeDownstreamConnections,
|
|
896
918
|
} = params
|
|
@@ -899,6 +921,7 @@ export function completeOriginalEndpoints(params: {
|
|
|
899
921
|
inputSrj,
|
|
900
922
|
routedSrj: fanoutSrj,
|
|
901
923
|
clearance,
|
|
924
|
+
allowBlindAndBuriedVias,
|
|
902
925
|
})
|
|
903
926
|
const baselineConnectivity = validateOriginalEndpointConnectivity({
|
|
904
927
|
inputSrj,
|
|
@@ -952,6 +975,7 @@ export function completeOriginalEndpoints(params: {
|
|
|
952
975
|
viaDiameter,
|
|
953
976
|
viaHoleDiameter,
|
|
954
977
|
clearance,
|
|
978
|
+
allowBlindAndBuriedVias,
|
|
955
979
|
})
|
|
956
980
|
searchPassCount++
|
|
957
981
|
if (
|
|
@@ -995,6 +1019,7 @@ export function completeOriginalEndpoints(params: {
|
|
|
995
1019
|
viaDiameter,
|
|
996
1020
|
viaHoleDiameter,
|
|
997
1021
|
clearance,
|
|
1022
|
+
allowBlindAndBuriedVias,
|
|
998
1023
|
})
|
|
999
1024
|
if (terminalBranch) directDownstreamTraces.push(terminalBranch)
|
|
1000
1025
|
}
|
|
@@ -1052,6 +1077,7 @@ export function completeOriginalEndpoints(params: {
|
|
|
1052
1077
|
downstreamConnectionNames.has(trace.connection_name),
|
|
1053
1078
|
),
|
|
1054
1079
|
clearance,
|
|
1080
|
+
allowBlindAndBuriedVias,
|
|
1055
1081
|
})
|
|
1056
1082
|
} catch (error) {
|
|
1057
1083
|
errors.push(
|
|
@@ -1102,6 +1128,7 @@ export function completeOriginalEndpoints(params: {
|
|
|
1102
1128
|
viaDiameter,
|
|
1103
1129
|
viaHoleDiameter,
|
|
1104
1130
|
clearance,
|
|
1131
|
+
allowBlindAndBuriedVias,
|
|
1105
1132
|
})
|
|
1106
1133
|
if (terminalBranch) traces.push(terminalBranch)
|
|
1107
1134
|
}
|
|
@@ -1121,6 +1148,7 @@ export function completeOriginalEndpoints(params: {
|
|
|
1121
1148
|
inputSrj,
|
|
1122
1149
|
routedSrj: simpleRouteJson,
|
|
1123
1150
|
clearance,
|
|
1151
|
+
allowBlindAndBuriedVias,
|
|
1124
1152
|
})
|
|
1125
1153
|
if (!drc.valid) {
|
|
1126
1154
|
errors.push("Final endpoint-completion output failed emitted-copper DRC")
|