Files
periscope/docs/piano-pcb-review.md
T
michele 721ede4243 Add PCB review plan: verify placement and routing, no auto-place.
Documents a parallel MODE=pcb exam job (inventory + datasheet-gated
checks) that reuses placement_check, si_check, and the placement job
pattern. Parks F2 packing/export.
2026-09-19 21:41:27 +02:00

16 KiB
Raw Blame History

Piano — PCB review (controllo placement + routing)

Documento solo di piano. Non è un implementazione sprint e non è un auto-placer.

Prodotto: Periscope Layout review — esaminare un board KiCad già sbrogliato (o parzialmente sbrogliato) e dire dove placement e routing violano geometria + datasheet.

Contratto rispetto a docs/piano-implementazione.md:

Schema (già in produzione) PCB review (questo piano) Placement F2 pack (fuori da questo piano)
Promessa Lo schema rispetta il datasheet Il rame e le posizioni esistenti rispettano datasheet + geometria Proporre xy nuovi
Trigger BOM + netlist / .kicad_sch + .kicad_pcb PCB + layout_rules numerici
Output Finding pin/net, derating, review LLM Finding mm / net / Z0 / skew / via EP, rule_id PE-PLC* / PE-SI* / PE-LAY* placement_pack.json proposte
Job MODE=run (status) Job parallelo MODE=pcb (pcb_status) già MODE=placement (placement_status)

Senza .kicad_pcb il progetto resta un Periscope schema completo. Il report schema non si riempie di PE-PLC / PE-SI.


0. Postura: esame, non progettazione

Placement = verifica

Si misura ciò che è già sul board:

  • distanza footprint/pad (euclidea e shortest-path sui segmenti);
  • layer (F.Cu vs B.Cu) vs same_layer;
  • via nel courtyard del pad termico vs min_via_count;
  • keepout: net estraneo nel courtyard;
  • crystal: load cap vs pin XIN/XOUT con la stessa metrica.

Non si spostano footprint. Non si scrive .kicad_pcb. Non si usa placement_pack / build_placement_pack come obiettivo di questo piano.

placement_check (backend/periscopex/placement_check.py, PE-PLC-001004) è già questa postura. Resta il nucleo placement del job PCB.

La pipeline Placement esistente (POST …/placement/start, placement_plan.json) è topologia routing-first (domini, satelliti, role_hint). Utile come contesto (“questo C è decoupling di U1”) ma non è auto-placement. F2 packing (placement_pack.json, collisioni, export pcbnew) resta parcheggiato.

Routing = la stessa postura

Si misura ciò che è già in rame:

  • lunghezza net / coppia / bus (somma segmenti, non folklore USB);
  • intra-pair skew vs layout_rules.kind=length_match solo se c’è un mm nel datasheet (si_check, PE-SI-001);
  • Z0 sulle tracce dove stackup + larghezza esistono (impedance_traces.py + ImpedanceFinder);
  • mismatch pad PCB vs pin schema (net name su pad vs DesignGraph.pin_net);
  • elenco tracce / bus per il report (inventario, poi check).

Non si sbroglia. Non si cambia larghezza. Non si inventano 3W, creepage IEC, o target 50 Ω se il datasheet/stackup non li danno.

Datasheet compliance (layout)

Solo vincoli strutturati già estratti in ComponentConstraints.layout_rules (skill C4, layout_rules.py):

kind Check PCB Skip se
decoupling_proximity distanza / path vs max_distance_mm; crystal stesso max_distance_mm null
thermal_via via in courtyard vs min_via_count courtyard o count assenti
keepout endpoint net estraneo in courtyard courtyard assente
length_match skew coppia vs max_distance_mm mm assente

Niente default 3 mm. Niente “foto del layout TI vs gerber”. Citazione: source_page sul finding, come oggi.


1. Come sta in parallelo alla pipeline schema

Oggi i check layout (placement_check, si_check) girano dentro la review schema (backend/services/validation.py_run_deterministic_checks) se esiste layout_graph.json. Funziona, ma mescola due prodotti nello stesso report.json e nello stesso status.

Target:

                    ┌─ MODE=run        → status              → report.json (schema)
  Project files  ───┼─ MODE=placement  → placement_status    → placement_plan.json
                    └─ MODE=pcb        → pcb_status          → pcb_report.json

