astra/f1c100s

The code defines the physical pin layout, labels, and footprint for the F1C100S system-on-chip (SoC) component used in electronic devices.

Version
0.9.2
License
unset
Stars
0

src/schematic.tsx

import { BreakoutTestPoint } from "./BreakoutTestPoint";
import { ClockCrystal } from "./ClockCrystal";
import { attachSavedPaths } from "./saved-paths";
import type { LayoutProfile } from "./profiles";
import type { Subcircuit } from "tscircuit";
import { PIN_NETS, RAILS, EXTERNAL_NETS } from "./pin-map";
import { makeLayout } from "./layout";
import {
	SCHEMATIC_BANKS,
	SCHEMATIC_PIN_LABELS,
	createSchematicBox,
} from "./schematic-boxes";
export { SCHEMATIC_BANK_PINS } from "./schematic-boxes";

/** Schematic-only compatibility adapter for the pinned core's JSON inflator.
 * Run immediately after inflation, before source/port/schematic rendering.
 * The imported PCB footprint and positions are retained.
 */
export function attachSchematicLayout(
	instance: Subcircuit | null,
	layout: "default" | "custom" = "default",
	profile: LayoutProfile = "native",
) {
	if (!instance || (instance as any).__f1cSchematicLayout) return;
	(instance as any).__f1cSchematicLayout = true;
	const inflate = instance.doInitialInflateSubcircuitCircuitJson.bind(instance);
	instance.doInitialInflateSubcircuitCircuitJson = () => {
		inflate();
		const module = instance.selectOne(".MODULE") as any;
		const importedCrystal = module.selectOne(".Y1");
		module.children = module.children.filter((c: any) => c !== importedCrystal);
		const placement = makeLayout(profile);
		module.add(<ClockCrystal placement={placement.crystal} />);
		for (const terminal of placement.terminals) {
			const imported = module.children.find(
				(c: any) => c.name === terminal.name,
			);
			if (!imported) throw Error(`Missing imported exit ${terminal.name}`);
			module.children = module.children.filter((c: any) => c !== imported);
			module.add(<BreakoutTestPoint terminal={terminal} />);
		}
		configureSchematic(instance, layout);
		attachSavedPaths(instance, profile);
	};
}

