Files
periscope/backend/periscopex/errata_check.py
T
micheleandCursor 8d2b85600f Rebrand Pinscope to Periscope across product and codebase.
Rename the core package to periscopex, update UI/docs/Docker/deploy defaults to periscope.michelebigi.it, and keep legacy version/storage key aliases so existing projects keep working.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-13 20:02:04 +02:00

91 lines
2.9 KiB
Python

"""Errata workarounds from a known-URL catalog. No HTML scrape."""
from __future__ import annotations
import logging
import re
from backend.periscopex.models import ComponentConstraints, DesignGraph, Finding
from backend.periscopex.passive_rail_check import (
_pin_name_tokens,
_resistor_to_power,
)
from backend.periscopex.validate import _match_constraints
log = logging.getLogger(__name__)
# Exact MPN → URL + structured workarounds. Empty by default so eval is quiet.
DEFAULT_ERRATA_CATALOG: dict[str, dict] = {}
def check_errata(
graph: DesignGraph,
constraints_map: dict[str, ComponentConstraints] | None,
catalog: dict[str, dict] | None = None,
) -> list[Finding]:
cmap = constraints_map or {}
cat = DEFAULT_ERRATA_CATALOG if catalog is None else catalog
findings: list[Finding] = []
for ref, comp in sorted(graph.components.items()):
mpn = (comp.mpn or "").strip()
if not mpn:
continue
entry = cat.get(mpn)
if entry is None:
continue
url = (entry.get("url") or "").strip()
if not url:
log.info("errata skip %s: catalog row has no url", mpn)
continue
cons = _match_constraints(mpn, cmap)
for wa in entry.get("workarounds") or []:
kind = (wa.get("kind") or "").lower()
pin_name = (wa.get("pin_name") or "").strip()
if kind != "pullup" or not pin_name:
continue
net = _net_for_pin_name(graph, ref, cons, pin_name)
if not net:
continue
if _resistor_to_power(graph, net):
continue
findings.append(Finding(
designator=ref,
mpn=mpn,
aspect="errata",
source="errata_check",
status="WARNING",
finding=(
f"{ref} {pin_name} is missing the errata pull-up on '{net}'."
),
why=wa.get("note") or "Vendor errata workaround is not on the schematic.",
recommendation="Add the pull-up described in the errata, or confirm the die revision.",
reference=url,
net=net,
pins=[f"{ref}.{pin_name}"],
rule_id="PE-ERRATA-001",
))
return findings
def _net_for_pin_name(
graph: DesignGraph,
ref: str,
cons: ComponentConstraints | None,
pin_name: str,
) -> str | None:
comp = graph.components.get(ref)
if not comp:
return None
want = pin_name.upper()
for pin_num, net in comp.pins.items():
if (net or "").upper() == want:
return net
tokens = _pin_name_tokens(cons, pin_num)
if any(t.upper() == want or _token_match(t, pin_name) for t in tokens):
return net
return None
def _token_match(token: str, pin_name: str) -> bool:
return bool(re.search(rf"(?:^|[_/]){re.escape(pin_name)}(?:$|[_/\d])", token, re.I))