NUMERIC rows cite document title, revision, section/table, and page. USB Zdiff, Type-C Rp/Rd, 10BASE-T, and 1000BASE-T stay UNKNOWN. PDFs are not in git.
231 lines
8.4 KiB
Python
231 lines
8.4 KiB
Python
"""M6/M7 USB-C/Ethernet/HDMI packs: UNKNOWN + needed_document, no invented ohms."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import copy
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from backend.periscopex.models import (
|
|
Component,
|
|
ComponentType,
|
|
DesignGraph,
|
|
Net,
|
|
NetType,
|
|
PinConnection,
|
|
)
|
|
from backend.periscopex.protocol_catalog import (
|
|
ProtocolCatalogError,
|
|
build_m0_catalog,
|
|
load_catalog,
|
|
parse_catalog,
|
|
)
|
|
from backend.periscopex.protocol_recognize import 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 test_unknown_requires_needed_document():
|
|
raw = build_m0_catalog()
|
|
iface = copy.deepcopy(raw["physical_interfaces"][0])
|
|
iface["id"] = "usb2-hs-no-doc"
|
|
bad = copy.deepcopy(iface["constraints"][0])
|
|
bad["id"] = "no-doc"
|
|
bad["value_kind"] = "UNKNOWN"
|
|
bad["value"] = None
|
|
bad["needed_document"] = None
|
|
iface["constraints"] = [bad]
|
|
raw["physical_interfaces"].append(iface)
|
|
with pytest.raises(ProtocolCatalogError, match="needed_document"):
|
|
parse_catalog(raw)
|
|
|
|
|
|
def test_reject_typical_as_standard_still_holds():
|
|
raw = build_m0_catalog()
|
|
iface = copy.deepcopy(raw["physical_interfaces"][0])
|
|
typical = copy.deepcopy(iface["constraints"][0])
|
|
typical["id"] = "bad-typical-m67"
|
|
typical["typical"] = True
|
|
typical["value_origin"] = "TYPICAL"
|
|
typical["source_type"] = "STANDARD"
|
|
typical["source_class"] = "NORMATIVE"
|
|
typical["mandatory"] = "MANDATORY"
|
|
typical["value_kind"] = "UNKNOWN"
|
|
typical["value"] = None
|
|
typical["needed_document"] = "USB-IF Base Specification"
|
|
iface["id"] = "usb2-hs-typical-m67"
|
|
iface["constraints"] = [typical]
|
|
raw["physical_interfaces"].append(iface)
|
|
with pytest.raises(ProtocolCatalogError, match="typical"):
|
|
parse_catalog(raw)
|
|
|
|
|
|
def test_usb2_and_usb_c_unknown_with_usbif_needed_document_no_90ohm():
|
|
cat = load_catalog()
|
|
logical = {p.id for p in cat.logical_protocols}
|
|
assert {"usb2-ls", "usb2-fs", "usb2-hs", "usb-c", "usb3-x", "usb4"} <= logical
|
|
assert "mini-hdmi" not in logical
|
|
usb_ifaces = [
|
|
p for p in cat.physical_interfaces
|
|
if p.logical_protocol_id in {"usb2-ls", "usb2-fs", "usb2-hs", "usb2-ls-fs", "usb-c", "usb3-x", "usb4"}
|
|
or p.id.startswith("usb-")
|
|
]
|
|
assert usb_ifaces
|
|
z_blob = json.dumps([
|
|
c.model_dump() for p in usb_ifaces for c in p.constraints
|
|
if c.parameter == "differential_impedance"
|
|
])
|
|
assert "90" not in z_blob
|
|
for p in usb_ifaces:
|
|
for c in p.constraints:
|
|
if c.parameter == "differential_impedance":
|
|
assert c.value is None
|
|
assert c.value_kind != "NUMERIC"
|
|
if c.value_kind in {"UNKNOWN", "MISSING_SOURCE"}:
|
|
assert c.needed_document
|
|
assert "USB-IF" in c.needed_document or "specification" in c.needed_document.lower() or "Specification" in c.needed_document
|
|
if p.logical_protocol_id == "usb-c" and c.parameter in {"rp", "rd"}:
|
|
assert c.value_kind == "UNKNOWN"
|
|
over = next(p for p in cat.physical_interfaces if p.id == "usb-c-usb2-receptacle")
|
|
assert over.logical_protocol_id == "usb2-hs"
|
|
assert over.connector == "Type-C"
|
|
sys = next(p for p in cat.physical_interfaces if p.id == "usb-c-receptacle")
|
|
assert sys.logical_protocol_id == "usb-c"
|
|
assert sys.connector == "Type-C"
|
|
|
|
|
|
def test_usb_c_connector_alone_is_not_usb3_or_usb2_protocol():
|
|
g = DesignGraph(
|
|
components={
|
|
"J1": Component(
|
|
reference="J1", value="USB Type-C receptacle",
|
|
footprint="USB_C_Receptacle",
|
|
component_type=ComponentType.CONNECTOR,
|
|
pins={"A5": "CC1"},
|
|
),
|
|
},
|
|
nets={"CC1": _net("CC1", ("J1", "A5"))},
|
|
)
|
|
insts = recognize_physical_buses(g)
|
|
logs = {i.logical_protocol_id for i in insts}
|
|
assert "usb3-x" not in logs
|
|
assert "usb4" not in logs
|
|
assert "usb2-hs" not in logs
|
|
assert any(i.logical_protocol_id == "usb-c" for i in insts)
|
|
blob = json.dumps([i.model_dump() for i in insts])
|
|
assert "90" not in blob
|
|
|
|
|
|
def test_usb2_over_type_c_from_connector_nets_silicon_not_usb3():
|
|
g = 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")),
|
|
},
|
|
)
|
|
insts = recognize_physical_buses(g)
|
|
usb = [i for i in insts if i.logical_protocol_id.startswith("usb2")]
|
|
assert usb
|
|
one = usb[0]
|
|
assert one.connector == "Type-C"
|
|
assert "USB 2.0 over USB Type-C" in one.physical_implementation
|
|
assert one.logical_protocol_id != "usb3-x"
|
|
assert all(i.logical_protocol_id != "usb3-x" for i in insts)
|
|
assert "mux not assumed" in one.notes.lower() or "mux/switch not assumed" in one.notes.lower()
|
|
blob = json.dumps(one.model_dump())
|
|
assert "90" not in blob
|
|
|
|
|
|
def test_rj45_alone_is_not_1000base_t():
|
|
g = DesignGraph(
|
|
components={
|
|
"J9": Component(
|
|
reference="J9", value="RJ45",
|
|
footprint="RJ45",
|
|
component_type=ComponentType.CONNECTOR,
|
|
pins={"1": "TD+"},
|
|
),
|
|
},
|
|
nets={"TD+": _net("TD+", ("J9", "1"))},
|
|
)
|
|
insts = recognize_physical_buses(g)
|
|
logs = {i.logical_protocol_id for i in insts}
|
|
assert "1000base-t" not in logs
|
|
assert "100base-tx" not in logs
|
|
assert any(i.logical_protocol_id == "ethernet-unspecified" for i in insts)
|
|
|
|
|
|
def test_mini_hdmi_is_connector_not_protocol_id():
|
|
cat = load_catalog()
|
|
logical = {p.id for p in cat.logical_protocols}
|
|
assert "mini-hdmi" not in logical
|
|
assert "hdmi-mini" not in logical
|
|
mini = next(p for p in cat.physical_interfaces if p.id == "hdmi-1.4-tmds-type-c-mini")
|
|
assert mini.logical_protocol_id == "hdmi-1.4"
|
|
assert mini.connector == "HDMI-C-mini"
|
|
assert "CONNECTOR" in mini.notes or "connector" in mini.notes.lower()
|
|
g = DesignGraph(
|
|
components={
|
|
"J8": Component(
|
|
reference="J8", value="Mini HDMI",
|
|
footprint="HDMI_C_Mini",
|
|
component_type=ComponentType.CONNECTOR,
|
|
pins={"1": "HPD"},
|
|
),
|
|
},
|
|
nets={"HPD": _net("HPD", ("J8", "1"))},
|
|
)
|
|
insts = recognize_physical_buses(g)
|
|
assert all(i.logical_protocol_id != "mini-hdmi" for i in insts)
|
|
hdmi = [i for i in insts if "hdmi" in i.logical_protocol_id]
|
|
assert hdmi
|
|
assert hdmi[0].connector == "HDMI-C-mini"
|
|
assert hdmi[0].logical_protocol_id.startswith("hdmi-")
|
|
|
|
|
|
def test_ethernet_hdmi_skeletons_unknown_needed_document():
|
|
cat = load_catalog()
|
|
for lid in (
|
|
"10base-t", "1000base-t", "2.5gbase-t", "5gbase-t",
|
|
"10gbase-t", "sgmii", "1000base-x", "10gbase-r",
|
|
"hdmi-1.4", "hdmi-2.0", "hdmi-frl",
|
|
):
|
|
ifaces = [p for p in cat.physical_interfaces if p.logical_protocol_id == lid]
|
|
assert ifaces, lid
|
|
assert all(p.incomplete for p in ifaces)
|
|
for p in ifaces:
|
|
for c in p.constraints:
|
|
assert c.value is None
|
|
assert c.value_kind != "NUMERIC"
|
|
if c.value_kind in {"UNKNOWN", "MISSING_SOURCE"}:
|
|
assert c.needed_document
|
|
if lid.startswith("hdmi"):
|
|
assert "HDMI" in c.needed_document
|
|
else:
|
|
assert "IEEE" in c.needed_document or "PHY" in (c.needed_document or "") or "SGMII" in (c.needed_document or "")
|
|
tmds = next(p for p in cat.physical_interfaces if p.id == "hdmi-1.4-tmds-type-a")
|
|
frl = next(p for p in cat.physical_interfaces if p.id == "hdmi-frl-pcb")
|
|
assert tmds.logical_protocol_id != frl.logical_protocol_id
|