Rotated oval pads were built from padded corners that are not in outline
order, so they fell back to their bounding box; neighbouring boxes of a
rotated socket overlap, and the grid's own-pad exemption opened the
overlap to the pin's net, which the exact check then refused. Pads are now
the convex hull of their corners, the exact tree checks a rotated pad
against that outline, and the grid blocks cells against the same outline.
A via reused by a second connection of its net left that connection with
no claim on the via's footprint, so ripping the first owner freed cells
around a via still on the board. Footprint cells now keep every claimant.
A net class wider than the default was searched as a default trace: its
spread was empty below half a cell, halos were circular while the exact
check compares boxes, and a rip-up route only counted what its centre
crossed. The spread now covers the extra width in whole cells, halos are
square, and rip-up victims include what the spread crosses.
bench/diagnose.py now routes with net-class widths and hole spacing, as
the CLI does.
Copies the board (stripping its tracks unless --keep), exports the DSN
through mckicad, routes, applies the session and runs DRC with zones
refilled, counting hole problems whatever their severity. FREEROUTE and
MCK choose the builds under test.
A 1.0 mm trace from a 1.27 mm pitch pin (the RN2483 row on the TTN
Enschede mote) has to stay within 0.06 mm of the pin's centreline until
it is past the neighbouring pads. No grid column is that close, and the
router could not get there:
- A stub ran as one slanted segment to its cell centre. The exact check
sees that as its bounding box, which lands on the next pad. Orthogonal
stubs now leave straight along the pin's axis and jog to the cell
only at the end.
- End nodes were chosen as in free space even in the rip-up search, so
once another net crossed a pin's only exit, the pin had no end left
and rip-up could never take the exit back. In rip-up mode those cells
and stubs are now enterable at the rip penalty, and routes on a stub
are ripped along with the ones the path crosses.
- Two neighbouring pins' stubs each claim the cells a centred trace
would crowd, so their claims overlapped well before their copper
did. Stub against stub is now checked on the copper.
- Escapes also look one column either side, so two pins leaving the
same way can jog apart.
TTN Enschede mote, exact engine with --shove: 12 unrouted before, 0
after; KiCad DRC on the result has no clearance violations and no
unconnected items. The 0.5 mm pitch row test at resolution 1 now
routes all 5 pins (it pinned 2).
Committed vias were checked for hole spacing, but a route's own vias are
not committed until it is done, so a route hopping layers (more of them
on 6 layers) could put two of its vias 0.41 mm apart, as th-eda's
6-layer flight controller showed. A via is now also checked against
layer changes earlier on its own route.
A fixed via sits anywhere, not on a cell centre, and may have a bigger
drill than the router's via; checking hole spacing between cell centres
with one drill let the flight controller's routed 0.2 mm via come 0.19
mm from a hand-placed 0.25 mm one. The board now carries each fixed
via's drill (from KiCad's padstack name), and cells are checked against
fixed vias exactly.
A route could drop a via next to (or onto) a through-hole pad of its own
net, where it changes layer for free, and a via reusing a fixed via's
cell came out a little off the fixed via's centre, beside it. No via now
goes within hole spacing of a through-hole pad of any net; a via in a pad
is checked exactly against every via and hole; and a routed via on a
fixed via of its net is not written. charge-sense: 4 hole_to_hole to 0;
flight controller: 2 to 0, unconnected unchanged.
Fixed tracks and vias only kept other nets away, so a pin fanned out to
its plane was routed to the plane again, beside its own fan-out. Pins,
zones and planes that fixed copper of their net touches now form one
connected group: a fanned-out supply pin needs nothing more, a fanned-out
signal routes from its via, and copper touching no pin or zone adds
nothing to route.
The exact engine rebuilds its output from each connection's own vias,
so connections sharing a plane via each wrote a copy: th-eda's flight
controller had 71 spots carrying 2 to 4 vias of one net. Vias are now
written once per net and spot, and never on a via already on the board.
Every pin of a supply drops its own via to the plane, and nothing kept
two of them apart: the flight controller came back with 199 co-located
holes and 109 hole-to-hole errors, nearly all VDD_3V3 and GND. A via now
keeps its drill plus --hole-to-hole (default 0.25 mm, KiCad's) from
every via centre, fixed ones included, except an own-net via at the same
cell, which it reuses; such a via is written once, and none is written
on top of a via already on the board.
The grid is laid out for the default width; a wider trace also needs the
cells its extra half width reaches to be free, and claims a wider halo.
A connection that does not fit at its class width (typically at a
fine-pitch pin) is routed at the default instead and reported. A net is
written at its class width when all of its connections went in at it,
and the exact check verifies it at that width; nets that shove or the
diagonal pass reshaped go out at the default.
A start that was already a plane end joined the plane at once, with no
check that its via fits: under a sensor's via keepout the via went in
the pad and the exact check refused it. A via leaving or reaching a
walled-in pin goes in that pin's pad, but was only checked at the grid
cell's centre; it is now checked exactly at the pin against via
keepouts and other nets' pads and fixed copper.
Tracks and vias already on the board were ignored, so other nets could
be routed straight through them (the flight controller's 82 GND
stitching vias and its guard tracks). They are now copper of their net:
other nets keep a pad's distance from them in the grid, the exact check
and the room engine, and they add no connections to route.
A plane end is marked with its zone, not a pin, so a route from a pad
outside the plane's outline crashed looking for the pin's location
(the flight-controller board's first placement).
Over 8 net orders it left 25 to 30 fewer connections unrouted on the
dense sense boards and 16 to 18 fewer on two PCBench boards, and was
never clearly worse. KiCad DRC on the sense harness: 41 opens to 28.
The connections a rip-up route crosses are rerouted at once in free
space. If that leaves fewer connections routed than before, the new
route comes out and every displaced connection goes back on its old
path, with its rip count as it was. Even trades are kept.
A route may not leave a cell inside a wire keepout as a wire, nor reach
a pin there by one: it changes layer instead, and the via goes in the
pad. A pin whose every stub enters a keepout is reached by a via only.
A structure keepout named rule_area... (mckicad names KiCad rule areas
so) is checked for overlap only, as KiCad's DRC does, with the free ends
of each wire tested for their round caps.
--access-cost C makes a route pay C to pass where it would close another
pin's way out (its end cells, the via it may need there, and the
clearance around both) while that pin still has connections to route.
A pin inside a wire keepout can only leave by a via; its stub to the
cell centre was track inside the keepout, which the exact check refused.
The via now goes on the pin itself. Pin stubs are also checked exactly
against wire keepouts, as the exact engine will check them.
bench/diagnose.py reports what each refused connection collided with.
rejected by the exact check, unroutable even alone, walled in (a pin's
free region is closed off by other copper, with the nets that close it),
or congestion. The board state is the best pass's, rebuilt by replaying
its paths.
Each round routes every connection that has failed so far ahead of the
rest, which keep their order. The promoted set only grows, so two groups
cannot take turns failing; the best round is kept.
Runs every board with every variant under the same seeds and compares
them seed by seed, so a change is judged against the routing-order
noise rather than one lucky or unlucky run.
The router is deterministic, but its result depends heavily on which
connection routes first. A seed shuffles that order reproducibly, so a
change can be judged over several orders instead of the one it happened
to get.
On a four-layer board with a GND plane on an inner power layer, freeroute
ignored the plane (it only used zones on routing layers) and chained every
GND pad to the next with tracks across the board. Now a through-hole pad
inside a plane is joined to it already, and for a net with a plane every
other group connects straight to the plane: a route may end on any cell
inside it where a via fits (checked lazily, like other vias), and ending
there places that via. Signal nets are unaffected.
Tested on a four-layer DSN: three SMD GND pads each get a short stub and
their own via, the THT GND pad needs nothing, and no track crosses the
board. Ahead of the TerraHawk ESC, a four-layer board.
Recommend the exact engine for KiCad boards; document --edge-clearance,
--grid-resolution, --heuristic-weight and the unrouted report; list what is
read from a KiCad DSN (pads, vias, keepout kinds, outline, zones, fine
pitch), each checked against KiCad; replace the scope section with the
TerraHawk measurements and what still trails the JAR.
On the rc5 sense board at its real 0.2 mm clearance, ten pins of the
0.5 mm pitch STM32 could only reach a pocket of seven or eight cells
inside their own pads: the grid columns miss the narrow legal band past
the pad tip, so twelve connections could not route even on an empty
board. For a crowded pin (a neighbouring pad of another net closer than a
trace can pass) whose nearby cells are such a pocket against pads and
keepouts, offer straight stubs along each axis to the first open cell,
accepted only if the stub clears every other net's pad by the full
margin, measured exactly. All twelve then route alone; on the full board
unrouted connections drop from 61 to 59, the rest being congestion.
freeroute exited 0 and said nothing when it dropped connections, which
cost real diagnosis time: on rc4 two simple two-pin nets came back with no
copper and no explanation. The grid now records each connection it could
not route, the exact check adds each it rejects, and the CLI lists them on
stderr (the SES is still written; the exit code stays 0, as the JAR's).
A connection whose ends both sit in one zone of its net is listed apart,
as left to the zone fill: freeroute does not route through a zone it
cannot trust to be solid, but KiCad's fill often joins those pads anyway.
Resolution 2 reaches 0.5 mm pitch pins and cut unconnected items on both
rc4 boards; since lazy via checks it costs about 2x in time, not 4x.
The grid engine keeps 1, the old grid exactly.
A zone outline is where copper may go, not where it ends up: on the rc4
sense board the board-wide GND pours break into islands around other
nets' tracks, and treating every pad inside them as connected left 28
GND islands unjoined (63 unconnected against 42 without zones). Trust a
zone only when a no-tracks rule area covers it on its layer, as the
power stage's pours are covered; route the rest with tracks as before.
rc4, exact engine, resolution 2: power stage 5 unconnected (JAR 12),
sense board 41 (JAR 32), no DRC errors on either.
freeroute ignored the DSN's planes (KiCad's zones), so it never knew that
pads in a pour are joined by it. On the rc4 power stage those pads sit
inside "keep router off" areas as well, and every connection to them was
dropped without a word.
- Read (plane <net> <shape>) and put each zone on the board as a
ConductionArea of its net. Zones are not obstacles: KiCad refills them
around other nets' copper.
- The grid groups each net's pads and zone pieces that copper already
joins (a pad in a zone, two zones sharing a pad) and routes only between
groups. A connection may end on a zone by a via inside its copper, or by
a track where tracks are allowed.
- wire_keepout now blocks traces only, as KiCad's "no tracks" rule area
does; via_keepout only vias; keepout both, in the grid and in the exact
verifier.
rc4 power stage, exact engine, resolution 2: 2 unconnected, down from 8
(FreeRouting JAR: 12), no DRC errors.
Profiling the rc4 sense board at resolution 2 put half the time in
_via_placeable: every expanded cell checked a 5x5 via footprint on both
layers, for vias the search almost never takes. Queue a layer change as a
candidate and check its footprint (memoised per search) only when it is
popped. A check can only rule a via out, never make it cheaper, so this
cannot change which route is found.
The grid search estimated the remaining distance as a straight line even
when only orthogonal steps exist; Manhattan is the tighter estimate those
moves admit and still never overestimates. --heuristic-weight W (default
1) makes it weighted A*: routes at most W times the shortest (tested).
A cell was exactly one trace plus its clearance, so a trace owning its cell
was the whole clearance model, and the 0.4 mm grid could not reach most
pins of a 0.5 mm pitch package: between two 0.3 mm pads a trace's centre
has a legal band only 0.15 mm wide. Resolution K makes cells K times
finer and has every trace claim a halo, the cells whose centre is nearer
than trace + clearance to one of its own, released again on rip-up.
K = 1 has an empty halo and is the old grid exactly (tested). On a 0.5 mm
row, K = 4 gets every pin out where K = 1 gets two. On the rc4 boards, K = 2
cuts the sense board's opens from 66 to 41 (JAR: 32) and the power board's
to 7 (JAR: 12), still with no DRC errors from freeroute's own copper.
Every route starts and ends with a stub from the pin to its first grid
cell, and the grid never saw that copper: on the rc4 sense board 24 of 39
connections the exact verifier rejected had a later route pass beside an
earlier stub. A route's stubs now claim the cells they come near, a stub
must be clear of other nets to be used, and a route may start from any
enterable neighbour of the pin cell, costed by stub length (Manhattan on
the orthogonal track, so a pin on a cell centre still leaves straight).
Tests: a route near a rounded corner keeps its edge clearance; and a
strict xfail that pins down the remaining gap, a 0.5 mm pin row the grid
cannot give a column to every pin of.