Native Python PCB autorouter — a Java-free port of the FreeRouting engine (Specctra DSN in, SES out)
Project description
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
exactandroomengines 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.
freerouteis 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. --diagonalis 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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file freeroute-2026.7.14.tar.gz.
File metadata
- Download URL: freeroute-2026.7.14.tar.gz
- Upload date:
- Size: 97.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"EndeavourOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
61056f96fde5481fa2911c2ea1898c275fc75edc982d293dce84b3d7b258b31a
|
|
| MD5 |
6035b1a3fed889b69571f33cbbad36c7
|
|
| BLAKE2b-256 |
6e65b76c1911d9bc39a0a605eae27070d93af5227c6d283a311e087d77d3bab4
|
File details
Details for the file freeroute-2026.7.14-py3-none-any.whl.
File metadata
- Download URL: freeroute-2026.7.14-py3-none-any.whl
- Upload date:
- Size: 113.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"EndeavourOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a1b8b369a3e5df558dea15568876621f705e0c9510979caa92b3ff2c43ee4a93
|
|
| MD5 |
0d1a64204c3f987847705c196fa24b4d
|
|
| BLAKE2b-256 |
9e09350a04986472d750b0c12d04c844bb0f7d18c0a58494b1ddefb851104a43
|