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