astra/am3352-sbc
A 4-layer AM3352 single-board computer with 128‑MiB DDR3, PMIC power regulation, USB‑C 5‑V PD input, dual USB host ports, HDMI output with ESD protection, microSD/UART/JTAG interfaces, addressable RGB indicators, and a PWM-driven buzzer.
- Version
- 0.1.19
- License
- unset
- Stars
- 0
design/bus-lanes.ts
import { BusLanesPipelineSolver, type SimpleRouteJson, exteriorPairSpacingReports } from "@tscircuit/bus-lanes-solver";
import { auditDdrGeometry, signalNamesFromDeclaredBuses } from "./ddr-compliance";
import {compactRoutingInput} from './compact-routing-input';
/** Run the published 0.0.19 solver pinned by package-lock.json.
* This custom algorithm does not use core's older bundled solver.
* Pours are regenerated with antipads by tscircuit. Treating the entire GND
* plane as a keepout would prohibit every through-via before routing starts.
* All physical pads, holes, fixed tracks and vias remain routing obstacles.
*/
export async function ddrBusLanes(input: SimpleRouteJson) {
const prepared = compactRoutingInput(structuredClone(input));
prepared.obstacles = prepared.obstacles.filter((o) => !(o as typeof o & {isCopperPour?: boolean}).isCopperPour);
prepared.allowedLayers = ["inner1", "inner2"];
// Single-member buses only carried a layer restriction. The global restriction
// above preserves it and lets unconstrained controls choose either inner layer.
prepared.buses = prepared.buses?.filter((b) => b.connectionNames.length > 1);
const names = signalNamesFromDeclaredBuses(prepared);
const limits = auditDdrGeometry(prepared, [], names).groups;
for (const bus of prepared.buses ?? []) {
const limit = limits.find(group => group.name === (bus.name ?? bus.busId));
if (!limit || limit.maximumMm === undefined || limit.minimumMm === undefined)
throw new Error(`Cannot derive absolute length bounds for ${bus.busId}`);
bus.maxLength = limit.maximumMm;
bus.minLength = limit.minimumMm;
bus.allowedLayers = ["inner1", "inner2"];
}
const solver = new BusLanesPipelineSolver(prepared, {
maxSearchIterations: 2000000,
maxLaneIterations: 1000000,
});
const handlers: Record<string, ((event: any) => void)[]> = {};
const emit = (type: string, event: object) => handlers[type]?.forEach((fn) => fn({type, ...event}));
const checkedTraces = () => {
const audit = auditDdrGeometry(prepared, solver.traces, names);
if (!audit.pass) throw new Error(`DDR timing acceptance failed: ${audit.failures.join("; ")}`);
const coupling = exteriorPairSpacingReports(prepared, solver.traces);
if (coupling.length !== 3 || coupling.some(pair => !pair.applicable || !pair.matched))
throw new Error("DDR exterior pair coupling acceptance failed");
return solver.traces;
};
return {
input, isRouting: false,
on(type: string, fn: (event: any) => void) { (handlers[type] ??= []).push(fn); },
stop() {
if (!this.isRouting) return;
this.isRouting = false;
// Preserve a verified solution when cancellation interrupts envelope
// optimization. Strict board acceptance still gates the complete event.
solver.tryFinalAcceptance();
if (solver.solved) {
try { emit("complete", {traces: checkedTraces()}); }
catch (error) { emit("error", {error: error instanceof Error ? error : new Error(String(error))}); }
}
},
async start() {
this.isRouting = true;
try {
while (this.isRouting && !solver.solved && !solver.failed) {
const until = performance.now() + 30;
do { solver.step(); } while (!solver.solved && !solver.failed && performance.now() < until);
emit("progress", {steps: solver.iterations, progress: solver.progress, phase: solver.phase});
await new Promise((resolve) => setTimeout(resolve, 0));
}
if (!this.isRouting) return;
if (!solver.solved) solver.tryFinalAcceptance();
if (!solver.solved) throw new Error(solver.error ?? "DDR bus_lanes failed");
this.isRouting = false;
emit("complete", {traces: checkedTraces()});
} catch (error) {
this.isRouting = false;
emit("error", {error: error instanceof Error ? error : new Error(String(error))});
}
},
solveSync() { solver.solve(); if (!solver.solved) solver.tryFinalAcceptance(); if (!solver.solved) throw new Error(solver.error ?? "DDR bus_lanes failed"); return checkedTraces(); },
};
}