"""M8 DDR instance + L1 grouping: byte lanes, addr/cmd/ctrl/ck, controller-aware, HBM N/A.""" from __future__ import annotations import json from backend.periscopex.models import ( Component, ComponentType, DesignGraph, Net, NetType, PinConnection, ) from backend.periscopex.protocol_l0 import protocol_exam from backend.periscopex.protocol_l1 import certify_l1 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 _ddr4_pins() -> dict[str, str]: 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", "15": "DDR4_CS", "16": "DDR4_CKE", }) return pins def _fpga_ddr_graph() -> DesignGraph: pins = _ddr4_pins() mem = _ic("U5", pins, mpn="MT41K256M16TW", value="DDR4") fpga = _ic("U3", dict(pins), mpn="XC7A100T", value="Artix-7 DDR3/DDR4 controller") nets = {n: _net(n, ("U5", p), ("U3", p)) for p, n in pins.items()} return DesignGraph(components={"U5": mem, "U3": fpga}, nets=nets) def _two_ddr_graph() -> DesignGraph: a = _ddr4_pins() b = {p: n.replace("DDR4_", "DDR4B_") for p, n in a.items()} m1 = _ic("U5", a, mpn="MT41K256M16TW", value="DDR4") m2 = _ic("U6", b, mpn="MT41K256M16TW", value="DDR4") fpga = _ic( "U3", {**{f"a{p}": n for p, n in a.items()}, **{f"b{p}": n for p, n in b.items()}}, mpn="XC7A100T", value="FPGA memory controller", ) nets = {n: _net(n, ("U5", p), ("U3", f"a{p}")) for p, n in a.items()} nets.update({n: _net(n, ("U6", p), ("U3", f"b{p}")) for p, n in b.items()}) return DesignGraph(components={"U5": m1, "U6": m2, "U3": fpga}, nets=nets) def _hbm_graph() -> DesignGraph: pins = {str(i): f"HBM_DQ{i}" for i in range(8)} pins["8"] = "HBM_DQS0_P" return DesignGraph( components={"U8": _ic("U8", pins, mpn="H26M64202AMR", value="HBM2e stack")}, nets={n: _net(n, ("U8", p)) for p, n in pins.items()}, ) def test_controller_aware_groups_split(): insts = recognize_physical_buses(_fpga_ddr_graph()) ddr = [i for i in insts if i.logical_protocol_id == "ddr4"] assert len(ddr) == 1 one = ddr[0] assert one.host_ref == "U3" assert "U5" in one.peer_refs kinds = {g.kind for g in one.groups} assert "BYTE_LANE" in kinds assert "CLOCK_GROUP" in kinds assert "ADDRESS_GROUP" in kinds assert "COMMAND_GROUP" in kinds assert "CONTROL_GROUP" in kinds data = {n for g in one.groups if g.kind == "BYTE_LANE" for n in g.nets} addr = {n for g in one.groups if g.kind == "ADDRESS_GROUP" for n in g.nets} cmd = {n for g in one.groups if g.kind == "COMMAND_GROUP" for n in g.nets} ctrl = {n for g in one.groups if g.kind == "CONTROL_GROUP" for n in g.nets} assert "DDR4_DQ0" in data and "DDR4_DQS0_P" in data assert "DDR4_A0" in addr assert "DDR4_RAS" in cmd assert "DDR4_CS" in ctrl or "DDR4_CKE" in ctrl assert not (data & addr) assert not (data & cmd) assert not (data & ctrl) blob = json.dumps(one.model_dump()) assert "90" not in blob assert "ohm" not in blob.lower() assert "50ps" not in blob.lower() def test_two_ddr4_are_two_instances(): insts = recognize_physical_buses(_two_ddr_graph()) ddr = [i for i in insts if i.logical_protocol_id == "ddr4"] assert len(ddr) == 2 refs = {tuple(sorted(i.peer_refs)) for i in ddr} assert ("U5",) in refs and ("U6",) in refs def test_l1_grouping_pass_without_layout_skew_stays_dependent(): g = _fpga_ddr_graph() insts = recognize_physical_buses(g) by, findings = certify_l1(g, insts, layout=None) rows = [r for i in insts if i.logical_protocol_id == "ddr4" for r in by[i.instance_id]] mapping = [r for r in rows if r.check == "byte_lane_mapping"] assert mapping and mapping[0].result == "PASS" skew = [r for r in rows if r.check == "dq_to_dqs_skew"] assert skew assert skew[0].result in {"PHY_DEPENDENT", "CONTROLLER_DEPENDENT", "VENDOR_DEPENDENT", "UNKNOWN"} assert skew[0].result != "PASS" assert skew[0].limit_mm is None blob = json.dumps([r.model_dump() for r in rows]) assert "90" not in blob assert "universal" in blob.lower() or "controller" in blob.lower() def test_hbm_not_classic_ddr_dq_rules(): insts = recognize_physical_buses(_hbm_graph()) hbm = [i for i in insts if "hbm" in i.logical_protocol_id] ddr = [i for i in insts if i.logical_protocol_id.startswith("ddr")] assert hbm assert not ddr one = hbm[0] assert one.pcb_relevant == "NO" assert one.nets == [] assert one.groups == [] assert one.physical_interface_id.endswith("-package") sec, findings = protocol_exam(_hbm_graph()) assert sec.macrophase == "M8" assert all(f.status != "ERROR" for f in findings) rows = [ c for r in sec.recognized_instances if "hbm" in str(r.get("logical_protocol_id")) for c in (r.get("l0_checks") or []) + (r.get("l1_checks") or []) ] assert rows assert all(c.get("result") == "NOT_APPLICABLE" for c in rows) blob = json.dumps(one.model_dump()) assert "BYTE_LANE" not in blob def test_incomplete_grouping_not_pass(): pins = {str(i): f"MEM_DAT{i}" for i in range(8)} g = DesignGraph( components={"U5": _ic("U5", pins, mpn="MT41K256M16TW", value="DDR4")}, nets={n: _net(n, ("U5", p)) for p, n in pins.items()}, ) insts = recognize_physical_buses(g) ddr = [i for i in insts if "ddr" in i.logical_protocol_id] assert ddr assert not any(g.kind == "BYTE_LANE" for g in ddr[0].groups) by, _ = certify_l1(g, insts, layout=None) rows = [r for i in ddr for r in by[i.instance_id]] mapping = [r for r in rows if r.check == "byte_lane_mapping"] assert mapping assert mapping[0].result != "PASS"