@tscircuit/fanout-solver 0.0.25 → 0.0.27
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/build-output.ts +8 -3
- package/lib/complete-original-endpoints.ts +6 -1
- package/lib/fanout-output-ids.ts +33 -0
- package/lib/fanout-solver.ts +54 -13
- package/lib/route-bus.ts +13 -4
- package/lib/route-single-layer-adaptive-exits.ts +7 -3
- package/lib/route-single-layer-push-shove.ts +7 -2
- package/package.json +7 -1
package/lib/build-output.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Obstacle, SimpleRouteJson } from "@tscircuit/capacity-autorouter"
|
|
2
|
+
import { createFanoutOutputIds } from "./fanout-output-ids"
|
|
2
3
|
import type { FanoutRoutePlan, SimpleRouteJsonWithFanoutPlanes } from "./types"
|
|
3
4
|
|
|
4
5
|
function createViaObstacle(
|
|
@@ -17,8 +18,11 @@ function createViaObstacle(
|
|
|
17
18
|
}
|
|
18
19
|
return layerIndex
|
|
19
20
|
})
|
|
21
|
+
const outputIds = createFanoutOutputIds(plan)
|
|
20
22
|
return {
|
|
21
|
-
obstacleId:
|
|
23
|
+
obstacleId: endpoint
|
|
24
|
+
? outputIds.planeEndpointViaObstacleId
|
|
25
|
+
: outputIds.viaObstacleId,
|
|
22
26
|
type: "rect",
|
|
23
27
|
center: via.center,
|
|
24
28
|
width: via.diameter,
|
|
@@ -50,14 +54,15 @@ export function buildOutputSimpleRouteJson(params: {
|
|
|
50
54
|
`FanoutSolver: output connection index ${plan.connectionIndex} is missing`,
|
|
51
55
|
)
|
|
52
56
|
}
|
|
57
|
+
const outputIds = createFanoutOutputIds(plan)
|
|
53
58
|
connection.pointsToConnect[plan.sourcePointIndex] = {
|
|
54
59
|
x: plan.exitPoint.x,
|
|
55
60
|
y: plan.exitPoint.y,
|
|
56
61
|
layer: plan.targetLayer,
|
|
57
62
|
pointId:
|
|
58
63
|
plan.termination.type === "plane"
|
|
59
|
-
?
|
|
60
|
-
:
|
|
64
|
+
? outputIds.planeExitPointId
|
|
65
|
+
: outputIds.boundaryExitPointId,
|
|
61
66
|
}
|
|
62
67
|
if (plan.termination.type === "plane") {
|
|
63
68
|
planeTerminatedConnectionNames.add(plan.connectionName)
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
type SimpleRouteJson,
|
|
6
6
|
type SimplifiedPcbTrace,
|
|
7
7
|
} from "@tscircuit/capacity-autorouter"
|
|
8
|
+
import { createFanoutCompletionTraceId } from "./fanout-output-ids"
|
|
8
9
|
import {
|
|
9
10
|
distance,
|
|
10
11
|
distancePointToSegment,
|
|
@@ -226,7 +227,11 @@ function createBranchTrace(params: {
|
|
|
226
227
|
|
|
227
228
|
return {
|
|
228
229
|
type: "pcb_trace",
|
|
229
|
-
pcb_trace_id:
|
|
230
|
+
pcb_trace_id: createFanoutCompletionTraceId({
|
|
231
|
+
connectionName: plan.connectionName,
|
|
232
|
+
sourcePointIndex: plan.sourcePointIndex,
|
|
233
|
+
candidateIndex,
|
|
234
|
+
}),
|
|
230
235
|
connection_name: plan.connectionName,
|
|
231
236
|
route,
|
|
232
237
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface FanoutOutputIds {
|
|
2
|
+
boundaryExitPointId: string
|
|
3
|
+
planeExitPointId: string
|
|
4
|
+
traceId: string
|
|
5
|
+
viaObstacleId: string
|
|
6
|
+
planeEndpointPointId: string
|
|
7
|
+
planeEndpointTraceId: string
|
|
8
|
+
planeEndpointViaObstacleId: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createFanoutOutputIds(input: {
|
|
12
|
+
connectionName: string
|
|
13
|
+
sourcePointIndex: number
|
|
14
|
+
}): FanoutOutputIds {
|
|
15
|
+
const endpointKey = `${input.connectionName}:source-${input.sourcePointIndex}`
|
|
16
|
+
return {
|
|
17
|
+
boundaryExitPointId: `fanout-exit:${endpointKey}`,
|
|
18
|
+
planeExitPointId: `fanout-plane:${endpointKey}`,
|
|
19
|
+
traceId: `fanout:${endpointKey}`,
|
|
20
|
+
viaObstacleId: `fanout-via:${endpointKey}`,
|
|
21
|
+
planeEndpointPointId: `fanout-plane-endpoint-point:${endpointKey}`,
|
|
22
|
+
planeEndpointTraceId: `fanout-plane-endpoint:${endpointKey}`,
|
|
23
|
+
planeEndpointViaObstacleId: `fanout-plane-endpoint-via:${endpointKey}`,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createFanoutCompletionTraceId(input: {
|
|
28
|
+
connectionName: string
|
|
29
|
+
sourcePointIndex: number
|
|
30
|
+
candidateIndex: number
|
|
31
|
+
}): string {
|
|
32
|
+
return `fanout-completion:${input.connectionName}:source-${input.sourcePointIndex}:${input.candidateIndex}`
|
|
33
|
+
}
|
package/lib/fanout-solver.ts
CHANGED
|
@@ -1028,8 +1028,31 @@ export class FanoutSolver extends BaseSolver {
|
|
|
1028
1028
|
this.pendingRepairAssignments.push(...repairs)
|
|
1029
1029
|
}
|
|
1030
1030
|
|
|
1031
|
+
private hasCompleteBestAttempt(): boolean {
|
|
1032
|
+
return (
|
|
1033
|
+
this.bestAttempt?.summary.routedConnectionCount ===
|
|
1034
|
+
this.inputSrj.connections.length
|
|
1035
|
+
)
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
private shouldEvaluateGroupedBeam(): boolean {
|
|
1039
|
+
if (this.groupedBeamEvaluated || this.nextAssignmentIndex === 0) {
|
|
1040
|
+
return false
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
const targetedRepairSearchFinished =
|
|
1044
|
+
this.hasCompleteBestAttempt() ||
|
|
1045
|
+
this.pendingRepairAssignments.length === 0 ||
|
|
1046
|
+
this.nextAssignmentIndex >= this.config.maxLayerCombinations
|
|
1047
|
+
|
|
1048
|
+
return targetedRepairSearchFinished
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1031
1051
|
override _step(): void {
|
|
1032
|
-
|
|
1052
|
+
// Try the deterministic assignment and only its targeted repair queue
|
|
1053
|
+
// before paying for the grouped beam. If the beam cannot solve, continue
|
|
1054
|
+
// with the broader generated-assignment search below.
|
|
1055
|
+
if (this.shouldEvaluateGroupedBeam()) {
|
|
1033
1056
|
this.groupedBeamEvaluated = true
|
|
1034
1057
|
let beamAttempt = this.evaluateGroupedBeam(-1)
|
|
1035
1058
|
if (!beamAttempt) {
|
|
@@ -1037,19 +1060,33 @@ export class FanoutSolver extends BaseSolver {
|
|
|
1037
1060
|
}
|
|
1038
1061
|
if (beamAttempt) {
|
|
1039
1062
|
this.attempts.push(beamAttempt.summary)
|
|
1040
|
-
|
|
1063
|
+
if (
|
|
1064
|
+
!this.bestAttempt ||
|
|
1065
|
+
beamAttempt.summary.score < this.bestAttempt.summary.score
|
|
1066
|
+
) {
|
|
1067
|
+
this.bestAttempt = beamAttempt
|
|
1068
|
+
}
|
|
1069
|
+
const bestSummary = this.bestAttempt.summary
|
|
1041
1070
|
this.stats = {
|
|
1042
|
-
assignment:
|
|
1071
|
+
assignment:
|
|
1072
|
+
bestSummary.assignmentIndex < 0
|
|
1073
|
+
? 0
|
|
1074
|
+
: bestSummary.assignmentIndex + 1,
|
|
1043
1075
|
assignmentCount: this.config.maxLayerCombinations,
|
|
1044
|
-
routedBuses: `${
|
|
1045
|
-
routedConnections: `${
|
|
1076
|
+
routedBuses: `${bestSummary.routedBusCount}/${this.preparedBuses.length}`,
|
|
1077
|
+
routedConnections: `${bestSummary.routedConnectionCount}/${this.inputSrj.connections.length}`,
|
|
1046
1078
|
failedBuses: "none",
|
|
1047
|
-
bestScore:
|
|
1079
|
+
bestScore: bestSummary.score,
|
|
1048
1080
|
}
|
|
1049
1081
|
this.completeBestAttemptEndpoints()
|
|
1050
1082
|
this.solved = true
|
|
1051
1083
|
return
|
|
1052
1084
|
}
|
|
1085
|
+
if (this.hasCompleteBestAttempt()) {
|
|
1086
|
+
this.completeBestAttemptEndpoints()
|
|
1087
|
+
this.solved = true
|
|
1088
|
+
return
|
|
1089
|
+
}
|
|
1053
1090
|
}
|
|
1054
1091
|
|
|
1055
1092
|
let assignment: Readonly<Record<string, string>> | undefined
|
|
@@ -1060,13 +1097,19 @@ export class FanoutSolver extends BaseSolver {
|
|
|
1060
1097
|
const preferGeneratedAssignment = this.nextAssignmentIndex % 3 === 0
|
|
1061
1098
|
let candidate: Readonly<Record<string, string>> | undefined
|
|
1062
1099
|
let candidateCameFromRepairQueue = false
|
|
1063
|
-
if (
|
|
1100
|
+
if (!this.groupedBeamEvaluated && this.nextAssignmentIndex > 0) {
|
|
1101
|
+
candidate = this.pendingRepairAssignments.pop()
|
|
1102
|
+
candidateCameFromRepairQueue = candidate !== undefined
|
|
1103
|
+
} else if (preferGeneratedAssignment) {
|
|
1064
1104
|
candidate = this.layerAssignments[this.nextGeneratedAssignmentIndex++]
|
|
1065
1105
|
} else {
|
|
1066
1106
|
candidate = this.pendingRepairAssignments.pop()
|
|
1067
1107
|
candidateCameFromRepairQueue = candidate !== undefined
|
|
1068
1108
|
}
|
|
1069
|
-
if (
|
|
1109
|
+
if (
|
|
1110
|
+
!candidate &&
|
|
1111
|
+
(this.groupedBeamEvaluated || this.nextAssignmentIndex === 0)
|
|
1112
|
+
) {
|
|
1070
1113
|
candidate = preferGeneratedAssignment
|
|
1071
1114
|
? this.pendingRepairAssignments.pop()
|
|
1072
1115
|
: this.layerAssignments[this.nextGeneratedAssignmentIndex++]
|
|
@@ -1081,12 +1124,9 @@ export class FanoutSolver extends BaseSolver {
|
|
|
1081
1124
|
if (this.evaluatedAssignmentKeys.has(candidateKey)) continue
|
|
1082
1125
|
assignment = candidate
|
|
1083
1126
|
}
|
|
1127
|
+
if (!assignment && !this.groupedBeamEvaluated) return
|
|
1084
1128
|
if (!assignment) {
|
|
1085
|
-
if (
|
|
1086
|
-
this.bestAttempt &&
|
|
1087
|
-
this.bestAttempt.summary.routedConnectionCount ===
|
|
1088
|
-
this.inputSrj.connections.length
|
|
1089
|
-
) {
|
|
1129
|
+
if (this.hasCompleteBestAttempt()) {
|
|
1090
1130
|
this.completeBestAttemptEndpoints()
|
|
1091
1131
|
this.solved = true
|
|
1092
1132
|
} else {
|
|
@@ -1131,6 +1171,7 @@ export class FanoutSolver extends BaseSolver {
|
|
|
1131
1171
|
bestScore: this.bestAttempt.summary.score,
|
|
1132
1172
|
}
|
|
1133
1173
|
if (
|
|
1174
|
+
this.groupedBeamEvaluated &&
|
|
1134
1175
|
attempt.summary.routedConnectionCount === this.inputSrj.connections.length
|
|
1135
1176
|
) {
|
|
1136
1177
|
this.completeBestAttemptEndpoints()
|
package/lib/route-bus.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
distanceSegmentToObstacle,
|
|
11
11
|
segmentsAreClear,
|
|
12
12
|
} from "./geometry"
|
|
13
|
+
import { createFanoutOutputIds } from "./fanout-output-ids"
|
|
13
14
|
import { getLayerSpan } from "./layer-names"
|
|
14
15
|
import {
|
|
15
16
|
connectionsShareElectricalNet,
|
|
@@ -636,6 +637,10 @@ function buildPlan(params: {
|
|
|
636
637
|
})
|
|
637
638
|
}
|
|
638
639
|
|
|
640
|
+
const outputIds = createFanoutOutputIds({
|
|
641
|
+
connectionName: preparedConnection.connection.name,
|
|
642
|
+
sourcePointIndex: preparedConnection.sourcePointIndex,
|
|
643
|
+
})
|
|
639
644
|
return {
|
|
640
645
|
busId: bus.busId,
|
|
641
646
|
connectionName: preparedConnection.connection.name,
|
|
@@ -651,16 +656,16 @@ function buildPlan(params: {
|
|
|
651
656
|
exitPoint,
|
|
652
657
|
trace: {
|
|
653
658
|
type: "pcb_trace",
|
|
654
|
-
pcb_trace_id:
|
|
659
|
+
pcb_trace_id: outputIds.traceId,
|
|
655
660
|
connection_name: preparedConnection.connection.name,
|
|
656
661
|
connectsTo: [
|
|
657
|
-
preparedConnection.connection.name,
|
|
658
662
|
...(preparedConnection.sourcePoint.pointId
|
|
659
663
|
? [preparedConnection.sourcePoint.pointId]
|
|
660
664
|
: []),
|
|
661
665
|
...(preparedConnection.sourcePoint.pcb_port_id
|
|
662
666
|
? [preparedConnection.sourcePoint.pcb_port_id]
|
|
663
667
|
: []),
|
|
668
|
+
outputIds.boundaryExitPointId,
|
|
664
669
|
],
|
|
665
670
|
route,
|
|
666
671
|
},
|
|
@@ -804,18 +809,22 @@ function addPlaneEndpointTerminal(params: {
|
|
|
804
809
|
toLayer: targetEndpointLayer,
|
|
805
810
|
spanLayers,
|
|
806
811
|
}
|
|
812
|
+
const outputIds = createFanoutOutputIds({
|
|
813
|
+
connectionName: preparedConnection.connection.name,
|
|
814
|
+
sourcePointIndex: preparedConnection.sourcePointIndex,
|
|
815
|
+
})
|
|
807
816
|
const planeEndpointTrace: SimplifiedPcbTrace = {
|
|
808
817
|
type: "pcb_trace",
|
|
809
|
-
pcb_trace_id:
|
|
818
|
+
pcb_trace_id: outputIds.planeEndpointTraceId,
|
|
810
819
|
connection_name: preparedConnection.connection.name,
|
|
811
820
|
connectsTo: [
|
|
812
|
-
preparedConnection.connection.name,
|
|
813
821
|
...(preparedConnection.targetPoint.pointId
|
|
814
822
|
? [preparedConnection.targetPoint.pointId]
|
|
815
823
|
: []),
|
|
816
824
|
...(preparedConnection.targetPoint.pcb_port_id
|
|
817
825
|
? [preparedConnection.targetPoint.pcb_port_id]
|
|
818
826
|
: []),
|
|
827
|
+
outputIds.planeEndpointPointId,
|
|
819
828
|
],
|
|
820
829
|
route: [
|
|
821
830
|
{
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
distanceSegmentToObstacle,
|
|
10
10
|
distanceSegmentToSegment,
|
|
11
11
|
} from "./geometry"
|
|
12
|
+
import { createFanoutOutputIds } from "./fanout-output-ids"
|
|
12
13
|
import { type AvailableBoundaryRegion, getRegionAnchor } from "./prepare-buses"
|
|
13
14
|
import type {
|
|
14
15
|
FanoutDirection,
|
|
@@ -815,6 +816,10 @@ function buildPlan(route: FlowRoute, traceWidth: number): FanoutRoutePlan {
|
|
|
815
816
|
: {}),
|
|
816
817
|
}),
|
|
817
818
|
)
|
|
819
|
+
const outputIds = createFanoutOutputIds({
|
|
820
|
+
connectionName: item.connection.connection.name,
|
|
821
|
+
sourcePointIndex: item.connection.sourcePointIndex,
|
|
822
|
+
})
|
|
818
823
|
return {
|
|
819
824
|
busId: item.bus.busId,
|
|
820
825
|
connectionName: item.connection.connection.name,
|
|
@@ -830,17 +835,16 @@ function buildPlan(route: FlowRoute, traceWidth: number): FanoutRoutePlan {
|
|
|
830
835
|
exitPoint: points.at(-1)!,
|
|
831
836
|
trace: {
|
|
832
837
|
type: "pcb_trace",
|
|
833
|
-
pcb_trace_id:
|
|
838
|
+
pcb_trace_id: outputIds.traceId,
|
|
834
839
|
connection_name: item.connection.connection.name,
|
|
835
840
|
connectsTo: [
|
|
836
|
-
item.connection.connection.name,
|
|
837
|
-
item.netKey,
|
|
838
841
|
...(item.connection.sourcePoint.pointId
|
|
839
842
|
? [item.connection.sourcePoint.pointId]
|
|
840
843
|
: []),
|
|
841
844
|
...(item.connection.sourcePoint.pcb_port_id
|
|
842
845
|
? [item.connection.sourcePoint.pcb_port_id]
|
|
843
846
|
: []),
|
|
847
|
+
outputIds.boundaryExitPointId,
|
|
844
848
|
],
|
|
845
849
|
route: traceRoute,
|
|
846
850
|
},
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
distanceSegmentToObstacle,
|
|
9
9
|
distanceSegmentToSegment,
|
|
10
10
|
} from "./geometry"
|
|
11
|
+
import { createFanoutOutputIds } from "./fanout-output-ids"
|
|
11
12
|
import type {
|
|
12
13
|
FanoutBorderDistribution,
|
|
13
14
|
FanoutCorner,
|
|
@@ -751,6 +752,10 @@ function buildPlan(path: RoutedPath): FanoutRoutePlan {
|
|
|
751
752
|
? { start_pcb_port_id: item.connection.sourcePoint.pcb_port_id }
|
|
752
753
|
: {}),
|
|
753
754
|
}))
|
|
755
|
+
const outputIds = createFanoutOutputIds({
|
|
756
|
+
connectionName: item.connection.connection.name,
|
|
757
|
+
sourcePointIndex: item.connection.sourcePointIndex,
|
|
758
|
+
})
|
|
754
759
|
return {
|
|
755
760
|
busId: item.bus.busId,
|
|
756
761
|
connectionName: item.connection.connection.name,
|
|
@@ -766,16 +771,16 @@ function buildPlan(path: RoutedPath): FanoutRoutePlan {
|
|
|
766
771
|
exitPoint: points.at(-1)!,
|
|
767
772
|
trace: {
|
|
768
773
|
type: "pcb_trace",
|
|
769
|
-
pcb_trace_id:
|
|
774
|
+
pcb_trace_id: outputIds.traceId,
|
|
770
775
|
connection_name: item.connection.connection.name,
|
|
771
776
|
connectsTo: [
|
|
772
|
-
item.connection.connection.name,
|
|
773
777
|
...(item.connection.sourcePoint.pointId
|
|
774
778
|
? [item.connection.sourcePoint.pointId]
|
|
775
779
|
: []),
|
|
776
780
|
...(item.connection.sourcePoint.pcb_port_id
|
|
777
781
|
? [item.connection.sourcePoint.pcb_port_id]
|
|
778
782
|
: []),
|
|
783
|
+
outputIds.boundaryExitPointId,
|
|
779
784
|
],
|
|
780
785
|
route,
|
|
781
786
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/fanout-solver",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.27",
|
|
4
4
|
"description": "BGA fanout solver with coordinated bus-layer escapes for SimpleRouteJson",
|
|
5
5
|
"module": "lib/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -42,16 +42,22 @@
|
|
|
42
42
|
"@biomejs/biome": "2.5.5",
|
|
43
43
|
"@tsci/tscircuit.dataset-srj19-bga-passive-overlays": "github:tscircuit/dataset-srj19#c1206f3",
|
|
44
44
|
"@tsci/tscircuit.dataset-srj29-bga-decoupling": "github:tscircuit/dataset-srj29-bga-decoupling#bfdf7bc",
|
|
45
|
+
"@tscircuit/alphabet": "0.0.25",
|
|
46
|
+
"@tscircuit/circuit-json-util": "0.0.97",
|
|
45
47
|
"@tscircuit/footprinter": "0.0.394",
|
|
46
48
|
"@types/bun": "1.3.14",
|
|
47
49
|
"@types/react": "19.2.14",
|
|
48
50
|
"@types/react-dom": "19.2.3",
|
|
49
51
|
"bun-match-svg": "^0.0.16",
|
|
50
52
|
"circuit-json": "0.0.455",
|
|
53
|
+
"circuit-json-to-connectivity-map": "0.0.23",
|
|
54
|
+
"circuit-to-svg": "0.0.364",
|
|
55
|
+
"format-si-unit": "0.0.7",
|
|
51
56
|
"react": "19.2.8",
|
|
52
57
|
"react-cosmos": "7.3.0",
|
|
53
58
|
"react-cosmos-plugin-vite": "7.3.0",
|
|
54
59
|
"react-dom": "19.2.8",
|
|
60
|
+
"schematic-symbols": "0.0.231",
|
|
55
61
|
"vite": "8.1.5"
|
|
56
62
|
},
|
|
57
63
|
"peerDependencies": {
|