astra/f1c100s
The code defines the physical pin layout, labels, and footprint for the F1C100S system-on-chip (SoC) component used in electronic devices.
- Version
- 0.9.2
- License
- unset
- Stars
- 0
scripts/generate.ts
import { checkCapacitorOrientation } from "./check-capacitor-orientation";
import { getSimpleRouteJsonFromCircuitJson } from "tscircuit";
import { createHash } from "node:crypto";
import { mkdir } from "node:fs/promises";
import {
LAYOUT_PROFILES,
assertLayoutProfile,
RULES,
MODULE_SIZE,
type LayoutProfile,
} from "../src/profiles";
import { routeGrid } from "./grid-router";
import { renderProfile } from "./render";
import { validateCircuit, circuitMetrics } from "./validate";
import { convertCircuitJsonToPcbSvg } from "circuit-to-svg";
const chosen = process.argv[2];
if (chosen) assertLayoutProfile(chosen);
const profiles: LayoutProfile[] = chosen
? [chosen as LayoutProfile]
: [...LAYOUT_PROFILES];
const workDir = process.env.F1C100S_WORK_DIR ?? ".cache/f1c100s";
await mkdir("src/generated", { recursive: true });
await mkdir("previews", { recursive: true });
await mkdir(workDir, { recursive: true });
for (const profile of profiles) {
console.log(`GENERATING ${profile}`);
const { json: placed } = await renderProfile(profile);
const placementErrors = placed.filter(
(e: any) =>
e.type === "pcb_footprint_overlap_error" ||
e.type === "pcb_placement_error",
);
if (placementErrors.length) throw new Error(JSON.stringify(placementErrors));
const { simpleRouteJson: input } = getSimpleRouteJsonFromCircuitJson({
circuitJson: placed,
minTraceWidth: 0.12,
nominalTraceWidth: 0.12,
minTraceToPadEdgeClearance: 0.1,
minViaPadDiameter: 0.45,
minViaHoleDiameter: 0.2,
});
const hash = createHash("sha256").update(JSON.stringify(input)).digest("hex");
let first: string[] = [],
traces;
for (let attempt = 0; attempt < 20; attempt++) {
try {
traces = routeGrid(input, {
first,
onProgress: (s) => {
if (s.startsWith("1/") || s.startsWith("78/")) console.log(s);
},
});
break;
} catch (e) {
console.log(`Attempt ${attempt + 1}: ${(e as Error).message}`);
const connection = (e as any).connection as string | undefined;
if (!connection || attempt === 19) throw e;
first = [connection, ...first.filter((c) => c !== connection)];
}
}
if (!traces) throw new Error("No solved routes");
const { json } = await renderProfile(profile, traces);
const errors = [
...(await validateCircuit(json)),
...checkCapacitorOrientation(json),
];
await Bun.write(
`${workDir}/${profile}.drc.json`,
JSON.stringify(errors, null, 2),
);
if (errors.length)
throw new Error(`${profile}: ${errors.length} DRC errors; see ${workDir}`);
const metrics = circuitMetrics(json);
// Board elements are not part of the reusable child subcircuit.
const boardGroup = (json.find((e: any) => e.type === "source_board") as any)
?.source_group_id;
const stored = json
.filter(
(e: any) =>
!e.type.endsWith("_error") &&
!e.type.endsWith("_warning") &&
e.type !== "pcb_board" &&
e.type !== "source_board" &&
!(e.type === "source_group" && e.source_group_id === boardGroup),
)
.map((e: any) => {
const copy = { ...e };
if (
copy.type === "source_group" &&
copy.parent_source_group_id === boardGroup
)
delete copy.parent_source_group_id;
return copy;
});
await Bun.write(
`src/generated/${profile}.circuit.json`,
JSON.stringify(stored),
);
await Bun.write(
`src/generated/${profile}.metrics.json`,
JSON.stringify(
{
profile,
inputHash: hash,
moduleSizeMm: MODULE_SIZE,
rules: RULES,
...metrics,
drcErrors: 0,
},
null,
2,
) + "\n",
);
await Bun.write(
`previews/${profile}.svg`,
convertCircuitJsonToPcbSvg(json, { width: 1100, height: 1100 }),
);
console.log(`VALIDATED ${profile}`, metrics);
}