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>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""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))
|
||||
Reference in New Issue
Block a user