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
114 changes: 100 additions & 14 deletions lib/DuplicateCongestedPortSolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ import type { PortId, RouteId } from "./types"

type SerializedPort = SerializedHyperGraph["ports"][number]
type SerializedRegion = SerializedHyperGraph["regions"][number]
type SerializedRegionId = SerializedRegion["regionId"]
type LayerBoundaryKey = string

export const DUPLICATE_PORT_PROXIMITY = 0.05

export interface DuplicateCongestedPortSolverOptions {
duplicatePortProximity?: number
minPortSpacing?: number
routeSolveOptions?: TinyHyperGraphSolverOptions
/** Ignore serialized port penalties while estimating shared-port use. */
useSerializedPortPenalties?: boolean
Expand Down Expand Up @@ -166,6 +169,34 @@ const getRegionCenter = (region: SerializedRegion | undefined): Point => {
}
}

const getSharedBoundaryLength = (
source: SerializedPort,
regions: Map<SerializedRegionId, SerializedRegion>,
): number => {
const first = getRegionBounds(regions.get(source.region1Id))
const second = getRegionBounds(regions.get(source.region2Id))
const tolerance = 0.001
if (
Math.abs(first.maxX - second.minX) <= tolerance ||
Math.abs(second.maxX - first.minX) <= tolerance
) {
return Math.max(
0,
Math.min(first.maxY, second.maxY) - Math.max(first.minY, second.minY),
)
}
if (
Math.abs(first.maxY - second.minY) <= tolerance ||
Math.abs(second.maxY - first.minY) <= tolerance
) {
return Math.max(
0,
Math.min(first.maxX, second.maxX) - Math.max(first.minX, second.minX),
)
}
return 0
}

