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
scripts/audit-checkpoint.ts
import { readFile, writeFile } from "node:fs/promises";
import { createHash } from "node:crypto";
import type { FanoutTracePath } from "@tscircuit/props";
const raw = await readFile("design/ddr-routes.json", "utf8");
const paths: FanoutTracePath[] = JSON.parse(raw);
// Normalize only zero-length duplicate wire points and renderer-added port IDs.
// Coordinate, layer, width and via geometry must remain identical.
function normalize(route: any[]) {
const out: string[] = [];
for (const point of route) {
const p = { ...point };
// The renderer moves via layer spans to pcb_via; validate them below.
if(p.route_type==="via")delete p.layers;
delete p.start_pcb_port_id;
delete p.end_pcb_port_id;
const key = JSON.stringify(
Object.fromEntries(
Object.entries(p).sort(([a], [b]) => a.localeCompare(b)),
),
);
if (p.route_type === "wire" && out.at(-1) === key) continue;
out.push(key);
}
return JSON.stringify(out);
}
const json: any[] = JSON.parse(
await readFile(process.argv.find(a=>a.endsWith(".circuit.json")) ?? "output/board-ddr.circuit.json", "utf8"),
);
const actual = json
.filter((e) => e.type === "pcb_trace")
.map((e) => normalize(e.route));
const missing = paths.filter((p) => !actual.includes(normalize(p.route)));
const unexpectedErrors = json.filter(
(e) =>
e.type.endsWith("_error") &&
!(e.type==="pcb_trace_error" && e.message.includes("has dangling endpoint")) &&
!["pcb_port_not_connected_error", "pcb_trace_missing_error", "pcb_bus_length_skew_error"].includes(
e.type,
),
);
const report = {
stage:
"DDR + power/ground escape checkpoint; cached peripheral routes may also be present",
routeFileSha256: createHash("sha256").update(raw).digest("hex"),
expectedPaths: 47,
savedPaths: paths.length,
integratedPaths: actual.length,
preservedDdrPaths: paths.length - missing.length,
layerCount: json.find((e) => e.type === "pcb_board")?.num_layers,
preservedExactly: missing.length === 0 && paths.length === 47,
ddrViasSpanAllLayers: paths.flatMap(p=>p.route.filter(p=>p.route_type==="via")).every(v=>json.some(e=>e.type==="pcb_via" && Math.hypot(e.x-Number(v.x),e.y-Number(v.y))<1e-6 && ["top","inner1","inner2","bottom"].every(l=>e.layers.includes(l)))),
timingErrors:json.filter(e=>e.type==="pcb_bus_length_skew_error").length,
timingSignoff:false,
unexpectedNativeErrors: unexpectedErrors.length,
allowedIncompleteErrorCounts: Object.fromEntries(
["pcb_port_not_connected_error", "pcb_trace_missing_error"].map((type) => [
type,
json.filter((e) => e.type === type).length,
]),
),
fabricationReady: false,
};
await writeFile(
"output/checkpoint-audit.json",
JSON.stringify(report, null, 2),
);
console.log(report);
if (!report.preservedExactly || !report.ddrViasSpanAllLayers || unexpectedErrors.length) process.exitCode = 1;