@tscircuit/fanout-solver 0.0.65 → 0.0.67
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 +22 -15
- package/lib/fanout-solver.ts +97 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -282,8 +282,8 @@ extents.
|
|
|
282
282
|
|
|
283
283
|
## Dataset 31 benchmark
|
|
284
284
|
|
|
285
|
-
Run `./benchmark.sh` (or `bun run benchmark`) to benchmark **all
|
|
286
|
-
directional cases: 12 AM62L and 12
|
|
285
|
+
Run `./benchmark.sh` (or `bun run benchmark`) to benchmark **all 36 dataset 31
|
|
286
|
+
directional cases: 12 AM62L, 12 RK3308, and 12 K230** from
|
|
287
287
|
[`tscircuit/dataset-fanout31-am62l`](https://github.com/tscircuit/dataset-fanout31-am62l).
|
|
288
288
|
The upstream revision is pinned in `scripts/generate-repro/package.json` and
|
|
289
289
|
recorded in every report. Other datasets remain available for regression tests
|
|
@@ -294,6 +294,7 @@ and the debugger, but have no benchmark commands or workflows.
|
|
|
294
294
|
./benchmark.sh --list
|
|
295
295
|
./benchmark.sh --sample 11-left-center
|
|
296
296
|
./benchmark.sh --sample 13-rk3308-top-left-offset
|
|
297
|
+
./benchmark.sh --sample 25-k230-top-left-offset
|
|
297
298
|
./benchmark.sh --concurrency 8 --sample-timeout-seconds 300
|
|
298
299
|
```
|
|
299
300
|
|
|
@@ -307,11 +308,16 @@ constraints:
|
|
|
307
308
|
| --- | ---: | ---: | ---: | ---: | ---: |
|
|
308
309
|
| AM62L | 12 | 33 | 102 | 135 | 573 |
|
|
309
310
|
| RK3308 | 12 | 49 | 113 | 162 | 451 |
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
and
|
|
313
|
-
The
|
|
314
|
-
|
|
311
|
+
| K230 | 12 | 65 | 106 | 171 | 790 |
|
|
312
|
+
|
|
313
|
+
AM62L and RK3308 have nine DDR signal buses each. The RK3308 samples use a
|
|
314
|
+
355-ball SoC and 96-ball DDR3L RAM. The K230 samples use a 390-ball SoC and two
|
|
315
|
+
200-ball x16 LPDDR4 RAM packages, with 17 signal buses and six differential pairs.
|
|
316
|
+
Each family places its RAM on all four sides at three offsets per side. K230
|
|
317
|
+
retains both physical RAM endpoints of its shared RESET net; the two RAM packages
|
|
318
|
+
have separate breakout regions. All 790 pads in each K230 case remain obstacles,
|
|
319
|
+
including unused signal and supply pads. The timed workers run **this checkout's
|
|
320
|
+
solver**, not the upstream package's released solver.
|
|
315
321
|
To capture the inputs without solving, use `bun run generate:dataset31`.
|
|
316
322
|
The optional `--dataset dataset31` flag is accepted for explicit CI invocation;
|
|
317
323
|
other dataset selections are rejected.
|
|
@@ -343,20 +349,21 @@ Timed-out workers do not retain their in-flight routing counts.
|
|
|
343
349
|
Every solved case also writes `benchmark-results/<sample-id>.svg`. These SVGs
|
|
344
350
|
are committed so route changes can be reviewed in Git. A run replaces the selected
|
|
345
351
|
cases' snapshots and removes their stale SVGs if they no longer solve; filtered
|
|
346
|
-
runs preserve unselected snapshots. JSON
|
|
347
|
-
ignored. CI includes the
|
|
352
|
+
runs preserve unselected snapshots. The latest JSON and Markdown reports are
|
|
353
|
+
also committed; captured inputs remain ignored. CI includes the reports, inputs,
|
|
354
|
+
and SVGs in its benchmark artifacts.
|
|
348
355
|
Compare reports with the same dataset revision and budgets to track progress.
|
|
349
356
|
A case is solved only when every SoC connection has validated fanout: all 135
|
|
350
|
-
connections for AM62L or all
|
|
351
|
-
fanout and downstream inter-chip routing are separate phases.
|
|
352
|
-
and timeout rows are benchmark results (exit 0); invalid CLI arguments or report
|
|
357
|
+
connections for AM62L, all 162 for RK3308, or all 171 for K230. This covers the SoC
|
|
358
|
+
fanout phase; RAM fanout and downstream inter-chip routing are separate phases.
|
|
359
|
+
Partial, error, and timeout rows are benchmark results (exit 0); invalid CLI arguments or report
|
|
353
360
|
I/O failures are command failures (nonzero exit).
|
|
354
361
|
|
|
355
362
|
### PR comment trigger
|
|
356
363
|
|
|
357
364
|
Once `.github/workflows/benchmark.yml` is on the default branch, a repository
|
|
358
365
|
writer can comment **`/benchmark`** on an open PR. The workflow captures that
|
|
359
|
-
PR's exact head SHA, runs all
|
|
366
|
+
PR's exact head SHA, runs all 36 dataset 31 samples on a **32-vCPU Blacksmith ARM**
|
|
360
367
|
runner, then updates a status comment with solve totals, per-sample results,
|
|
361
368
|
and a link to the complete JSON/Markdown reports and captured inputs. The Actions
|
|
362
369
|
UI also supports a manual run, optionally supplying an open PR number. No custom
|
|
@@ -522,8 +529,8 @@ bun run render:dataset
|
|
|
522
529
|
bun run start
|
|
523
530
|
```
|
|
524
531
|
|
|
525
|
-
The benchmark runs all
|
|
526
|
-
counts, validation, and timing. `bun run start` opens all regression
|
|
532
|
+
The benchmark runs all 36 dataset 31 AM62L, RK3308, and K230 cases and reports
|
|
533
|
+
solve counts, validation, and timing. `bun run start` opens all regression
|
|
527
534
|
datasets in the standard tscircuit solver debugger. `bun run
|
|
528
535
|
render:dataset` writes `graphics-debug` PNGs under one subdirectory per dataset,
|
|
529
536
|
with a red shared boundary, gray component courtyards, and green fanout-exit
|
package/lib/fanout-solver.ts
CHANGED
|
@@ -1811,6 +1811,49 @@ export class FanoutSolver extends BaseSolver {
|
|
|
1811
1811
|
const wideBoundaryBuses = unsortedBoundaryBuses.filter(
|
|
1812
1812
|
(bus) => bus.connections.length >= 8,
|
|
1813
1813
|
)
|
|
1814
|
+
const hasForeignAllLayerObstaclesInComponent = wideBoundaryBuses.some(
|
|
1815
|
+
(bus) =>
|
|
1816
|
+
this.routingSrj.obstacles.some(
|
|
1817
|
+
(obstacle) =>
|
|
1818
|
+
obstacle.componentId !== bus.componentId &&
|
|
1819
|
+
this.config.layerNames.every((layer) =>
|
|
1820
|
+
obstacle.layers.includes(layer),
|
|
1821
|
+
) &&
|
|
1822
|
+
obstacle.center.x >= bus.componentBounds.minX &&
|
|
1823
|
+
obstacle.center.x <= bus.componentBounds.maxX &&
|
|
1824
|
+
obstacle.center.y >= bus.componentBounds.minY &&
|
|
1825
|
+
obstacle.center.y <= bus.componentBounds.maxY,
|
|
1826
|
+
),
|
|
1827
|
+
)
|
|
1828
|
+
const dogboneCandidateCountByConnectionIndex = new Map<number, number>()
|
|
1829
|
+
if (hasForeignAllLayerObstaclesInComponent) {
|
|
1830
|
+
for (const candidate of getComponentDogboneViaSiteCandidates(
|
|
1831
|
+
unsortedBoundaryBuses,
|
|
1832
|
+
{
|
|
1833
|
+
viaDiameter: this.config.viaDiameter,
|
|
1834
|
+
viaHoleDiameter: this.config.viaHoleDiameter,
|
|
1835
|
+
traceWidth: this.config.traceWidth,
|
|
1836
|
+
clearance: this.config.clearance,
|
|
1837
|
+
additionalObstacles: this.routingSrj.obstacles,
|
|
1838
|
+
},
|
|
1839
|
+
)) {
|
|
1840
|
+
dogboneCandidateCountByConnectionIndex.set(
|
|
1841
|
+
candidate.connectionIndex,
|
|
1842
|
+
(dogboneCandidateCountByConnectionIndex.get(
|
|
1843
|
+
candidate.connectionIndex,
|
|
1844
|
+
) ?? 0) + 1,
|
|
1845
|
+
)
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
const getBusDogboneCandidateCount = (bus: PreparedBus): number =>
|
|
1849
|
+
bus.connections.reduce(
|
|
1850
|
+
(total, connection) =>
|
|
1851
|
+
total +
|
|
1852
|
+
(dogboneCandidateCountByConnectionIndex.get(
|
|
1853
|
+
connection.connectionIndex,
|
|
1854
|
+
) ?? 0),
|
|
1855
|
+
0,
|
|
1856
|
+
)
|
|
1814
1857
|
// A single-layer turning bus beside the end of a centered source field
|
|
1815
1858
|
// has fewer escape choices than a corner bus farther behind it. Reserve
|
|
1816
1859
|
// that turning channel before the farther bus fences its local via sites.
|
|
@@ -1953,6 +1996,18 @@ export class FanoutSolver extends BaseSolver {
|
|
|
1953
1996
|
? first.connections.length - second.connections.length
|
|
1954
1997
|
: second.connections.length - first.connections.length
|
|
1955
1998
|
if (connectionCountDifference !== 0) return connectionCountDifference
|
|
1999
|
+
if (
|
|
2000
|
+
hasForeignAllLayerObstaclesInComponent &&
|
|
2001
|
+
first.connections.length >= 8 &&
|
|
2002
|
+
second.connections.length >= 8
|
|
2003
|
+
) {
|
|
2004
|
+
const dogboneCandidateCountDifference =
|
|
2005
|
+
getBusDogboneCandidateCount(first) -
|
|
2006
|
+
getBusDogboneCandidateCount(second)
|
|
2007
|
+
if (dogboneCandidateCountDifference !== 0) {
|
|
2008
|
+
return dogboneCandidateCountDifference
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
1956
2011
|
}
|
|
1957
2012
|
const firstLayer = params.busLayerAssignments[first.busId]
|
|
1958
2013
|
const secondLayer = params.busLayerAssignments[second.busId]
|
|
@@ -2571,11 +2626,14 @@ export class FanoutSolver extends BaseSolver {
|
|
|
2571
2626
|
yield
|
|
2572
2627
|
if (seedViaPoints) {
|
|
2573
2628
|
const denseBoundaryBusesInRoutingOrder = [
|
|
2574
|
-
...
|
|
2629
|
+
...(hasForeignAllLayerObstaclesInComponent
|
|
2630
|
+
? []
|
|
2631
|
+
: multiLayerLeadingSingletonBuses),
|
|
2575
2632
|
...boundaryBuses
|
|
2576
2633
|
.filter(
|
|
2577
2634
|
(bus) =>
|
|
2578
|
-
|
|
2635
|
+
(hasForeignAllLayerObstaclesInComponent ||
|
|
2636
|
+
!multiLayerLeadingSingletonBuses.includes(bus)) &&
|
|
2579
2637
|
!throughAllLeadingBuses.includes(bus),
|
|
2580
2638
|
)
|
|
2581
2639
|
.flatMap((bus) => [
|
|
@@ -4643,7 +4701,7 @@ export class FanoutSolver extends BaseSolver {
|
|
|
4643
4701
|
debugDense("plane-route:start", bus.busId)
|
|
4644
4702
|
const targetLayer = params.busLayerAssignments[bus.busId]
|
|
4645
4703
|
const blockingBusCounts = new Map<string, number>()
|
|
4646
|
-
|
|
4704
|
+
let busPlans = targetLayer
|
|
4647
4705
|
? routeBus({
|
|
4648
4706
|
srj: this.routingSrj,
|
|
4649
4707
|
bus,
|
|
@@ -4662,6 +4720,42 @@ export class FanoutSolver extends BaseSolver {
|
|
|
4662
4720
|
fixedViaPointsByConnectionIndex,
|
|
4663
4721
|
})
|
|
4664
4722
|
: null
|
|
4723
|
+
if (
|
|
4724
|
+
!busPlans &&
|
|
4725
|
+
targetLayer &&
|
|
4726
|
+
hasForeignAllLayerObstaclesInComponent
|
|
4727
|
+
) {
|
|
4728
|
+
// Plane dogbones are matched before their full routes exist. A
|
|
4729
|
+
// preceding plane escape can therefore consume copper clearance
|
|
4730
|
+
// that the site matcher could not account for. Re-select only the
|
|
4731
|
+
// failed dogbone against the routes that are actually committed.
|
|
4732
|
+
busPlans = routeBus({
|
|
4733
|
+
srj: this.routingSrj,
|
|
4734
|
+
bus,
|
|
4735
|
+
targetLayer,
|
|
4736
|
+
acceptedPlans: matchedPlans,
|
|
4737
|
+
layerNames: this.config.layerNames,
|
|
4738
|
+
traceWidth: this.config.traceWidth,
|
|
4739
|
+
viaDiameter: this.config.viaDiameter,
|
|
4740
|
+
viaHoleDiameter: this.config.viaHoleDiameter,
|
|
4741
|
+
clearance: this.config.clearance,
|
|
4742
|
+
compactBusTracks: this.config.compactBusTracks,
|
|
4743
|
+
allowBlindAndBuriedVias: false,
|
|
4744
|
+
allowSameNetMerges: this.config.allowSameNetMerges,
|
|
4745
|
+
staticClearanceCache: this.routeStaticClearanceCache,
|
|
4746
|
+
blockingBusCounts,
|
|
4747
|
+
})
|
|
4748
|
+
if (busPlans) {
|
|
4749
|
+
fixedViaPointsByConnectionIndex = new Map([
|
|
4750
|
+
...fixedViaPointsByConnectionIndex,
|
|
4751
|
+
...busPlans.flatMap((plan) =>
|
|
4752
|
+
plan.via
|
|
4753
|
+
? [[plan.connectionIndex, plan.via.center] as const]
|
|
4754
|
+
: [],
|
|
4755
|
+
),
|
|
4756
|
+
])
|
|
4757
|
+
}
|
|
4758
|
+
}
|
|
4665
4759
|
if (!busPlans) {
|
|
4666
4760
|
debugDense(
|
|
4667
4761
|
"plane-route:failed",
|