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.
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.
SES lengths and coordinates were written in the DSN's unit while the
session declared (resolution um 10), so readers that follow FreeRouting's
SesWriter (KiCad's importer, mckicad's ses_apply) placed every track at a
tenth of its size. The JAR, run on the same DSN, writes F1 at 65000 where
freeroute wrote 6500.
Scale every length by the resolution factor: placements, via padstack
shapes, wire widths and points, and vias. Rotations stay as they are. The
router tests compared SES output against DSN pad positions and so had the
same mistake built in; they now derive the expected values through
ses_scale.
Adds ripup_needed.dsn: one signal layer (B.Cu is a power plane, so vias
are impossible) with two nets whose greedy order strands NET_B, but where
rip-up reroutes NET_A around NET_B to connect both.
Tests: greedy (rip_up=False) leaves >= 1 connection unrouted with no vias;
rip-up connects all with no vias; the result is deterministic across
re-runs; rip-up keeps endpoints on pads and produces no same-layer cross
between nets (ripped traces leave no orphaned occupancy). The crossing
board's single-layer test now targets the greedy path explicitly, and a
regression test asserts the real KiCad board still connects all four of
its multi-pin nets. Oracle-gated parity added for the congested board.
Adds crossing_2net.dsn: NET_A is a full-width horizontal wall and NET_B's
pins sit above and below it, so single-layer routing cannot connect NET_B
but two-layer routing succeeds by dipping to the other layer through vias.
Tests assert the multi-layer invariants: single-layer routing leaves a net
unrouted with no vias; multi-layer connects both nets with >= 1 via; the
SES contains a via scope; every via sits at a real layer transition of its
net's path; trace/via endpoints still land on the pads; traces stay on
valid layers and within the outline; and no two different nets cross on the
same layer. Oracle-gated parity tests cover the simple and crossing boards.
Implements a working autorouter and the Java-free replacement for the
freerouting.jar step: dsn_text -> parse_dsn -> build_board -> route ->
write_ses.
GridRouter is a single-layer A* maze search over a uniform occupancy
grid: a cell is blocked by another net's pad (inflated by clearance +
half trace width) or a keepout; each net's ratsnest is connected pin to
pin; a routed trace then blocks other nets. The cell path becomes a
trace polyline whose endpoints are the exact pin locations. Board-unit
paths are converted back to DSN units for the SES (wire (path ...))
scopes.
This is an MVP, not a port of FreeRouting's expansion-room maze:
free-space rooms, rip-up-and-retry, multi-layer via search, and shove
are deferred (they raise quality/coverage, not the connectivity
milestone). It reaches connectivity on boards whose nets route on one
layer without crossing.
Adds tests/dsn/fixtures/simple_2net.dsn (a guaranteed-routable 2-net
board). Invariant tests: both nets route, the emitted SES parses, every
trace stays on a valid layer and within the board outline, and every
routed net's trace endpoints sit exactly on its two pads. An oracle-gated
test asserts connectivity parity with the reference FreeRouting JAR on
the simple board.