main
Ray Publish 0.2.7 db87ed5 1d ago

name: eda-routing-gates description: Turn PCB routing rules into measured gates that fail the build - 0/45/90 geometry, no extra bends, no via inside a trace, cap-first decoupling, measured decoupling loops, power-stage loop distances, an unbroken plane under the hot loop, and power paths that must not neck or wander (a switching trace thinner than the pin it leaves). Use after routing and before calling a board done, when reviewing someone else's layout, or when a reviewer points at a trace and you want the check that would have caught it.

Parent skill: eda-engineering

Routing gates: rules that fail the build

A routing rule that is only written down gets argued about trace by trace. These gates turn the rules into numbers with thresholds, in a script that exits non-zero, so a board that breaks one stops the build and says where. They complement two existing skills:

  • eda-visible-routing and eda-electrical-routing say how to route: the priorities, the electrical judgement and the copper decisions;
  • this skill checks the result mechanically, after routing and before anyone calls the board done.

The rules are tool-agnostic. The script reads KiCad files (.kicad_pcb) with the Python standard library, so no KiCad install is needed to check a board. For Fusion or Altium designs, KiCad 10 imports Altium .PcbDoc and EAGLE/Fusion .brd boards: import a copy, refill its zones in KiCad, and run the gates on that. Alternatively, reimplement the same measurements on the native API. The thresholds and their reasons carry over unchanged.

The gates

gate the rule default threshold why
grid every straight segment at 0, 45 or 90 degrees 0 off-grid free-angle links read as unfinished and hide search-grid artefacts; arcs are reported, and allowed only when the spec says so
extra bends no redundant collinear vertex, no short jog that sidesteps and returns to its heading 0 an extra bend is visible on every render and adds nothing
via inside a trace where a trace changes layer, the via hangs off the trace on a side stub; two same-layer track ends never meet at a via 0 an in-line via reads as a kink and hides whether the trace continues
cap first each decoupled supply pin reaches its own cap's pad before any junction: no third route joining and no pour on the way none allowed the cap must see the pin before the rail or plane does; otherwise the pin's current loop closes through the junction and the cap hangs off it
decoupling loops supply path pin -> cap and ground sink cap -> via, measured along copper an untraced path FAILS a measurement that silently returns nothing is a gate that stopped measuring
input loop the best input cap's pads to the IC's VIN and GND pins, and the loop area pin -> cap -> cap -> pin 2.5 mm, 6.0 mm2 the datasheet's own layout rule for a switcher: the input ceramic directly across VIN and GND
output caps spread of the output caps, and the furthest from the inductor's output pad 5.0 mm, 5.0 mm clustered caps keep the output loop and its return short
plane under the hot loop no non-plane-net copper (tracks, vias, fills) on the plane layer under the input loop and switch pin 0 a slot under the hot loop breaks the return path exactly where di/dt is highest
power path: trace neck the narrowest track on a power path, against the narrow side of the pin it leaves at least the pin a switching trace thinner than its pin is a neck, whatever the copper under it does
power path: filled-copper neck the widest conductor that fits from pin to pin through the net's filled copper (tracks, pads and refilled pours) at least the pin the true bottleneck when a pour carries the current
power path: meander routed track length against the straight pin-to-pin distance, and the bends along it x1.15, 4 bends a wandering power path adds inductance, length and the "wiggle" a reviewer sees

The two neck measurements are separate on purpose. A thin trace drawn over a wide pour is still a defect: it is what the board shows, and it carries the current alone wherever the pour does not fill. The filled-copper figure tells you whether the pour saves the current path.

Run it

python3 scripts/routing_gates.py <board>.kicad_pcb <spec>.json [--json result.json] [--only grid,bends,...]
python3 tests/run.py          # the regressions: every gate made to fail on purpose

Run it on a board whose zones KiCad has refilled (kicad-cli pcb drc --refill-zones --save-board). Stored fills can be stale, and two gates measure the filled copper. The spec names the parts each rule is about, because nothing is guessed from reference designators. Two worked specs are in examples/:

{"powerStage": {"ic": "U1", "vinPin": "3", "gndPin": "1", "inputCaps": ["C2", "C3"], "outputCaps": ["C6", "C7", "C8"],
                "inductor": "L1", "inductorOutPin": "2", "planeLayer": "B.Cu", "planeNet": "GND"},
 "powerPaths": [{"net": "SW", "from": "U1.2", "to": "L1.1"}],
 "decoupling": [{"pin": "U1.3", "cap": "C2"}],
 "thresholds": {}}

Every threshold can be overridden in thresholds. Change one only with a reason written beside it in the spec, and never after seeing a result in order to pass it.

Calibrated on two real boards

Two layouts of one 12 V to 5 V / 1 A TPS54202 buck:

The neck gate was calibrated on the SW trace a reviewer pointed at in the buck process video at 2:32: the trace leaving U1 pin 2 visibly necks and wiggles.

gate john/buck-12v5v-molecule adom/buck-12v-5v-1a-molecule
segments off the 0/45/90 grid 38 of 248 0 of 63
extra bends (redundant vertices, jogs) 1, 7 0, 0
vias inside a trace 4 0
cap first, VIN pin 3 to its decoupling cap through the VIN pour (C4) direct (C2)
decoupling loop, supply / ground sink 1.55 / 0.88 mm (C4) 2.39 / 1.08 mm (C2)
best input cap to VIN / GND pins 3.67 / 5.82 mm (C2) 2.23 / 2.23 mm (C2)
input loop area 7.17 mm2 5.24 mm2
output caps spread / furthest from L1 11.0 / 6.5 mm 4.1 / 4.9 mm
non-GND copper on B.Cu under the hot loop 0 0
SW trace: narrowest track vs the 0.6 mm pin 0.30 mm 1.10 mm
SW trace: routed / straight, bends 4.63 / 3.69 mm (x1.26), 8 bends 7.13 / 7.13 mm (x1.00), 0 bends
SW filled copper: narrowest conductor 0.96 mm 1.12 mm
result FAIL (11 gates) PASS

What the table says about the SW trace the reviewer pointed at: on John's board, a 0.3 mm trace leaves U1's 0.6 mm SW pin and jogs eight times on its way to the inductor. The SW pour patch underneath is 0.96 mm at its narrowest, which is why a current-density solve of that board (Adom Fields, 49.8 A/mm2 peak) found no hot spot. The gates fail the trace and report that the copper beneath it is adequate; both statements are true. On the re-layout, the SW trace is 1.1 to 1.4 mm wide and straight, and the copper bottleneck is the trace itself.

Calibrating your own thresholds

Defaults are for a two-layer, 1 A-class switcher in a SOT-23 or similar small package. For another design:

  • pad-to-pin and loop area: start from the datasheet's layout example and the caps' own pad pitch;
  • neck ratios: keep them at 1.0 against the pin unless the current is far below the pin's rating, and say so;
  • meander and bend limits: derive them from a route you would accept.

Then make each gate fail on purpose against your board, as tests/test_routing_gates.py does: move the input cap 5 mm away, thin the switch trace, add a jog, or put a via in a trace. A gate that has only ever seen a passing board tests nothing.

What this does not claim

These are geometry gates. They do not replace native DRC, connectivity and schematic parity (see eda-native-connectivity-conformance), a current-density or thermal solve (eda-thermal-bottlenecks, eda-multilayer-current-review), or bench and EMI measurement. Pads are modelled as their bounding rectangles. The filled-copper width is a 0.02 mm raster with a chamfer distance transform, accurate to about 2 % plus one cell. The cap-first walk sees tracks, vias, pads and refilled pours; a connection made only through an unrefilled zone is reported as untraced.