Add protocol-certification M10 catalog skeletons (2.72.0).
PCIe/CXL keep logical vs physical ids. I2C, CAN, RGMII and missing high-speed processor interfaces get required_checks; numbers stay UNKNOWN/MISSING_SOURCE until an official cite. No invented ohms.
This commit is contained in:
@@ -320,6 +320,27 @@ def _c(
|
||||
return row
|
||||
|
||||
|
||||
def _skel_constraints(
|
||||
iface_id: str,
|
||||
parameters: list[str],
|
||||
value_kind: ValueKind = "MISSING_SOURCE",
|
||||
*,
|
||||
mandatory: MandatoryClass = "MANDATORY",
|
||||
source_type: SourceType = "STANDARD",
|
||||
source_class: SourceClass = "NORMATIVE",
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Named checks with no numbers. Cite later; never invent ohm/dB."""
|
||||
return [
|
||||
_c(
|
||||
iface_id, p, value_kind,
|
||||
mandatory=mandatory,
|
||||
source_type=source_type,
|
||||
source_class=source_class,
|
||||
)
|
||||
for p in parameters
|
||||
]
|
||||
|
||||
|
||||
def _iface(
|
||||
pid: str,
|
||||
logical: str,
|
||||
@@ -415,6 +436,35 @@ PCIE_CHECKS = [
|
||||
"connectors",
|
||||
"return_path",
|
||||
]
|
||||
PCI_PARALLEL_CHECKS = [
|
||||
"impedance",
|
||||
"length",
|
||||
"topology",
|
||||
"termination",
|
||||
"stub_length",
|
||||
"return_path",
|
||||
]
|
||||
I2C_CHECKS = [
|
||||
"pull_up",
|
||||
"rise_time",
|
||||
"bus_capacitance",
|
||||
"voltage",
|
||||
"levels",
|
||||
]
|
||||
CAN_CHECKS = [
|
||||
"termination",
|
||||
"topology",
|
||||
"stub_length",
|
||||
"bus_impedance",
|
||||
"bitrate_dependent",
|
||||
]
|
||||
RGMII_CHECKS = [
|
||||
"timing",
|
||||
"clock_data_relationship",
|
||||
"length",
|
||||
"delay",
|
||||
"topology",
|
||||
]
|
||||
HYPERBUS_CHECKS = [
|
||||
"dq_to_rwds_skew",
|
||||
"ck_to_dq_relationship",
|
||||
@@ -481,8 +531,22 @@ def build_m0_catalog() -> dict[str, Any]:
|
||||
L("avalon-mm", "Avalon-MM", "PROCESSOR_INTERCONNECT", "INTEL_FPGA")
|
||||
L("avalon-st", "Avalon-ST", "PROCESSOR_INTERCONNECT", "INTEL_FPGA")
|
||||
L("tilelink", "TileLink", "PROCESSOR_INTERCONNECT", "RISC-V")
|
||||
L("pcie", "PCI Express (logical)", "CHIP_TO_CHIP", "PCIE")
|
||||
L("cxl", "CXL (logical)", "CHIP_TO_CHIP", "CXL")
|
||||
L("pcie", "PCI Express (logical)", "CHIP_TO_CHIP", "PCIE",
|
||||
"Logical protocol. PCB rules attach to pcie-phy-pcb, not this id.")
|
||||
L("cxl", "CXL (logical)", "CHIP_TO_CHIP", "CXL",
|
||||
"Logical protocol. Not the same id as pcie; PCB rules on cxl-phy-pcb.")
|
||||
L("pci", "PCI (logical)", "CHIP_TO_CHIP", "PCI")
|
||||
L("pci-x", "PCI-X (logical)", "CHIP_TO_CHIP", "PCI")
|
||||
L("ccix", "CCIX (logical)", "CHIP_TO_CHIP", "CCIX")
|
||||
L("opencapi", "OpenCAPI (logical)", "CHIP_TO_CHIP", "OPENCAPI")
|
||||
L("upi", "Intel UPI (logical)", "CHIP_TO_CHIP", "INTEL")
|
||||
L("dmi", "Intel DMI (logical)", "CHIP_TO_CHIP", "INTEL")
|
||||
L("infinity-fabric", "AMD Infinity Fabric (logical)", "CHIP_TO_CHIP", "AMD")
|
||||
L("hypertransport", "HyperTransport (logical)", "CHIP_TO_CHIP", "HT")
|
||||
L("ucie", "UCIe (logical)", "CHIP_TO_CHIP", "UCIE")
|
||||
L("i2c", "I2C / SMBus", "PERIPHERAL", "I2C")
|
||||
L("can", "CAN / CAN FD", "PERIPHERAL", "CAN")
|
||||
L("rgmii", "RGMII MAC-PHY", "PERIPHERAL", "ETHERNET")
|
||||
|
||||
missing_usb = [
|
||||
_c("usb2-hs-dpair", "differential_impedance", "MISSING_SOURCE",
|
||||
@@ -742,23 +806,133 @@ def build_m0_catalog() -> dict[str, Any]:
|
||||
physical.append(_iface(
|
||||
"pcie-phy-pcb", "pcie", "CHIP_TO_CHIP", "SERIAL_DIFFERENTIAL", "YES",
|
||||
PCIE_CHECKS,
|
||||
[
|
||||
_c("pcie-phy-pcb", "differential_impedance", "MISSING_SOURCE"),
|
||||
_c("pcie-phy-pcb", "intra_pair_skew", "MISSING_SOURCE"),
|
||||
_c("pcie-phy-pcb", "insertion_loss", "MISSING_SOURCE"),
|
||||
],
|
||||
_skel_constraints("pcie-phy-pcb", PCIE_CHECKS),
|
||||
hints=["PCIE", "PETX", "PERX", "REFCLK"],
|
||||
notes="Logical vs physical split. Empty numbers until PCI-SIG cite.",
|
||||
notes=(
|
||||
"Logical pcie vs physical pcie-phy-pcb. Empty numbers until PCI-SIG cite. "
|
||||
"Not USB-IF. Not invented ohms."
|
||||
),
|
||||
))
|
||||
physical.append(_iface(
|
||||
"cxl-phy-pcb", "cxl", "CHIP_TO_CHIP", "SERIAL_DIFFERENTIAL", "YES",
|
||||
PCIE_CHECKS,
|
||||
[
|
||||
_c("cxl-phy-pcb", "differential_impedance", "MISSING_SOURCE"),
|
||||
_c("cxl-phy-pcb", "intra_pair_skew", "MISSING_SOURCE"),
|
||||
],
|
||||
_skel_constraints("cxl-phy-pcb", PCIE_CHECKS),
|
||||
hints=["CXL"],
|
||||
notes="Physical electrical pack empty until official cite.",
|
||||
notes=(
|
||||
"Logical cxl vs physical cxl-phy-pcb (not pcie). "
|
||||
"pcb_relevant YES. Empty numbers until CXL Consortium/PCI-SIG cite."
|
||||
),
|
||||
))
|
||||
physical.append(_iface(
|
||||
"pci-pcb", "pci", "CHIP_TO_CHIP", "PARALLEL_SINGLE_ENDED", "YES",
|
||||
PCI_PARALLEL_CHECKS,
|
||||
_skel_constraints("pci-pcb", PCI_PARALLEL_CHECKS),
|
||||
hints=["PCI_AD", "PCI_C/BE", "FRAME#"],
|
||||
notes="PCI logical vs pci-pcb. No invented bus ohms."
|
||||
))
|
||||
physical.append(_iface(
|
||||
"pci-x-pcb", "pci-x", "CHIP_TO_CHIP", "PARALLEL_SINGLE_ENDED", "YES",
|
||||
PCI_PARALLEL_CHECKS,
|
||||
_skel_constraints("pci-x-pcb", PCI_PARALLEL_CHECKS),
|
||||
hints=["PCIX", "PCI-X"],
|
||||
notes="PCI-X logical vs pci-x-pcb. Numbers stay MISSING_SOURCE."
|
||||
))
|
||||
for hid, lid, hints, note in (
|
||||
("ccix-phy-pcb", "ccix", ["CCIX"], "CCIX PHY on PCB; not the logical id."),
|
||||
("opencapi-phy-pcb", "opencapi", ["OPENCAPI", "OCAPI"],
|
||||
"OpenCAPI PHY on PCB; empty until cite."),
|
||||
("hypertransport-phy-pcb", "hypertransport", ["HYPERTRANSPORT", "HT_"],
|
||||
"HyperTransport chip-to-chip PCB PHY."),
|
||||
):
|
||||
physical.append(_iface(
|
||||
hid, lid, "CHIP_TO_CHIP", "SERIAL_DIFFERENTIAL", "YES",
|
||||
PCIE_CHECKS,
|
||||
_skel_constraints(hid, PCIE_CHECKS),
|
||||
hints=hints,
|
||||
notes=note,
|
||||
))
|
||||
for hid, lid, hints, note in (
|
||||
("upi-phy", "upi", ["UPI", "QPI"],
|
||||
"Intel UPI: package vs board is CONDITIONAL. No invented Z."),
|
||||
("dmi-phy", "dmi", ["DMI"],
|
||||
"Intel DMI: CPU–PCH may be package or PCB — CONDITIONAL."),
|
||||
("infinity-fabric-phy", "infinity-fabric", ["INFINITY_FABRIC", "GMI"],
|
||||
"AMD Infinity Fabric: on-package vs board CONDITIONAL."),
|
||||
):
|
||||
physical.append(_iface(
|
||||
hid, lid, "CHIP_TO_CHIP", "SERIAL_DIFFERENTIAL", "CONDITIONAL",
|
||||
PCIE_CHECKS,
|
||||
_skel_constraints(
|
||||
hid, PCIE_CHECKS, "PHY_DEPENDENT",
|
||||
source_type="PHY", source_class="VENDOR",
|
||||
),
|
||||
hints=hints,
|
||||
notes=note,
|
||||
))
|
||||
physical.append(_iface(
|
||||
"ucie-die-to-die", "ucie", "CHIP_TO_CHIP", "INTERNAL", "CONDITIONAL",
|
||||
["package", "silicon", "impedance", "timing"],
|
||||
[
|
||||
_c("ucie-die-to-die", "package", "NOT_APPLICABLE",
|
||||
mandatory="INFORMATIONAL", source_type="COMPONENT",
|
||||
source_class="IMPLEMENTATION"),
|
||||
_c("ucie-die-to-die", "impedance", "PHY_DEPENDENT",
|
||||
source_type="PHY", source_class="VENDOR"),
|
||||
_c("ucie-die-to-die", "timing", "PHY_DEPENDENT",
|
||||
source_type="PHY", source_class="VENDOR"),
|
||||
],
|
||||
hints=["UCIE"],
|
||||
notes=(
|
||||
"UCIe is die-to-die / advanced package. pcb_relevant CONDITIONAL; "
|
||||
"classic PCB routing is not assumed. Not OpenEMS."
|
||||
),
|
||||
))
|
||||
physical.append(_iface(
|
||||
"i2c-pcb", "i2c", "PERIPHERAL", "SERIAL_SINGLE_ENDED", "YES",
|
||||
I2C_CHECKS,
|
||||
[
|
||||
_c("i2c-pcb", "pull_up", "MISSING_SOURCE"),
|
||||
_c("i2c-pcb", "rise_time", "MISSING_SOURCE"),
|
||||
_c("i2c-pcb", "bus_capacitance", "MISSING_SOURCE"),
|
||||
_c("i2c-pcb", "voltage", "PHY_DEPENDENT",
|
||||
source_type="PHY", source_class="VENDOR"),
|
||||
_c("i2c-pcb", "levels", "PHY_DEPENDENT",
|
||||
source_type="PHY", source_class="VENDOR"),
|
||||
],
|
||||
hints=["SCL", "SDA", "I2C", "SMBUS"],
|
||||
notes="I2C skeleton. No invented pull-up ohms or 400 kHz folklore."
|
||||
))
|
||||
physical.append(_iface(
|
||||
"can-pcb", "can", "PERIPHERAL", "SERIAL_DIFFERENTIAL", "YES",
|
||||
CAN_CHECKS,
|
||||
[
|
||||
_c("can-pcb", "termination", "MISSING_SOURCE"),
|
||||
_c("can-pcb", "topology", "UNKNOWN"),
|
||||
_c("can-pcb", "stub_length", "UNKNOWN"),
|
||||
_c("can-pcb", "bus_impedance", "MISSING_SOURCE"),
|
||||
_c("can-pcb", "bitrate_dependent", "CONTROLLER_DEPENDENT",
|
||||
source_type="CONTROLLER", source_class="VENDOR"),
|
||||
],
|
||||
hints=["CANH", "CANL", "CAN_H", "CAN_L"],
|
||||
notes="CAN skeleton. Termination not invented (no typical-ohm folklore)."
|
||||
))
|
||||
physical.append(_iface(
|
||||
"rgmii-pcb", "rgmii", "PERIPHERAL", "PARALLEL_SINGLE_ENDED", "YES",
|
||||
RGMII_CHECKS,
|
||||
[
|
||||
_c("rgmii-pcb", "timing", "PHY_DEPENDENT",
|
||||
source_type="PHY", source_class="VENDOR"),
|
||||
_c("rgmii-pcb", "clock_data_relationship", "PHY_DEPENDENT",
|
||||
source_type="PHY", source_class="VENDOR"),
|
||||
_c("rgmii-pcb", "length", "UNKNOWN"),
|
||||
_c("rgmii-pcb", "delay", "UNKNOWN"),
|
||||
_c("rgmii-pcb", "topology", "UNKNOWN"),
|
||||
],
|
||||
hints=["RGMII", "TX_CTL", "RX_CTL", "GTX_CLK", "RX_CLK"],
|
||||
notes=(
|
||||
"RGMII MAC–PHY. Timing/delay UNKNOWN without PHY cite. "
|
||||
"Length is not delay. Not 100BASE-TX magnetics."
|
||||
),
|
||||
))
|
||||
|
||||
return {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,13 @@
|
||||
|
||||
What's new in Periscope.
|
||||
|
||||
## 2.72.0 — 2026-09-22 — Protocol certification M10 (catalog skeletons)
|
||||
|
||||
Catalog skeletons and `required_checks` for **PCIe / CXL** (logical vs physical), **I2C**, **CAN**, **RGMII**, plus missing high-speed processor ids (PCI, PCI-X, CCIX, OpenCAPI, UPI, DMI, Infinity Fabric, HyperTransport, UCIe). Numbers stay **UNKNOWN / MISSING_SOURCE / PHY_DEPENDENT** until an official cite is in the pack. `pcb_relevant` is YES or CONDITIONAL as appropriate. No invented ohms, no USB-IF fill, no OpenEMS.
|
||||
|
||||
- [Changed] `protocol_catalog.py` + committed `catalog.json`.
|
||||
- [New] pytest `tests/pcb/test_protocol_catalog_m10.py`.
|
||||
|
||||
## 2.71.0 — 2026-09-22 — Protocol certification M9 (L3 channel + visible skip)
|
||||
|
||||
L3 **CHANNEL** certifier: structure for timing budget (TOTAL−USED=MARGIN), insertion loss, return loss, crosstalk, and complete channel. Numerical PASS/FAIL only when **existing channel FACT** (dB/ps) is already present — never invented S-parameters, never OpenEMS/FEM. Otherwise a visible skip: «Interfaccia X non certificata a L3 per mancanza di …». Internals (AXI/HBM) stay NOT_APPLICABLE. L0/L1 are not electrical. No M6 numeric packs.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "periscope-web",
|
||||
"version": "2.71.0",
|
||||
"version": "2.72.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"sync-version": "node scripts/sync-version.mjs",
|
||||
|
||||
@@ -29,6 +29,9 @@ REQUIRED_LOGICAL = {
|
||||
"hbm-family", "hbm3", "hyperbus", "qspi", "octal-spi", "emmc", "ufs",
|
||||
"axi4", "axi4-lite", "ahb", "apb", "wishbone", "avalon-mm", "avalon-st",
|
||||
"tilelink", "pcie", "cxl",
|
||||
"pci", "pci-x", "ccix", "opencapi", "upi", "dmi",
|
||||
"infinity-fabric", "hypertransport", "ucie",
|
||||
"i2c", "can", "rgmii",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
"""M10 catalog skeletons: PCIe/CXL logical vs physical, I2C/CAN/RGMII, HS processor ids."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from backend.periscopex.protocol_catalog import (
|
||||
CAN_CHECKS,
|
||||
I2C_CHECKS,
|
||||
PCIE_CHECKS,
|
||||
PCI_PARALLEL_CHECKS,
|
||||
RGMII_CHECKS,
|
||||
build_m0_catalog,
|
||||
load_catalog,
|
||||
parse_catalog,
|
||||
)
|
||||
|
||||
HS_LOGICAL = {
|
||||
"pci", "pci-x", "pcie", "cxl", "ccix", "opencapi",
|
||||
"upi", "dmi", "infinity-fabric", "hypertransport", "ucie",
|
||||
}
|
||||
|
||||
NO_INVENTED = ("90", "100 ", "50 ", "120")
|
||||
|
||||
|
||||
def test_m10_logical_physical_split_pcie_cxl():
|
||||
cat = load_catalog()
|
||||
pcie_log = next(p for p in cat.logical_protocols if p.id == "pcie")
|
||||
cxl_log = next(p for p in cat.logical_protocols if p.id == "cxl")
|
||||
pcie_phy = next(p for p in cat.physical_interfaces if p.id == "pcie-phy-pcb")
|
||||
cxl_phy = next(p for p in cat.physical_interfaces if p.id == "cxl-phy-pcb")
|
||||
assert pcie_log.id != pcie_phy.id
|
||||
assert cxl_log.id != cxl_phy.id
|
||||
assert pcie_phy.logical_protocol_id == "pcie"
|
||||
assert cxl_phy.logical_protocol_id == "cxl"
|
||||
assert pcie_phy.logical_protocol_id != cxl_phy.logical_protocol_id
|
||||
assert pcie_phy.pcb_relevant == "YES"
|
||||
assert cxl_phy.pcb_relevant == "YES"
|
||||
assert set(pcie_phy.required_checks) == set(PCIE_CHECKS)
|
||||
assert set(cxl_phy.required_checks) == set(PCIE_CHECKS)
|
||||
|
||||
|
||||
def test_m10_high_speed_processor_ids_present():
|
||||
cat = load_catalog()
|
||||
logical = {p.id for p in cat.logical_protocols}
|
||||
assert HS_LOGICAL <= logical
|
||||
physical = {p.id: p for p in cat.physical_interfaces}
|
||||
assert physical["pci-pcb"].pcb_relevant == "YES"
|
||||
assert physical["pci-x-pcb"].pcb_relevant == "YES"
|
||||
assert set(physical["pci-pcb"].required_checks) == set(PCI_PARALLEL_CHECKS)
|
||||
assert physical["ccix-phy-pcb"].pcb_relevant == "YES"
|
||||
assert physical["opencapi-phy-pcb"].pcb_relevant == "YES"
|
||||
assert physical["hypertransport-phy-pcb"].pcb_relevant == "YES"
|
||||
assert physical["upi-phy"].pcb_relevant == "CONDITIONAL"
|
||||
assert physical["dmi-phy"].pcb_relevant == "CONDITIONAL"
|
||||
assert physical["infinity-fabric-phy"].pcb_relevant == "CONDITIONAL"
|
||||
assert physical["ucie-die-to-die"].pcb_relevant == "CONDITIONAL"
|
||||
assert physical["ucie-die-to-die"].logical_protocol_id == "ucie"
|
||||
for pid in HS_LOGICAL:
|
||||
ifaces = [p for p in cat.physical_interfaces if p.logical_protocol_id == pid]
|
||||
assert ifaces, pid
|
||||
assert all(p.id != pid for p in ifaces)
|
||||
|
||||
|
||||
def test_m10_i2c_can_rgmii_skeletons_named_checks_no_numbers():
|
||||
cat = load_catalog()
|
||||
i2c = next(p for p in cat.physical_interfaces if p.id == "i2c-pcb")
|
||||
can = next(p for p in cat.physical_interfaces if p.id == "can-pcb")
|
||||
rgmii = next(p for p in cat.physical_interfaces if p.id == "rgmii-pcb")
|
||||
assert i2c.logical_protocol_id == "i2c"
|
||||
assert can.logical_protocol_id == "can"
|
||||
assert rgmii.logical_protocol_id == "rgmii"
|
||||
assert i2c.pcb_relevant == "YES"
|
||||
assert can.pcb_relevant == "YES"
|
||||
assert rgmii.pcb_relevant == "YES"
|
||||
assert set(i2c.required_checks) == set(I2C_CHECKS)
|
||||
assert set(can.required_checks) == set(CAN_CHECKS)
|
||||
assert set(rgmii.required_checks) == set(RGMII_CHECKS)
|
||||
for iface in (i2c, can, rgmii):
|
||||
for c in iface.constraints:
|
||||
assert c.value is None
|
||||
assert c.value_kind in {
|
||||
"MISSING_SOURCE", "UNKNOWN", "PHY_DEPENDENT",
|
||||
"CONTROLLER_DEPENDENT", "VENDOR_DEPENDENT", "NOT_APPLICABLE",
|
||||
}
|
||||
assert c.value_kind != "NUMERIC"
|
||||
|
||||
|
||||
def test_m10_no_invented_ohms_and_no_usbif_fill():
|
||||
loaded = load_catalog()
|
||||
for iface in loaded.physical_interfaces:
|
||||
for c in iface.constraints:
|
||||
assert c.value is None
|
||||
assert c.value_kind != "NUMERIC" or (c.source is not None)
|
||||
if c.value_kind != "NUMERIC":
|
||||
assert c.unit is None
|
||||
blob = json.dumps([
|
||||
c.model_dump() for p in loaded.physical_interfaces for c in p.constraints
|
||||
if p.logical_protocol_id in HS_LOGICAL | {"i2c", "can", "rgmii"}
|
||||
])
|
||||
for token in NO_INVENTED:
|
||||
assert token not in blob
|
||||
usb = next(p for p in loaded.physical_interfaces if p.id == "usb2-hs-dpair")
|
||||
z = next(c for c in usb.constraints if c.parameter == "differential_impedance")
|
||||
assert z.value_kind == "MISSING_SOURCE"
|
||||
assert z.value is None
|
||||
assert z.source is None
|
||||
|
||||
|
||||
def test_m10_builder_matches_committed_catalog():
|
||||
loaded = load_catalog()
|
||||
rebuilt = parse_catalog(build_m0_catalog())
|
||||
assert loaded.model_dump() == rebuilt.model_dump()
|
||||
Reference in New Issue
Block a user