# freeroute A native Python PCB autorouter — a Java-free reimplementation of the [FreeRouting](https://github.com/freerouting/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 ```bash uv tool install freeroute # or: pipx install freeroute ``` ## Usage ```bash 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 ```python 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 ```bash 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`](docs/ARCHITECTURE.md) for the port map and [`docs/PORTING_PLAN.md`](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.