Stesso pattern job/stage/findings della Placement sibling, non un secondo auto-placer.

Riuso obbligatorio (non reinventare)

Pezzo Path Ruolo nel job PCB
Workspace / SSE broker backend/services/pipeline.py (PipelineWorkspace, broker) Download upload, layout_graph.json, eventi
Job dispatch backend/services/job_runner.py, backend/pipeline_worker.py Nuovo MODE=pcb accanto a run / regen / placement
API sibling backend/routers/pipeline.py (`placement/start cancel
Meta progetto backend/services/projects.py (placement_status*) pcb_status, pcb_state, pcb_execution_name, pcb_cancel_requested
Parser board backend/periscopex/parsers_kicad_pcb.py LayoutGraph (footprint, pad net, segmenti, via, zone, stackup)
Grafo schema design_graph.json / build_graph(..., pcb_path=) Pin↔net; capacitors_on_net; CAD index
Placement verify placement_check.py PE-PLC-*
SI verify si_check.py PE-SI-001
BOM/schema bom_match_check.py Resta schema. Sul PCB: analogo pad-net match (nuovo check piccolo, stesso schema Finding)
Cad bridge cad_bridge.py (target: pcb per PE-PLC/PE-SI/PE-LAY) Plugin pcbnew focus
Validation helpers assign_finding_ids, fail-soft per check, annotate_findings_cad IDs + CAD
Impedenza tracce impedance_traces.py, routers/impedance.py, impedance_nets.json Inventario Z0; finding solo se target numerico esiste
UI finding-card, report filters, types.ts / api.ts, pcb-upload.tsx, pagine/hooks placement come stampo Tab Layout + findings nel report
Test tests/test_kicad_pcb.py, test_placement_check.py, test_si_check.py, test_placement_pipeline.py Fixtures + job smoke

Contratto job (come Placement)

  • Non tocca ProjectMeta.status dellanalisi schema.
  • Soft-cancel: flag pcb_cancel_requested (come placement_cancel_requested).
  • Mutex: non partire se analisi queued|running o placement queued|running o pcb già attivo (levent log è condiviso e Placement fa clear_history).
  • SSE: eventi pcb_step_update / pcb_complete / pcb_error / pcb_cancelled. Lo stream schema ignora pcb_* (come già ignora placement_*).
  • Locale: enqueue con proc_key=pcb:{id}, execution_name=local/pcb/{id} (stesso trick di local/placement/).
  • Heal zombie: heal_if_pcb_stuck speculare a heal_if_placement_stuck (evento terminale, o artefatto pcb_report.json se il worker è morto dopo la scrittura).
  • Billing: job free (come placement). Nessun LLM in v1.

Stages del job PCB (v1)

  1. ensure_graph — riusa design_graph.json; se manca, graph_build da BOM+netlist (stesso helper Placement). Senza grafo: 400, non inventare connettività dal solo PCB.
  2. parse_pcb — riusa layout_graph.json o parse_kicad_pcb(uploads/pcb.kicad_pcb) e upload. Fail → pcb_status=error (a differenza dello schema, qui il PCB è lingresso).
  3. inventory — lista net con rame, lunghezze, coppie _DP/_DM _P/_N, bus omonimi, Z0 dove stackup+width ci sono. Artefatto pcb_inventory.json (leggibile in UI). Nessun finding da soli numeri.
  4. checks — funzioni pure in periscopex/ (fail-soft per check). Vedi §3.
  5. write_reportpcb_report.json (ValidationReport), cad-bridge merge, IDs PCB-{designator}-{001} per non collidere con lo schema.

Senza datasheet / layout_rules: linventory c’è comunque; i finding datasheet-gated sono lista vuota, non errore.


2. Ingressi

Ingresso Dove Obbligatorio v1
.kicad_pcb uploads/pcb.kicad_pcb, has_pcb
Design graph design_graph.json (da BOM+netlist / KiCad sch flatten) Sì (per pin↔net e MPN)
Estrazioni IC extracted/ + library Per layout_rules / pintable pin names
Datasheet PDF library blobs Solo per source_page in UI; i check v1 sono deterministici
Stackup nel PCB LayoutGraph.stackup (εr, h, t, layer names) Per Z0; skip se assente
layout_rules JSON MPN, KNOWN_KINDS Per PE-PLC / PE-SI; skip se mm/count null

Non in v1: Gerber come ingresso, EasyEDA JSON, PADS dump come layout, zip gerarchico PCB (il board non è gerarchico).

Allineamento net: i nomi net del PCB devono combaciare con lo schema dopo flatten gerarchico (U1.4 ↔ pad). Un mismatch è un finding (PE-LAY-001), non un crash.


3. Cosa elenca e cosa controlla questa stage

Due uscite distinte: inventario (sempre, se il parse riesce) e findings (solo regola + numero).

3.1 Inventario (report Layout, non finding)

Per ogni net con rame:

  • nome, NetType dallo schema se noto;
  • lunghezza mm (somma segmenti, come si_check.net_length_mm);
  • layer usati, min/max width, n. via;
  • coppia differenziale se suffisso noto;
  • Z0 stimata se stackup+width (riuso impedance_traces.analyze_where_needed / impedance_nets.json);
  • appartenenza a bus (prefisso comune: D[0-7], A[0-15], USB_DP/DM) — lunghezze affiancate, senza soglia inventata.

UI: tabella “Traces / buses” sulla pagina PCB o tab Layout. Serve a vedere il board prima dei finding.

3.2 Check deterministici (findings, stesso modello Finding)

Registrati in un unico run_pcb_checks(graph, constraints_map, layout) (nuovo wrapper sottile; non SDK LLM in periscopex/).

ID Source Cosa Gate
PE-LAY-001 nuovo pcb_net_match (specchio di bom_match_check) Pad net ≠ pin net schema, stesso ref+pin Pad ha net e lo schema ha pin_net
PE-LAY-002 stesso modulo Ref in schema senza footprint PCB (o viceversa, skip #PWR) Graph + footprints
PE-PLC-001 placement_check Decoupling / crystal oltre max_distance_mm (path se esiste, senno euclidea) regola + mm
PE-PLC-002 idem Via termiche < min_via_count courtyard + count
PE-PLC-003 idem same_layer: true e cap sullaltro lato senza via sotto il pin boolean regola + layer footprint
PE-PLC-004 idem Keepout: net estraneo in courtyard courtyard
PE-SI-001 si_check Intra-pair skew > length_match mm coppia nominale e mm datasheet
PE-Z0-001 (fase 2) da impedance_traces Z0 fuori target target in layout_rules o netclass KiCad esplicita; mai 50 Ω di default

Campi finding (già sul modello): rule_id, net, pins[], source, cad_sheet / cad_uuid se in cad_index, status ERROR/WARNING, source_page dalla regola.

Normalization: downgrade-only se un giorno si aggiunge un pass; v1 non alza severità.

3.3 Fuori dai finding (skip esplicito, log)

  • 3W / crosstalk senza distanza datasheet o calcolatore.
  • Creepage/clearance IEC 62368 senza V/mm nel datasheet.
  • Isolation barrier / HV courtyard senza keepout HV.
  • CPWG se il vendor ImpedanceFinder non lo dà.
  • Length-match “USB spec” / 150 mil folklore.
  • Confronto visivo application-circuit vs PCB.

Queste restano Wave G residue in piano-implementazione.md, non questo job.


4. UI

Gating: hasPcb. Senza PCB, nessun bottone “Run PCB review”, nessun finding layout nel report schema.

Superficie Comportamento
Project hub Accanto a “Build placement plan”: Run PCB review (verify). Upload già PcbUploadButton.
/project/[id]/pcb Stampo di /placement: stepper SSE (ensure_graphparse_pcbinventorycheckswrite_report), cancel, errore, link al report.
Sidebar Voce Layout (/pcb o report?domain=layout). Nested route = forceDocument come placement.
Report GET /report/{id} merge report.json + pcb_report.json. Filtro URL ?domain=schema|layout|all. Finding card: badge “Layout” + rule_id (PE-PLC-001) oltre “Automated check”.
Inventory Tabella tracce/bus/Z0 sulla pagina PCB (dati pcb_inventory.json / impedance_nets.json).
Plugin periscope-findings.json include finding target: pcb dopo il job PCB (cad-bridge già distingue prefissi).

Commenti / review_states restano keyed by finding_id su report.json (o dict parallelo); il merge in GET non deve perdere commenti schema. IDs prefissati PCB- evitano collisioni U3-001.

Re-run schema non cancella pcb_report.json. Re-run PCB non tocca findings LLM.


5. Fasi

Ordine: non parallelizzare con auto-pack. Non mischiare changelog Core vs Layout.

Fase 0 — Confine (doc + split)

  • Questo documento = contratto.
  • Togliere placement_check / si_check da _run_deterministic_checks della review schema (restano no-op senza layout; lo split evita PE-PLC nello schema).
  • Eval harness schema (eval_report.py) continua a chiamarli con layout=None (no-op) oppure li sposta in eval PCB dedicato.

Done when: run schema senza PCB = zero PE-PLC/PE-SI nel report.

Fase 1 — Job parallelo + inventory + check già esistenti (slice usabile)

  • Meta pcb_*, enqueue MODE=pcb, router start/cancel/events, worker.
  • Stages §1; artefatti layout_graph.json, pcb_inventory.json, pcb_report.json.
  • Check: pcb_net_match + check_placement + check_si.
  • UI: start, progress, merge findings nel report, filtro Layout, rule_id sulla card.
  • Test: wrapper run_pcb_checks su fixture (decoupling 15 mm → PE-PLC-001; 1 mm → niente); pad net mismatch → PE-LAY-001; job busy helpers come test_placement_pipeline.py; pytest API start 400 senza PCB.

Done when: upload .kicad_pcb → start PCB → finding visibili nel report; schema invariato.

Fase 2 — Inventario ricco (tracce, bus, Z0)

  • Tabella lunghezze + coppie + bus.
  • Z0 per net signal con stackup (già impedance_traces); finding PE-Z0-001 solo con target numerico (regola o netclass).
  • Collegare impedance_nets.json al job PCB (oggi è side-effect dello schema graph_build).

Done when: board con stackup mostra Z0 in UI; senza stackup, skip loggato, zero finding inventati.

Fase 3 — Plugin + eval PCB

  • Cad-bridge dopo write_report; click finding centra footprint in pcbnew (plugins/kicad/periscope_plugin.py).
  • Golden keys su fixture (USB skew voluta → PE-SI-001; simple_project senza PCB → vuoto).
  • Changelog Layout (voce distinta, non “un po di Periscope in più”).

Fase 4 — Non fare (parcheggio)

  • placement_pack / collisioni courtyard / export xy / muovere rame.
  • Auto-place antenna in .kicad_pcb.
  • 3W, creepage IEC, CPWG, isolation HV senza numero.
  • LLM che “legge” il layout come review schema.
  • Secondo ingresso Gerber/EasyEDA.

6. Fuori scope (esplicito)

  • Auto-placement, packing mm, write-back pcbnew, “migliora il floorplan”.
  • Autore di sbroglio / change width / length-tune.
  • Simulazione SPICE / IBIS / EM / VSWR.
  • Inventare millimetri, Ω, o V/mm.
  • Plugin EasyEDA; parser JSON EasyEDA.
  • Layout da !PADS-POWERPCB.
  • Unire forzatamente job PCB nella run schema.
  • Toccare ssh / keys / sshd (irrilevante a questo piano).

7. Come si misura il “done”

Fixture minima (non un USB inventato in produzione, ma un .kicad_pcb di test in tests/):

  1. C di decoupling a 15 mm da VDD con regola 2 mm → PE-PLC-001.
  2. Stesso C a 1 mm → nessun finding proximity.
  3. Coppia USB_DP/USB_DM con skew > length_matchPE-SI-001.
  4. Pad U1.1 net +3V3 vs schema GNDPE-LAY-001.
  5. Progetto senza PCB: job PCB rifiutato; report schema pulito.

Niente LLM. Niente coordinate proposte.


8. Relazione col piano esistente

piano-implementazione.md Wave G (G0 ingest, G1 SI gated, G2 placement vs datasheet) è questo prodotto, riletto come job parallelo di esame.

La roadmap Placement F2 (pack v1/v2/export) non entra in queste fasi. placement_check resta verifica; placement_pack resta skeleton gated e non si estende qui.

Chats Cursor precedenti: nessuna decisione di “PCB error pipeline” come job; solo note RF (“auto-place rame nel PCB = dopo”). Questo piano è la decisione: review, non place.