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

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);
	}
});