Gate SI layout_rules to the quoted bus, not EN RC onto USB.
PE-SI-009 no longer treats ESP32 CHIP_PU/EN RC as a USB series R. USB without a library Z number stays PE-SI-010 measurement-only. Pintable skill 1.13.0 requires net_class on SI kinds (usb2/usb3/MDI/RGMII/DDR3).
This commit is contained in:
@@ -48,10 +48,35 @@ def test_si_refresh_when_old_extract_has_only_decoupling():
|
||||
)
|
||||
assert not needs_layout_rules_refresh(
|
||||
{
|
||||
"model_version": "1.9.0",
|
||||
"layout_rules": [{"kind": "decoupling_proximity", "max_distance_mm": None}],
|
||||
"model_version": "1.12.0",
|
||||
"layout_rules": [{"kind": "series_resistor", "value_ohms": 10000}],
|
||||
},
|
||||
min_scan_version="1.10.0",
|
||||
min_scan_version="1.12.0",
|
||||
)
|
||||
assert needs_layout_rules_refresh(
|
||||
{
|
||||
"model_version": "1.12.0",
|
||||
"layout_rules": [{
|
||||
"kind": "series_resistor",
|
||||
"value_ohms": 10000,
|
||||
"note": "EN RC 10 kΩ / 1 µF",
|
||||
}],
|
||||
},
|
||||
min_scan_version="1.13.0",
|
||||
)
|
||||
assert not needs_layout_rules_refresh(
|
||||
{
|
||||
"model_version": "1.12.0",
|
||||
"layout_rules": [{"kind": "impedance", "net_class": "usb2", "zdiff_ohm": 90}],
|
||||
},
|
||||
min_scan_version="1.13.0",
|
||||
)
|
||||
assert not needs_layout_rules_refresh(
|
||||
{
|
||||
"model_version": "1.12.0",
|
||||
"layout_rules": [],
|
||||
},
|
||||
min_scan_version="1.13.0",
|
||||
)
|
||||
|
||||
|
||||
|
||||
+208
-2
@@ -69,7 +69,13 @@ def test_skip_i2c_gpio_cc_regn():
|
||||
assert skip_si_net("ESP32_EN")
|
||||
assert not skip_si_net("USB_D+")
|
||||
assert not skip_si_net("USB_DP")
|
||||
assert bus_class("USB_D+") == "usb"
|
||||
assert bus_class("USB_D+") == "usb2"
|
||||
assert bus_class("USB_SSTX_P") == "usb3"
|
||||
assert bus_class("ETH_TX+") == "eth_mdi"
|
||||
assert bus_class("RGMII_TXD0") == "rgmii"
|
||||
assert bus_class("SGMII_TX_P") == "sgmii"
|
||||
assert bus_class("DDR3_DQ0") == "ddr3_dq"
|
||||
assert bus_class("DDR3_DQS0_P") == "ddr3_dqs"
|
||||
assert bus_class("USB_CC1") is None
|
||||
assert bus_class("I2C_SCL") is None
|
||||
|
||||
@@ -170,7 +176,10 @@ def test_usb_without_library_z_is_insufficient_not_90ohm_fail():
|
||||
f = next(x for x in findings if x.rule_id == "PE-SI-010")
|
||||
assert f.evidence_status == "INSUFFICIENT"
|
||||
assert f.status == "INFO"
|
||||
assert "90" not in (f.requirement or "") or "folklore" in (f.inference or "").lower() or True
|
||||
assert "90" not in (f.requirement or "")
|
||||
blob = f"{f.finding} {f.requirement} {f.inference}"
|
||||
assert "FAIL" not in f.finding
|
||||
assert "90 Ω" not in blob or "folklore" in (f.inference or "").lower()
|
||||
assert "CC" not in (f.net or "")
|
||||
|
||||
|
||||
@@ -215,3 +224,200 @@ def test_i2c_not_checked_as_50_ohm():
|
||||
])
|
||||
assert all("I2C" not in (f.net or "") for f in findings)
|
||||
assert all("SDA" not in f.finding for f in findings)
|
||||
|
||||
|
||||
def test_en_rc_series_resistor_does_not_paint_usb():
|
||||
"""PE-SI-009 must not use ESP32 EN RC 10 kΩ / 1 µF as a USB series R."""
|
||||
cons = ComponentConstraints(
|
||||
mpn="ESP32",
|
||||
pintable=[Pin(number="1", name="D+"), Pin(number="3", name="EN")],
|
||||
absolute_maximum_ratings=[],
|
||||
rules=[],
|
||||
layout_rules=[{
|
||||
"kind": "series_resistor",
|
||||
"pin": "EN",
|
||||
"value_ohms": 10000,
|
||||
"note": "EN RC 10 kΩ / 1 µF",
|
||||
"source_page": 28,
|
||||
}],
|
||||
)
|
||||
findings = check_si(_usb_graph(), {"PHY": cons}, _usb_layout(), _if_rows())
|
||||
si009 = [f for f in findings if f.rule_id == "PE-SI-009"]
|
||||
assert si009 == []
|
||||
zfail = [f for f in findings if f.rule_id == "PE-SI-002"]
|
||||
assert zfail == []
|
||||
si010 = [f for f in findings if f.rule_id == "PE-SI-010"]
|
||||
assert si010
|
||||
assert si010[0].evidence_status == "INSUFFICIENT"
|
||||
assert "88" in (si010[0].finding or "")
|
||||
assert "90" not in (si010[0].requirement or "")
|
||||
|
||||
|
||||
def _phy_graph() -> DesignGraph:
|
||||
pins = {
|
||||
"1": "ETH_TX+", "2": "ETH_TX-",
|
||||
"3": "RGMII_TXD0", "4": "RGMII_TXD1",
|
||||
"5": "USB_SSTX_P", "6": "USB_SSTX_N",
|
||||
"7": "DDR3_DQ0", "8": "DDR3_DQ1",
|
||||
"9": "DDR3_DQS0_P", "10": "DDR3_DQS0_N",
|
||||
"11": "USB_D+", "12": "USB_D-",
|
||||
}
|
||||
nets = {
|
||||
n: Net(name=n, net_type=NetType.SIGNAL, pins=[
|
||||
PinConnection(component_ref="U1", pin_number=p),
|
||||
])
|
||||
for p, n in pins.items()
|
||||
}
|
||||
return DesignGraph(
|
||||
components={
|
||||
"U1": Component(
|
||||
reference="U1", value="PHY", footprint="",
|
||||
component_type=ComponentType.IC, mpn="PHY",
|
||||
pins=pins,
|
||||
),
|
||||
},
|
||||
nets=nets,
|
||||
)
|
||||
|
||||
|
||||
def _phy_layout() -> LayoutGraph:
|
||||
segs = [
|
||||
LayoutSegment(start=(0, 0), end=(10, 0), width=0.2, layer="F.Cu", net="ETH_TX+"),
|
||||
LayoutSegment(start=(0, 0.4), end=(10.2, 0.4), width=0.2, layer="F.Cu", net="ETH_TX-"),
|
||||
LayoutSegment(start=(0, 2), end=(8, 2), width=0.15, layer="F.Cu", net="RGMII_TXD0"),
|
||||
LayoutSegment(start=(0, 2.4), end=(8.4, 2.4), width=0.15, layer="F.Cu", net="RGMII_TXD1"),
|
||||
LayoutSegment(start=(0, 4), end=(20, 4), width=0.12, layer="F.Cu", net="USB_SSTX_P"),
|
||||
LayoutSegment(start=(0, 4.3), end=(20.5, 4.3), width=0.12, layer="F.Cu", net="USB_SSTX_N"),
|
||||
LayoutSegment(start=(0, 6), end=(15, 6), width=0.1, layer="F.Cu", net="DDR3_DQ0"),
|
||||
LayoutSegment(start=(0, 6.2), end=(15.1, 6.2), width=0.1, layer="F.Cu", net="DDR3_DQ1"),
|
||||
LayoutSegment(start=(0, 7), end=(12, 7), width=0.1, layer="F.Cu", net="DDR3_DQS0_P"),
|
||||
LayoutSegment(start=(0, 7.2), end=(12.3, 7.2), width=0.1, layer="F.Cu", net="DDR3_DQS0_N"),
|
||||
LayoutSegment(start=(0, 9), end=(10, 9), width=0.2, layer="F.Cu", net="USB_D+"),
|
||||
LayoutSegment(start=(0, 9.4), end=(12.5, 9.4), width=0.2, layer="F.Cu", net="USB_D-"),
|
||||
]
|
||||
return LayoutGraph(segments=segs, vias=[LayoutVia(x=1, y=0.2, net="GND", drill=0.3)])
|
||||
|
||||
|
||||
def _z(name, partner, avg, length, **kw):
|
||||
row = {
|
||||
"net_name": name,
|
||||
"partner_net_name": partner,
|
||||
"is_differential": True,
|
||||
"z0_avg_ohms": avg,
|
||||
"z0_min_ohms": avg - 2,
|
||||
"z0_max_ohms": avg + 2,
|
||||
"length_mm": length,
|
||||
"topologies": ["MICROSTRIP"],
|
||||
"flags": (),
|
||||
}
|
||||
row.update(kw)
|
||||
return row
|
||||
|
||||
|
||||
def test_coverage_mdi_rgmii_ddr3_usb3_gated_not_cross_mapped():
|
||||
cons = ComponentConstraints(
|
||||
mpn="PHY",
|
||||
pintable=[Pin(number="1", name="TX+")],
|
||||
absolute_maximum_ratings=[],
|
||||
rules=[],
|
||||
layout_rules=[
|
||||
{
|
||||
"kind": "impedance",
|
||||
"net_class": "eth_mdi",
|
||||
"zdiff_ohm": 100,
|
||||
"tolerance_pct": 10,
|
||||
"note": "MDI 100 Ω to RJ45",
|
||||
"source_page": 40,
|
||||
},
|
||||
{
|
||||
"kind": "max_length",
|
||||
"net_class": "rgmii",
|
||||
"max_distance_mm": 20,
|
||||
"note": "RGMII MAC–PHY trace < 20 mm",
|
||||
"source_page": 41,
|
||||
},
|
||||
{
|
||||
"kind": "length_match",
|
||||
"net_class": "ddr3",
|
||||
"max_distance_mm": 1.0,
|
||||
"note": "DDR3 DQS intra-pair",
|
||||
"source_page": 42,
|
||||
},
|
||||
{
|
||||
"kind": "impedance",
|
||||
"net_class": "usb3",
|
||||
"zdiff_ohm": 90,
|
||||
"tolerance_pct": 10,
|
||||
"note": "USB-C SuperSpeed 90 Ω",
|
||||
"source_page": 43,
|
||||
},
|
||||
{
|
||||
"kind": "series_resistor",
|
||||
"net_class": "rgmii",
|
||||
"value_ohms": 22,
|
||||
"note": "RGMII series 22 Ω",
|
||||
"source_page": 41,
|
||||
},
|
||||
],
|
||||
)
|
||||
rows = [
|
||||
_z("ETH_TX+", "ETH_TX-", 95.0, 10.0),
|
||||
_z("ETH_TX-", "ETH_TX+", 95.0, 10.2),
|
||||
_z("USB_SSTX_P", "USB_SSTX_N", 88.0, 20.0),
|
||||
_z("USB_SSTX_N", "USB_SSTX_P", 88.0, 20.5),
|
||||
_z("DDR3_DQS0_P", "DDR3_DQS0_N", 50.0, 12.0),
|
||||
_z("DDR3_DQS0_N", "DDR3_DQS0_P", 50.0, 12.3),
|
||||
_z("USB_D+", "USB_D-", 88.0, 10.0),
|
||||
_z("USB_D-", "USB_D+", 88.0, 12.5),
|
||||
{"net_name": "RGMII_TXD0", "z0_avg_ohms": 48.0, "length_mm": 8.0, "topologies": ["MICROSTRIP"]},
|
||||
{"net_name": "RGMII_TXD1", "z0_avg_ohms": 48.0, "length_mm": 8.4, "topologies": ["MICROSTRIP"]},
|
||||
{"net_name": "DDR3_DQ0", "z0_avg_ohms": 50.0, "length_mm": 15.0, "topologies": ["MICROSTRIP"]},
|
||||
]
|
||||
findings = check_si(_phy_graph(), {"PHY": cons}, _phy_layout(), rows)
|
||||
by = {}
|
||||
for f in findings:
|
||||
by.setdefault(f.rule_id, []).append(f)
|
||||
|
||||
mdi_z = [f for f in by.get("PE-SI-002", []) if f.net and "ETH" in f.net]
|
||||
assert mdi_z and mdi_z[0].finding.startswith("PASS:")
|
||||
usb3_z = [f for f in by.get("PE-SI-002", []) if f.net and "SSTX" in f.net]
|
||||
assert usb3_z and usb3_z[0].finding.startswith("PASS:")
|
||||
usb2_z = [f for f in by.get("PE-SI-002", []) if f.net and "USB_D" in (f.net or "")]
|
||||
assert usb2_z == []
|
||||
|
||||
rgmii_len = [f for f in by.get("PE-SI-003", []) if f.net and "RGMII" in f.net]
|
||||
assert rgmii_len and rgmii_len[0].finding.startswith("PASS:")
|
||||
assert not any(f.net and "ETH_TX" in f.net for f in by.get("PE-SI-003", []))
|
||||
|
||||
dqs = [f for f in by.get("PE-SI-001", []) if f.net and "DQS" in f.net]
|
||||
assert dqs and dqs[0].finding.startswith("PASS:")
|
||||
|
||||
si009 = by.get("PE-SI-009", [])
|
||||
assert si009
|
||||
assert all(f.net and "RGMII" in f.net for f in si009)
|
||||
assert not any(f.net and "USB" in f.net for f in si009)
|
||||
|
||||
si010 = by.get("PE-SI-010", [])
|
||||
nets010 = {f.net for f in si010}
|
||||
assert any(n and "USB_D" in n for n in nets010)
|
||||
assert not any(n and "ETH_TX" in n for n in nets010)
|
||||
assert not any(n and "SSTX" in n for n in nets010)
|
||||
|
||||
|
||||
def test_empty_net_class_impedance_does_not_paint_usb():
|
||||
cons = ComponentConstraints(
|
||||
mpn="PHY",
|
||||
pintable=[Pin(number="1", name="D+")],
|
||||
absolute_maximum_ratings=[],
|
||||
rules=[],
|
||||
layout_rules=[{
|
||||
"kind": "impedance",
|
||||
"z0_ohm": 50,
|
||||
"tolerance_pct": 10,
|
||||
"note": "generic 50 Ω with no bus",
|
||||
"source_page": 1,
|
||||
}],
|
||||
)
|
||||
findings = check_si(_usb_graph(), {"PHY": cons}, _usb_layout(), _if_rows())
|
||||
assert all(f.rule_id != "PE-SI-002" for f in findings)
|
||||
assert any(f.rule_id == "PE-SI-010" for f in findings)
|
||||
|
||||
Reference in New Issue
Block a user