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