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/validate.ts

import type { AnyCircuitElement } from "circuit-json";
import {
	runAllRoutingChecks,
	runAllPlacementChecks,
	checkViasInPads,
	checkEachPcbTraceNonOverlapping,
} from "@tscircuit/checks";
export async function validateCircuit(json: AnyCircuitElement[]) {
	const errors = [
		...(await runAllRoutingChecks(json)),
		...(await runAllPlacementChecks(json)),
		...checkViasInPads(json),
		...checkEachPcbTraceNonOverlapping(json, { minClearance: 0.1 }),
	].filter(
		(e: any) => e.type.endsWith("_error") || e.error_type?.endsWith("_error"),
	);
	const unique = [...new Map(errors.map((e: any) => [e.message, e])).values()];
	return unique;
}
export function circuitMetrics(json: AnyCircuitElement[]) {
	const traces = json.filter((e: any) => e.type === "pcb_trace") as any[];
	const length = (route: any[]) =>
		route.reduce((sum, p, i) => {
			const a = route[i - 1];
			return (
				sum +
				(a?.route_type === "wire" &&
				p.route_type === "wire" &&
				a.layer === p.layer
					? Math.hypot(a.x - p.x, a.y - p.y)
					: 0)
			);
		}, 0);
	const netTrace = (name: string) => {
		const source = json.find(
			(e: any) => e.type === "source_trace" && e.name === `N_${name}`,
		) as any;
		return traces.filter((t) => t.source_trace_id === source?.source_trace_id);
	};
	const usb = { dm: netTrace("USB_DM"), dp: netTrace("USB_DP") };
	const usbLength = (key: "dm" | "dp") =>
		usb[key].reduce((n, t) => n + length(t.route), 0);
	if (
		usb.dm.length !== 1 ||
		usb.dp.length !== 1 ||
		[...usb.dm, ...usb.dp].some((t) =>
			t.route.some((p: any) => p.route_type !== "wire" || p.layer !== "top"),
		)
	)
		throw new Error("USB pair must use two uninterrupted top-layer traces");
	if (Math.abs(usbLength("dm") - usbLength("dp")) > 0.001)
		throw new Error("USB pair exceeds 1um local skew budget");
	const plated = json.filter((e) => e.type === "pcb_plated_hole") as any[];
	const nonTopExits = plated.filter((h) =>
		traces.some((t) =>
			[t.route[0], t.route.at(-1)].some(
				(p) => p.layer !== "top" && Math.hypot(p.x - h.x, p.y - h.y) < 1e-5,
			),
		),
	);
	return {
		platedTestPoints: plated.length,
		nonTopExitContacts: nonTopExits.length,
		traces: traces.length,
		wireSegments: traces.reduce(
			(sum, t) =>
				sum +
				t.route.filter((p: any, i: number, route: any[]) => {
					const a = route[i - 1];
					return (
						a?.route_type === "wire" &&
						p.route_type === "wire" &&
						a.layer === p.layer &&
						Math.hypot(a.x - p.x, a.y - p.y) > 1e-6
					);
				}).length,
			0,
		),
		vias: json.filter((e) => e.type === "pcb_via").length,
		capacitors: json.filter(
			(e: any) =>
				e.type === "source_component" && e.ftype === "simple_capacitor",
		).length,
		localDecouplers: json.filter(
			(e: any) => e.type === "source_component" && /^C_D\d+$/.test(e.name),
		).length,
		routedLengthMm: +traces
			.reduce((sum, t) => sum + length(t.route), 0)
			.toFixed(3),
		usbDmLengthMm: +usbLength("dm").toFixed(6),
		usbDpLengthMm: +usbLength("dp").toFixed(6),
		usbSkewMm: +Math.abs(usbLength("dm") - usbLength("dp")).toFixed(6),
	};
}