Tighten PCB findings: actions, stitch gating, real copper.

Fill action on every finding and show it in the report. Stitch/return only
on large HS/power nets. Parse stackup thickness, arcs, and vias from the
board. IC courtyard pad copper is not PE-PLC-004 RULE. Recommended RC/cap
stay REVIEW.
This commit is contained in:
2026-09-20 08:58:54 +02:00
parent 1e379f6042
commit f63c1c3411
9 changed files with 326 additions and 59 deletions
+47 -7
View File
@@ -394,12 +394,19 @@ def test_power_trace_ipc2221_errors_when_load_exceeds_ampacity():
assert findings[0].recommendation
def test_power_trace_uses_parsed_width_and_thickness():
from backend.periscopex.pcb_power_thermal import check_pcb_power_traces
graph, cmap, layout = _ldo_graph_layout(i_load=0.05, width=1.0, thickness=0.035)
findings = check_pcb_power_traces(graph, cmap, layout)
assert findings == []
def test_power_trace_skips_without_i_load():
from backend.periscopex.pcb_power_thermal import check_pcb_power_traces
graph, cmap, layout = _ldo_graph_layout(i_load=10)
graph.components["U1"].specs.values["i_load_a"] = None # type: ignore[union-attr]
# drop i_load
graph.components["U1"].specs = None
assert check_pcb_power_traces(graph, cmap, layout) == []
@@ -502,6 +509,38 @@ def test_thermal_copper_warns_without_pour_or_vias():
assert findings[0].recommendation
def test_gnd_stitch_skips_tiny_gpio():
from backend.periscopex.models import LayoutZone, PinConnection
from backend.periscopex.pcb_power_thermal import check_pcb_gnd_stitch
graph = DesignGraph(
components={
"U1": Component(
reference="U1", value="", footprint="",
component_type=ComponentType.IC, mpn="X",
pins={"1": "GPIO4"},
),
},
nets={
"GPIO4": Net(
name="GPIO4", net_type=NetType.SIGNAL,
pins=[PinConnection(component_ref="U1", pin_number="1")],
),
},
)
layout = LayoutGraph(
footprints={},
segments=[
LayoutSegment(start=(0, 0), end=(0.1, 3), width=0.2, layer="F.Cu", net="GPIO4"),
],
zones=[
LayoutZone(net="GND", layer="B.Cu", outlines=[[(0, -5), (20, -5), (20, 5), (0, 5)]]),
],
vias=[],
)
assert check_pcb_gnd_stitch(graph, layout) == []
def test_gnd_stitch_info_when_signal_has_pour_but_no_via():
from backend.periscopex.models import LayoutVia, LayoutZone, PinConnection
from backend.periscopex.pcb_power_thermal import check_pcb_gnd_stitch
@@ -511,12 +550,12 @@ def test_gnd_stitch_info_when_signal_has_pour_but_no_via():
"U1": Component(
reference="U1", value="", footprint="",
component_type=ComponentType.IC, mpn="X",
pins={"1": "SDA"},
pins={"1": "USB_DP"},
),
},
nets={
"SDA": Net(
name="SDA", net_type=NetType.SIGNAL,
"USB_DP": Net(
name="USB_DP", net_type=NetType.SIGNAL,
pins=[PinConnection(component_ref="U1", pin_number="1")],
),
},
@@ -524,17 +563,18 @@ def test_gnd_stitch_info_when_signal_has_pour_but_no_via():
layout = LayoutGraph(
footprints={},
segments=[
LayoutSegment(start=(0, 0), end=(20, 0), width=0.2, layer="F.Cu", net="SDA"),
LayoutSegment(start=(0, 0), end=(20, 8), width=0.2, layer="F.Cu", net="USB_DP"),
],
zones=[
LayoutZone(net="GND", layer="B.Cu", outlines=[[(0, -5), (20, -5), (20, 5), (0, 5)]]),
LayoutZone(net="GND", layer="B.Cu", outlines=[[(0, -5), (20, -5), (20, 10), (0, 10)]]),
],
vias=[],
)
findings = check_pcb_gnd_stitch(graph, layout)
assert findings and findings[0].rule_id == "PE-STCH-001"
assert findings[0].status == "INFO"
layout.vias = [LayoutVia(x=10, y=0, net="GND", drill=0.3)]
assert findings[0].action
layout.vias = [LayoutVia(x=10, y=4, net="GND", drill=0.3)]
assert check_pcb_gnd_stitch(graph, layout) == []