@tscircuit/fanout-solver 0.0.24 → 0.0.25
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 +6 -7
- package/lib/index.ts +1 -0
- package/lib/prepare-buses.ts +29 -8
- package/lib/route-bus.ts +1 -1
- package/lib/route-single-layer-push-shove.ts +5 -2
- package/lib/types.ts +11 -0
- package/package.json +1 -1
package/lib/fanout-solver.ts
CHANGED
|
@@ -208,15 +208,14 @@ function getBusDistanceToBoundary(bus: PreparedBus): number {
|
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
function
|
|
211
|
+
function busUsesDestinationGuidedTracks(bus: PreparedBus): boolean {
|
|
212
212
|
const isHorizontal = bus.direction === "left" || bus.direction === "right"
|
|
213
213
|
return bus.connections.some((connection) => {
|
|
214
|
+
const exitTargetPoint = connection.exitTargetPoint ?? connection.targetPoint
|
|
214
215
|
const sourceTrack = isHorizontal
|
|
215
216
|
? connection.sourcePoint.y
|
|
216
217
|
: connection.sourcePoint.x
|
|
217
|
-
const targetTrack = isHorizontal
|
|
218
|
-
? connection.targetPoint.y
|
|
219
|
-
: connection.targetPoint.x
|
|
218
|
+
const targetTrack = isHorizontal ? exitTargetPoint.y : exitTargetPoint.x
|
|
220
219
|
return Math.abs(sourceTrack - targetTrack) > 1e-6
|
|
221
220
|
})
|
|
222
221
|
}
|
|
@@ -293,7 +292,7 @@ function createPreferredLayerAssignment(params: {
|
|
|
293
292
|
)
|
|
294
293
|
if (
|
|
295
294
|
routableEscapeLayers.includes(sourceLayer) &&
|
|
296
|
-
(
|
|
295
|
+
(busUsesDestinationGuidedTracks(bus) || busIsOnOutwardComponentEdge(bus))
|
|
297
296
|
) {
|
|
298
297
|
assignment[bus.busId] = sourceLayer
|
|
299
298
|
} else if (viaLayers.length > 0) {
|
|
@@ -795,7 +794,7 @@ export class FanoutSolver extends BaseSolver {
|
|
|
795
794
|
(count, bus) => {
|
|
796
795
|
if (
|
|
797
796
|
bus.termination.type !== "boundary" ||
|
|
798
|
-
!
|
|
797
|
+
!busUsesDestinationGuidedTracks(bus)
|
|
799
798
|
) {
|
|
800
799
|
return count
|
|
801
800
|
}
|
|
@@ -840,7 +839,7 @@ export class FanoutSolver extends BaseSolver {
|
|
|
840
839
|
)
|
|
841
840
|
}
|
|
842
841
|
const sourceLayer = bus.connections[0]?.sourceLayer
|
|
843
|
-
const preferSourceLayer =
|
|
842
|
+
const preferSourceLayer = busUsesDestinationGuidedTracks(bus)
|
|
844
843
|
const orderedLayers = candidateLayers.toSorted(
|
|
845
844
|
(first, second) =>
|
|
846
845
|
(layerLoads.get(first) ?? 0) - (layerLoads.get(second) ?? 0) ||
|
package/lib/index.ts
CHANGED
package/lib/prepare-buses.ts
CHANGED
|
@@ -643,6 +643,7 @@ function prepareConnection(params: {
|
|
|
643
643
|
sourceGrid: ComponentGrid
|
|
644
644
|
componentGrids: ComponentGrid[]
|
|
645
645
|
termination: FanoutBusTermination
|
|
646
|
+
exitTargetPoint?: { x: number; y: number }
|
|
646
647
|
}): PreparedConnection {
|
|
647
648
|
const {
|
|
648
649
|
connection,
|
|
@@ -650,6 +651,7 @@ function prepareConnection(params: {
|
|
|
650
651
|
sourceGrid,
|
|
651
652
|
componentGrids,
|
|
652
653
|
termination,
|
|
654
|
+
exitTargetPoint,
|
|
653
655
|
} = params
|
|
654
656
|
for (
|
|
655
657
|
let sourcePointIndex = 0;
|
|
@@ -671,6 +673,12 @@ function prepareConnection(params: {
|
|
|
671
673
|
`FanoutSolver: connection "${connection.name}" has no source layer shared with its BGA pad`,
|
|
672
674
|
)
|
|
673
675
|
}
|
|
676
|
+
const targetPoint = chooseTargetPoint(
|
|
677
|
+
sourcePoint,
|
|
678
|
+
connection,
|
|
679
|
+
sourcePointIndex,
|
|
680
|
+
termination,
|
|
681
|
+
)
|
|
674
682
|
return {
|
|
675
683
|
connection,
|
|
676
684
|
connectionIndex,
|
|
@@ -678,12 +686,8 @@ function prepareConnection(params: {
|
|
|
678
686
|
sourcePointIndex,
|
|
679
687
|
sourceLayer,
|
|
680
688
|
sourceObstacle: sourceMatch.obstacle,
|
|
681
|
-
targetPoint
|
|
682
|
-
|
|
683
|
-
connection,
|
|
684
|
-
sourcePointIndex,
|
|
685
|
-
termination,
|
|
686
|
-
),
|
|
689
|
+
targetPoint,
|
|
690
|
+
exitTargetPoint: exitTargetPoint ?? targetPoint,
|
|
687
691
|
}
|
|
688
692
|
}
|
|
689
693
|
throw new Error(
|
|
@@ -698,8 +702,10 @@ function inferDirection(
|
|
|
698
702
|
let dx = 0
|
|
699
703
|
let dy = 0
|
|
700
704
|
for (const preparedConnection of connections) {
|
|
701
|
-
|
|
702
|
-
|
|
705
|
+
const exitTargetPoint =
|
|
706
|
+
preparedConnection.exitTargetPoint ?? preparedConnection.targetPoint
|
|
707
|
+
dx += exitTargetPoint.x - preparedConnection.sourcePoint.x
|
|
708
|
+
dy += exitTargetPoint.y - preparedConnection.sourcePoint.y
|
|
703
709
|
}
|
|
704
710
|
if (Math.abs(dx) < 1e-9 && Math.abs(dy) < 1e-9) {
|
|
705
711
|
throw new Error(
|
|
@@ -941,6 +947,20 @@ export function prepareFanoutBuses(
|
|
|
941
947
|
srj.connections.map((connection, index) => [connection.name, index]),
|
|
942
948
|
)
|
|
943
949
|
const resolvedBusInputs = resolveBusSpecs(srj, options).map((busSpec) => {
|
|
950
|
+
for (const [connectionName, point] of Object.entries(
|
|
951
|
+
busSpec.connectionExitTargets ?? {},
|
|
952
|
+
)) {
|
|
953
|
+
if (!busSpec.connectionNames.includes(connectionName)) {
|
|
954
|
+
throw new Error(
|
|
955
|
+
`FanoutSolver: connectionExitTargets contains connection "${connectionName}" outside bus "${busSpec.busId}"`,
|
|
956
|
+
)
|
|
957
|
+
}
|
|
958
|
+
if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) {
|
|
959
|
+
throw new Error(
|
|
960
|
+
`FanoutSolver: connectionExitTargets for connection "${connectionName}" must contain finite x and y coordinates`,
|
|
961
|
+
)
|
|
962
|
+
}
|
|
963
|
+
}
|
|
944
964
|
const connections = busSpec.connectionNames.map((connectionName) => {
|
|
945
965
|
const connectionIndex = connectionIndexByName.get(connectionName)
|
|
946
966
|
if (connectionIndex === undefined) {
|
|
@@ -962,6 +982,7 @@ export function prepareFanoutBuses(
|
|
|
962
982
|
sourceGrid,
|
|
963
983
|
componentGrids,
|
|
964
984
|
termination: busSpec.termination ?? { type: "boundary" },
|
|
985
|
+
exitTargetPoint: busSpec.connectionExitTargets?.[connection.name],
|
|
965
986
|
}),
|
|
966
987
|
)
|
|
967
988
|
return { busSpec, sourceGrid, preparedConnections }
|
package/lib/route-bus.ts
CHANGED
|
@@ -294,7 +294,7 @@ function getPreferredTrack(params: {
|
|
|
294
294
|
connection: PreparedConnection
|
|
295
295
|
}): number {
|
|
296
296
|
return getPerpendicularAxis(
|
|
297
|
-
params.connection.targetPoint,
|
|
297
|
+
params.connection.exitTargetPoint ?? params.connection.targetPoint,
|
|
298
298
|
params.bus.direction,
|
|
299
299
|
)
|
|
300
300
|
}
|
|
@@ -197,7 +197,10 @@ function getCornerChannelPrefixes(params: {
|
|
|
197
197
|
maximum: bus.componentBounds.maxX,
|
|
198
198
|
}
|
|
199
199
|
const directionalDistance = sign * (componentExitAxis - sourceAxis)
|
|
200
|
-
const targetTrack = getPerpendicularAxis(
|
|
200
|
+
const targetTrack = getPerpendicularAxis(
|
|
201
|
+
connection.exitTargetPoint ?? connection.targetPoint,
|
|
202
|
+
direction,
|
|
203
|
+
)
|
|
201
204
|
const lanePitch = traceWidth + clearance
|
|
202
205
|
const exitAxis = getExitAxis(bus)
|
|
203
206
|
const candidates = ([-1, 1] as const)
|
|
@@ -280,7 +283,7 @@ function completeCornerChannelRoute(params: {
|
|
|
280
283
|
const minimumTrack = boundaryMinimum + traceWidth / 2
|
|
281
284
|
const maximumTrack = boundaryMaximum - traceWidth / 2
|
|
282
285
|
const targetTrack = getPerpendicularAxis(
|
|
283
|
-
item.connection.targetPoint,
|
|
286
|
+
item.connection.exitTargetPoint ?? item.connection.targetPoint,
|
|
284
287
|
direction,
|
|
285
288
|
)
|
|
286
289
|
const trackStep = lanePitch / 2
|
package/lib/types.ts
CHANGED
|
@@ -63,6 +63,15 @@ export interface FanoutBusSpec extends SimpleRouteBus {
|
|
|
63
63
|
sourceComponentId?: string
|
|
64
64
|
direction?: FanoutDirection
|
|
65
65
|
preferredExit?: FanoutBorderTarget
|
|
66
|
+
/**
|
|
67
|
+
* Preferred downstream routing point for each connection after it leaves the
|
|
68
|
+
* fanout boundary. This is routing guidance only and does not replace the
|
|
69
|
+
* connection's electrical endpoint in SimpleRouteJson.
|
|
70
|
+
*
|
|
71
|
+
* A caller coordinating two fanouts can pass the paired fanout's selected
|
|
72
|
+
* exit here so both sides aim toward the same track.
|
|
73
|
+
*/
|
|
74
|
+
connectionExitTargets?: Readonly<Record<string, Point2D>>
|
|
66
75
|
/**
|
|
67
76
|
* Defaults to `{ type: "boundary" }`.
|
|
68
77
|
*
|
|
@@ -209,6 +218,8 @@ export interface PreparedConnection {
|
|
|
209
218
|
sourceLayer: string
|
|
210
219
|
sourceObstacle: Obstacle
|
|
211
220
|
targetPoint: ConnectionPoint
|
|
221
|
+
/** Preferred downstream point used to choose the boundary exit track. */
|
|
222
|
+
exitTargetPoint?: Point2D
|
|
212
223
|
}
|
|
213
224
|
|
|
214
225
|
export interface PreparedBus {
|