Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 108 additions & 33 deletions lib/fanout-solver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ interface ResolvedFanoutConfig {

interface EvaluatedAssignment extends AssignmentAttempt {
blockingBusIds: string[]
blockingBusIdsByFailedBusId: Readonly<Record<string, readonly string[]>>
routingOrderBusIds: readonly string[]
}

interface GroupedBeamState {
Expand Down Expand Up @@ -888,9 +890,11 @@ export class FanoutSolver extends BaseSolver {
plans: AssignmentAttempt["plans"]
failedBusIds: string[]
blockingBusCounts: Map<string, number>
blockingBusIdsByFailedBusId: Readonly<Record<string, readonly string[]>>
}
>()
private groupedBeamEvaluated = false
private routingOrderRepairEvaluated = false
private routingInitialized = false
private nextCandidateLayerBusIndex = 0
private nextAssignmentIndex = 0
Expand Down Expand Up @@ -989,7 +993,7 @@ export class FanoutSolver extends BaseSolver {
const estimatedWorkUnitCount =
this.boundaryBuses.length +
1 +
this.config.maxLayerCombinations * workUnitsPerAssignment +
(this.config.maxLayerCombinations + 1) * workUnitsPerAssignment +
this.preparedBuses.length * 2 +
20
this.MAX_ITERATIONS = Math.max(10_000, estimatedWorkUnitCount)
Expand Down Expand Up @@ -4057,10 +4061,12 @@ export class FanoutSolver extends BaseSolver {
assignmentIndex: number,
busLayerAssignments: Readonly<Record<string, string>>,
routingStrategy: RoutingStrategy,
routingOrderBusIds?: readonly string[],
): Generator<FanoutWorkYield, EvaluatedAssignment, unknown> {
let plans: AssignmentAttempt["plans"] = []
let failedBusIds: string[] = []
let blockingBusCounts = new Map<string, number>()
let blockingBusIdsByFailedBusId: Record<string, readonly string[]> = {}
const isSingleLayerFanout = this.config.escapeLayers.length === 1
const useSingleLayerPushAndShove =
isSingleLayerFanout &&
Expand Down Expand Up @@ -4124,39 +4130,46 @@ export class FanoutSolver extends BaseSolver {
})
yield
}
const busesInRoutingOrder = [...this.preparedBuses].sort((a, b) => {
const aUsesCoordinatedWinding = busUsesCoordinatedWinding(a)
const bUsesCoordinatedWinding = busUsesCoordinatedWinding(b)
const aLayerIndex = this.config.layerNames.indexOf(
busLayerAssignments[a.busId] ?? "",
)
const bLayerIndex = this.config.layerNames.indexOf(
busLayerAssignments[b.busId] ?? "",
)
return (
comparePlaneRoutingPriority(
a,
b,
this.config.allowBlindAndBuriedVias,
) ||
Number(bUsesCoordinatedWinding) - Number(aUsesCoordinatedWinding) ||
(aUsesCoordinatedWinding && bUsesCoordinatedWinding
? bLayerIndex - aLayerIndex
: 0) ||
(routingStrategy === "group-by-layer"
? (busLayerAssignments[a.busId] ?? "").localeCompare(
busLayerAssignments[b.busId] ?? "",
)
: 0) ||
b.componentObstacles.length - a.componentObstacles.length ||
(isSingleLayerFanout
? getBusDistanceToBoundary(b) - getBusDistanceToBoundary(a)
: b.connections.length - a.connections.length ||
(routingStrategy === "deep-first"
const busesInRoutingOrder = routingOrderBusIds
? (() => {
const busById = new Map(
this.preparedBuses.map((bus) => [bus.busId, bus]),
)
return routingOrderBusIds.map((busId) => busById.get(busId)!)
})()
: [...this.preparedBuses].sort((a, b) => {
const aUsesCoordinatedWinding = busUsesCoordinatedWinding(a)
const bUsesCoordinatedWinding = busUsesCoordinatedWinding(b)
const aLayerIndex = this.config.layerNames.indexOf(
busLayerAssignments[a.busId] ?? "",
)
const bLayerIndex = this.config.layerNames.indexOf(
busLayerAssignments[b.busId] ?? "",
)
return (
comparePlaneRoutingPriority(
a,
b,
this.config.allowBlindAndBuriedVias,
) ||
Number(bUsesCoordinatedWinding) - Number(aUsesCoordinatedWinding) ||
(aUsesCoordinatedWinding && bUsesCoordinatedWinding
? bLayerIndex - aLayerIndex
: 0) ||
(routingStrategy === "group-by-layer"
? (busLayerAssignments[a.busId] ?? "").localeCompare(
busLayerAssignments[b.busId] ?? "",
)
: 0) ||
b.componentObstacles.length - a.componentObstacles.length ||
(isSingleLayerFanout
? getBusDistanceToBoundary(b) - getBusDistanceToBoundary(a)
: getBusDistanceToBoundary(a) - getBusDistanceToBoundary(b)))
)
})
: b.connections.length - a.connections.length ||
(routingStrategy === "deep-first"
? getBusDistanceToBoundary(b) - getBusDistanceToBoundary(a)
: getBusDistanceToBoundary(a) - getBusDistanceToBoundary(b)))
)
})

let mixedTerminationState: MixedTerminationState | null = null
if (!useSingleLayerPushAndShove && routingStrategy === "default") {
Expand Down Expand Up @@ -4225,6 +4238,9 @@ export class FanoutSolver extends BaseSolver {
plans = [...cachedPrefix.plans]
failedBusIds = [...cachedPrefix.failedBusIds]
blockingBusCounts = new Map(cachedPrefix.blockingBusCounts)
blockingBusIdsByFailedBusId = {
...cachedPrefix.blockingBusIdsByFailedBusId,
}
this.setInProgressPlans({
phase: "route-assignment",
plans,
Expand Down Expand Up @@ -4255,6 +4271,13 @@ export class FanoutSolver extends BaseSolver {
})
if (!busPlans) {
failedBusIds.push(bus.busId)
blockingBusIdsByFailedBusId[bus.busId] = [
...currentBusBlockingCounts.entries(),
]
.toSorted(
([, firstCount], [, secondCount]) => secondCount - firstCount,
)
.map(([busId]) => busId)
for (const [blockingBusId, count] of currentBusBlockingCounts) {
blockingBusCounts.set(
blockingBusId,
Expand All @@ -4268,6 +4291,9 @@ export class FanoutSolver extends BaseSolver {
plans: [...plans],
failedBusIds: [...failedBusIds],
blockingBusCounts: new Map(blockingBusCounts),
blockingBusIdsByFailedBusId: {
...blockingBusIdsByFailedBusId,
},
})
this.setInProgressPlans({
phase: "route-assignment",
Expand Down Expand Up @@ -4302,6 +4328,7 @@ export class FanoutSolver extends BaseSolver {
.filter((busId) => busId !== constrainedBus.busId),
]
blockingBusCounts.clear()
blockingBusIdsByFailedBusId = {}
}
}
let outputSrj = buildOutputSimpleRouteJson({
Expand All @@ -4320,6 +4347,7 @@ export class FanoutSolver extends BaseSolver {
plans = []
failedBusIds = this.preparedBuses.map((bus) => bus.busId)
blockingBusCounts.clear()
blockingBusIdsByFailedBusId = {}
outputSrj = buildOutputSimpleRouteJson({
inputSrj: this.inputSrj,
plans,
Expand Down Expand Up @@ -4362,6 +4390,8 @@ export class FanoutSolver extends BaseSolver {
blockingBusIds: [...blockingBusCounts.entries()]
.toSorted(([, firstCount], [, secondCount]) => secondCount - firstCount)
.map(([busId]) => busId),
blockingBusIdsByFailedBusId,
routingOrderBusIds: busesInRoutingOrder.map((bus) => bus.busId),
outputSrj,
}
}
Expand All @@ -4375,6 +4405,7 @@ export class FanoutSolver extends BaseSolver {
busLayerAssignments,
"default",
)
let bestRoutingStrategy: RoutingStrategy = "default"
if (process.env.FANOUT_DEBUG_DENSE_ONLY === "1") return bestAttempt
if (
bestAttempt.summary.routedConnectionCount ===
Expand All @@ -4392,6 +4423,7 @@ export class FanoutSolver extends BaseSolver {
)
if (this.isAttemptBetter(attempt, bestAttempt)) {
bestAttempt = attempt
bestRoutingStrategy = routingStrategy
}
if (
bestAttempt.summary.routedConnectionCount ===
Expand All @@ -4401,6 +4433,47 @@ export class FanoutSolver extends BaseSolver {
return bestAttempt
}
}
const failedBusId =
bestAttempt.summary.failedBusIds.length === 1
? bestAttempt.summary.failedBusIds[0]
: undefined
const beforeBusId = failedBusId
? bestAttempt.blockingBusIdsByFailedBusId[failedBusId]?.[0]
: undefined
const repair =
failedBusId && beforeBusId
? { busId: failedBusId, beforeBusId }
: undefined
if (
repair &&
!this.routingOrderRepairEvaluated &&
(!this.bestAttempt || this.isAttemptBetter(bestAttempt, this.bestAttempt))
) {
// A valid early route can consume the only channel of a later bus.
// Retry the same assignment once with that blocked bus immediately
// before its highest-impact blocker.
// This is the routing-order counterpart of the layer-assignment repairs
// performed after an attempt and keeps the additional search bounded.
this.routingOrderRepairEvaluated = true
const repairedRoutingOrderBusIds = [...bestAttempt.routingOrderBusIds]
const busIndex = repairedRoutingOrderBusIds.indexOf(repair.busId)
const blockerIndex = repairedRoutingOrderBusIds.indexOf(
repair.beforeBusId,
)
if (busIndex > blockerIndex && blockerIndex >= 0) {
repairedRoutingOrderBusIds.splice(busIndex, 1)
repairedRoutingOrderBusIds.splice(blockerIndex, 0, repair.busId)
}
const repairedAttempt = yield* this.evaluateAssignmentWithStrategySteps(
assignmentIndex,
busLayerAssignments,
bestRoutingStrategy,
repairedRoutingOrderBusIds,
)
if (this.isAttemptBetter(repairedAttempt, bestAttempt)) {
bestAttempt = repairedAttempt
}
}
return bestAttempt
}

Expand Down Expand Up @@ -4703,6 +4776,8 @@ export class FanoutSolver extends BaseSolver {
summary,
plans: bestState.plans,
blockingBusIds: [],
blockingBusIdsByFailedBusId: {},
routingOrderBusIds: [],
outputSrj,
}
}
Expand Down
Loading
Loading