EDA Skillpack
Public Made by Adomby adom
All skills for all EDA capabilities across the Adom ecosystem.
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:
- john/buck-12v5v-molecule: the published board, refilled with KiCad 10.0.1;
- adom/buck-12v-5v-1a-molecule (org-private): a re-layout of the same circuit to these rules, refilled with KiCad 10.
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.
---
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](../eda-visible-routing/SKILL.md) and [eda-electrical-routing](../eda-electrical-routing/SKILL.md) 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/`:
```json
{"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:
- [john/buck-12v5v-molecule](https://wiki.adom.inc/john/buck-12v5v-molecule): the published board, refilled with KiCad 10.0.1;
- [adom/buck-12v-5v-1a-molecule](https://wiki.adom.inc/adom/buck-12v-5v-1a-molecule) (org-private): a re-layout of the same circuit to these rules, refilled with KiCad 10.
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](../eda-native-connectivity-conformance/SKILL.md)), a current-density or thermal solve ([eda-thermal-bottlenecks](../eda-thermal-bottlenecks/SKILL.md), [eda-multilayer-current-review](../eda-multilayer-current-review/SKILL.md)), 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.