This document describes all configuration options for the KiCad Grid Router.
# Single-ended routing (all nets by default)
python route.py input.kicad_pcb [output.kicad_pcb] [OPTIONS] # Default output: input_routed.kicad_pcb
python route.py input.kicad_pcb --overwrite [OPTIONS] # Overwrite input file
# Differential pair routing (all nets by default)
python route_diff.py input.kicad_pcb [output.kicad_pcb] [OPTIONS] # Default output: input_routed.kicad_pcb
python route_diff.py input.kicad_pcb --overwrite [OPTIONS] # Overwrite input fileUse route.py for single-ended nets and route_diff.py for differential pairs. By default, all nets are routed. Use --nets to filter specific patterns.
route.py always writes the output file, even when nothing routes (no valid nets, or all already connected) — it writes an unchanged copy of the input in that case, so output→input pipelines don't break on a missing file.
Pads with no pad number (paste/thermal-via artifacts KiCad doesn't netlist individually) are not used as routing targets; they remain copper obstacles.
Net names support glob wildcards. Use --nets (or -n) to specify patterns:
# Exact net names
python route.py in.kicad_pcb out.kicad_pcb --nets "Net-(U2A-DATA_0)" "Net-(U2A-DATA_1)"
# Wildcard patterns
python route.py in.kicad_pcb out.kicad_pcb --nets "Net-(U2A-DATA_*)"
# Multiple patterns
python route.py in.kicad_pcb out.kicad_pcb --nets "Net-(*CLK*)" "Net-(*DATA*)"
# Route all nets on a component (auto-excludes GND/VCC/VDD/unconnected)
python route.py in.kicad_pcb out.kicad_pcb --component U1| Option | Default | Description |
|---|---|---|
--track-width |
0.3 | Track width in mm (ignored if --impedance specified) |
--impedance |
- | Target single-ended impedance in ohms (calculates width per layer from stackup) |
--clearance |
0.25 | Track-to-track clearance in mm |
--via-size |
0.5 | Via outer diameter in mm |
--via-drill |
0.3 | Via drill diameter in mm |
--grid-step |
0.1 | Grid resolution in mm |
Impedance-controlled routing: When --impedance is specified, track widths are automatically calculated per layer using IPC-2141 formulas based on the board stackup. Outer layers use microstrip formulas (typically wider tracks) and inner layers use stripline formulas (typically narrower tracks). Via clearance calculations account for the varying track widths per layer.
The fab tier is the JLCPCB manufacturing floor every routing step shrinks tracks,
vias and clearances down toward when it needs to. It is shared by every CLI
(route.py, route_diff.py, route_planes.py, route_disconnected_planes.py,
bga_fanout.py, qfn_fanout.py, check_drc.py, fix_kicad_drc_settings.py,
list_nets.py) and the GUI (one selector on the Basic tab). Values are sourced from
jlcpcb.com/capabilities.
| Option | Default | Description |
|---|---|---|
--fab-tier |
standard |
standard (no extra fab cost) or advanced (tighter, "more costly") |
--fab-overrides |
- | Path to a file overlaying the tier's floors (only the keys it lists) |
The tier is a floor ladder:
standard— the cheap floor (per layer count: 2-layer track/clearance 0.127/0.127, 4+ layer 0.0889/0.10; via 0.45 / drill 0.20; via hole-to-hole 0.20). Routing prefers it but auto-escalates to theadvancedfloor — printing a one-line warning — when a fine-pitch fan-out genuinely cannot escape at the standard floor.advanced— the JLC "more costly" floor (track/clearance 0.10/0.10 on 2-layer, 0.0762/0.09 on 4+; via 0.25 / drill 0.15). A hard floor: no escalation.
| Floor (per layer count) | standard | advanced |
|---|---|---|
| via diameter / drill | 0.45 / 0.20 | 0.25 / 0.15 |
| track / clearance (2-layer) | 0.127 / 0.127 | 0.10 / 0.10 |
| track / clearance (4+ layer) | 0.0889 / 0.10 | 0.0762 / 0.09 |
| via hole-to-hole / pad hole-to-hole | 0.20 / 0.45 | 0.20 / 0.45 |
Override file (--fab-overrides) — a plain, human-editable file overlaying the
selected tier. Only the floor values it lists change; the rest come from the base tier.
Supplying one disables escalation (the floor becomes exactly base tier + file),
since the file states your exact fab limits. One key = value (or key: value, or
key value) per line; # starts a comment; keys are track_width, clearance,
via_diameter, via_drill, hole_to_hole, pad_hole_to_hole, annular:
# my_fab.txt — JLC + a tighter drill I've confirmed
via_drill = 0.15
clearance = 0.09
A ready-to-copy, fully-commented template listing every key and the built-in tier
values lives at fab_overrides.example.txt in the
repo root. (track_width / clearance are layer-dependent in the tier tables, but an
override sets one fixed value for every board — override them only if you want that.)
# Route to the cheap floor (default); dense fan-outs warn when they escalate
python route.py in.kicad_pcb out.kicad_pcb --nets "Net*"
# Opt the whole board into the tighter, more-costly floor
python route.py in.kicad_pcb out.kicad_pcb --nets "Net*" --fab-tier advanced
# Declare your own fab capability
python route.py in.kicad_pcb out.kicad_pcb --nets "Net*" --fab-overrides my_fab.txtFloor enforcement. The CLI errors if --track-width, --clearance,
--via-size, --via-drill or --hole-to-hole-clearance is set below the active tier's
floor (raise the value, or declare a smaller capability with --fab-overrides). The GUI
instead pins the corresponding Basic-tab spin control to the floor and warns. Grade
verification (check_drc.py) defaults its size/clearance floors to the same tier, so
legitimately-escalated fine geometry is not flagged.
| Option | Default | Description |
|---|---|---|
--power-nets |
- | Glob patterns for power nets (e.g., "*GND*" "*VCC*") |
--power-nets-widths |
- | Track widths in mm for each power-net pattern (must match --power-nets length) |
--neckdown-length |
2.5 | Narrow track length in mm from the pads when a failed wide power route is retried at the default width (neck-down) |
--neckdown-taper-length |
0.5 | Narrow-to-wide width taper length in mm (0 = abrupt) |
--no-power-tap-neckdown |
off | Disable the neck-down retry of failed wide power routes |
# Route with wider tracks for power nets
python route.py input.kicad_pcb output.kicad_pcb --nets "Net*" \
--power-nets "*GND*" "*VCC*" "+3.3V" \
--power-nets-widths 0.4 0.5 0.3
# default track-width is 0.3 for non-power netsPatterns are matched in order - first match determines width. Obstacle clearances automatically adjust for wider power traces.
See Power Net Analysis for automatic detection, AI-powered analysis, and IPC-2152 track width guidelines.
| Option | Default | Description |
|---|---|---|
--via-cost |
50 | Via penalty in 0.1mm grid steps, i.e. 50 = 5mm of path; mm-equivalent at any --grid-step (effectively doubled for diff pairs since two vias are placed) |
--max-iterations |
200000 | A* iteration limit per route |
--max-probe-iterations |
5000 | Quick probe per direction to detect stuck routes |
--heuristic-weight |
1.9 | A* greediness (>1 = faster, <1 = more optimal) |
--turn-cost |
1000 | Penalty for direction changes (encourages straighter paths) |
--max-ripup |
3 | Max blockers to rip up at once during rip-up and retry |
--max-setback-angle |
45.0 | Maximum angle for setback position search (degrees) |
--routing-clearance-margin |
1.0 | Multiplier on track-via clearance (1.0 = minimum DRC) |
--hole-to-hole-clearance |
0.20 | Minimum drill hole edge-to-edge clearance (mm) |
--board-edge-clearance |
0.0 | Clearance from board edge in mm (0 = use track clearance) |
--proximity-heuristic-factor |
0.02 | Factor for proximity-aware A* heuristic (higher = faster but may find suboptimal paths, 0 = disabled) |
--ripped-route-avoidance-radius |
1.0 | Radius around ripped route corridors to apply soft penalty (mm) |
--ripped-route-avoidance-cost |
0.1 | Cost penalty for routing through ripped corridors (0 = disabled) |
See Rip-Up and Reroute for how failed routes trigger rip-up, how blockers are identified and ranked, and how ripped nets are rerouted.
| Option | Default | Description |
|---|---|---|
--ordering / -o |
mps | Net ordering: mps, inside_out, or original |
--direction / -d |
forward | Direction: forward or backward |
--layers / -l |
F.Cu B.Cu | Routing layers |
--layer-costs |
(see below) | Per-layer cost multipliers (1.0-1000). Default: all 1.0 for 4+ layers; F.Cu=1.0, B.Cu=3.0 for 2 layers |
--no-bga-zones [REFS...] |
(auto-detect) | Disable BGA exclusion zones. No args = all. With refs (U1 U3) = only those |
| Option | Default | Description |
|---|---|---|
--stub-proximity-radius |
2.0 | Radius around stubs to penalize (mm) |
--stub-proximity-cost |
0.2 | Cost penalty at stub center (mm equivalent) |
--via-proximity-cost |
10.0 | Via cost multiplier in stub proximity zones (0 = block vias) |
--bga-proximity-radius |
7.0 | Radius around BGA edges to penalize (mm) |
--bga-proximity-cost |
0.2 | Cost penalty at BGA edge (mm equivalent) |
--track-proximity-distance |
2.0 | Radius around routed tracks to penalize on same layer (mm) |
--track-proximity-cost |
0.0 | Cost penalty near routed tracks (0 = disabled) |
--vertical-attraction-radius |
1.0 | Radius for cross-layer track attraction (mm) |
--vertical-attraction-cost |
0.0 | Cost bonus for aligning with tracks on other layers (0 = disabled) |
Detects groups of nets with clustered endpoints (buses) and routes them as parallel tracks, each net attracted to its already-routed neighbor. See Bus Routing for the detection algorithm, middle-out routing order, and tuning guidance.
| Option | Default | Description |
|---|---|---|
--bus |
off | Enable bus detection and routing |
--bus-detection-radius |
5.0 | Max endpoint distance for nets to form a bus (mm) |
--bus-min-nets |
2 | Minimum number of nets to form a bus group |
--bus-attraction-radius |
5.0 | Attraction radius around the neighbor's track (mm) |
--bus-attraction-bonus |
5000 | Cost bonus for moving parallel to the neighbor's track |
Draw a polyline on a User layer in KiCad, then enable --guide-corridor to route the
selected nets so they follow that line. The router takes the polyline's vertices as
waypoints (--guide-corridor-spacing > 0 subdivides long segments for tighter curves) and
routes source → waypoints → target. It keeps the route on a single layer where it can — a
waypoint it can't reach cleanly on the current layer is skipped rather than forcing a layer
change, so a corridor doesn't add vias the direct route wouldn't need. It applies to all nets
routed in the run and works in either draw direction; multiple nets following the same
corridor pack alongside each other without overlapping. Supported by route.py and the
plugin ("Follow User-layer guide path" checkbox); not currently supported by route_diff.py
or route_planes.py.
| Option | Default | Description |
|---|---|---|
--guide-corridor |
off | Route selected nets to follow a user-drawn guide polyline |
--guide-corridor-layer |
User.1 | User layer the guide polyline is drawn on |
--guide-corridor-spacing |
0.0 | Max mm between waypoints. 0 uses only the drawn segment endpoints (vertices); >0 subdivides long segments to follow curves more tightly |
# Route two nets so they follow a corridor drawn on User.1
python route.py in.kicad_pcb out.kicad_pcb --nets "Net-(A)" "Net-(B)" --guide-corridorNotes:
- Endpoints/topology are untouched. The waypoints only steer the path between the pads the router already chose to connect. For a multi-point net the existing MST is kept, and each waypoint steers the MST segment it is nearest to (so the net follows the whole drawn line across its topology); a waypoint used by one segment is not reused by others.
- A corridor never makes a route fail. It is strictly best-effort: a waypoint that can't be reached (or that would strand the route) is dropped, and if no waypoints can be followed the segment routes directly — identical to routing with no corridor. With the flag off, routing is byte-for-byte unchanged.
- Routes are placed one at a time; each becomes an obstacle for the next, which keeps multiple corridor nets from overlapping. Draw the corridor wide enough (or use separate lines) if you want several nets to follow it comfortably.
- The guide applies to every net routed in the run, so select only the nets you want it to
steer. Nets routed as a detected bus (
--bus) follow their neighbor instead of the guide. - The route is built by routing A* legs between the waypoints and concatenating them. A multi-point tap or a detour can cross an earlier leg of the same net. KiCad allows same-net copper to overlap, so the net stays connected and DRC-clean against other nets, but the trace may be less tidy than a hand-route.
- Tested by
tests/test_guide_corridor.py— follow, blocked-waypoint snap, shared-corridor non-overlap, flag-off regression, never-blocks-routing, spacing subdivision, and a real bundled board (kicad_files/lvds_converter_dualclk.kicad_pcb) with aUser.1guide.
Draw one or more closed polygons (gr_poly) or rectangles (gr_rect) on a User layer in KiCad,
then enable --keepout to keep routed tracks out of those areas. This is a hard keepout:
routes (and vias) cannot enter any of the polygons on any copper layer. It applies to every net
routed in the run.
| Option | Default | Description |
|---|---|---|
--keepout |
off | Keep routed tracks out of polygons drawn on a User layer |
--keepout-layer |
User.2 | User layer the keepout polygons are drawn on |
# Route a net so it avoids keepout polygons drawn on User.2
python route.py in.kicad_pcb out.kicad_pcb --nets "Net-(A)" --keepoutNotes:
- All closed polygons (
gr_poly) and rectangles (gr_rect) found on the keepout layer are blocked — draw as many as you need. Use the polygon or rectangle tool, not the line tool: open lines (gr_line) don't bound an area and are ignored. - The default keepout layer is
User.2, distinct from the guide-corridor layer (User.1), so you can use both features at once. - Coverage: keepouts are respected across single-ended, multipoint, and differential-pair
routing (
route_diff.py --keepout), and across plane via-stitching and plane repair. KiCad native keep-out rule areas ((zone … (keepout …))) are honoured automatically everywhere too, with no flag. (Guide corridors, by contrast, steer single-ended/multipoint routing only.) - It's a non-copper User layer, so the polygon never affects manufacturing — it only steers the router.
- Don't draw a zone over a pad you need to route. Cells inside the polygon are blocked unconditionally, so a zone covering a routed net's pad can make that net unroutable. Zones are meant for open board area.
- Unlike a guide corridor (which is best-effort), a keepout is absolute — if a zone walls off the only path to a pad, that net will fail to route. With the flag off, routing is unchanged.
- Plugin only: the Basic tab has optional "Clear guide layer after routing" / "Clear keepout layer after routing" checkboxes (unchecked by default). When ticked, a successful route deletes the drawn guide/keepout graphics from that User layer so you can draw fresh ones — it only acts for the feature it pairs with, and never runs if nothing routed.
- Tested by
tests/test_keepout.py— flag-off regression, single-net detour, multi-net avoidance, and a real bundled board (kicad_files/lvds_converter_dualclk.kicad_pcb).
These options are only available in route_diff.py. All nets passed to route_diff.py are treated as differential pairs.
| Option | Default | Description |
|---|---|---|
--impedance |
- | Target differential impedance in ohms (calculates width per layer from stackup) |
--diff-pair-gap |
0.101 | Gap between P and N traces (mm), also used as spacing for impedance calculation |
--diff-pair-centerline-setback |
2x P-N dist | Distance in front of stubs to start centerline (mm) |
--min-turning-radius |
0.2 | Minimum turning radius for pose-based routing (mm) |
--max-turn-angle |
180 | Max cumulative turn angle (degrees) to prevent U-turns |
--max-setback-angle |
45.0 | Maximum angle for setback position search (degrees) |
--polarity-swap-nets |
(none = deny all) | Glob patterns naming pairs allowed to polarity-swap; '*' = all (#279) |
--no-gnd-vias |
false | Disable GND via placement near signal vias |
--diff-chamfer-extra |
1.5 | Chamfer multiplier for diff pair meanders (>1 avoids P/N crossings) |
--diff-pair-intra-match |
false | Match P/N lengths within each diff pair |
--swappable-nets |
- | Glob patterns for diff pair nets that can have targets swapped |
--crossing-penalty |
1000.0 | Penalty for crossing assignments in target swap optimization |
--mps-reverse-rounds |
false | Route most-conflicting MPS groups first (instead of least) |
--mps-segment-intersection |
auto | Force segment intersection method for MPS (auto-enabled when no nets on BGAs) |
These options control stub layer switching, which moves stubs to different layers before routing to avoid vias. Works for both differential pairs and single-ended nets.
| Option | Default | Description |
|---|---|---|
--no-stub-layer-swap |
false | Disable stub layer switching (enabled by default) |
--can-swap-to-top-layer |
false | Allow swapping stubs to F.Cu (off by default for diff pairs due to clearance) |
How it works:
- When source and target stubs are on different layers, a via is normally required
- Stub layer switching moves one stub to match the other's layer, eliminating the via
- For swap pairs, two nets exchange layers to help each other (e.g., Net1 src:A→B and Net2 src:B→A)
- For solo switches, a single stub moves when it doesn't conflict with other stubs
- For bare-pad target swaps, a diff-pair target that is a bare outer-layer pad (F.Cu/B.Cu) with no stub - e.g. a connector pin boxed in by neighbouring pads such as the front row of a 2-row header - is fanned out onto the pair's source layer: a through-via is dropped on each pad and a short stub grown on the source layer, so an inner-layer pair can land on the pad via the via instead of fighting through the congested surface channel
- Multiple swap options are tried: src/src, tgt/tgt, src/tgt, tgt/src
Length matching adds trombone-style meanders to match route lengths within groups. Useful for DDR4 DQ/DQS signals.
| Option | Default | Description |
|---|---|---|
--length-match-group |
- | Group of nets to length match. Use auto for DDR4 auto-grouping, or specify patterns |
--length-match-tolerance |
0.1 | Acceptable length variance within group (mm) |
--meander-amplitude |
1.0 | Height of meanders perpendicular to trace (mm) |
--diff-chamfer-extra |
1.5 | Chamfer multiplier for diff pair meanders (>1 avoids P/N crossings) |
--diff-pair-intra-match |
false | Match P/N lengths within each diff pair (meander shorter track) |
Auto-grouping for DDR4:
--length-match-group autoGroups nets by byte lane: DQ0-7 + DQS0, DQ8-15 + DQS1, etc.
Manual grouping:
--length-match-group "Net-(*DQ[0-7]*)" "Net-(*DQS0*)"
--length-match-group "Net-(*DQ[8-9]*)" "Net-(*DQ1[0-5]*)" "Net-(*DQS1*)"How it works:
- Routes all nets first, then adds meanders to shorter routes
- Calculates pad-to-pad length including existing stub segments and via barrel lengths (parsed from board stackup)
- Stub via barrel lengths use actual stub-layer-to-pad-layer distance (not full via span for through-hole BGA pad vias)
- Via barrel length matches KiCad's length calculation for accurate matching
- Finds longest straight segment for meander insertion
- Iteratively adds meander bumps until length exceeds target, then scales down amplitude to hit exact target
- Per-bump clearance checking reduces amplitude to avoid conflicts with other traces
- Uses 45° chamfered corners for smooth trombone patterns
- Supports multi-layer routes: meanders are applied to same-layer sections, preserving via positions
See Length Matching for the full algorithm: meander geometry, per-bump clearance checking, via barrel lengths, and DDR4 auto-grouping details.
Time matching is an alternative to length matching that matches propagation delay instead of physical length. This accounts for different signal speeds on different layers:
- Outer layers (microstrip): Faster propagation, effective dielectric = (Er + 1) / 2
- Inner layers (stripline): Slower propagation, effective dielectric = Er
| Option | Default | Description |
|---|---|---|
--time-matching |
false | Match propagation time instead of length |
--time-match-tolerance |
1.0 | Acceptable time variance within group (ps) |
When to use time matching:
- When routes in a group use different layers with different dielectric properties
- For high-speed signals where propagation delay matters more than physical length
- Typical FR4: outer layers ~5.4 ps/mm, inner layers ~6.9 ps/mm (~28% difference)
Example:
python route.py input.kicad_pcb output.kicad_pcb --nets "*DQ*" \
--length-match-group auto \
--time-matching \
--time-match-tolerance 1.0See Length Matching for how propagation delay is computed from the stackup, including via barrel delays.
Available in route.py only (not route_diff.py or route_planes.py). See the PyGame visualizer documentation for the full list of interactive controls (pause/step, zoom/pan, layer filtering, speed). Requires pygame (pip install pygame-ce); without it, routing falls back to normal batch mode.
| Option | Default | Description |
|---|---|---|
--visualize / -V |
false | Show real-time visualization of routing (requires pygame) |
--auto |
false | Auto-advance to next net without waiting (with --visualize) |
--display-time |
0.0 | Seconds to display completed route before advancing (with --visualize --auto) |
| Option | Default | Description |
|---|---|---|
--debug-lines |
false | Output debug geometry on User.3/4/5/6/8/9 layers (zones + proximity circles on User.5) |
--verbose / -v |
false | Print detailed diagnostic output (setback checks, bus routing order, etc.) |
--skip-routing |
false | Skip actual routing, only do swaps and write debug info |
--debug-memory |
false | Print memory usage statistics at key points during routing |
--stats |
false | Print A* search statistics (cells expanded, heuristic efficiency) |
--add-teardrops |
false | Add teardrop settings to all pads in output file |
The GridRouteConfig dataclass holds all routing parameters:
@dataclass
class GridRouteConfig:
# Track geometry (see routing_defaults.py for values)
track_width: float = 0.3 # mm (default for non-power nets)
clearance: float = 0.25 # mm between tracks
via_size: float = 0.5 # mm via outer diameter
via_drill: float = 0.3 # mm via drill
power_net_widths: Dict[int, float] = {} # net_id -> width for power nets
power_tap_neckdown: bool = True # retry failed wide power routes at default width
neckdown_length: float = 2.5 # mm of narrow track from the pads on necked routes
neckdown_taper_length: float = 0.5 # mm narrow->wide width taper (0 = abrupt)
# Grid
grid_step: float = 0.1 # mm grid resolution
# A* algorithm
via_cost: int = 50 # via penalty in 0.1mm grid steps = 5mm of path (diff pairs place 2 vias)
max_iterations: int = 200000
max_probe_iterations: int = 5000 # quick probe per direction to detect stuck routes
heuristic_weight: float = 1.9
turn_cost: int = 1000 # penalty for direction changes (straighter paths)
max_rip_up_count: int = 3 # max blockers to rip up at once (progressive N+1)
max_setback_angle: float = 45.0 # degrees
routing_clearance_margin: float = 1.0 # multiplier on track-via clearance (1.0 = min DRC)
hole_to_hole_clearance: float = 0.20 # mm - drill-to-drill fab floor
board_edge_clearance: float = 0.0 # mm - clearance from board edge (0 = use clearance)
proximity_heuristic_factor: float = 0.02 # factor for proximity-aware heuristic (0 = disabled)
ripped_route_avoidance_radius: float = 1.0 # mm - radius around ripped route corridors
ripped_route_avoidance_cost: float = 0.1 # cost for routing through ripped corridors
# Layers
layers: List[str] = ['F.Cu', 'B.Cu']
layer_costs: List[float] = None # per-layer cost multipliers (default: 1.0 for 4+, F.Cu=1.0/B.Cu=3.0 for 2)
# BGA zones
bga_exclusion_zones: List[Tuple[float, float, float, float]] = []
# Stub proximity
stub_proximity_radius: float = 2.0 # mm
stub_proximity_cost: float = 0.2 # mm equivalent
via_proximity_cost: float = 10.0 # multiplier for vias near stubs
# BGA proximity
bga_proximity_radius: float = 7.0 # mm from BGA edges
bga_proximity_cost: float = 0.2 # mm equivalent
# Track proximity (same layer)
track_proximity_distance: float = 2.0 # mm
track_proximity_cost: float = 0.0 # mm equivalent (0 = disabled)
# Vertical track alignment (cross-layer attraction)
vertical_attraction_radius: float = 1.0 # mm
vertical_attraction_cost: float = 0.0 # mm equivalent bonus
# Direction
direction_order: str = "forward" # forward or backward
# Differential pairs
diff_pair_gap: float = 0.101 # mm between P and N
diff_pair_centerline_setback: float = None # mm in front of stubs (None = 2x P-N spacing)
min_turning_radius: float = 0.2 # mm for pose-based routing
max_turn_angle: float = 180.0 # degrees, prevents U-turns
stub_layer_swap: bool = True # enable stub layer switching optimization
gnd_via_enabled: bool = True # place GND vias near signal vias
target_swap_crossing_penalty: float = 1000.0 # penalty for crossing assignments
crossing_layer_check: bool = True # only count crossings on same layer
# Length matching
length_match_groups: List[List[str]] = [] # groups of net patterns to match
length_match_tolerance: float = 0.1 # mm - acceptable length variance
meander_amplitude: float = 1.0 # mm - height of meander perpendicular to trace
diff_chamfer_extra: float = 1.5 # chamfer multiplier for diff pair meanders
diff_pair_intra_match: bool = False # match P/N lengths within each diff pair
# Time matching (alternative to length matching)
time_matching: bool = False # match propagation time instead of length
time_match_tolerance: float = 1.0 # ps - acceptable time variance
# Debug
debug_lines: bool = False
verbose: bool = False
debug_memory: bool = FalseThe via_cost parameter controls how much the router penalizes layer changes:
| Value | Effect |
|---|---|
| 0-25 | Many vias, shorter paths |
| 50 (default) | Balanced, discourages unnecessary vias |
| 75-100 | Few vias, longer paths |
For BGA escape routing, lower values (10-25) work well since vias are necessary.
All cost knobs (via cost, proximity costs, attraction bonuses) are calibrated at a 0.1mm
reference grid and scale internally so the cost per mm of path is the same at any
--grid-step - changing the grid resolution does not change what the router optimizes for.
Controls A* behavior:
| Value | Effect |
|---|---|
| 1.0 | Optimal paths (slower) |
| 1.9 (default) | Good balance of speed and quality |
| 2.5+ | Faster but may miss tight routes |
Smaller grid steps allow finer routing but increase computation:
| Value | Use Case |
|---|---|
| 0.05 | Fine-pitch BGAs, tight clearances |
| 0.1 (default) | General purpose |
| 0.2 | Fast routing, less detail |
The --layer-costs parameter controls per-layer routing preferences. Higher costs make the router avoid that layer:
| Value | Effect |
|---|---|
| 1.0 | No penalty, layer used freely |
| 3.0 | Moderately avoided (3x cost per grid step) |
| 10.0+ | Strongly avoided, used only when necessary |
Defaults:
- 4+ layers: all 1.0 (inner layers available for balanced routing)
- 2 layers: F.Cu=1.0, B.Cu=3.0 (prefer top layer for cleaner assembly)
Example: To keep signals on F.Cu and only use B.Cu when necessary:
--layers F.Cu B.Cu --layer-costs 1.0 5.0Penalizes routes that pass near unrouted stubs:
Cost = stub_proximity_cost * (1 - distance/stub_proximity_radius)
This encourages routes to avoid blocking future routing paths.
--layers F.Cu In1.Cu In2.Cu B.Cu--layers F.Cu B.Cu# Skip In1.Cu and In4.Cu (ground planes)
--layers F.Cu In2.Cu In3.Cu B.Cupython route.py input.kicad_pcb output.kicad_pcb --nets "Net-(*)" \
--ordering inside_out \
--via-cost 10 \
--heuristic-weight 1.2 \
--stub-proximity-radius 2.0 \
--stub-proximity-cost 5.0python route.py input.kicad_pcb output.kicad_pcb --nets "Net-(*)" \
--ordering mps \
--via-cost 50 \
--heuristic-weight 2.0python route_diff.py input.kicad_pcb output.kicad_pcb --nets "*lvds*" \
--diff-pair-gap 0.1 \
--track-width 0.1 \
--clearance 0.1 \
--no-bga-zonespython route.py input.kicad_pcb output.kicad_pcb --nets "Net-(*)" \
--grid-step 0.2 \
--heuristic-weight 2.0 \
--max-iterations 50000python route.py input.kicad_pcb output.kicad_pcb --nets "Net-(*)" \
--grid-step 0.05 \
--track-width 0.075 \
--clearance 0.075 \
--via-size 0.2 \
--via-drill 0.1python route.py input.kicad_pcb output.kicad_pcb --nets "*" \
--power-nets "*GND*" "*VCC*" "*VDD*" "+*V" \
--power-nets-widths 0.5 0.4 0.4 0.3 \
--track-width 0.2 \
--clearance 0.2