rsp2k cdb3c7b568 Say which connections were not routed
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.
2026-09-27 12:34:20 -06:00
2026-07-13 18:17:13 -06:00

freeroute

A native Python PCB autorouter — a Java-free reimplementation of the FreeRouting engine. Specctra DSN in, Specctra SES out, no JVM anywhere in the pipeline.

Why

Toolchains that autoroute (kicad-mcp among them) do it by shelling out to freerouting.jar, which drags a JVM into every install and container image. freeroute reimplements the routing engine in pure Python — no dependencies at all, standard library only — so the JAR step becomes a pip install.

The contract

The entire integration surface is two Specctra files:

board.dsn  ──▶  freeroute  ──▶  board.ses
(unrouted)                       (routed session: traces + vias)

kicad-cli already exports .dsn and imports .ses, so freeroute drops in wherever java -jar freerouting.jar was invoked — nothing else in the pipeline changes. The -de / -do / -mp flags are the JAR's, on purpose.

Install

uv tool install freeroute      # or: pipx install freeroute

Usage

freeroute board.dsn -o board.ses          # grid engine, the default
freeroute -de board.dsn -do board.ses     # the FreeRouting JAR spelling

freeroute board.dsn --engine room --pack --shove -do board.ses
freeroute board.dsn --engine exact --diagonal --shove -do board.ses

With no -do / -o, the SES goes to stdout.

Engines

Three routing tracks, selected with --engine:

  • grid (default) — multi-layer maze router over a uniform occupancy grid, with rip-up-and-retry and via search. Highest raw connectivity of the three; its geometry is grid-quantised, so it is the least precise about clearance.
  • exact — orthogonal router whose output is verified against exact integer geometry (tile/octagon clearance, not bounding boxes). Cleaner, DRC-clean output where it succeeds; it will drop a net rather than emit a violation.
  • room — continuous expansion-room router: an exact decomposition of free space, so it routes off-grid channels that the grid quantisation cannot see.

Flags

flag effect
--engine {grid,exact,room} routing track (default: grid)
-de, --design input .dsn (FreeRouting-compatible alias)
-do, --output output .ses (default: stdout)
-mp, --max-passes rip-up-and-retry pass bound (default 10)
--no-rip-up single greedy pass, no rip-up
--layers comma-separated signal layer indices to route on
--pack coordinated multi-trace channel packing
--shove move committed traces aside to recover dropped nets
--diagonal 45° recovery pass for dropped 2-pin nets

Not every engine implements every pass. This is the supported matrix, and an unsupported combination is a hard error (exit 2) rather than a silent no-op:

--pack --shove --diagonal --no-rip-up -mp
grid — — — yes yes
exact — yes yes yes yes
room yes yes — — —

--layers works on all three.

What the opt-in passes buy you:

  • --pack (room) — when several 2-pin nets have to cross the same obstacle gap, they are assigned consistent parallel lanes and routed together, so a channel physically wide enough for all of them stops dropping nets to greedy per-net gate collisions.
  • --shove (exact, room) — a net that would be dropped nudges the blocking committed traces aside and re-routes, rolling every moved trace back if the retry fails. On the room engine it also turns on occupancy-aware gate placement. It recovers ordering drops; it cannot manufacture space that is not there.
  • --diagonal (exact) — a still-dropped 2-pin net is retried as a 45° trace whose diagonal copper is covered by an exact integer octagon, so it fits corridors the orthogonal L cannot, at the ideal hypotenuse length.

Library use

from freeroute.route import route

ses_text = route(dsn_text, engine="room", shove=True, pack=True)

Status and honest scope

Alpha, version 0.1.0. It parses real kicad-cli DSN output, routes, and writes SES that KiCad imports. What is actually implemented:

  • Specctra DSN parser (structure, library, placement, network, rules, keepouts) and SES writer.
  • Multi-layer routing with via search and rip-up-and-retry.
  • Exact integer geometry (tiles, octagons) with a spatial index, so the exact and room engines verify their own output is clearance-clean.
  • Continuous expansion-room free-space decomposition.
  • Channel packing, shove, and 45° routing as described above.

What it is not, stated plainly:

  • Not at FreeRouting/JAR density parity on dense commercial boards. The JAR remains the better router when the board is hard. freeroute is honest about drops: it leaves a net unrouted rather than emit a DRC violation, so on a crowded board expect fewer completed nets than the JAR would manage.
  • --diagonal is a shortening/recovery pass, not diagonal-native search. The maze search is orthogonal; 45° geometry is applied to nets the orthogonal pass dropped or to shorten a staircase. A true 45°-native expansion-room maze is not implemented.
  • No fanout pass, no post-route optimiser, no interactive/incremental routing, no net classes with per-class widths beyond the default rules, no plane/pour handling.

If you need maximum completion on a dense board today, use the JAR. If you need routing without a JVM — CI, containers, an MCP server, a library call — this is that.

Development

uv sync
uv run pytest -m "not oracle"     # the suite; oracle tests need Java + the JAR
uv run ruff check src/ tests/

The oracle-marked tests run the real freerouting.jar as a differential reference and skip cleanly when Java or the JAR is absent. The JAR is never vendored, and neither is the upstream Java source — see docs/ARCHITECTURE.md for the port map and docs/PORTING_PLAN.md for the roadmap.

License

GPL-3.0-or-later, matching the FreeRouting source it is ported from. No upstream Java code is distributed with this package; the reference clone used during porting is git-ignored and excluded from the sdist.

S
Description
Native Python PCB autorouter: negotiated congestion, tree routing, exact checks (Specctra DSN in, SES out). Formerly freeroute.
Readme GPL-3.0
886 KiB
Languages
Python 99.6%
Shell 0.4%