After the exact engine, every net routed wholly inside a window around a stuck connection's pins is ripped, the rest of the board is laid down as fixed copper, and the window alone is routed again at --window-resolution (default 4) with the stuck net and the ripped nets only. The result is kept only if fewer connections are left unrouted; each round doubles the margin. The grid and exact routers gain only_nets and bounds for it. Off by default.
96 lines
3.5 KiB
Python
96 lines
3.5 KiB
Python
"""Window re-route: a finer grid around a stuck connection, kept only when
|
|
it leaves fewer connections unrouted."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from freeroute.board import build_board
|
|
from freeroute.dsn import parse_dsn
|
|
from freeroute.geometry import IntBox
|
|
from freeroute.route.grid_router import GridRouter
|
|
from freeroute.route.pipeline import _rule_clearance_dsn, _rule_width_dsn, route_dsn_board_exact
|
|
|
|
|
|
def _slot(gap: int = 640, far: bool = False) -> str:
|
|
"""A wall across the board with a slot in it. A trace fits the slot
|
|
only along a narrow band of heights that the board's 1x grid has no row
|
|
in and a 4x grid does. ``far`` adds a second net nowhere near the wall."""
|
|
yc = -5000
|
|
lo, hi = yc - gap // 2, yc + gap // 2
|
|
extra_place = "(place Q1 2000 -1000 front 0) (place Q2 6000 -1000 front 0)" if far else ""
|
|
extra_net = "(net M (pins Q1-1 Q2-1))" if far else ""
|
|
return f"""(pcb "slot.dsn"
|
|
(parser (string_quote ") (space_in_quoted_tokens on) (host_cad "t") (host_version "1"))
|
|
(resolution um 10)
|
|
(unit um)
|
|
(structure
|
|
(layer F.Cu (type signal) (property (index 0)))
|
|
(boundary (path pcb 0 0 0 20000 0 20000 -10000 0 -10000 0 0))
|
|
(keepout "" (rect F.Cu 9000 -10000 11000 {lo}))
|
|
(keepout "" (rect F.Cu 9000 {hi} 11000 0))
|
|
(rule (width 200) (clearance 200))
|
|
)
|
|
(library
|
|
(padstack Rect_Pad (shape (rect F.Cu -100 -100 100 100)) (attach off))
|
|
(image PAD (pin Rect_Pad 1 0 0))
|
|
)
|
|
(placement
|
|
(component PAD
|
|
(place P1 3000 {yc} front 0)
|
|
(place P2 17000 {yc} front 0)
|
|
{extra_place}
|
|
)
|
|
)
|
|
(network
|
|
(net N (pins P1-1 P2-1))
|
|
{extra_net}
|
|
(class default (rule (width 200) (clearance 200)))
|
|
)
|
|
(wiring)
|
|
)
|
|
"""
|
|
|
|
|
|
def _route(text: str, rounds: int):
|
|
exact, _scale, _layers = route_dsn_board_exact(
|
|
parse_dsn(text), resolution=1, window_reroute=rounds, window_resolution=4,
|
|
)
|
|
return exact.result
|
|
|
|
|
|
def test_the_board_grid_misses_the_slot_and_a_finer_window_finds_it():
|
|
plain = _route(_slot(), 0)
|
|
assert len(plain.failed) == 1
|
|
windowed = _route(_slot(), 1)
|
|
assert windowed.failed == []
|
|
assert windowed.wires # and the copper it found is in the result
|
|
|
|
|
|
def test_a_window_that_cannot_help_leaves_the_board_as_it_was():
|
|
plain = _route(_slot(gap=500, far=True), 0) # too narrow for any grid
|
|
windowed = _route(_slot(gap=500, far=True), 2)
|
|
assert len(windowed.failed) == len(plain.failed) == 1
|
|
assert windowed.wires == plain.wires
|
|
assert windowed.vias == plain.vias
|
|
|
|
|
|
def test_copper_outside_the_window_is_kept_as_it_was():
|
|
plain = _route(_slot(far=True), 0)
|
|
windowed = _route(_slot(far=True), 1)
|
|
assert windowed.failed == []
|
|
far_net = 2 # M, the second net in the DSN
|
|
assert windowed.wires[far_net] == plain.wires[far_net]
|
|
|
|
|
|
def test_a_router_limited_to_some_nets_and_a_box_builds_only_those():
|
|
dsn = parse_dsn(_slot(far=True))
|
|
s = dsn.resolution.value
|
|
board = build_board(dsn)
|
|
whole = GridRouter(board, trace_width=round(_rule_width_dsn(dsn) * s),
|
|
clearance=round(_rule_clearance_dsn(dsn) * s), orthogonal=True)
|
|
part = GridRouter(board, trace_width=round(_rule_width_dsn(dsn) * s),
|
|
clearance=round(_rule_clearance_dsn(dsn) * s), orthogonal=True,
|
|
only_nets=frozenset({2}), bounds=IntBox(0, -20000, 80000, 0))
|
|
assert {c.net_no for c in whole._build_connections()} == {1, 2}
|
|
assert {c.net_no for c in part._build_connections()} == {2}
|
|
assert part.cols * part.rows < whole.cols * whole.rows
|