Ship Fase B slices: hierarchy, derating bands, timing, PI, ESD/return.
ERROR stays only for RULE+MANDATORY. Hierarchy walks component→pin→net→block. Derating is PASS/MARGIN/RISK from Vop/Vrated. Timing and PI skip without numbers. ESD and HS return path are REVIEW, not RULE ERROR.
This commit is contained in:
@@ -5,7 +5,11 @@ from __future__ import annotations
|
||||
import logging
|
||||
from collections import Counter
|
||||
|
||||
from backend.periscopex.derating import build_derating_table
|
||||
from backend.periscopex.esd_return_check import check_esd, check_return_path
|
||||
from backend.periscopex.finding_engine import complete_findings
|
||||
from backend.periscopex.functional_groups import FunctionalGroupsReport
|
||||
from backend.periscopex.hierarchy import check_hierarchy
|
||||
from backend.periscopex.models import DesignGraph, Finding, LayoutGraph
|
||||
from backend.periscopex.pcb_net_match import check_pcb_net_match
|
||||
from backend.periscopex.pcb_power_thermal import (
|
||||
@@ -15,8 +19,10 @@ from backend.periscopex.pcb_power_thermal import (
|
||||
check_pcb_thermal_copper,
|
||||
check_pcb_via_current,
|
||||
)
|
||||
from backend.periscopex.pi_check import check_power_integrity
|
||||
from backend.periscopex.placement_check import check_placement
|
||||
from backend.periscopex.si_check import check_si
|
||||
from backend.periscopex.timing_check import check_timing
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -40,32 +46,89 @@ def assign_pcb_finding_ids(findings: list[Finding]) -> None:
|
||||
|
||||
|
||||
def check_pcb_derating(graph: DesignGraph) -> list[Finding]:
|
||||
"""ERROR only when both Vop and Vrated exist and Vop exceeds Vrated."""
|
||||
"""PASS / MARGIN / RISK (exceed) when both Vop and Vrated exist."""
|
||||
out: list[Finding] = []
|
||||
n_pass = 0
|
||||
for row in build_derating_table(graph):
|
||||
rated = row.get("rated_voltage_v")
|
||||
op = row.get("operating_voltage_v")
|
||||
stress = row.get("stress") or "UNKNOWN"
|
||||
if rated is None or op is None:
|
||||
continue
|
||||
if float(op) <= float(rated):
|
||||
continue
|
||||
ref = str(row.get("designator") or "")
|
||||
out.append(Finding(
|
||||
designator=ref,
|
||||
mpn=str(row.get("mpn") or ""),
|
||||
aspect="derating",
|
||||
finding=(
|
||||
f"{ref} operates at {op:g} V with a {rated:g} V rating."
|
||||
),
|
||||
why="Capacitor voltage rating must exceed the rail (no invented derating %).",
|
||||
status="ERROR",
|
||||
recommendation=(
|
||||
mpn = str(row.get("mpn") or "")
|
||||
net = row.get("net_plus")
|
||||
ratio = float(op) / float(rated)
|
||||
if stress == "RISK":
|
||||
rec = (
|
||||
f"Replace {ref} with a capacitor rated above {op:g} V, "
|
||||
"or lower the rail."
|
||||
),
|
||||
)
|
||||
out.append(Finding(
|
||||
designator=ref,
|
||||
mpn=mpn,
|
||||
aspect="derating",
|
||||
finding=(
|
||||
f"{ref} operates at {op:g} V with a {rated:g} V rating "
|
||||
f"(Vop/Vrated={ratio:.2f})."
|
||||
),
|
||||
facts=f"Vop={op:g} V, Vrated={rated:g} V, ratio={ratio:.3f}.",
|
||||
requirement="Operating voltage must not exceed the capacitor rating.",
|
||||
inference="Exceeding Vr is a measured RULE, not a dielectric %.",
|
||||
why="Capacitor voltage rating must exceed the rail (no invented derating %).",
|
||||
status="ERROR",
|
||||
recommendation=rec,
|
||||
action=rec,
|
||||
source="pcb_derating",
|
||||
rule_id="PE-DRT-001",
|
||||
evidence_status="SUFFICIENT",
|
||||
net=net,
|
||||
pins=[],
|
||||
))
|
||||
elif stress == "MARGIN":
|
||||
rec = (
|
||||
f"Confirm {ref} voltage margin ({op:g}/{rated:g} V) or pick a "
|
||||
"higher Vr. This is utilization, not a 50% ceramic rule."
|
||||
)
|
||||
out.append(Finding(
|
||||
designator=ref,
|
||||
mpn=mpn,
|
||||
aspect="derating",
|
||||
finding=(
|
||||
f"{ref} Vop/Vrated={ratio:.2f} ({op:g}/{rated:g} V) — MARGIN."
|
||||
),
|
||||
facts=f"Vop={op:g} V, Vrated={rated:g} V, ratio={ratio:.3f}.",
|
||||
requirement="Utilization > 0.8 of Vr is flagged as MARGIN (RISK).",
|
||||
inference="Not an invented IPC/ceramic derating percentage.",
|
||||
why="PASS/MARGIN/RISK from Vop and Vrated only.",
|
||||
status="WARNING",
|
||||
recommendation=rec,
|
||||
action=rec,
|
||||
source="pcb_derating",
|
||||
rule_id="PE-DRT-002",
|
||||
evidence_status="SUFFICIENT",
|
||||
net=net,
|
||||
pins=[],
|
||||
))
|
||||
elif stress == "PASS":
|
||||
n_pass += 1
|
||||
if n_pass:
|
||||
rec = "No voltage-rating change required for PASS capacitors."
|
||||
out.append(Finding(
|
||||
designator="C",
|
||||
mpn="",
|
||||
aspect="derating",
|
||||
finding=f"{n_pass} capacitor(s) PASS (Vop/Vrated ≤ 0.8).",
|
||||
facts=f"PASS count={n_pass} (both Vop and Vrated present).",
|
||||
requirement="Rated voltage exists and Vop is ≤ 80% of Vr.",
|
||||
inference="Summary INFO — per-cap PASS lives on the derating table.",
|
||||
why="Avoid one INFO card per capacitor.",
|
||||
status="INFO",
|
||||
recommendation=rec,
|
||||
action=rec,
|
||||
source="pcb_derating",
|
||||
rule_id="PE-DRT-001",
|
||||
net=row.get("net_plus"),
|
||||
rule_id="PE-DRT-003",
|
||||
evidence_status="SUFFICIENT",
|
||||
pins=[],
|
||||
))
|
||||
return out
|
||||
@@ -110,8 +173,9 @@ def run_pcb_checks(
|
||||
graph: DesignGraph,
|
||||
constraints_map: dict,
|
||||
layout: LayoutGraph | None,
|
||||
plan: FunctionalGroupsReport | None = None,
|
||||
) -> list[Finding]:
|
||||
"""Placement, SI, pad-net match, derating — skip individually on error."""
|
||||
"""Placement, SI, pad-net match, derating, hierarchy, timing, PI, ESD."""
|
||||
out: list[Finding] = []
|
||||
for name, fn in (
|
||||
("pcb_net_match", lambda: check_pcb_net_match(graph, layout)),
|
||||
@@ -123,6 +187,11 @@ def run_pcb_checks(
|
||||
("pcb_thermal_copper", lambda: check_pcb_thermal_copper(graph, constraints_map, layout)),
|
||||
("pcb_kelvin", lambda: check_pcb_kelvin(graph, constraints_map)),
|
||||
("pcb_gnd_stitch", lambda: check_pcb_gnd_stitch(graph, layout)),
|
||||
("hierarchy_check", lambda: check_hierarchy(graph, plan)),
|
||||
("timing_check", lambda: check_timing(graph, constraints_map)),
|
||||
("pi_check", lambda: check_power_integrity(graph, constraints_map, layout)),
|
||||
("esd_check", lambda: check_esd(graph, constraints_map)),
|
||||
("return_path", lambda: check_return_path(graph, layout)),
|
||||
):
|
||||
try:
|
||||
out.extend(fn())
|
||||
|
||||
Reference in New Issue
Block a user