function configureSchematic(
	instance: Subcircuit,
	layout: "default" | "custom",
) {
	const patch = (component: any, props: Record<string, unknown>) => {
		if (!component)
			throw new Error("Missing component while arranging F1C100S schematic");
		Object.assign(component.props, props);
		Object.assign(component._parsedProps, props);
	};
	const find = (name: string) =>
		instance.selectOne(`.MODULE > .${name}`) as any;
	const u1 = find("U1");
	patch(u1, { noSchematicRepresentation: true });
	const labelsByPin = SCHEMATIC_PIN_LABELS;
	for (const [p, label] of Object.entries(labelsByPin)) {
		const port = u1.selectOne(`.pin${p}`, { type: "port" });
		patch(port, {
			aliases: [...new Set([...(port._parsedProps.aliases ?? []), label])],
		});
	}
	for (const terminal of EXTERNAL_NETS) {
		const c = find(terminal);
		for (const child of [...c.children])
			if (child.componentName === "Symbol") c.remove(child);
		patch(c, { noSchematicRepresentation: true });
	}
	patch(instance.selectOne(".MODULE"), {
		schTraceAutoLabelEnabled: true,
		schMaxTraceDistance: 3,
		schLayout: { layoutMode: "relative" },
	});
	// Sections control net-label boundaries; positions are explicitly authored.
	// The pinned core otherwise runs match-pack even for positioned sections.
	instance._doInitialSchematicLayoutSections = () => {};
	(
		instance.selectOne(".MODULE") as Subcircuit
	)._doInitialSchematicLayoutSections = () => {};
	const sectionNames = new Set<string>();
	const section = (key: string, title = key) => {
		const name = `${instance.name}_${key}`;
		if (sectionNames.has(name)) return name;
		sectionNames.add(name);
		instance.add(
			<schematicsection
				name={name}
				displayName={title}
				sectionTitleFontSize={0.3}
			/>,
		);
		return name;
	};
	const supportOffsetY = layout === "custom" ? 0 : -20;
	if (layout === "default") {
		const positions = [
			[-10, 5],
			[-1, 5],
			[8, 5],
			[-10, -4],
			[-1, -4],
			[8, -4],
			[-11, 6.5 + supportOffsetY],
		];
		SCHEMATIC_BANKS.forEach((bank, i) =>
			instance.add(
				createSchematicBox(bank, {
					chipRef: ".MODULE .U1",
					schX: positions[i]![0],
					schY: positions[i]![1],
					schSectionName: section(bank.name, bank.title),
				}),
			),
		);
	}
	const passives = makeLayout("native").passives;
	// Preserve capacitor ratings and the oscillator's specified C0G loads;
	// the pinned Circuit JSON inflator omits these source properties.
	for (const p of passives)
		if (p.kind === "capacitor")
			patch(find(p.name), {
				maxVoltageRating: p.name.startsWith("C_OSC") ? 50 : 6.3,
				...(p.name.startsWith("C_OSC")
					? { manufacturerPartNumber: "GRM1555C1H180JA01D" }
					: {}),
			});
	const place = (name: string, x: number, y: number, sectionName: string) => {
		const c = find(name);
		// Inflated custom symbol geometry omits standard reference/value text.
		for (const child of [...c.children])
			if (child.componentName === "Symbol") c.remove(child);
		patch(c, {
			schX: x,
			schY: y + supportOffsetY,
			schRotation: 270,
			schSectionName: sectionName,
		});
	};
	RAILS.forEach((rail, row) => {
		const caps = passives.filter(
			(p) =>
				p.a === rail && (p.name.startsWith("C_D") || p.name.startsWith("C_B_")),
		);
		const [x, y] = [
			[-6, 8],
			[-2, 8],
			[4, 8],
			[-6, 5],
			[2, 5],
			[6, 5],
			[10, 5],
		][row]!;
		const key = section("DECOUPLING", "Decoupling");
		caps.forEach((c, i) => place(c.name, x + i * 1.1, y, key));
	});
	for (const [i, net] of ["VRA1", "VRA2", "TV_VRN", "TV_VRP"].entries()) {
		const x = -12 + i * 6.5;
		const key = section("ANALOG_REFERENCES", "Analog references");
		place(`C_${net}`, x, -5, key);
		if (net.startsWith("VRA")) place(`R_${net}`, x + 1.5, -5, key);
	}
	const sv = section("SYSTEM_SUPPORT", "Clock / reset / SDRAM"),
		reset = sv;
	place("R_SV_H", -12, 0, sv);
	place("R_SV_L", -12, -2, sv);
	place("C_SV_H", -10, 0, sv);
	place("C_SV_L", -10, -2, sv);
	place("R_RESET", -7, 0, reset);
	place("C_RESET", -7, -2, reset);
	const clock = sv;
	place("Y1", 7, -1, clock);
	patch(find("Y1"), { schRotation: 0 });
	place("C_OSCI", 4.5, -2, clock);
	place("C_OSCO", 9.5, -2, clock);
	place(
		"C_TV_REF",
		10.5,
		-5,
		section("ANALOG_REFERENCES", "Analog references"),
	);
	const pullups = section("PULLUPS", "SDMMC / SPI / I²C pull-ups");
	passives
		.filter((p) => p.name.startsWith("R_PU_"))
		.forEach((p, i) => place(p.name, -11 + i * 3.1, -8, pullups));
	for (const trace of instance.selectAll("trace") as any[]) {
		const net = trace._parsedProps.path
			?.find((p: string) => p.startsWith("net."))
			?.slice(4);
		if (net) patch(trace, { name: `N_${net}`, schDisplayLabel: net });
	}
}