A short diagonal at a wire's end is a kink beside its pad, and the pour
refilled round it left a copper sliver (KiCad copper_sliver) on the
TerraHawk lights node. Small offsets now stay square jogs; chamfers get
the same floor.
A last stage on the finished result, wire by wire: a run of legs between
two points becomes the octilinear shape joining them when its copper is
clear, the furthest point first; right-angle corners left are chamfered;
straight runs are merged. Ends, vias, points where another wire of the net
meets the wire, and legs across own-net pads stay. Every new leg and corner
is checked on the exact shapes against other nets, pads, keepouts and the
board edge, slanted legs both ways round as the verifier counts a pair.
A net whose pads end up joined differently is put back, and the whole
smoothing is dropped if the finished check finds more violations than
before. --smooth-report adds track length per layer.
--reduce-vias is a last stage: each net that has vias is routed again on
its own with vias made eight times dearer, everything else held as fixed
copper (the window stage's board), and its new copper is kept only if the
net fails nothing new and has fewer vias; the nets with the most vias go
first, within a share of the routing's own time. --via-cost exposes the
search's layer-change price (default 10 grid steps). bench.py records
each run's via count and prints via totals per variant.
The grid and room engines write copper they never check. After a grid or
room run the result is now put through the exact engine's geometry (its
pads, fixed copper and keepouts; wires as boxes or slanted slabs; vias as
their pads) and the report gives the shorts and clearance violations, with
examples, instead of a blanket warning. --strict exits 3 when there are any;
by default the exit status stays 0 so the JAR drop-in contract holds.
The exact engine's static-copper setup moves into insert_static_copper so
both use it; exact runs are byte-identical before and after.
The grid engine was the default and its copper is not checked against exact
shapes: a full reroute of the Labrador lid with no flags came out with 15
tracks_crossing and 87 clearance violations under KiCad's DRC while the
report listed only unrouted connections. The exact engine (what mckicad and
every benchmark already use) is now the default, and grid or room runs end
with a warning that their output needs a DRC.
The router began as a port of FreeRouting and has grown its own engine
(negotiated congestion, tree nets, exact checks, axis escapes), and
'freeroute' was one letter from the project it no longer follows. The
package, module and command are now rpmroute; the freeroute command stays
as an alias, and the RPMROUTE / RPMROUTE_ORACLE_JAVA variables fall back
to the old FREEROUTE names. Version 2026.10.07. Routing is unchanged: a
PCBench board routes to a byte-identical session either way.
With items away from the window left off its board, a stub from the window
edge out to a rerouted net's far pin crossed copper the exact check never
saw: 38 shorts on th-eda's flight controller. A connection with an end
outside the bounds is now left unrouted, and a window whose new copper
leaves the window is refused.
A window's board now holds every pin and piece of copper of the nets it
reroutes and, of the rest, only items near the window, instead of the whole
board rebuilt per window. The smallest windows go first, at most
--window-cap per round (40), and no window starts once --window-budget of
the routing's own time is spent (0.25). On th-eda's flight controller the
stage had taken 12x the routing time for no gain.
After the exact engine, every net routed wholly inside a window around a
stuck connection's pins is ripped, the rest of the board is laid down as
fixed copper, and the window alone is routed again at --window-resolution
(default 4) with the stuck net and the ripped nets only. The result is kept
only if fewer connections are left unrouted; each round doubles the margin.
The grid and exact routers gain only_nets and bounds for it. Off by default.
Anchoring a T only on the connection whose copper it touched missed the
connections that tie that copper to the T's own group. When one of those
was ripped, the T stayed routed but cut off: on charge-sense freeroute
counted 15 unrouted while KiCad found 27 opens. Routed connections of a net
form a forest, so the path back is unique; the T now depends on all of it.
KiCad's opens and freeroute's count agree again (18 against 17, the extra
one a GND pour island left to the fill).
A net's pin groups were joined as a chain in pin order, and each
connection had to end on one chosen pin. Groups are now paired by a
minimum spanning tree (Manhattan distance between their nearest pins), and
a connection may start or end on any copper its net has already routed on
that side (path cells, via cells on every layer; not walled cells, whose
via moves into the pad). Plane nets keep their drops to the plane.
A connection that tees onto another records it as an anchor. Ripping a
connection rips what hangs on it, transitively, inside the same rip-up
transaction, so rollback restores all of them. The exact engine gets the
connections in commit order with their anchors and drops a T whose anchor
it rejects. --no-tree restores the chain.
A QFN placed at 45 degrees had no exit on the grid: every horizontal or
vertical step from a pin passes its neighbours inside clearance. A pin whose
pad is turned more than ten degrees off the grid axes now leaves along the
pad's own axis, away from the part, until an L from there to a grid cell
clears every other net's pad, then searches as usual. The slanted leg is
checked and stored as an exact slab (segment_slab), not its bounding box,
in the grid stub checks and in the exact engine.
Where pins leave side by side, a cell names only its first owner, so stubs
are also indexed per cell for every connection near them; the exact
stub-vs-stub check, trace entry and rip-up all consult that index. A pin
with an axis exit and no clear cell reports no way out instead of falling
back to a cell on another net's route. diagnose.py replays with the exits.
Every connection is routed N times on the empty board, where routes may
share cells at a price that doubles each round for every other net on a
cell, and a cell that stays shared gains history. The real routing then
pays that history on free cells, so the first routes stop taking the space
later routes (a fine-pitch part's escapes) have no way round. Off by
default.
The old history term never steered: it only accrued at rip-up fights,
after the first routes had claimed the contested space, and routed
connections are never revisited in later passes.
Two connections of a net can share a via. The grid kept one owner per via
cell next to the claim list, and the search and rip-up read only that owner:
a route of another net through the via ripped the first user and left the
second's via under the new trace. The exact check then threw one of them
out. The search and rip-up now read the claim list, and the single owner is
gone.
The exact check also tested a via against the outline as its square box,
whose corner reaches past a 45-degree chamfer where the round via does not.
The grid keeps the via's centre its radius plus the edge clearance from the
outline, as KiCad measures it; the exact check now does the same.
kitspace_minisumo_v3 seed 4: 4 rejected connections to 0, 11 failed to 7.
A circle pad was its bounding box in both the grid and the exact check, so a
4 mm mounting hole's square corners refused legal routes 0.8 mm clear of the
real copper. It is now a 16-sided polygon whose sides touch the circle, so
the router is never closer than KiCad measures. diagnose.py takes --seed.
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).