@tscircuit/fanout-solver 0.0.40 → 0.0.41
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/lib/fanout-solver.ts +94 -60
- package/package.json +1 -1
package/lib/fanout-solver.ts
CHANGED
|
@@ -722,48 +722,60 @@ export class FanoutSolver extends BaseSolver {
|
|
|
722
722
|
}): MixedTerminationState | null {
|
|
723
723
|
if (this.config.allowBlindAndBuriedVias) return null
|
|
724
724
|
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
725
|
+
const unsortedBoundaryBuses = params.busesInRoutingOrder.filter(
|
|
726
|
+
(bus) => bus.termination.type === "boundary",
|
|
727
|
+
)
|
|
728
|
+
const useJointFourBusReservation =
|
|
729
|
+
unsortedBoundaryBuses.length === 4 &&
|
|
730
|
+
new Set(unsortedBoundaryBuses.map((bus) => bus.connections.length)).size >
|
|
731
|
+
1
|
|
732
|
+
const boundaryBuses = unsortedBoundaryBuses.toSorted((first, second) => {
|
|
733
|
+
// Reserve the dense escape field for the widest buses first. Small
|
|
734
|
+
// control groups can usually route around their copper, while routing
|
|
735
|
+
// a two-line corner bus first can consume a critical channel needed by
|
|
736
|
+
// an eight-line winding bus and force the expensive fallback search.
|
|
737
|
+
if (useJointFourBusReservation) {
|
|
738
|
+
const connectionCountDifference =
|
|
739
|
+
second.connections.length - first.connections.length
|
|
740
|
+
if (connectionCountDifference !== 0) return connectionCountDifference
|
|
741
|
+
}
|
|
742
|
+
const cornerBandDifference =
|
|
743
|
+
Number(
|
|
744
|
+
Boolean(getCornerBandSide(second.exitEdge, second.preferredExit)),
|
|
745
|
+
) -
|
|
746
|
+
Number(Boolean(getCornerBandSide(first.exitEdge, first.preferredExit)))
|
|
747
|
+
if (cornerBandDifference !== 0) return cornerBandDifference
|
|
748
|
+
const firstIsCorner = Boolean(
|
|
749
|
+
getCornerBandSide(first.exitEdge, first.preferredExit),
|
|
750
|
+
)
|
|
751
|
+
if (!firstIsCorner) {
|
|
752
|
+
const getSourceSpan = (bus: PreparedBus): number => {
|
|
753
|
+
const xCoordinates = bus.connections.map(
|
|
754
|
+
(connection) => connection.sourcePoint.x,
|
|
755
|
+
)
|
|
756
|
+
const yCoordinates = bus.connections.map(
|
|
757
|
+
(connection) => connection.sourcePoint.y,
|
|
758
|
+
)
|
|
759
|
+
return (
|
|
760
|
+
Math.max(...xCoordinates) -
|
|
761
|
+
Math.min(...xCoordinates) +
|
|
762
|
+
Math.max(...yCoordinates) -
|
|
763
|
+
Math.min(...yCoordinates)
|
|
734
764
|
)
|
|
735
|
-
if (cornerBandDifference !== 0) return cornerBandDifference
|
|
736
|
-
const firstIsCorner = Boolean(
|
|
737
|
-
getCornerBandSide(first.exitEdge, first.preferredExit),
|
|
738
|
-
)
|
|
739
|
-
if (!firstIsCorner) {
|
|
740
|
-
const getSourceSpan = (bus: PreparedBus): number => {
|
|
741
|
-
const xCoordinates = bus.connections.map(
|
|
742
|
-
(connection) => connection.sourcePoint.x,
|
|
743
|
-
)
|
|
744
|
-
const yCoordinates = bus.connections.map(
|
|
745
|
-
(connection) => connection.sourcePoint.y,
|
|
746
|
-
)
|
|
747
|
-
return (
|
|
748
|
-
Math.max(...xCoordinates) -
|
|
749
|
-
Math.min(...xCoordinates) +
|
|
750
|
-
Math.max(...yCoordinates) -
|
|
751
|
-
Math.min(...yCoordinates)
|
|
752
|
-
)
|
|
753
|
-
}
|
|
754
|
-
const sourceSpanDifference =
|
|
755
|
-
getSourceSpan(second) - getSourceSpan(first)
|
|
756
|
-
if (Math.abs(sourceSpanDifference) > 1e-9) {
|
|
757
|
-
return sourceSpanDifference
|
|
758
|
-
}
|
|
759
765
|
}
|
|
760
|
-
const
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
766
|
+
const sourceSpanDifference =
|
|
767
|
+
getSourceSpan(second) - getSourceSpan(first)
|
|
768
|
+
if (Math.abs(sourceSpanDifference) > 1e-9) {
|
|
769
|
+
return sourceSpanDifference
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
const firstLayer = params.busLayerAssignments[first.busId]
|
|
773
|
+
const secondLayer = params.busLayerAssignments[second.busId]
|
|
774
|
+
const layerDifference =
|
|
775
|
+
this.config.layerNames.indexOf(firstLayer ?? "") -
|
|
776
|
+
this.config.layerNames.indexOf(secondLayer ?? "")
|
|
777
|
+
return -layerDifference
|
|
778
|
+
})
|
|
767
779
|
// Preserve the caller/input order for the dense singleton fill. The
|
|
768
780
|
// general routing sort is useful for heterogeneous buses, but ordering a
|
|
769
781
|
// regular BGA power field by obstacle depth creates artificial local
|
|
@@ -821,9 +833,26 @@ export class FanoutSolver extends BaseSolver {
|
|
|
821
833
|
),
|
|
822
834
|
)
|
|
823
835
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
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
|
+
})
|
|
852
|
+
: null
|
|
853
|
+
const seedViaPoints =
|
|
854
|
+
jointViaPoints ??
|
|
855
|
+
matchComponentDogboneViaSites([...planeBuses, boundaryBuses[0]!], {
|
|
827
856
|
viaDiameter: this.config.viaDiameter,
|
|
828
857
|
viaHoleDiameter: this.config.viaHoleDiameter,
|
|
829
858
|
traceWidth: this.config.traceWidth,
|
|
@@ -832,8 +861,7 @@ export class FanoutSolver extends BaseSolver {
|
|
|
832
861
|
preferredBoundaryPerpendicularSideByBusId,
|
|
833
862
|
preferBoundaryOutwardByBusId,
|
|
834
863
|
canShareCopper,
|
|
835
|
-
}
|
|
836
|
-
)
|
|
864
|
+
})
|
|
837
865
|
if (seedViaPoints) {
|
|
838
866
|
let fixedViaPointsByConnectionIndex: ReadonlyMap<
|
|
839
867
|
number,
|
|
@@ -948,21 +976,27 @@ export class FanoutSolver extends BaseSolver {
|
|
|
948
976
|
candidateIndex++
|
|
949
977
|
) {
|
|
950
978
|
const candidateBus = remainingBoundaryBuses[candidateIndex]!
|
|
951
|
-
const extendedViaPoints =
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
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
|
+
)
|
|
966
1000
|
if (!extendedViaPoints) continue
|
|
967
1001
|
const previousFixedViaPoints = fixedViaPointsByConnectionIndex
|
|
968
1002
|
const previousPlanCount = matchedPlans.length
|