@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 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, length-match buses, or route arbitrary obstacles between
477
- the breakout boundary and the final destination.
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 { inputSrj, fanoutSrj, localTraces, candidates, clearance } = params
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")