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);
}