Add protocol-certification M1 instance recognition (2.65.0).
physical_bus_instance from declaration, silicon/part, pin/net names, and pairs. USB and DDR fixtures emit groups; AXI4 internal attaches no PCB nets. Ambiguity is REVIEW, not FAIL. No L0–L3 certifier and no invented Z.
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
"""M1 physical_bus_instance recognition: USB/DDR fixtures; AXI has no PCB nets."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from backend.periscopex.models import (
|
||||
Component,
|
||||
ComponentType,
|
||||
DesignGraph,
|
||||
Net,
|
||||
NetType,
|
||||
PinConnection,
|
||||
)
|
||||
from backend.periscopex.protocol_recognize import (
|
||||
PACK_MACROPHASE,
|
||||
protocol_section_for_graph,
|
||||
recognize_physical_buses,
|
||||
)
|
||||
|
||||
|
||||
def _ic(ref: str, pins: dict[str, str], *, mpn: str = "", value: str = "") -> Component:
|
||||
return Component(
|
||||
reference=ref, value=value or mpn, footprint="",
|
||||
component_type=ComponentType.IC, mpn=mpn or None, pins=pins,
|
||||
)
|
||||
|
||||
|
||||
def _net(name: str, *pairs: tuple[str, str]) -> Net:
|
||||
return Net(
|
||||
name=name, net_type=NetType.SIGNAL,
|
||||
pins=[PinConnection(component_ref=r, pin_number=p) for r, p in pairs],
|
||||
)
|
||||
|
||||
|
||||
def _usb_graph() -> DesignGraph:
|
||||
return DesignGraph(
|
||||
components={
|
||||
"U1": _ic("U1", {"1": "USB_D+", "2": "USB_D-"}, mpn="CH340E"),
|
||||
"J2": Component(
|
||||
reference="J2", value="USB_C_Receptacle_USB2.0_16P",
|
||||
footprint="USB_C_Receptacle_USB2.0_16P",
|
||||
component_type=ComponentType.CONNECTOR,
|
||||
pins={"A6": "USB_D+", "A7": "USB_D-"},
|
||||
),
|
||||
},
|
||||
nets={
|
||||
"USB_D+": _net("USB_D+", ("U1", "1"), ("J2", "A6")),
|
||||
"USB_D-": _net("USB_D-", ("U1", "2"), ("J2", "A7")),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _ddr_graph() -> DesignGraph:
|
||||
pins = {str(i): f"DDR4_DQ{i}" for i in range(8)}
|
||||
pins.update({
|
||||
"8": "DDR4_DQS0_P", "9": "DDR4_DQS0_N", "10": "DDR4_DM0",
|
||||
"11": "DDR4_CK_P", "12": "DDR4_CK_N",
|
||||
"13": "DDR4_A0", "14": "DDR4_RAS",
|
||||
})
|
||||
u_mem = _ic("U5", pins, mpn="MT41K256M16TW", value="DDR4")
|
||||
nets = {n: _net(n, ("U5", p)) for p, n in pins.items()}
|
||||
return DesignGraph(components={"U5": u_mem}, nets=nets)
|
||||
|
||||
|
||||
def _axi_graph() -> DesignGraph:
|
||||
pins = {
|
||||
"1": "AXI_AWVALID",
|
||||
"2": "AXI_WDATA",
|
||||
"3": "AXI_RDATA",
|
||||
"4": "VCCINT",
|
||||
}
|
||||
return DesignGraph(
|
||||
components={
|
||||
"U3": _ic("U3", pins, mpn="XC7A100T", value="AXI4 interconnect"),
|
||||
},
|
||||
nets={
|
||||
"AXI_AWVALID": _net("AXI_AWVALID", ("U3", "1")),
|
||||
"AXI_WDATA": _net("AXI_WDATA", ("U3", "2")),
|
||||
"AXI_RDATA": _net("AXI_RDATA", ("U3", "3")),
|
||||
"VCCINT": _net("VCCINT", ("U3", "4")),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def test_usb_net_names_make_diff_pair_instance():
|
||||
insts = recognize_physical_buses(_usb_graph())
|
||||
usb = [i for i in insts if "usb" in i.logical_protocol_id]
|
||||
assert usb, insts
|
||||
one = usb[0]
|
||||
assert one.physical_interface_id != one.logical_protocol_id
|
||||
assert one.pcb_relevant in {"YES", "CONDITIONAL"}
|
||||
pairs = [g for g in one.groups if g.kind == "DIFFERENTIAL_PAIR"]
|
||||
assert pairs
|
||||
nets = set(pairs[0].nets)
|
||||
assert "USB_D+" in nets and "USB_D-" in nets
|
||||
assert one.recognition_status != "FAIL"
|
||||
assert one.evidence_kind in {"part", "net_name", "silicon"}
|
||||
|
||||
|
||||
def test_usb_speed_ambiguity_is_review_not_fail():
|
||||
insts = recognize_physical_buses(_usb_graph())
|
||||
usb = [i for i in insts if "usb" in i.logical_protocol_id][0]
|
||||
assert usb.recognition_status == "REVIEW"
|
||||
assert "FAIL" not in usb.recognition_status
|
||||
assert usb.ambiguous_logical_ids
|
||||
|
||||
|
||||
def test_ddr_net_names_rebuild_byte_lane():
|
||||
insts = recognize_physical_buses(_ddr_graph())
|
||||
ddr = [i for i in insts if i.logical_protocol_id.startswith("ddr")]
|
||||
assert ddr
|
||||
one = ddr[0]
|
||||
assert one.physical_interface_id == "ddr4-sdram-pcb"
|
||||
assert one.logical_protocol_id == "ddr4"
|
||||
lanes = [g for g in one.groups if g.kind == "BYTE_LANE"]
|
||||
assert lanes
|
||||
assert "DDR4_DQ0" in lanes[0].nets
|
||||
assert "DDR4_DQS0_P" in lanes[0].nets
|
||||
clocks = [g for g in one.groups if g.kind == "CLOCK_GROUP"]
|
||||
assert clocks
|
||||
assert one.recognition_status != "FAIL"
|
||||
blob = json.dumps(one.model_dump())
|
||||
assert "90" not in blob
|
||||
assert "ohm" not in blob.lower()
|
||||
|
||||
|
||||
def test_axi4_internal_does_not_create_pcb_nets():
|
||||
insts = recognize_physical_buses(_axi_graph())
|
||||
axi = [i for i in insts if i.logical_protocol_id == "axi4"]
|
||||
assert axi
|
||||
one = axi[0]
|
||||
assert one.physical_interface_id == "axi4-internal"
|
||||
assert one.pcb_relevant == "NO"
|
||||
assert one.nets == []
|
||||
assert one.groups == []
|
||||
assert one.recognition_status == "NOT_APPLICABLE"
|
||||
assert "AXI_AWVALID" not in one.nets
|
||||
assert "AXI_WDATA" not in one.nets
|
||||
|
||||
|
||||
def test_empty_graph_keeps_nessun_protocollo():
|
||||
sec = protocol_section_for_graph(DesignGraph())
|
||||
assert sec.message == "nessun protocollo riconosciuto"
|
||||
assert sec.recognized_instances == []
|
||||
assert sec.macrophase == PACK_MACROPHASE
|
||||
blob = json.dumps(sec.model_dump())
|
||||
assert "ohm" not in blob.lower()
|
||||
|
||||
|
||||
def test_report_section_lists_usb_without_z():
|
||||
sec = protocol_section_for_graph(_usb_graph())
|
||||
assert sec.recognized_instances
|
||||
assert sec.max_level_reached is None
|
||||
blob = json.dumps(sec.model_dump())
|
||||
assert "90" not in blob
|
||||
assert "Zdiff" not in blob
|
||||
assert all(row.get("recognition_status") != "FAIL" for row in sec.recognized_instances)
|
||||
Reference in New Issue
Block a user