seveibar/f1c1990s-dev-board

This code manages the entire PCB assembly process for a computer hardware module, including component placement, schematic integration, automated routing, copper pouring, and design rule checks, focused on a four-layer carrier board with integrated support components and connectors.

Version
1.8.0
License
unset
Stars
0

modules/f1c100s/scripts/check-conventions.ts

import type { CircuitJson } from "circuit-json";

/** Inspect emitted copper, rather than relying on router settings. */
export function checkConventionalRouting(json: CircuitJson, singleSided = false): string[] {
	const errors: string[] = [];
	for (const t of json as any[])
		if (t.type === "pcb_trace") {
			for (let i = 1; i < t.route.length; i++) {
				const a = t.route[i - 1],
					b = t.route[i];
				// Include wire-to-via approaches as well as wire-to-wire runs.
				// A layer change at one via has zero XY length.
				const dx = Math.abs(b.x - a.x),
					dy = Math.abs(b.y - a.y);
				if (Math.min(dx, dy, Math.abs(dx - dy)) > 1e-6)
					errors.push(`${t.pcb_trace_id}[${i}]: non-45-degree segment`);
			}
		}
	const components = (json as any[]).filter(
		(e) => e.type === "source_component",
	);
	const pcbComponents = (json as any[]).filter(
		(e) => e.type === "pcb_component",
	);
	for (const e of json as any[]) {
		if (e.type !== "pcb_component" && e.type !== "pcb_smtpad") continue;
		const pcb =
			e.type === "pcb_component"
				? e
				: pcbComponents.find((p) => p.pcb_component_id === e.pcb_component_id);
		const c = components.find(
			(c) => c.source_component_id === pcb?.source_component_id,
		);
		const underside = !singleSided && (
			c?.name.startsWith("C_B_") ||
			["C_TV_VRN", "C_TV_VRP", "C_TV_REF"].includes(c?.name));
		if (e.layer !== (underside ? "bottom" : "top"))
			errors.push(`${c?.name}: component copper is on the wrong side`);
	}

	const nets = (json as any[]).filter(
		(e) => e.type === "source_trace" && /^N_HOSC[IO]$/.test(e.name),
	);
	for (const net of nets) {
		const routes = (json as any[]).filter(
			(e) =>
				e.type === "pcb_trace" && e.source_trace_id === net.source_trace_id,
		);
		if (
			routes.some((t) =>
				t.route.some((p: any) => p.route_type !== "wire" || p.layer !== "top"),
			)
		)
			errors.push(
				`${net.name}: crystal signal must remain on top without vias`,
			);
		for (const t of routes) {
			const length = t.route.reduce(
				(sum: number, b: any, i: number) =>
					i
						? sum + Math.hypot(b.x - t.route[i - 1].x, b.y - t.route[i - 1].y)
						: sum,
				0,
			);
			if (length > 10) errors.push(`${net.name}: crystal route exceeds 10 mm`);
		}
	}
	return errors;
}