Share library/extracted for schematic and PCB review.

PCB AI skips a second PDF exam (pintable cache, pdf_path=None) and the report
gains a PCB exam section plus power-trace, thermal, via, Kelvin, and GND-stitch
checks grounded in board geometry and extracted specs.
This commit is contained in:
2026-09-20 08:32:23 +02:00
parent f3f96ea4d1
commit 8f4ebc645c
15 changed files with 1014 additions and 107 deletions
+49 -23
View File
@@ -5,41 +5,67 @@ from __future__ import annotations
import math
from backend.periscopex.functional_groups import FunctionalGroupsReport
from backend.periscopex.models import DesignGraph, LayoutGraph
from backend.periscopex.models import ComponentConstraints, DesignGraph, LayoutGraph
from backend.periscopex.pcb_inventory import PcbInventoryReport
from backend.periscopex.si_check import net_length_mm
PCB_SYSTEM_PROMPT = """\
You are an electrical engineer reviewing a PCB layout against the IC \
datasheet. This is an EXAM of the existing board — do not propose a new \
floorplan, do not invent millimetres, and do not write Gerbers.
datasheet knowledge already stored in the shared library extraction \
(library/extracted). Do NOT re-read or request the PDF — schematic review \
already did the deep datasheet exam once.
### Coverage checklist (layout)
- Domains (power-rail islands) and functional groups (IC + satellites: \
decoupling, bulk, filter, crystal, pullup).
- Placement: decoupling and crystal load caps vs datasheet layout notes \
and any numeric layout_rules (max_distance_mm, same_layer, thermal vias).
- Routing: net lengths, differential-pair skew, obvious stubs; length \
match only when the datasheet gives millimetres.
- Impedance: comment on Z0 only when stackup + width numbers are in the \
context. Never assume 50 Ω.
- Derating: flag a capacitor only when both operating and rated voltages \
are present and Vop exceeds Vrated.
- Filters: topology already on the schematic — check whether filter parts \
sit with the IC they serve when coordinates exist.
- Datasheet layout pages (typical application / PCB layout) vs this \
neighborhood.
This is an EXAM of the existing board — do not propose a new floorplan, \
do not invent millimetres, and do not write Gerbers.
### Coverage checklist (layout-only)
- Use pintable, layout_rules, and abs-max from the library JSON in context.
- Domains and functional groups (IC + satellites).
- Placement vs numeric layout_rules (max_distance_mm, same_layer, thermal vias).
- Routing: lengths, skew, stubs — length match only with datasheet millimetres.
- Power copper: comment on width/thickness vs I_load only when those numbers \
are in the context. Never assume 1 oz or 50 Ω.
- Thermal: copper pour / vias under the package vs θJA / layout notes in the extraction.
- Kelvin/sense pins, crystal keepout — only if named in the pintable/layout_rules.
- Creepage/clearance only if the extraction or IEC number is in context.
### Findings
Every finding MUST have status ERROR, WARNING, or INFO and a non-empty \
recommendation (what to change on the board). INFO still needs a next \
step (e.g. "Measure Z0 after stackup is filled in").
Call submit_review. Empty findings with checked_areas is valid when the \
layout matches the datasheet.
recommendation. Call submit_review. Empty findings with checked_areas is \
valid when the layout matches the extraction.
"""
def format_library_extraction(cons: ComponentConstraints) -> str:
"""Compact shared-library dump so PCB review does not re-ingest the PDF."""
lines = [
"### Shared library extraction (library/extracted — do not re-read PDF)",
f"MPN: {cons.mpn}",
f"subtype: {cons.component_subtype or ''}",
f"model_version: {cons.model_version}",
]
if cons.package_info:
lines.append(
f"package: {cons.package_info.package} "
f"({cons.package_info.pin_count} pins)"
)
if cons.layout_rules:
lines.append("layout_rules:")
for rule in cons.layout_rules[:40]:
lines.append(f" {rule}")
if cons.absolute_maximum_ratings:
lines.append("absolute_maximum_ratings:")
for r in cons.absolute_maximum_ratings[:30]:
lines.append(
f" {r.parameter}: min={r.min} max={r.max} {r.unit} (p.{r.source_page})"
)
if cons.pintable:
lines.append("pintable (number name):")
for p in cons.pintable[:80]:
lines.append(f" {p.number} {p.name}")
return "\n".join(lines)
def build_pcb_layout_context(
ic_ref: str,
graph: DesignGraph,