@tscircuit/fanout-solver 0.0.55 → 0.0.57
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/README.md +13 -0
- package/lib/fanout-solver.ts +260 -9
- package/lib/get-dogbone-side-variants.ts +26 -0
- package/lib/refine-adaptive-plane-reservations.ts +94 -0
- package/lib/route-bus.ts +375 -134
- package/lib/route-via-minimal-winding.ts +28 -0
- package/lib/select-compatible-candidates.ts +253 -0
- package/lib/should-use-adaptive-dense-plane-routing.ts +95 -0
- package/package.json +1 -1
package/lib/route-bus.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
segmentsAreClear,
|
|
19
19
|
} from "./geometry"
|
|
20
20
|
import { getAllRoutedTraceCopper } from "./get-routed-trace-copper"
|
|
21
|
+
import { getDogboneSideVariants } from "./get-dogbone-side-variants"
|
|
21
22
|
import { getViaSpanLayers } from "./layer-names"
|
|
22
23
|
import { matchComponentDogboneViaSites } from "./match-component-dogbone-via-sites"
|
|
23
24
|
import {
|
|
@@ -62,7 +63,7 @@ export interface RouteBusParams {
|
|
|
62
63
|
fixedViaPointsByConnectionIndex?: ReadonlyMap<number, Point2D>
|
|
63
64
|
reservedVias?: readonly ViaMinimalWindingReservedVia[]
|
|
64
65
|
viaMinimalOnly?: boolean
|
|
65
|
-
/** Permit a singleton to move
|
|
66
|
+
/** Permit a singleton or pair to move provisional vias near the boundary. */
|
|
66
67
|
allowBoundarySideViaFallback?: boolean
|
|
67
68
|
/** Reserve corner tuning space when selecting a boundary-side via. */
|
|
68
69
|
preferCornerBoundaryVia?: boolean
|
|
@@ -2113,7 +2114,7 @@ function routePlaneTerminatedBus(
|
|
|
2113
2114
|
otherPlans: [...acceptedPlans, ...fixedPlans],
|
|
2114
2115
|
staticClearanceCache,
|
|
2115
2116
|
blockingBusCounts,
|
|
2116
|
-
cacheKey: `plane-fixed:${bus.busId}:${targetLayer}:${preparedConnection.connectionIndex}:${candidateIndex}`,
|
|
2117
|
+
cacheKey: `plane-fixed:${bus.busId}:${targetLayer}:${preparedConnection.connectionIndex}:${fixedViaPoint.x}:${fixedViaPoint.y}:${candidateIndex}`,
|
|
2117
2118
|
srj,
|
|
2118
2119
|
sharedBoundary: bus.sharedBoundary,
|
|
2119
2120
|
clearance,
|
|
@@ -2604,6 +2605,7 @@ export function* routeBusAlternativesSteps(
|
|
|
2604
2605
|
maximumRouteOrderAttempts?: number
|
|
2605
2606
|
windingOrderIndex?: number
|
|
2606
2607
|
preferTargetDirectedLaneBias?: boolean
|
|
2608
|
+
localDogboneRepair?: boolean
|
|
2607
2609
|
}
|
|
2608
2610
|
const maximumThroughAllRouteOrderAttempts = 24
|
|
2609
2611
|
const windingTargetOrderCount = cornerSide
|
|
@@ -2836,6 +2838,48 @@ export function* routeBusAlternativesSteps(
|
|
|
2836
2838
|
fixedViaPointsByConnectionIndex
|
|
2837
2839
|
? fixedViaTerminalPatterns
|
|
2838
2840
|
: [...fixedViaTerminalPatterns, ...unreservedTerminalPatterns]
|
|
2841
|
+
if (
|
|
2842
|
+
!fixedViaPointsByConnectionIndex &&
|
|
2843
|
+
!allowBlindAndBuriedVias &&
|
|
2844
|
+
bus.connections.length > 1 &&
|
|
2845
|
+
bus.connections.every(
|
|
2846
|
+
(connection) => connection.sourceLayer !== targetLayer,
|
|
2847
|
+
) &&
|
|
2848
|
+
viaHandednesses.includes(-1) &&
|
|
2849
|
+
viaHandednesses.includes(1)
|
|
2850
|
+
) {
|
|
2851
|
+
// Keep the existing successful routes unchanged. Only after those site
|
|
2852
|
+
// assignments fail, try local inward dogbones that reopen a channel
|
|
2853
|
+
// closed by one or two pads in the same source row.
|
|
2854
|
+
const variants = getDogboneSideVariants(bus.connections, bus.direction)
|
|
2855
|
+
for (const handedness of viaHandednesses) {
|
|
2856
|
+
for (const flippedIndices of variants) {
|
|
2857
|
+
terminalPatterns.push({
|
|
2858
|
+
label: `local-dogbone-repair-${handedness}-${flippedIndices.join("-")}`,
|
|
2859
|
+
useViaInPad: false,
|
|
2860
|
+
getViaHandedness: () => handedness,
|
|
2861
|
+
maximumRouteOrderAttempts: 6,
|
|
2862
|
+
localDogboneRepair: true,
|
|
2863
|
+
getViaPoint: (connection) => {
|
|
2864
|
+
const point = getInitialViaPoint({
|
|
2865
|
+
preparedConnection: connection,
|
|
2866
|
+
bus,
|
|
2867
|
+
targetLayer,
|
|
2868
|
+
traceWidth,
|
|
2869
|
+
viaDiameter,
|
|
2870
|
+
clearance,
|
|
2871
|
+
viaHandedness: handedness,
|
|
2872
|
+
})
|
|
2873
|
+
if (!flippedIndices.includes(connection.connectionIndex))
|
|
2874
|
+
return point
|
|
2875
|
+
return bus.direction === "up" || bus.direction === "down"
|
|
2876
|
+
? { x: point.x, y: 2 * connection.sourcePoint.y - point.y }
|
|
2877
|
+
: { x: 2 * connection.sourcePoint.x - point.x, y: point.y }
|
|
2878
|
+
},
|
|
2879
|
+
})
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2839
2883
|
const seenTerminalSignatures = new Set<string>()
|
|
2840
2884
|
for (const terminalPattern of terminalPatterns) {
|
|
2841
2885
|
const terminals = bus.connections.map((preparedConnection) => {
|
|
@@ -2887,6 +2931,42 @@ export function* routeBusAlternativesSteps(
|
|
|
2887
2931
|
const alignGridToPads =
|
|
2888
2932
|
alignWindingGridToPads ||
|
|
2889
2933
|
Boolean(terminalPattern.getViaPoint && !fixedViaPointsByConnectionIndex)
|
|
2934
|
+
if (
|
|
2935
|
+
terminalPattern.localDogboneRepair &&
|
|
2936
|
+
!matchComponentDogboneViaSites([bus], {
|
|
2937
|
+
viaDiameter,
|
|
2938
|
+
viaHoleDiameter,
|
|
2939
|
+
traceWidth,
|
|
2940
|
+
clearance,
|
|
2941
|
+
additionalObstacles: srj.obstacles,
|
|
2942
|
+
fixedViaPointsByConnectionIndex: new Map(
|
|
2943
|
+
terminals.map((terminal) => [
|
|
2944
|
+
terminal.connection.connectionIndex,
|
|
2945
|
+
terminal.viaPoint,
|
|
2946
|
+
]),
|
|
2947
|
+
),
|
|
2948
|
+
blockingSegments: acceptedPlans.flatMap((plan) =>
|
|
2949
|
+
getPlanSegments(plan).map((segment) => ({
|
|
2950
|
+
connectionIndex: plan.connectionIndex,
|
|
2951
|
+
segment,
|
|
2952
|
+
})),
|
|
2953
|
+
),
|
|
2954
|
+
blockingVias: [
|
|
2955
|
+
...acceptedPlans.flatMap((plan) =>
|
|
2956
|
+
getPlanVias(plan).map((via) => ({
|
|
2957
|
+
connectionIndex: plan.connectionIndex,
|
|
2958
|
+
center: via.center,
|
|
2959
|
+
diameter: via.diameter,
|
|
2960
|
+
spanLayers: via.spanLayers,
|
|
2961
|
+
})),
|
|
2962
|
+
),
|
|
2963
|
+
// These are future connections, not members of the current bus.
|
|
2964
|
+
...reservedVias.map(({ via }) => ({ connectionIndex: -1, ...via })),
|
|
2965
|
+
],
|
|
2966
|
+
})
|
|
2967
|
+
) {
|
|
2968
|
+
continue
|
|
2969
|
+
}
|
|
2890
2970
|
const gridStepDivisor =
|
|
2891
2971
|
terminalPattern.getViaPoint &&
|
|
2892
2972
|
Math.min(bus.pitchX, bus.pitchY) -
|
|
@@ -2901,7 +2981,7 @@ export function* routeBusAlternativesSteps(
|
|
|
2901
2981
|
)
|
|
2902
2982
|
.join(
|
|
2903
2983
|
"|",
|
|
2904
|
-
)}:${terminalPattern.maximumRouteOrderAttempts ?? "all"}:${gridStepDivisor}:${alignGridToPads}`
|
|
2984
|
+
)}:${terminalPattern.maximumRouteOrderAttempts ?? "all"}:${gridStepDivisor}:${alignGridToPads}:${Boolean(terminalPattern.localDogboneRepair)}`
|
|
2905
2985
|
if (seenTerminalSignatures.has(terminalSignature)) continue
|
|
2906
2986
|
seenTerminalSignatures.add(terminalSignature)
|
|
2907
2987
|
const windingSteps = routeViaMinimalWindingAlternativesSteps(
|
|
@@ -2921,6 +3001,7 @@ export function* routeBusAlternativesSteps(
|
|
|
2921
3001
|
maximumRouteOrderAttempts: terminalPattern.maximumRouteOrderAttempts,
|
|
2922
3002
|
adaptiveRouteOrder: adaptiveWindingRouteOrder,
|
|
2923
3003
|
alignGridToPads,
|
|
3004
|
+
includeReverseTargetRotation: terminalPattern.localDogboneRepair,
|
|
2924
3005
|
reservedVias,
|
|
2925
3006
|
gridStepDivisor,
|
|
2926
3007
|
preferTargetDirectedLaneBias:
|
|
@@ -2976,24 +3057,28 @@ export function* routeBusAlternativesSteps(
|
|
|
2976
3057
|
}
|
|
2977
3058
|
}
|
|
2978
3059
|
|
|
2979
|
-
// A through-via does not have to sit next to the source pad. A
|
|
3060
|
+
// A through-via does not have to sit next to the source pad. A narrow bus
|
|
2980
3061
|
// embedded in another bus's source field can have every local dogbone site
|
|
2981
|
-
// occupied while still having a clear
|
|
2982
|
-
//
|
|
2983
|
-
//
|
|
2984
|
-
// clearance rule.
|
|
3062
|
+
// occupied while still having a clear source-layer escape. A pair first
|
|
3063
|
+
// relocates its two vias just outside the nearest package edge; a singleton
|
|
3064
|
+
// retains the boundary-side-via fallback. Both forms preserve one via per
|
|
3065
|
+
// signal without weakening any clearance rule.
|
|
2985
3066
|
if (
|
|
2986
3067
|
alternatives.length === 0 &&
|
|
2987
3068
|
allowBoundarySideViaFallback &&
|
|
2988
3069
|
fixedViaPointsByConnectionIndex &&
|
|
2989
|
-
bus.connections.length
|
|
3070
|
+
bus.connections.length <= 2 &&
|
|
2990
3071
|
bus.exitEdge &&
|
|
2991
|
-
bus.connections
|
|
3072
|
+
bus.connections.every(
|
|
3073
|
+
(connection) =>
|
|
3074
|
+
connection.sourceLayer === bus.connections[0]!.sourceLayer &&
|
|
3075
|
+
connection.sourceLayer !== targetLayer,
|
|
3076
|
+
)
|
|
2992
3077
|
) {
|
|
2993
|
-
const
|
|
3078
|
+
const sourceLayer = bus.connections[0]!.sourceLayer
|
|
2994
3079
|
const boundaryDirection = getDirectionForExitEdge(bus.exitEdge)
|
|
2995
3080
|
const boundaryExitAxis = getExitAxis(bus, boundaryDirection)
|
|
2996
|
-
const
|
|
3081
|
+
const finalTracks = bus.connections.map((preparedConnection) =>
|
|
2997
3082
|
preferCornerBoundaryVia && getCornerSide(bus)
|
|
2998
3083
|
? getCornerTargetTrack({
|
|
2999
3084
|
bus,
|
|
@@ -3010,27 +3095,30 @@ export function* routeBusAlternativesSteps(
|
|
|
3010
3095
|
bus,
|
|
3011
3096
|
connection: preparedConnection,
|
|
3012
3097
|
boundaryDirection,
|
|
3013
|
-
})
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
boundaryDirection,
|
|
3098
|
+
}),
|
|
3099
|
+
)
|
|
3100
|
+
const finalExitPoints = finalTracks.map((track) =>
|
|
3101
|
+
makePoint(boundaryExitAxis, track, boundaryDirection),
|
|
3018
3102
|
)
|
|
3019
3103
|
const sourceLayerSrj: SimpleRouteJson = {
|
|
3020
3104
|
...srj,
|
|
3021
|
-
obstacles: srj.obstacles.map((obstacle) =>
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3105
|
+
obstacles: srj.obstacles.map((obstacle) => {
|
|
3106
|
+
const connection = bus.connections.find(
|
|
3107
|
+
(connection) =>
|
|
3108
|
+
obstacle === connection.sourceObstacle ||
|
|
3109
|
+
(distance(obstacle.center, connection.sourcePoint) <= 1e-7 &&
|
|
3110
|
+
obstacle.layers.includes(connection.sourceLayer)),
|
|
3111
|
+
)
|
|
3112
|
+
return connection
|
|
3025
3113
|
? {
|
|
3026
3114
|
...obstacle,
|
|
3027
3115
|
connectedTo: [
|
|
3028
3116
|
...obstacle.connectedTo,
|
|
3029
|
-
|
|
3117
|
+
connection.connection.name,
|
|
3030
3118
|
],
|
|
3031
3119
|
}
|
|
3032
|
-
: obstacle
|
|
3033
|
-
),
|
|
3120
|
+
: obstacle
|
|
3121
|
+
}),
|
|
3034
3122
|
}
|
|
3035
3123
|
const insetStep = Math.max(
|
|
3036
3124
|
viaDiameter / 2 + clearance,
|
|
@@ -3039,36 +3127,124 @@ export function* routeBusAlternativesSteps(
|
|
|
3039
3127
|
const cornerSide = preferCornerBoundaryVia ? getCornerSide(bus) : undefined
|
|
3040
3128
|
const boundaryViaCandidates = [1, 2, 3, 4, 5].flatMap((multiple) => {
|
|
3041
3129
|
const inset = multiple * insetStep
|
|
3042
|
-
const straight =
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3130
|
+
const straight = finalTracks.map((finalTrack) =>
|
|
3131
|
+
makePoint(
|
|
3132
|
+
boundaryExitAxis - directionSign(boundaryDirection) * inset,
|
|
3133
|
+
finalTrack,
|
|
3134
|
+
boundaryDirection,
|
|
3135
|
+
),
|
|
3046
3136
|
)
|
|
3047
3137
|
if (!cornerSide) return [straight]
|
|
3048
3138
|
// Approach corner exits diagonally to leave the adjacent pair's tuning
|
|
3049
3139
|
// lane free of the through-via barrel. Retain the straight fallback.
|
|
3050
3140
|
return [
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3141
|
+
finalTracks.map((finalTrack) =>
|
|
3142
|
+
makePoint(
|
|
3143
|
+
boundaryExitAxis - directionSign(boundaryDirection) * inset,
|
|
3144
|
+
finalTrack + (cornerSide === "maximum" ? inset : -inset),
|
|
3145
|
+
boundaryDirection,
|
|
3146
|
+
),
|
|
3055
3147
|
),
|
|
3056
3148
|
straight,
|
|
3057
3149
|
]
|
|
3058
3150
|
})
|
|
3059
|
-
|
|
3151
|
+
const sourceCenter = {
|
|
3152
|
+
x:
|
|
3153
|
+
bus.connections.reduce(
|
|
3154
|
+
(sum, connection) => sum + connection.sourcePoint.x,
|
|
3155
|
+
0,
|
|
3156
|
+
) / bus.connections.length,
|
|
3157
|
+
y:
|
|
3158
|
+
bus.connections.reduce(
|
|
3159
|
+
(sum, connection) => sum + connection.sourcePoint.y,
|
|
3160
|
+
0,
|
|
3161
|
+
) / bus.connections.length,
|
|
3162
|
+
}
|
|
3163
|
+
const directions = [
|
|
3164
|
+
{ x: -1, y: 0, distance: sourceCenter.x - Math.min(...bus.xCoordinates) },
|
|
3165
|
+
{ x: 1, y: 0, distance: Math.max(...bus.xCoordinates) - sourceCenter.x },
|
|
3166
|
+
{ x: 0, y: -1, distance: sourceCenter.y - Math.min(...bus.yCoordinates) },
|
|
3167
|
+
{ x: 0, y: 1, distance: Math.max(...bus.yCoordinates) - sourceCenter.y },
|
|
3168
|
+
].toSorted((first, second) => first.distance - second.distance)
|
|
3169
|
+
const displacedViaCandidates =
|
|
3170
|
+
bus.connections.length === 2
|
|
3171
|
+
? directions.flatMap((direction) =>
|
|
3172
|
+
[3].map((multiple) =>
|
|
3173
|
+
bus.connections.map((connection) => {
|
|
3174
|
+
const original = fixedViaPointsByConnectionIndex.get(
|
|
3175
|
+
connection.connectionIndex,
|
|
3176
|
+
)!
|
|
3177
|
+
return {
|
|
3178
|
+
x: original.x + direction.x * multiple * bus.pitchX,
|
|
3179
|
+
y: original.y + direction.y * multiple * bus.pitchY,
|
|
3180
|
+
}
|
|
3181
|
+
}),
|
|
3182
|
+
),
|
|
3183
|
+
)
|
|
3184
|
+
: []
|
|
3185
|
+
const viaCandidates = [
|
|
3186
|
+
...displacedViaCandidates.map((points) => ({
|
|
3187
|
+
points,
|
|
3188
|
+
boundarySide: false,
|
|
3189
|
+
})),
|
|
3190
|
+
...boundaryViaCandidates.map((points) => ({
|
|
3191
|
+
points,
|
|
3192
|
+
boundarySide: true,
|
|
3193
|
+
})),
|
|
3194
|
+
]
|
|
3195
|
+
for (const { points: boundaryViaPoints, boundarySide } of viaCandidates) {
|
|
3196
|
+
const boundaryVias = bus.connections.map((connection, index) => ({
|
|
3197
|
+
connectionName: connection.connection.name,
|
|
3198
|
+
via: {
|
|
3199
|
+
center: boundaryViaPoints[index]!,
|
|
3200
|
+
diameter: viaDiameter,
|
|
3201
|
+
spanLayers: getViaSpanLayers({
|
|
3202
|
+
fromLayer: sourceLayer,
|
|
3203
|
+
toLayer: targetLayer,
|
|
3204
|
+
layerNames,
|
|
3205
|
+
allowBlindAndBuriedVias,
|
|
3206
|
+
}),
|
|
3207
|
+
},
|
|
3208
|
+
}))
|
|
3209
|
+
if (
|
|
3210
|
+
boundaryVias.some((candidate, index) =>
|
|
3211
|
+
boundaryVias.some(
|
|
3212
|
+
(other, otherIndex) =>
|
|
3213
|
+
index !== otherIndex &&
|
|
3214
|
+
(distance(candidate.via.center, other.via.center) <
|
|
3215
|
+
viaDiameter + clearance - 1e-9 ||
|
|
3216
|
+
(boundarySide &&
|
|
3217
|
+
distancePointToSegment(
|
|
3218
|
+
candidate.via.center,
|
|
3219
|
+
other.via.center,
|
|
3220
|
+
finalExitPoints[otherIndex]!,
|
|
3221
|
+
) <
|
|
3222
|
+
viaDiameter / 2 + traceWidth / 2 + clearance - 1e-9)),
|
|
3223
|
+
),
|
|
3224
|
+
)
|
|
3225
|
+
)
|
|
3226
|
+
continue
|
|
3227
|
+
if (
|
|
3228
|
+
boundaryVias.some(({ via }) =>
|
|
3229
|
+
srj.obstacles.some(
|
|
3230
|
+
(obstacle) =>
|
|
3231
|
+
obstacle.layers.some((layer) => via.spanLayers.includes(layer)) &&
|
|
3232
|
+
distancePointToObstacle(via.center, obstacle) <
|
|
3233
|
+
via.diameter / 2 + clearance - 1e-9,
|
|
3234
|
+
),
|
|
3235
|
+
)
|
|
3236
|
+
)
|
|
3237
|
+
continue
|
|
3060
3238
|
const sourceLayerSteps = routeViaMinimalWindingAlternativesSteps(
|
|
3061
3239
|
{
|
|
3062
3240
|
srj: sourceLayerSrj,
|
|
3063
3241
|
bus,
|
|
3064
|
-
targetLayer:
|
|
3065
|
-
terminals:
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
},
|
|
3071
|
-
],
|
|
3242
|
+
targetLayer: sourceLayer,
|
|
3243
|
+
terminals: bus.connections.map((preparedConnection, index) => ({
|
|
3244
|
+
connection: preparedConnection,
|
|
3245
|
+
viaPoint: preparedConnection.sourcePoint,
|
|
3246
|
+
exitPoint: boundaryViaPoints[index]!,
|
|
3247
|
+
})),
|
|
3072
3248
|
acceptedPlans,
|
|
3073
3249
|
layerNames,
|
|
3074
3250
|
traceWidth,
|
|
@@ -3077,8 +3253,11 @@ export function* routeBusAlternativesSteps(
|
|
|
3077
3253
|
clearance,
|
|
3078
3254
|
allowBlindAndBuriedVias,
|
|
3079
3255
|
allowSameNetMerges,
|
|
3080
|
-
maximumRouteOrderAttempts: 3,
|
|
3081
|
-
reservedVias
|
|
3256
|
+
maximumRouteOrderAttempts: bus.connections.length === 1 ? 3 : 6,
|
|
3257
|
+
reservedVias:
|
|
3258
|
+
bus.connections.length > 1
|
|
3259
|
+
? [...reservedVias, ...boundaryVias]
|
|
3260
|
+
: reservedVias,
|
|
3082
3261
|
gridStepDivisor: 2,
|
|
3083
3262
|
allowSourceLayerRouting: true,
|
|
3084
3263
|
alignGridToPads: true,
|
|
@@ -3091,75 +3270,133 @@ export function* routeBusAlternativesSteps(
|
|
|
3091
3270
|
yield {
|
|
3092
3271
|
phase: "via-minimal-winding",
|
|
3093
3272
|
busId: bus.busId,
|
|
3094
|
-
targetLayer:
|
|
3273
|
+
targetLayer: sourceLayer,
|
|
3095
3274
|
winding: sourceLayerResult.value,
|
|
3096
3275
|
}
|
|
3097
3276
|
sourceLayerResult = sourceLayerSteps.next()
|
|
3098
3277
|
}
|
|
3099
|
-
const
|
|
3100
|
-
if (
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
end: finalExitPoint,
|
|
3104
|
-
width: traceWidth,
|
|
3105
|
-
layer: targetLayer,
|
|
3106
|
-
}
|
|
3107
|
-
const via = {
|
|
3108
|
-
center: boundaryViaPoint,
|
|
3109
|
-
diameter: viaDiameter,
|
|
3110
|
-
holeDiameter: viaHoleDiameter,
|
|
3111
|
-
fromLayer: preparedConnection.sourceLayer,
|
|
3112
|
-
toLayer: targetLayer,
|
|
3113
|
-
spanLayers: getViaSpanLayers({
|
|
3114
|
-
fromLayer: preparedConnection.sourceLayer,
|
|
3115
|
-
toLayer: targetLayer,
|
|
3116
|
-
layerNames,
|
|
3117
|
-
allowBlindAndBuriedVias,
|
|
3118
|
-
}),
|
|
3119
|
-
}
|
|
3120
|
-
const sourceRoute = sourceLayerPlan.trace.route.filter(
|
|
3121
|
-
(item) => item.route_type === "wire",
|
|
3278
|
+
const sourceLayerPlans = sourceLayerResult.value[0]
|
|
3279
|
+
if (
|
|
3280
|
+
!sourceLayerPlans ||
|
|
3281
|
+
sourceLayerPlans.length !== bus.connections.length
|
|
3122
3282
|
)
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
route_type: "wire",
|
|
3149
|
-
...boundaryViaPoint,
|
|
3150
|
-
width: traceWidth,
|
|
3151
|
-
layer: targetLayer,
|
|
3152
|
-
},
|
|
3153
|
-
{
|
|
3154
|
-
route_type: "wire",
|
|
3155
|
-
...finalExitPoint,
|
|
3156
|
-
width: traceWidth,
|
|
3157
|
-
layer: targetLayer,
|
|
3158
|
-
},
|
|
3159
|
-
],
|
|
3283
|
+
continue
|
|
3284
|
+
let targetLayerPlans: FanoutRoutePlan[] | undefined
|
|
3285
|
+
if (!boundarySide) {
|
|
3286
|
+
const targetSteps = routeViaMinimalWindingAlternativesSteps(
|
|
3287
|
+
{
|
|
3288
|
+
srj,
|
|
3289
|
+
bus,
|
|
3290
|
+
targetLayer,
|
|
3291
|
+
terminals: bus.connections.map((connection, index) => ({
|
|
3292
|
+
connection,
|
|
3293
|
+
viaPoint: boundaryViaPoints[index]!,
|
|
3294
|
+
exitPoint: finalExitPoints[index]!,
|
|
3295
|
+
})),
|
|
3296
|
+
acceptedPlans,
|
|
3297
|
+
layerNames,
|
|
3298
|
+
traceWidth,
|
|
3299
|
+
viaDiameter,
|
|
3300
|
+
viaHoleDiameter,
|
|
3301
|
+
clearance,
|
|
3302
|
+
allowBlindAndBuriedVias,
|
|
3303
|
+
allowSameNetMerges,
|
|
3304
|
+
maximumRouteOrderAttempts: 6,
|
|
3305
|
+
reservedVias,
|
|
3306
|
+
gridStepDivisor: 2,
|
|
3307
|
+
alignGridToPads: true,
|
|
3160
3308
|
},
|
|
3309
|
+
1,
|
|
3310
|
+
includeVisualization,
|
|
3311
|
+
)
|
|
3312
|
+
let targetResult = targetSteps.next()
|
|
3313
|
+
while (!targetResult.done) {
|
|
3314
|
+
yield {
|
|
3315
|
+
phase: "via-minimal-winding",
|
|
3316
|
+
busId: bus.busId,
|
|
3317
|
+
targetLayer,
|
|
3318
|
+
winding: targetResult.value,
|
|
3319
|
+
}
|
|
3320
|
+
targetResult = targetSteps.next()
|
|
3321
|
+
}
|
|
3322
|
+
targetLayerPlans = targetResult.value[0]
|
|
3323
|
+
if (!targetLayerPlans) continue
|
|
3324
|
+
}
|
|
3325
|
+
const plans: FanoutRoutePlan[] = sourceLayerPlans.map(
|
|
3326
|
+
(sourceLayerPlan, index) => {
|
|
3327
|
+
const boundaryViaPoint = boundaryViaPoints[index]!
|
|
3328
|
+
const finalExitPoint = finalExitPoints[index]!
|
|
3329
|
+
const targetSegment: RoutedSegment = {
|
|
3330
|
+
start: boundaryViaPoint,
|
|
3331
|
+
end: finalExitPoint,
|
|
3332
|
+
width: traceWidth,
|
|
3333
|
+
layer: targetLayer,
|
|
3334
|
+
}
|
|
3335
|
+
const targetSegments = targetLayerPlans
|
|
3336
|
+
? targetLayerPlans[index]!.segments.filter(
|
|
3337
|
+
(segment) => segment.layer === targetLayer,
|
|
3338
|
+
)
|
|
3339
|
+
: [targetSegment]
|
|
3340
|
+
const via = {
|
|
3341
|
+
center: boundaryViaPoint,
|
|
3342
|
+
diameter: viaDiameter,
|
|
3343
|
+
holeDiameter: viaHoleDiameter,
|
|
3344
|
+
fromLayer: sourceLayer,
|
|
3345
|
+
toLayer: targetLayer,
|
|
3346
|
+
spanLayers: getViaSpanLayers({
|
|
3347
|
+
fromLayer: sourceLayer,
|
|
3348
|
+
toLayer: targetLayer,
|
|
3349
|
+
layerNames,
|
|
3350
|
+
allowBlindAndBuriedVias,
|
|
3351
|
+
}),
|
|
3352
|
+
}
|
|
3353
|
+
const sourceRoute = sourceLayerPlan.trace.route.filter(
|
|
3354
|
+
(item) => item.route_type === "wire",
|
|
3355
|
+
)
|
|
3356
|
+
return {
|
|
3357
|
+
...sourceLayerPlan,
|
|
3358
|
+
...(preferCornerBoundaryVia || bus.connections.length > 1
|
|
3359
|
+
? { sourceEscapeSegmentCount: sourceLayerPlan.segments.length }
|
|
3360
|
+
: {}),
|
|
3361
|
+
targetLayer,
|
|
3362
|
+
exitPoint: finalExitPoint,
|
|
3363
|
+
via,
|
|
3364
|
+
segments: [...sourceLayerPlan.segments, ...targetSegments],
|
|
3365
|
+
length:
|
|
3366
|
+
sourceLayerPlan.length +
|
|
3367
|
+
targetSegments.reduce(
|
|
3368
|
+
(sum, segment) => sum + distance(segment.start, segment.end),
|
|
3369
|
+
0,
|
|
3370
|
+
),
|
|
3371
|
+
trace: {
|
|
3372
|
+
...sourceLayerPlan.trace,
|
|
3373
|
+
route: [
|
|
3374
|
+
...sourceRoute,
|
|
3375
|
+
{
|
|
3376
|
+
route_type: "via",
|
|
3377
|
+
...boundaryViaPoint,
|
|
3378
|
+
from_layer: sourceLayer,
|
|
3379
|
+
to_layer: targetLayer,
|
|
3380
|
+
via_diameter: viaDiameter,
|
|
3381
|
+
via_hole_diameter: viaHoleDiameter,
|
|
3382
|
+
},
|
|
3383
|
+
{
|
|
3384
|
+
route_type: "wire",
|
|
3385
|
+
...boundaryViaPoint,
|
|
3386
|
+
width: traceWidth,
|
|
3387
|
+
layer: targetLayer,
|
|
3388
|
+
},
|
|
3389
|
+
...targetSegments.map((segment) => ({
|
|
3390
|
+
route_type: "wire" as const,
|
|
3391
|
+
...segment.end,
|
|
3392
|
+
width: traceWidth,
|
|
3393
|
+
layer: targetLayer,
|
|
3394
|
+
})),
|
|
3395
|
+
],
|
|
3396
|
+
},
|
|
3397
|
+
}
|
|
3161
3398
|
},
|
|
3162
|
-
|
|
3399
|
+
)
|
|
3163
3400
|
const boundaryViaPlansAreClear = fanoutPlansAreClear({
|
|
3164
3401
|
plans: [...acceptedPlans, ...plans],
|
|
3165
3402
|
srj,
|
|
@@ -3168,35 +3405,39 @@ export function* routeBusAlternativesSteps(
|
|
|
3168
3405
|
allowBlindAndBuriedVias,
|
|
3169
3406
|
allowSameNetMerges,
|
|
3170
3407
|
})
|
|
3171
|
-
const reservedViasAreClear =
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
(
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3408
|
+
const reservedViasAreClear = plans.every((plan) =>
|
|
3409
|
+
reservedVias.every((reserved) => {
|
|
3410
|
+
const via = plan.via!
|
|
3411
|
+
if (
|
|
3412
|
+
reserved.connectionName === plan.connectionName ||
|
|
3413
|
+
(allowSameNetMerges &&
|
|
3414
|
+
connectionsShareElectricalNet(
|
|
3415
|
+
srj,
|
|
3416
|
+
reserved.connectionName,
|
|
3417
|
+
plan.connectionName,
|
|
3418
|
+
))
|
|
3419
|
+
)
|
|
3420
|
+
return true
|
|
3421
|
+
if (
|
|
3422
|
+
via.spanLayers.some((layer) =>
|
|
3423
|
+
reserved.via.spanLayers.includes(layer),
|
|
3424
|
+
) &&
|
|
3425
|
+
distance(via.center, reserved.via.center) <
|
|
3426
|
+
(via.diameter + reserved.via.diameter) / 2 + clearance - 1e-9
|
|
3427
|
+
)
|
|
3428
|
+
return false
|
|
3429
|
+
return plan.segments.every(
|
|
3430
|
+
(segment) =>
|
|
3431
|
+
!reserved.via.spanLayers.includes(segment.layer) ||
|
|
3432
|
+
distancePointToSegment(
|
|
3433
|
+
reserved.via.center,
|
|
3434
|
+
segment.start,
|
|
3435
|
+
segment.end,
|
|
3436
|
+
) >=
|
|
3437
|
+
reserved.via.diameter / 2 + traceWidth / 2 + clearance - 1e-9,
|
|
3438
|
+
)
|
|
3439
|
+
}),
|
|
3440
|
+
)
|
|
3200
3441
|
if (boundaryViaPlansAreClear && reservedViasAreClear) {
|
|
3201
3442
|
addAlternative(plans)
|
|
3202
3443
|
return alternatives
|