@tscircuit/fanout-solver 0.0.41 → 0.0.42

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.
@@ -440,6 +440,36 @@ function busUsesCoordinatedWinding(bus: PreparedBus): boolean {
440
440
  )
441
441
  }
442
442
 
443
+ export function shouldUseJointBoundaryViaReservation(
444
+ boundaryBusConnectionCounts: readonly number[],
445
+ ): boolean {
446
+ return (
447
+ boundaryBusConnectionCounts.length === 5 ||
448
+ (boundaryBusConnectionCounts.length === 4 &&
449
+ new Set(boundaryBusConnectionCounts).size > 1)
450
+ )
451
+ }
452
+
453
+ export function shouldDeferSingletonBoundaryViaReservation(
454
+ boundaryBusConnectionCounts: readonly number[],
455
+ ): boolean {
456
+ return (
457
+ boundaryBusConnectionCounts.length === 5 &&
458
+ boundaryBusConnectionCounts.filter((count) => count === 1).length === 1
459
+ )
460
+ }
461
+
462
+ export function shouldSearchAdditionalBoundaryRouteTopologies(params: {
463
+ boundaryBusCount: number
464
+ rawSkew: number
465
+ maximumSkew: number
466
+ }): boolean {
467
+ if (params.boundaryBusCount >= 5) return false
468
+ return (
469
+ params.rawSkew - params.maximumSkew > Math.max(1, params.maximumSkew * 0.25)
470
+ )
471
+ }
472
+
443
473
  function getCandidateEscapeLayersForBus(params: {
444
474
  bus: PreparedBus
445
475
  srj: SimpleRouteJson
@@ -725,16 +755,15 @@ export class FanoutSolver extends BaseSolver {
725
755
  const unsortedBoundaryBuses = params.busesInRoutingOrder.filter(
726
756
  (bus) => bus.termination.type === "boundary",
727
757
  )
728
- const useJointFourBusReservation =
729
- unsortedBoundaryBuses.length === 4 &&
730
- new Set(unsortedBoundaryBuses.map((bus) => bus.connections.length)).size >
731
- 1
758
+ const useJointBoundaryViaReservation = shouldUseJointBoundaryViaReservation(
759
+ unsortedBoundaryBuses.map((bus) => bus.connections.length),
760
+ )
732
761
  const boundaryBuses = unsortedBoundaryBuses.toSorted((first, second) => {
733
762
  // Reserve the dense escape field for the widest buses first. Small
734
763
  // control groups can usually route around their copper, while routing
735
764
  // a two-line corner bus first can consume a critical channel needed by
736
765
  // an eight-line winding bus and force the expensive fallback search.
737
- if (useJointFourBusReservation) {
766
+ if (useJointBoundaryViaReservation) {
738
767
  const connectionCountDifference =
739
768
  second.connections.length - first.connections.length
740
769
  if (connectionCountDifference !== 0) return connectionCountDifference
@@ -786,7 +815,7 @@ export class FanoutSolver extends BaseSolver {
786
815
  )
787
816
  if (
788
817
  boundaryBuses.length === 0 ||
789
- boundaryBuses.length > 4 ||
818
+ boundaryBuses.length > 5 ||
790
819
  planeBuses.length < 8 ||
791
820
  boundaryBuses.some((bus) => !busUsesCoordinatedWinding(bus)) ||
792
821
  planeBuses.some((bus) => bus.connections.length !== 1) ||
@@ -833,22 +862,37 @@ export class FanoutSolver extends BaseSolver {
833
862
  ),
834
863
  )
835
864
  }
836
- // Four boundary buses leave too little slack for incremental site
837
- // allocation: a valid early trace can consume the last dogbone site of a
838
- // later narrow bus. Reserve every physical barrel before routing copper.
839
- // Plane sites remain provisional and are rematched around the completed
840
- // boundary plans below.
841
- const jointViaPoints = useJointFourBusReservation
842
- ? matchComponentDogboneViaSites([...planeBuses, ...boundaryBuses], {
843
- viaDiameter: this.config.viaDiameter,
844
- viaHoleDiameter: this.config.viaHoleDiameter,
845
- traceWidth: this.config.traceWidth,
846
- clearance: this.config.clearance,
847
- maximumSearchStates: 100_000,
848
- preferredBoundaryPerpendicularSideByBusId,
849
- preferBoundaryOutwardByBusId,
850
- canShareCopper,
851
- })
865
+ // Five boundary buses, and heterogeneous four-bus groups, leave too little
866
+ // slack for incremental site allocation: a valid early trace can consume
867
+ // the last dogbone site of a later narrow bus. Reserve the multi-line bus
868
+ // barrels before routing copper. A single one-line bus stays provisional
869
+ // because its flexible site can be rematched around the wide-bus copper.
870
+ // Plane sites are likewise rematched around completed boundary plans.
871
+ const boundaryBusConnectionCounts = boundaryBuses.map(
872
+ (bus) => bus.connections.length,
873
+ )
874
+ const provisionalSingletonBus = shouldDeferSingletonBoundaryViaReservation(
875
+ boundaryBusConnectionCounts,
876
+ )
877
+ ? boundaryBuses.find((bus) => bus.connections.length === 1)
878
+ : undefined
879
+ const initiallyMatchedBoundaryBuses = provisionalSingletonBus
880
+ ? boundaryBuses.filter((bus) => bus !== provisionalSingletonBus)
881
+ : boundaryBuses
882
+ const jointViaPoints = useJointBoundaryViaReservation
883
+ ? matchComponentDogboneViaSites(
884
+ [...planeBuses, ...initiallyMatchedBoundaryBuses],
885
+ {
886
+ viaDiameter: this.config.viaDiameter,
887
+ viaHoleDiameter: this.config.viaHoleDiameter,
888
+ traceWidth: this.config.traceWidth,
889
+ clearance: this.config.clearance,
890
+ maximumSearchStates: 100_000,
891
+ preferredBoundaryPerpendicularSideByBusId,
892
+ preferBoundaryOutwardByBusId,
893
+ canShareCopper,
894
+ },
895
+ )
852
896
  : null
853
897
  const seedViaPoints =
854
898
  jointViaPoints ??
@@ -929,7 +973,11 @@ export class FanoutSolver extends BaseSolver {
929
973
  const lengths = busPlans.map((plan) => plan.length)
930
974
  const rawSkew = Math.max(...lengths) - Math.min(...lengths)
931
975
  const needsRouteDiversity =
932
- rawSkew - bus.maxLengthSkew > Math.max(1, bus.maxLengthSkew * 0.25)
976
+ shouldSearchAdditionalBoundaryRouteTopologies({
977
+ boundaryBusCount: boundaryBuses.length,
978
+ rawSkew,
979
+ maximumSkew: bus.maxLengthSkew,
980
+ })
933
981
  // Only pay for additional A* variants when the first topology is so
934
982
  // skewed that compact meanders are unlikely to absorb the deficit.
935
983
  // This keeps already-near-matched buses on the single-attempt path.
@@ -976,27 +1024,32 @@ export class FanoutSolver extends BaseSolver {
976
1024
  candidateIndex++
977
1025
  ) {
978
1026
  const candidateBus = remainingBoundaryBuses[candidateIndex]!
979
- const extendedViaPoints = jointViaPoints
980
- ? // The joint map is deliberately kept intact so getReservedVias()
981
- // blocks every future through-barrel during A*. Plane dogbones are
982
- // validated/rematched once the boundary copper is complete.
983
- new Map(fixedViaPointsByConnectionIndex)
984
- : matchComponentDogboneViaSites(
985
- [...planeBuses, ...routedBoundaryBuses, candidateBus],
986
- {
987
- viaDiameter: this.config.viaDiameter,
988
- viaHoleDiameter: this.config.viaHoleDiameter,
989
- traceWidth: this.config.traceWidth,
990
- clearance: this.config.clearance,
991
- maximumSearchStates: 100_000,
992
- preferredBoundaryPerpendicularSideByBusId,
993
- preferBoundaryOutwardByBusId,
994
- fixedViaPointsByConnectionIndex:
995
- fixedViaPointsByConnectionIndex,
996
- blockingSegments,
997
- canShareCopper,
998
- },
999
- )
1027
+ const candidateHasFixedViaPoints = candidateBus.connections.every(
1028
+ (connection) =>
1029
+ fixedViaPointsByConnectionIndex.has(connection.connectionIndex),
1030
+ )
1031
+ const extendedViaPoints =
1032
+ jointViaPoints && candidateHasFixedViaPoints
1033
+ ? // The joint map is deliberately kept intact so getReservedVias()
1034
+ // blocks every already-reserved future through-barrel during A*.
1035
+ // Provisional singleton and plane dogbones are rematched later.
1036
+ new Map(fixedViaPointsByConnectionIndex)
1037
+ : matchComponentDogboneViaSites(
1038
+ [...planeBuses, ...routedBoundaryBuses, candidateBus],
1039
+ {
1040
+ viaDiameter: this.config.viaDiameter,
1041
+ viaHoleDiameter: this.config.viaHoleDiameter,
1042
+ traceWidth: this.config.traceWidth,
1043
+ clearance: this.config.clearance,
1044
+ maximumSearchStates: 100_000,
1045
+ preferredBoundaryPerpendicularSideByBusId,
1046
+ preferBoundaryOutwardByBusId,
1047
+ fixedViaPointsByConnectionIndex:
1048
+ fixedViaPointsByConnectionIndex,
1049
+ blockingSegments,
1050
+ canShareCopper,
1051
+ },
1052
+ )
1000
1053
  if (!extendedViaPoints) continue
1001
1054
  const previousFixedViaPoints = fixedViaPointsByConnectionIndex
1002
1055
  const previousPlanCount = matchedPlans.length
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/fanout-solver",
3
- "version": "0.0.41",
3
+ "version": "0.0.42",
4
4
  "description": "BGA fanout solver with coordinated bus-layer escapes for SimpleRouteJson",
5
5
  "module": "lib/index.ts",
6
6
  "type": "module",