seveibar/f1c100s-dev-board

This code defines the hardware layout and schematic for a four-layer F1C100S-based module, including footprint placement, pin mappings, power regulation, decoupling capacitors, support components, and routing configurations, enabling automated PCB generation, routing, and verification.

Version
1.3.0
License
unset
Stars
0

modules/f1c100s/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;
}