@tscircuit/fanout-solver 0.0.49 → 0.0.51
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 +1617 -143
- package/lib/match-component-dogbone-via-sites.ts +101 -14
- package/lib/route-bus.ts +345 -52
- package/lib/route-via-minimal-winding.ts +94 -23
- package/lib/types.ts +11 -0
- package/package.json +1 -1
package/lib/route-bus.ts
CHANGED
|
@@ -19,13 +19,14 @@ import {
|
|
|
19
19
|
} from "./geometry"
|
|
20
20
|
import { getAllRoutedTraceCopper } from "./get-routed-trace-copper"
|
|
21
21
|
import { getViaSpanLayers } from "./layer-names"
|
|
22
|
+
import { matchComponentDogboneViaSites } from "./match-component-dogbone-via-sites"
|
|
22
23
|
import {
|
|
23
24
|
connectionsShareElectricalNet,
|
|
24
25
|
obstacleSharesElectricalNet,
|
|
25
26
|
} from "./net-identity"
|
|
26
27
|
import {
|
|
27
|
-
routeViaMinimalWindingAlternativesSteps,
|
|
28
28
|
type RouteViaMinimalWindingProgress,
|
|
29
|
+
routeViaMinimalWindingAlternativesSteps,
|
|
29
30
|
type ViaMinimalWindingReservedVia,
|
|
30
31
|
} from "./route-via-minimal-winding"
|
|
31
32
|
import type {
|
|
@@ -61,6 +62,16 @@ export interface RouteBusParams {
|
|
|
61
62
|
fixedViaPointsByConnectionIndex?: ReadonlyMap<number, Point2D>
|
|
62
63
|
reservedVias?: readonly ViaMinimalWindingReservedVia[]
|
|
63
64
|
viaMinimalOnly?: boolean
|
|
65
|
+
/** Permit a singleton to move its provisional via near the boundary. */
|
|
66
|
+
allowBoundarySideViaFallback?: boolean
|
|
67
|
+
/** Retry blocked winding terminals ahead of already-routed terminals. */
|
|
68
|
+
adaptiveWindingRouteOrder?: boolean
|
|
69
|
+
/** Preserve pad-lattice channels in the automatic dense routing path. */
|
|
70
|
+
alignWindingGridToPads?: boolean
|
|
71
|
+
/** Bounds the final fixed-via winding fallback after ordered attempts. */
|
|
72
|
+
fixedViaFallbackRouteOrderAttempts?: number
|
|
73
|
+
/** Skip this many otherwise-clear plane escapes when enumerating alternatives. */
|
|
74
|
+
planeCandidateSkipCount?: number
|
|
64
75
|
/** Dense corner-band phase that preserves existing lane centers when leading lanes are prepended. */
|
|
65
76
|
cornerBandTargetTrackOffset?: number
|
|
66
77
|
}
|
|
@@ -1873,6 +1884,24 @@ function planIsClearOfPlans(params: {
|
|
|
1873
1884
|
return true
|
|
1874
1885
|
}
|
|
1875
1886
|
|
|
1887
|
+
export function fanoutPlansAreMutuallyClear(params: {
|
|
1888
|
+
plans: readonly FanoutRoutePlan[]
|
|
1889
|
+
srj: SimpleRouteJson
|
|
1890
|
+
clearance: number
|
|
1891
|
+
allowSameNetMerges?: boolean
|
|
1892
|
+
}): boolean {
|
|
1893
|
+
const { plans, srj, clearance, allowSameNetMerges = false } = params
|
|
1894
|
+
return plans.every((plan, index) =>
|
|
1895
|
+
planIsClearOfPlans({
|
|
1896
|
+
plan,
|
|
1897
|
+
otherPlans: plans.filter((_, otherIndex) => otherIndex !== index),
|
|
1898
|
+
srj,
|
|
1899
|
+
allowSameNetMerges,
|
|
1900
|
+
clearance,
|
|
1901
|
+
}),
|
|
1902
|
+
)
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1876
1905
|
function planIsClear(params: {
|
|
1877
1906
|
plan: FanoutRoutePlan
|
|
1878
1907
|
otherPlans: FanoutRoutePlan[]
|
|
@@ -1991,11 +2020,27 @@ function routePlaneTerminatedBus(
|
|
|
1991
2020
|
allowBlindAndBuriedVias = true,
|
|
1992
2021
|
allowSameNetMerges = false,
|
|
1993
2022
|
fixedViaPointsByConnectionIndex,
|
|
2023
|
+
planeCandidateSkipCount = 0,
|
|
1994
2024
|
} = params
|
|
1995
2025
|
const sourceObstacle = bus.connections[0]?.sourceObstacle
|
|
1996
2026
|
if (!sourceObstacle || bus.termination.type !== "plane") return null
|
|
1997
2027
|
const sourceLayer = bus.connections[0]!.sourceLayer
|
|
1998
2028
|
if (targetLayer === sourceLayer) return null
|
|
2029
|
+
let remainingPlaneCandidatesToSkip = planeCandidateSkipCount
|
|
2030
|
+
const selectClearPlanePlan = (
|
|
2031
|
+
plans: FanoutRoutePlan[],
|
|
2032
|
+
isClear: (plan: FanoutRoutePlan, index: number) => boolean,
|
|
2033
|
+
): FanoutRoutePlan | undefined => {
|
|
2034
|
+
for (const [index, plan] of plans.entries()) {
|
|
2035
|
+
if (!isClear(plan, index)) continue
|
|
2036
|
+
if (remainingPlaneCandidatesToSkip > 0) {
|
|
2037
|
+
remainingPlaneCandidatesToSkip--
|
|
2038
|
+
continue
|
|
2039
|
+
}
|
|
2040
|
+
return plan
|
|
2041
|
+
}
|
|
2042
|
+
return undefined
|
|
2043
|
+
}
|
|
1999
2044
|
|
|
2000
2045
|
if (fixedViaPointsByConnectionIndex) {
|
|
2001
2046
|
const fixedPlans: FanoutRoutePlan[] = []
|
|
@@ -2052,19 +2097,21 @@ function routePlaneTerminatedBus(
|
|
|
2052
2097
|
),
|
|
2053
2098
|
basePlan,
|
|
2054
2099
|
]
|
|
2055
|
-
const clearPlan =
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2100
|
+
const clearPlan = selectClearPlanePlan(
|
|
2101
|
+
plansToTry,
|
|
2102
|
+
(candidatePlan, candidateIndex) =>
|
|
2103
|
+
planIsClear({
|
|
2104
|
+
plan: candidatePlan,
|
|
2105
|
+
otherPlans: [...acceptedPlans, ...fixedPlans],
|
|
2106
|
+
staticClearanceCache,
|
|
2107
|
+
blockingBusCounts,
|
|
2108
|
+
cacheKey: `plane-fixed:${bus.busId}:${targetLayer}:${preparedConnection.connectionIndex}:${candidateIndex}`,
|
|
2109
|
+
srj,
|
|
2110
|
+
sharedBoundary: bus.sharedBoundary,
|
|
2111
|
+
clearance,
|
|
2112
|
+
allowBlindAndBuriedVias,
|
|
2113
|
+
allowSameNetMerges,
|
|
2114
|
+
}),
|
|
2068
2115
|
)
|
|
2069
2116
|
if (!clearPlan) return null
|
|
2070
2117
|
fixedPlans.push(clearPlan)
|
|
@@ -2128,19 +2175,21 @@ function routePlaneTerminatedBus(
|
|
|
2128
2175
|
),
|
|
2129
2176
|
viaInPadPlan,
|
|
2130
2177
|
]
|
|
2131
|
-
const clearPlan =
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2178
|
+
const clearPlan = selectClearPlanePlan(
|
|
2179
|
+
plansToTry,
|
|
2180
|
+
(candidatePlan, candidateIndex) =>
|
|
2181
|
+
planIsClear({
|
|
2182
|
+
plan: candidatePlan,
|
|
2183
|
+
otherPlans: acceptedPlans,
|
|
2184
|
+
staticClearanceCache,
|
|
2185
|
+
blockingBusCounts,
|
|
2186
|
+
cacheKey: `plane-via-in-pad:${bus.busId}:${targetLayer}:${candidateIndex}`,
|
|
2187
|
+
srj,
|
|
2188
|
+
sharedBoundary: bus.sharedBoundary,
|
|
2189
|
+
clearance,
|
|
2190
|
+
allowBlindAndBuriedVias,
|
|
2191
|
+
allowSameNetMerges,
|
|
2192
|
+
}),
|
|
2144
2193
|
)
|
|
2145
2194
|
if (clearPlan) return [clearPlan]
|
|
2146
2195
|
}
|
|
@@ -2203,7 +2252,7 @@ function routePlaneTerminatedBus(
|
|
|
2203
2252
|
)
|
|
2204
2253
|
const perpendicularPitch = getPerpendicularPitch(directionalBus)
|
|
2205
2254
|
const sourceEscapePaths: Point2D[][] = []
|
|
2206
|
-
const maximumCandidatePaths =
|
|
2255
|
+
const maximumCandidatePaths = 128
|
|
2207
2256
|
for (
|
|
2208
2257
|
let totalSteps = 0;
|
|
2209
2258
|
totalSteps <= maximumEscapeSteps &&
|
|
@@ -2345,19 +2394,21 @@ function routePlaneTerminatedBus(
|
|
|
2345
2394
|
),
|
|
2346
2395
|
basePlan,
|
|
2347
2396
|
]
|
|
2348
|
-
plan =
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2397
|
+
plan = selectClearPlanePlan(
|
|
2398
|
+
plansToTry,
|
|
2399
|
+
(candidatePlan, candidateIndex) =>
|
|
2400
|
+
planIsClear({
|
|
2401
|
+
plan: candidatePlan,
|
|
2402
|
+
otherPlans: [...acceptedPlans, ...candidatePlans],
|
|
2403
|
+
staticClearanceCache,
|
|
2404
|
+
blockingBusCounts,
|
|
2405
|
+
cacheKey: `plane:${bus.busId}:${targetLayer}:${direction}:${preparedConnection.connectionIndex}:${viaHandedness}:${pathIndex}:${candidateIndex}`,
|
|
2406
|
+
srj,
|
|
2407
|
+
sharedBoundary: bus.sharedBoundary,
|
|
2408
|
+
clearance,
|
|
2409
|
+
allowBlindAndBuriedVias,
|
|
2410
|
+
allowSameNetMerges,
|
|
2411
|
+
}),
|
|
2361
2412
|
)
|
|
2362
2413
|
if (plan) break
|
|
2363
2414
|
}
|
|
@@ -2400,6 +2451,10 @@ export function* routeBusAlternativesSteps(
|
|
|
2400
2451
|
fixedViaPointsByConnectionIndex,
|
|
2401
2452
|
reservedVias = [],
|
|
2402
2453
|
viaMinimalOnly = false,
|
|
2454
|
+
allowBoundarySideViaFallback = false,
|
|
2455
|
+
adaptiveWindingRouteOrder = false,
|
|
2456
|
+
alignWindingGridToPads = false,
|
|
2457
|
+
fixedViaFallbackRouteOrderAttempts = 24,
|
|
2403
2458
|
cornerBandTargetTrackOffset,
|
|
2404
2459
|
} = params
|
|
2405
2460
|
if (!Number.isInteger(maxAlternatives) || maxAlternatives < 1) {
|
|
@@ -2417,8 +2472,20 @@ export function* routeBusAlternativesSteps(
|
|
|
2417
2472
|
return []
|
|
2418
2473
|
}
|
|
2419
2474
|
if (bus.termination.type === "plane") {
|
|
2420
|
-
const
|
|
2421
|
-
|
|
2475
|
+
const alternatives: FanoutRoutePlan[][] = []
|
|
2476
|
+
for (
|
|
2477
|
+
let planeCandidateSkipCount = 0;
|
|
2478
|
+
planeCandidateSkipCount < maxAlternatives;
|
|
2479
|
+
planeCandidateSkipCount++
|
|
2480
|
+
) {
|
|
2481
|
+
const plan = routePlaneTerminatedBus({
|
|
2482
|
+
...params,
|
|
2483
|
+
planeCandidateSkipCount,
|
|
2484
|
+
})
|
|
2485
|
+
if (!plan) break
|
|
2486
|
+
alternatives.push(plan)
|
|
2487
|
+
}
|
|
2488
|
+
return alternatives
|
|
2422
2489
|
}
|
|
2423
2490
|
const exitAxis = getExitAxis(bus)
|
|
2424
2491
|
const sourceObstacle = bus.connections[0]?.sourceObstacle
|
|
@@ -2666,8 +2733,36 @@ export function* routeBusAlternativesSteps(
|
|
|
2666
2733
|
useViaInPad: true,
|
|
2667
2734
|
getViaHandedness: () => 0 as const,
|
|
2668
2735
|
}
|
|
2736
|
+
const coordinatedViaPoints =
|
|
2737
|
+
fixedViaPointsByConnectionIndex ??
|
|
2738
|
+
(!allowBlindAndBuriedVias &&
|
|
2739
|
+
bus.connections.length >= 8 &&
|
|
2740
|
+
reservedVias.length === 0
|
|
2741
|
+
? matchComponentDogboneViaSites([bus], {
|
|
2742
|
+
viaDiameter,
|
|
2743
|
+
viaHoleDiameter,
|
|
2744
|
+
traceWidth,
|
|
2745
|
+
clearance,
|
|
2746
|
+
additionalObstacles: srj.obstacles,
|
|
2747
|
+
blockingSegments: acceptedPlans.flatMap((plan) =>
|
|
2748
|
+
getPlanSegments(plan).map((segment) => ({
|
|
2749
|
+
connectionIndex: plan.connectionIndex,
|
|
2750
|
+
segment,
|
|
2751
|
+
})),
|
|
2752
|
+
),
|
|
2753
|
+
blockingVias: acceptedPlans.flatMap((plan) =>
|
|
2754
|
+
getPlanVias(plan).map((via) => ({
|
|
2755
|
+
connectionIndex: plan.connectionIndex,
|
|
2756
|
+
...via.center,
|
|
2757
|
+
center: via.center,
|
|
2758
|
+
diameter: via.diameter,
|
|
2759
|
+
spanLayers: via.spanLayers,
|
|
2760
|
+
})),
|
|
2761
|
+
),
|
|
2762
|
+
})
|
|
2763
|
+
: null)
|
|
2669
2764
|
const fixedViaTerminalPatterns: CoordinatedTerminalPattern[] =
|
|
2670
|
-
|
|
2765
|
+
coordinatedViaPoints
|
|
2671
2766
|
? [
|
|
2672
2767
|
...Array.from(
|
|
2673
2768
|
{ length: windingTargetOrderCount },
|
|
@@ -2676,9 +2771,7 @@ export function* routeBusAlternativesSteps(
|
|
|
2676
2771
|
useViaInPad: false,
|
|
2677
2772
|
getViaHandedness: () => 0 as const,
|
|
2678
2773
|
getViaPoint: (connection: PreparedConnection) =>
|
|
2679
|
-
|
|
2680
|
-
connection.connectionIndex,
|
|
2681
|
-
)!,
|
|
2774
|
+
coordinatedViaPoints.get(connection.connectionIndex)!,
|
|
2682
2775
|
maximumRouteOrderAttempts: 1,
|
|
2683
2776
|
windingOrderIndex,
|
|
2684
2777
|
preferTargetDirectedLaneBias: true,
|
|
@@ -2689,12 +2782,10 @@ export function* routeBusAlternativesSteps(
|
|
|
2689
2782
|
useViaInPad: false,
|
|
2690
2783
|
getViaHandedness: () => 0,
|
|
2691
2784
|
getViaPoint: (connection) =>
|
|
2692
|
-
|
|
2693
|
-
connection.connectionIndex,
|
|
2694
|
-
)!,
|
|
2785
|
+
coordinatedViaPoints.get(connection.connectionIndex)!,
|
|
2695
2786
|
// Preserve the existing bounded search after every inexpensive
|
|
2696
2787
|
// layer-interleave candidate has had one deterministic attempt.
|
|
2697
|
-
maximumRouteOrderAttempts:
|
|
2788
|
+
maximumRouteOrderAttempts: fixedViaFallbackRouteOrderAttempts,
|
|
2698
2789
|
windingOrderIndex: 0,
|
|
2699
2790
|
preferTargetDirectedLaneBias: true,
|
|
2700
2791
|
},
|
|
@@ -2789,9 +2880,13 @@ export function* routeBusAlternativesSteps(
|
|
|
2789
2880
|
allowBlindAndBuriedVias,
|
|
2790
2881
|
allowSameNetMerges,
|
|
2791
2882
|
maximumRouteOrderAttempts: terminalPattern.maximumRouteOrderAttempts,
|
|
2883
|
+
adaptiveRouteOrder: adaptiveWindingRouteOrder,
|
|
2884
|
+
alignGridToPads:
|
|
2885
|
+
alignWindingGridToPads ||
|
|
2886
|
+
Boolean(coordinatedViaPoints && !fixedViaPointsByConnectionIndex),
|
|
2792
2887
|
reservedVias,
|
|
2793
2888
|
gridStepDivisor:
|
|
2794
|
-
|
|
2889
|
+
coordinatedViaPoints &&
|
|
2795
2890
|
Math.min(bus.pitchX, bus.pitchY) -
|
|
2796
2891
|
2 * (viaDiameter / 2 + traceWidth / 2 + clearance) <
|
|
2797
2892
|
traceWidth + clearance
|
|
@@ -2850,6 +2945,204 @@ export function* routeBusAlternativesSteps(
|
|
|
2850
2945
|
}
|
|
2851
2946
|
}
|
|
2852
2947
|
|
|
2948
|
+
// A through-via does not have to sit next to the source pad. A singleton
|
|
2949
|
+
// embedded in another bus's source field can have every local dogbone site
|
|
2950
|
+
// occupied while still having a clear top-layer escape. In that case, route
|
|
2951
|
+
// to one via near the boundary and make the short final hop on the requested
|
|
2952
|
+
// escape layer. This retains the one-via invariant and avoids weakening any
|
|
2953
|
+
// clearance rule.
|
|
2954
|
+
if (
|
|
2955
|
+
alternatives.length === 0 &&
|
|
2956
|
+
allowBoundarySideViaFallback &&
|
|
2957
|
+
fixedViaPointsByConnectionIndex &&
|
|
2958
|
+
bus.connections.length === 1 &&
|
|
2959
|
+
bus.exitEdge &&
|
|
2960
|
+
bus.connections[0]!.sourceLayer !== targetLayer
|
|
2961
|
+
) {
|
|
2962
|
+
const preparedConnection = bus.connections[0]!
|
|
2963
|
+
const boundaryDirection = getDirectionForExitEdge(bus.exitEdge)
|
|
2964
|
+
const boundaryExitAxis = getExitAxis(bus, boundaryDirection)
|
|
2965
|
+
const finalTrack = getBoundaryTargetTrack({
|
|
2966
|
+
bus,
|
|
2967
|
+
connection: preparedConnection,
|
|
2968
|
+
boundaryDirection,
|
|
2969
|
+
})
|
|
2970
|
+
const finalExitPoint = makePoint(
|
|
2971
|
+
boundaryExitAxis,
|
|
2972
|
+
finalTrack,
|
|
2973
|
+
boundaryDirection,
|
|
2974
|
+
)
|
|
2975
|
+
const sourceLayerSrj: SimpleRouteJson = {
|
|
2976
|
+
...srj,
|
|
2977
|
+
obstacles: srj.obstacles.map((obstacle) =>
|
|
2978
|
+
obstacle === preparedConnection.sourceObstacle ||
|
|
2979
|
+
(distance(obstacle.center, preparedConnection.sourcePoint) <= 1e-7 &&
|
|
2980
|
+
obstacle.layers.includes(preparedConnection.sourceLayer))
|
|
2981
|
+
? {
|
|
2982
|
+
...obstacle,
|
|
2983
|
+
connectedTo: [
|
|
2984
|
+
...obstacle.connectedTo,
|
|
2985
|
+
preparedConnection.connection.name,
|
|
2986
|
+
],
|
|
2987
|
+
}
|
|
2988
|
+
: obstacle,
|
|
2989
|
+
),
|
|
2990
|
+
}
|
|
2991
|
+
const insetStep = Math.max(
|
|
2992
|
+
viaDiameter / 2 + clearance,
|
|
2993
|
+
Math.min(bus.pitchX, bus.pitchY) / 2,
|
|
2994
|
+
)
|
|
2995
|
+
for (const multiple of [1, 2, 3, 4, 5]) {
|
|
2996
|
+
const inset = multiple * insetStep
|
|
2997
|
+
const boundaryViaPoint = makePoint(
|
|
2998
|
+
boundaryExitAxis - directionSign(boundaryDirection) * inset,
|
|
2999
|
+
finalTrack,
|
|
3000
|
+
boundaryDirection,
|
|
3001
|
+
)
|
|
3002
|
+
const sourceLayerSteps = routeViaMinimalWindingAlternativesSteps(
|
|
3003
|
+
{
|
|
3004
|
+
srj: sourceLayerSrj,
|
|
3005
|
+
bus,
|
|
3006
|
+
targetLayer: preparedConnection.sourceLayer,
|
|
3007
|
+
terminals: [
|
|
3008
|
+
{
|
|
3009
|
+
connection: preparedConnection,
|
|
3010
|
+
viaPoint: preparedConnection.sourcePoint,
|
|
3011
|
+
exitPoint: boundaryViaPoint,
|
|
3012
|
+
},
|
|
3013
|
+
],
|
|
3014
|
+
acceptedPlans,
|
|
3015
|
+
layerNames,
|
|
3016
|
+
traceWidth,
|
|
3017
|
+
viaDiameter,
|
|
3018
|
+
viaHoleDiameter,
|
|
3019
|
+
clearance,
|
|
3020
|
+
allowBlindAndBuriedVias,
|
|
3021
|
+
allowSameNetMerges,
|
|
3022
|
+
maximumRouteOrderAttempts: 3,
|
|
3023
|
+
reservedVias,
|
|
3024
|
+
gridStepDivisor: 2,
|
|
3025
|
+
allowSourceLayerRouting: true,
|
|
3026
|
+
alignGridToPads: true,
|
|
3027
|
+
},
|
|
3028
|
+
1,
|
|
3029
|
+
includeVisualization,
|
|
3030
|
+
)
|
|
3031
|
+
let sourceLayerResult = sourceLayerSteps.next()
|
|
3032
|
+
while (!sourceLayerResult.done) {
|
|
3033
|
+
yield {
|
|
3034
|
+
phase: "via-minimal-winding",
|
|
3035
|
+
busId: bus.busId,
|
|
3036
|
+
targetLayer: preparedConnection.sourceLayer,
|
|
3037
|
+
winding: sourceLayerResult.value,
|
|
3038
|
+
}
|
|
3039
|
+
sourceLayerResult = sourceLayerSteps.next()
|
|
3040
|
+
}
|
|
3041
|
+
const sourceLayerPlan = sourceLayerResult.value[0]?.[0]
|
|
3042
|
+
if (!sourceLayerPlan) continue
|
|
3043
|
+
const targetSegment: RoutedSegment = {
|
|
3044
|
+
start: boundaryViaPoint,
|
|
3045
|
+
end: finalExitPoint,
|
|
3046
|
+
width: traceWidth,
|
|
3047
|
+
layer: targetLayer,
|
|
3048
|
+
}
|
|
3049
|
+
const via = {
|
|
3050
|
+
center: boundaryViaPoint,
|
|
3051
|
+
diameter: viaDiameter,
|
|
3052
|
+
holeDiameter: viaHoleDiameter,
|
|
3053
|
+
fromLayer: preparedConnection.sourceLayer,
|
|
3054
|
+
toLayer: targetLayer,
|
|
3055
|
+
spanLayers: getViaSpanLayers({
|
|
3056
|
+
fromLayer: preparedConnection.sourceLayer,
|
|
3057
|
+
toLayer: targetLayer,
|
|
3058
|
+
layerNames,
|
|
3059
|
+
allowBlindAndBuriedVias,
|
|
3060
|
+
}),
|
|
3061
|
+
}
|
|
3062
|
+
const sourceRoute = sourceLayerPlan.trace.route.filter(
|
|
3063
|
+
(item) => item.route_type === "wire",
|
|
3064
|
+
)
|
|
3065
|
+
const plans: FanoutRoutePlan[] = [
|
|
3066
|
+
{
|
|
3067
|
+
...sourceLayerPlan,
|
|
3068
|
+
targetLayer,
|
|
3069
|
+
exitPoint: finalExitPoint,
|
|
3070
|
+
via,
|
|
3071
|
+
segments: [...sourceLayerPlan.segments, targetSegment],
|
|
3072
|
+
length:
|
|
3073
|
+
sourceLayerPlan.length + distance(boundaryViaPoint, finalExitPoint),
|
|
3074
|
+
trace: {
|
|
3075
|
+
...sourceLayerPlan.trace,
|
|
3076
|
+
route: [
|
|
3077
|
+
...sourceRoute,
|
|
3078
|
+
{
|
|
3079
|
+
route_type: "via",
|
|
3080
|
+
...boundaryViaPoint,
|
|
3081
|
+
from_layer: preparedConnection.sourceLayer,
|
|
3082
|
+
to_layer: targetLayer,
|
|
3083
|
+
via_diameter: viaDiameter,
|
|
3084
|
+
via_hole_diameter: viaHoleDiameter,
|
|
3085
|
+
},
|
|
3086
|
+
{
|
|
3087
|
+
route_type: "wire",
|
|
3088
|
+
...boundaryViaPoint,
|
|
3089
|
+
width: traceWidth,
|
|
3090
|
+
layer: targetLayer,
|
|
3091
|
+
},
|
|
3092
|
+
{
|
|
3093
|
+
route_type: "wire",
|
|
3094
|
+
...finalExitPoint,
|
|
3095
|
+
width: traceWidth,
|
|
3096
|
+
layer: targetLayer,
|
|
3097
|
+
},
|
|
3098
|
+
],
|
|
3099
|
+
},
|
|
3100
|
+
},
|
|
3101
|
+
]
|
|
3102
|
+
const boundaryViaPlansAreClear = fanoutPlansAreClear({
|
|
3103
|
+
plans: [...acceptedPlans, ...plans],
|
|
3104
|
+
srj,
|
|
3105
|
+
sharedBoundary: bus.sharedBoundary,
|
|
3106
|
+
clearance,
|
|
3107
|
+
allowBlindAndBuriedVias,
|
|
3108
|
+
allowSameNetMerges,
|
|
3109
|
+
})
|
|
3110
|
+
const reservedViasAreClear = reservedVias.every((reserved) => {
|
|
3111
|
+
if (
|
|
3112
|
+
reserved.connectionName === preparedConnection.connection.name ||
|
|
3113
|
+
(allowSameNetMerges &&
|
|
3114
|
+
connectionsShareElectricalNet(
|
|
3115
|
+
srj,
|
|
3116
|
+
reserved.connectionName,
|
|
3117
|
+
preparedConnection.connection.name,
|
|
3118
|
+
))
|
|
3119
|
+
)
|
|
3120
|
+
return true
|
|
3121
|
+
if (
|
|
3122
|
+
via.spanLayers.some((layer) =>
|
|
3123
|
+
reserved.via.spanLayers.includes(layer),
|
|
3124
|
+
) &&
|
|
3125
|
+
distance(via.center, reserved.via.center) <
|
|
3126
|
+
(via.diameter + reserved.via.diameter) / 2 + clearance - 1e-9
|
|
3127
|
+
)
|
|
3128
|
+
return false
|
|
3129
|
+
return (
|
|
3130
|
+
!reserved.via.spanLayers.includes(targetLayer) ||
|
|
3131
|
+
distancePointToSegment(
|
|
3132
|
+
reserved.via.center,
|
|
3133
|
+
targetSegment.start,
|
|
3134
|
+
targetSegment.end,
|
|
3135
|
+
) >=
|
|
3136
|
+
reserved.via.diameter / 2 + traceWidth / 2 + clearance - 1e-9
|
|
3137
|
+
)
|
|
3138
|
+
})
|
|
3139
|
+
if (boundaryViaPlansAreClear && reservedViasAreClear) {
|
|
3140
|
+
addAlternative(plans)
|
|
3141
|
+
return alternatives
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
|
|
2853
3146
|
if (viaMinimalOnly) return alternatives
|
|
2854
3147
|
|
|
2855
3148
|
const searchConnectionOrder = (
|