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/tests/selective-breakouts.test.tsx

import { checkDecouplingPlacement } from "../scripts/check-decoupling-targets";
import { test, expect } from "bun:test";
import { Circuit } from "tscircuit";
import { F1C100SModule, getF1C100SCircuitJson, LAYOUT_PROFILES } from "../src";
import { selectBreakouts } from "../src/selective-breakouts";
import { EXTERNAL_NETS } from "../src/pin-map";

for (const profile of LAYOUT_PROFILES) {
	for (const requested of [
		[],
		["GND", "VCC_IO", "UART0_TX"],
		[...EXTERNAL_NETS],
	]) {
		test(`${profile}: ${requested.length} requested breakouts preserve support connectivity`, async () => {
			const original: any[] = getF1C100SCircuitJson(profile);
			expect(checkDecouplingPlacement(original)).toEqual([]);
			const before = JSON.stringify(original);
			const selected = new Set(requested);
			const result = selectBreakouts(original, selected);
			const components: any[] = result.circuitJson.filter(
				(e) => e.type === "source_component",
			);
			expect(
				components
					.filter((c) => EXTERNAL_NETS.includes(c.name))
					.map((c) => c.name)
					.sort(),
			).toEqual([...selected].sort());
			expect(
				components.filter((c) => !EXTERNAL_NETS.includes(c.name)).length,
			).toBe(
				original.filter(
					(e) =>
						e.type === "source_component" && !EXTERNAL_NETS.includes(e.name),
				).length,
			);
			const originalPorts = new Map(
				original
					.filter((e) => e.type === "source_port")
					.map((e) => [e.source_port_id, e]),
			);
			const originalComponents = new Map(
				original
					.filter((e) => e.type === "source_component")
					.map((e) => [e.source_component_id, e]),
			);
			for (const trace of original.filter((e) => e.type === "source_trace")) {
				const retained = trace.connected_source_port_ids.filter(
					(id: string) => {
						const name = originalComponents.get(
							originalPorts.get(id).source_component_id,
						).name;
						return !EXTERNAL_NETS.includes(name) || selected.has(name);
					},
				);
				const actual: any = result.circuitJson.find(
					(e: any) => e.type === "source_trace" && e.name === trace.name,
				);
				expect(actual?.connected_source_port_ids ?? []).toEqual(
					retained.length > 1 ? retained : [],
				);
			}
			expect(JSON.stringify(original)).toBe(before);
			const c = new Circuit();
			c.add(
				<board
					width={28}
					height={28}
					layers={4}
					minTraceWidth={0.12}
					minViaPadDiameter={0.45}
					minViaHoleDiameter={0.2}
				>
					<F1C100SModule
						name="SOC"
						layoutProfile={profile}
						connections={Object.fromEntries(
							requested.map((n) => [n, `net.${n}`]),
						)}
					/>
				</board>,
			);
			await c.renderUntilSettled();
			const json: any[] = c.getCircuitJson();
			const processor = json.find(
				(e) => e.type === "source_component" && e.name === "U1",
			);
			for (const cap of json.filter(
				(e) => e.type === "source_component" && /^C_D\d+$/.test(e.name),
			)) {
				const cp = json.find(
					(e) =>
						e.type === "source_port" &&
						e.source_component_id === cap.source_component_id &&
						e.pin_number === 1,
				);
				const pp = json.find(
					(e) =>
						e.type === "source_port" &&
						e.source_component_id === processor.source_component_id &&
						e.pin_number === Number(cap.name.slice(3)),
				);
				const traces = json.filter(
					(e) =>
						e.type === "source_trace" &&
						e.connected_source_port_ids.includes(cp.source_port_id),
				);
				expect(traces).toHaveLength(1);
				expect(traces[0].connected_source_port_ids.slice().sort()).toEqual(
					[cp.source_port_id, pp.source_port_id].sort(),
				);
				expect(traces[0].connected_source_net_ids).toEqual([]);
			}
			expect(json.filter((e) => e.type.endsWith("_error"))).toEqual([]);
			expect(
				json
					.filter(
						(e) =>
							e.type === "source_component" && EXTERNAL_NETS.includes(e.name),
					)
					.map((e) => e.name)
					.sort(),
			).toEqual([...selected].sort());
			if (!requested.length)
				expect(
					json.some((e) => e.type === "source_trace" && e.name === "N_LCD_D2"),
				).toBe(false);
		}, 60000);
	}
}

test("empty targets do not create exits; instances do not leak selections", () => {
	const a: any = F1C100SModule({
		name: "A",
		connections: { UART0_TX: "", PE2: "net.LED" },
	});
	const b: any = F1C100SModule({ name: "B" });
	for (const [element, expected] of [
		[a, ["PE2"]],
		[b, []],
	] as any[]) {
		const json = element.props.children[0].props.circuitJson;
		expect(
			json
				.filter(
					(e: any) =>
						e.type === "source_component" && EXTERNAL_NETS.includes(e.name),
				)
				.map((e: any) => e.name),
		).toEqual(expected);
	}
});