const findNearestPortOnSameBoundary = (
sourcePort: SerializedPort,
boundaryPorts: SerializedPort[],
Expand All @@ -187,25 +218,50 @@ const findNearestPortOnSameBoundary = (
return nearestPort
}

const getFallbackBoundaryDirection = (
sourcePort: SerializedPort,
regionById: Map<string, SerializedRegion>,
): Point => {
const getFallbackBoundaryDirection = ({
sourcePort,
regionById,
minPortSpacing,
}: {
sourcePort: SerializedPort
regionById: Map<SerializedRegionId, SerializedRegion>
minPortSpacing?: number
}): Point => {
const region1Center = getRegionCenter(regionById.get(sourcePort.region1Id))
const region2Center = getRegionCenter(regionById.get(sourcePort.region2Id))
const perpendicular = normalize({
x: -(region2Center.y - region1Center.y),
y: region2Center.x - region1Center.x,
})

return perpendicular ?? { x: 1, y: 0 }
if (!perpendicular) return { x: 1, y: 0 }
const fixedNetIds = sourcePort.d?._preloadedFixedNetIds
if (
minPortSpacing === undefined &&
(!Array.isArray(fixedNetIds) || fixedNetIds.length === 0)
) {
return perpendicular
}
if (
perpendicular.y < -EPSILON ||
(Math.abs(perpendicular.y) <= EPSILON && perpendicular.x < 0)
) {
return { x: -perpendicular.x, y: -perpendicular.y }
}
return perpendicular
}

const getDuplicateDirection = (
sourcePort: SerializedPort,
nearestBoundaryPort: SerializedPort | undefined,
regionById: Map<string, SerializedRegion>,
): Point => {
const getDuplicateDirection = ({
sourcePort,
nearestBoundaryPort,
regionById,
minPortSpacing,
}: {
sourcePort: SerializedPort
nearestBoundaryPort: SerializedPort | undefined
regionById: Map<SerializedRegionId, SerializedRegion>
minPortSpacing?: number
}): Point => {
const sourcePoint = getPortPoint(sourcePort)

if (nearestBoundaryPort) {
Expand All @@ -217,7 +273,11 @@ const getDuplicateDirection = (
if (awayFromNearest) return awayFromNearest
}

return getFallbackBoundaryDirection(sourcePort, regionById)
return getFallbackBoundaryDirection({
sourcePort,
regionById,
minPortSpacing,
})
}

const createDuplicatePortId = (
Expand Down Expand Up @@ -412,6 +472,7 @@ export class DuplicateCongestedPortSolver extends BaseSolver {
)
const usedPortIds = new Set(ports.map((port) => port.portId))
const duplicatedPorts: DuplicatedPortSummary[] = []
const portCountByLayerBoundary = new Map<LayerBoundaryKey, number>()

for (const [sourcePortId, useCount] of [...portUseCounts.entries()].sort(
([leftPortId], [rightPortId]) => leftPortId.localeCompare(rightPortId),
Expand All @@ -421,20 +482,45 @@ export class DuplicateCongestedPortSolver extends BaseSolver {
const sourcePort = sourcePortById.get(sourcePortId)
if (!sourcePort) continue

const duplicateCount = useCount - 1
let duplicateCount = useCount - 1
const boundaryPorts = portsByBoundary.get(getBoundaryKey(sourcePort))
if (!boundaryPorts) {
throw new Error(`No boundary ports found for "${sourcePortId}"`)
}
if (this.options.minPortSpacing !== undefined) {
const spacing = this.options.minPortSpacing
if (!Number.isFinite(spacing) || spacing <= 0) {
throw new Error("minPortSpacing must be finite and greater than zero")
}
const z = getNumber(sourcePort.d?.z)
const layerBoundaryKey: LayerBoundaryKey = `${getBoundaryKey(sourcePort)}:${z}`
const existing =
portCountByLayerBoundary.get(layerBoundaryKey) ??
boundaryPorts.filter((port) => getNumber(port.d?.z) === z).length
const capacity = Math.max(
1,
Math.floor(getSharedBoundaryLength(sourcePort, regionById) / spacing),
)
duplicateCount = Math.min(
duplicateCount,
Math.max(0, capacity - existing),
)
if (duplicateCount === 0) continue
portCountByLayerBoundary.set(
layerBoundaryKey,
existing + duplicateCount,
)
}
const nearestBoundaryPort = findNearestPortOnSameBoundary(
sourcePort,
boundaryPorts,
)
const duplicateDirection = getDuplicateDirection(
const duplicateDirection = getDuplicateDirection({
sourcePort,
nearestBoundaryPort,
regionById,
)
minPortSpacing: this.options.minPortSpacing,
})
const sourcePoint = getPortPoint(sourcePort)
const duplicatePortIds: string[] = []

Expand Down
110 changes: 105 additions & 5 deletions lib/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ export interface TinyHyperGraphWorkingState {

export interface TinyHyperGraphSolverOptions {
minViaPadDiameter?: number
/** Constrain layer changes to physical regions that can fit the via pad. */
ENFORCE_VIA_SPACE?: boolean
/** Minimum distance between crossings assigned to different nets on one layer. */
minPortSpacing?: number
DISTANCE_TO_COST?: number
RIP_THRESHOLD_START?: number
RIP_THRESHOLD_END?: number
Expand Down Expand Up @@ -320,6 +324,8 @@ export interface TinyHyperGraphSolverOptions {

export interface TinyHyperGraphSolverOptionTarget {
minViaPadDiameter: number
ENFORCE_VIA_SPACE?: boolean
minPortSpacing?: number
DISTANCE_TO_COST: number
RIP_THRESHOLD_START: number
RIP_THRESHOLD_END: number
Expand Down Expand Up @@ -358,6 +364,12 @@ export const applyTinyHyperGraphSolverOptions = (
return
}

if (options.ENFORCE_VIA_SPACE !== undefined) {
solver.ENFORCE_VIA_SPACE = options.ENFORCE_VIA_SPACE
}
if (options.minPortSpacing !== undefined) {
solver.minPortSpacing = options.minPortSpacing
}
if (options.minViaPadDiameter !== undefined) {
solver.minViaPadDiameter = options.minViaPadDiameter
}
Expand Down Expand Up @@ -463,6 +475,8 @@ export const getTinyHyperGraphSolverOptions = (
solver: TinyHyperGraphSolverOptionTarget,
): TinyHyperGraphSolverOptions => ({
minViaPadDiameter: solver.minViaPadDiameter,
ENFORCE_VIA_SPACE: solver.ENFORCE_VIA_SPACE,
minPortSpacing: solver.minPortSpacing,
DISTANCE_TO_COST: solver.DISTANCE_TO_COST,
RIP_THRESHOLD_START: solver.RIP_THRESHOLD_START,
RIP_THRESHOLD_END: solver.RIP_THRESHOLD_END,
Expand Down Expand Up @@ -555,6 +569,8 @@ export class TinyHyperGraphSolver extends BaseSolver {
protected costAwareFinalReripBudget = 0

DISTANCE_TO_COST = 0.05 // 50mm = 1 cost unit (1 cost unit ~ 100% chance of failure)
ENFORCE_VIA_SPACE = false
minPortSpacing = 0
minViaPadDiameter = DEFAULT_MIN_VIA_PAD_DIAMETER

RIP_THRESHOLD_START = 0.05
Expand Down Expand Up @@ -885,12 +901,25 @@ export class TinyHyperGraphSolver extends BaseSolver {
}

for (const neighborPortId of neighbors) {
if (
topology.portZ[currentCandidate.portId] !==
topology.portZ[neighborPortId] &&
!this.canTransitionLayersInRegion(currentCandidate.nextRegionId)
) {
continue
}
if (neighborPortId === state.goalPortId) {
const assignedNetId = state.portAssignment[neighborPortId]
if (this.isPortReservedForDifferentNet(neighborPortId)) continue
if (assignedNetId !== -1 && assignedNetId !== state.currentRouteNetId) {
continue
}
if (
this.ENFORCE_VIA_SPACE &&
!Number.isFinite(this.computeG(currentCandidate, neighborPortId))
) {
continue
}
this.onPathFound(currentCandidate)
return
}
Expand Down Expand Up @@ -1038,12 +1067,25 @@ export class TinyHyperGraphSolver extends BaseSolver {
const previousIndex = previousNeighborIndex
previousNeighborIndex = currentNeighborIndex
const neighborPortId = neighbors[currentNeighborIndex]!
if (
topology.portZ[currentCandidate.portId] !==
topology.portZ[neighborPortId] &&
!this.canTransitionLayersInRegion(currentCandidate.nextRegionId)
) {
continue
}
if (neighborPortId === state.goalPortId) {
const assignedNetId = state.portAssignment[neighborPortId]
if (this.isPortReservedForDifferentNet(neighborPortId)) continue
if (assignedNetId !== -1 && assignedNetId !== state.currentRouteNetId) {
continue
}
if (
this.ENFORCE_VIA_SPACE &&
!Number.isFinite(this.computeG(currentCandidate, neighborPortId))
) {
continue
}
this.onPathFound(currentCandidate)
return
}
Expand Down Expand Up @@ -1254,10 +1296,42 @@ export class TinyHyperGraphSolver extends BaseSolver {
isPortReservedForDifferentNet(portId: PortId): boolean {
const reservedNetId =
this.problemSetup.portEndpointReservationNetId[portId] ?? -1
return (
if (
reservedNetId === -2 ||
(reservedNetId !== -1 && reservedNetId !== this.state.currentRouteNetId)
)
) {
return true
}
if (this.minPortSpacing <= 0) return false
const { topology, state, problemSetup, problem } = this
const netId = state.currentRouteNetId
const minDistanceSquared = this.minPortSpacing ** 2 - 1e-9
for (const regionId of topology.incidentPortRegion[portId]) {
if (problem.regionNetId[regionId] !== -1) continue
for (const otherPortId of topology.regionIncidentPorts[regionId]) {
if (
otherPortId === portId ||
topology.portZ[portId] !== topology.portZ[otherPortId]
) {
continue
}
const assignedNetId = state.portAssignment[otherPortId]
const otherReservedNetId =
problemSetup.portEndpointReservationNetId[otherPortId]
// CAD terminal positions are fixed rather than selectable crossings.
if (reservedNetId >= 0 && otherReservedNetId >= 0) continue
if (
(assignedNetId === -1 || assignedNetId === netId) &&
(otherReservedNetId === -1 || otherReservedNetId === netId)
) {
continue
}
const dx = topology.portX[otherPortId] - topology.portX[portId]
const dy = topology.portY[otherPortId] - topology.portY[portId]
if (dx * dx + dy * dy < minDistanceSquared) return true
}
}
return false
}

isRegionReservedForDifferentNet(regionId: RegionId): boolean {
Expand All @@ -1267,6 +1341,19 @@ export class TinyHyperGraphSolver extends BaseSolver {
)
}

canTransitionLayersInRegion(regionId: RegionId): boolean {
return (
!this.ENFORCE_VIA_SPACE ||
(this.topology.regionAvailableZMask?.[regionId] ?? 0) === 0 ||
this.problem.regionNetId[regionId] !== -1 ||
Math.min(
this.topology.regionWidth[regionId],
this.topology.regionHeight[regionId],
) >=
this.minViaPadDiameter - 1e-9
)
}

isKnownSingleLayerRegion(regionId: RegionId): boolean {
const regionAvailableZMask =
this.topology.regionAvailableZMask?.[regionId] ?? 0
Expand Down Expand Up @@ -1713,7 +1800,13 @@ export class TinyHyperGraphSolver extends BaseSolver {
options: TinyHyperGraphSolverOptions,
_attempt: number,
): TinyHyperGraphSolver {
return new GreedyFinalRouteSolver(this.topology, this.problem, options)
return new GreedyFinalRouteSolver(this.topology, this.problem, {
...options,
MAX_ITERATIONS: Math.max(
options.MAX_ITERATIONS ?? GREEDY_FINAL_ROUTE_MAX_ITERATIONS,
this.topology.portCount + this.problem.routeCount,
),
})
}

protected tryGreedyFinalRouteAcceptance(): boolean {
Expand Down Expand Up @@ -2012,7 +2105,8 @@ export class TinyHyperGraphSolver extends BaseSolver {

if (
newSameLayerIntersections > 0 &&
this.isKnownSingleLayerRegion(nextRegionId)
(this.isKnownSingleLayerRegion(nextRegionId) ||
!this.canTransitionLayersInRegion(nextRegionId))
) {
return Number.POSITIVE_INFINITY
}
Expand Down Expand Up @@ -2113,8 +2207,14 @@ export class TinyHyperGraphSolver extends BaseSolver {
class GreedyFinalRouteSolver extends TinyHyperGraphSolver {
override computeG(
currentCandidate: Candidate,
_neighborPortId: PortId,
neighborPortId: PortId,
): number {
if (
this.ENFORCE_VIA_SPACE &&
!Number.isFinite(super.computeG(currentCandidate, neighborPortId))
) {
return Number.POSITIVE_INFINITY
}
return currentCandidate.g
}
}
Loading
Loading