astra/f1c100s

This code defines and renders a four-layer, top-mounted F1C100S module with detailed pin assignments, schematic annotations, and layout profiles, facilitating design, validation, and routing of the hardware including support for decoupling capacitors, crystal oscillators, and external connectors.

Version
0.11.0
License
unset
Stars
0

scripts/check-decoupling-targets.ts

/** Require a physical pad-to-pad branch for every dedicated supply decoupler.
 * Same-net reachability alone is insufficient for this check. */
export function checkDecouplingTargets(json: any[]): string[] {
	const errors: string[] = [];
	const components = json.filter((e) => e.type === "source_component");
	const port = (name: string, pin: number) => {
		const c = components.find((c) => c.name === name);
		const sp = json.find(
			(e) =>
				e.type === "source_port" &&
				e.source_component_id === c?.source_component_id &&
				e.pin_number === pin,
		);
		return json.find(
			(e) => e.type === "pcb_port" && e.source_port_id === sp?.source_port_id,
		);
	};
	for (const cap of components.filter((c) => /^C_D\d+$/.test(c.name))) {
		const a = port(cap.name, 1),
			b = port("U1", Number(cap.name.slice(3)));
		const at = (p: any, q: any) =>
			p &&
			q &&
			p.route_type === "wire" &&
			Math.hypot(p.x - q.x, p.y - q.y) < 1e-4 &&
			q.layers.includes(p.layer);
		if (
			!json.some(
				(t) =>
					t.type === "pcb_trace" &&
					((at(t.route[0], a) && at(t.route.at(-1), b)) ||
						(at(t.route[0], b) && at(t.route.at(-1), a))),
			)
		)
			errors.push(
				`${cap.name}.pin1 needs a direct copper branch to U1.pin${cap.name.slice(3)}`,
			);
	}
	return errors;
}

/** Project layout limits, not processor datasheet limits. Run after optimization. */
export function checkDecouplingPlacement(json: any[]): string[] {
	const errors = checkDecouplingTargets(json);
	const components = json.filter((e) => e.type === "source_component");
	const port = (n: string, pin: number) => {
		const c = components.find((c) => c.name === n);
		const p = json.find(
			(e) =>
				e.type === "source_port" &&
				e.source_component_id === c.source_component_id &&
				e.pin_number === pin,
		);
		return json.find(
			(e) => e.type === "pcb_port" && e.source_port_id === p.source_port_id,
		);
	};
	const distance = (a: any, b: any) => Math.hypot(a.x - b.x, a.y - b.y);
	const groundTraceIds = new Set(
		json
			.filter((e) => e.type === "source_trace" && e.name === "N_GND")
			.map((e) => e.source_trace_id),
	);
	const groundVias = json
		.filter(
			(e) => e.type === "pcb_trace" && groundTraceIds.has(e.source_trace_id),
		)
		.flatMap((t) => t.route.filter((p: any) => p.route_type === "via"));
	for (const c of components.filter((c) => /^C_D\d+$/.test(c.name))) {
		const a = port(c.name, 1),
			b = port("U1", Number(c.name.slice(3))),
			g = port(c.name, 2);
		const t = json.find(
			(t) =>
				t.type === "pcb_trace" &&
				((distance(t.route[0], a) < 1e-4 &&
					distance(t.route.at(-1), b) < 1e-4) ||
					(distance(t.route[0], b) < 1e-4 &&
						distance(t.route.at(-1), a) < 1e-4)),
		);
		if (!t) continue;
		const length = t.route.reduce(
			(n: number, p: any, i: number) =>
				n + (i ? distance(p, t.route[i - 1]) : 0),
			0,
		);
		if (distance(a, b) > 3.4)
			errors.push(`${c.name}: supply pad is over 3.4 mm from assigned pin`);
		if (length > 5)
			errors.push(
				`${c.name}: supply branch exceeds 5 mm (${length.toFixed(2)})`,
			);
		if (t.route.some((p: any) => p.route_type !== "wire" || p.layer !== "top"))
			errors.push(`${c.name}: supply branch must stay on top without vias`);
		if (!groundVias.some((v) => distance(v, g) <= 1.3))
			errors.push(`${c.name}: needs nearby ground via within 1.3 mm`);
	}
	return errors;
}