"""Window re-route: a finer grid around a stuck connection, kept only when it leaves fewer connections unrouted.""" from __future__ import annotations from rpmroute.board import build_board from rpmroute.dsn import parse_dsn from rpmroute.geometry import IntBox from rpmroute.route.grid_router import GridRouter from rpmroute.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, **window): exact, _scale, _layers = route_dsn_board_exact( parse_dsn(text), resolution=1, window_reroute=rounds, window_resolution=4, **window, ) 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 def test_a_spent_budget_starts_no_window(): assert len(_route(_slot(), 1, window_budget=0.0).failed) == 1 assert _route(_slot(), 1, window_budget=1000.0).failed == [] def test_window_board_keeps_far_items_of_the_rerouted_nets_only(): from rpmroute.route.window import _item_box, _window_board board = build_board(parse_dsn(_slot(far=True))) items = [(item, _item_box(item)) for item in board.get_items()] view = IntBox(80000, -60000, 120000, -40000) # around the slot, far from Q1/Q2 sub = _window_board(board, items, view, {1}, {}, set(), {}, 100, 300, 300, [0], "via") refs = {p.component_ref for p in sub.get_pins()} assert {"P1", "P2"} <= refs # net N's pins, though outside the view assert not refs & {"Q1", "Q2"} # net M's, far away, left out def test_a_connection_ending_outside_the_window_gets_no_copper(): """Its far end has no grid cells; a stub out to it would cross items the window board never saw.""" from rpmroute.route.exact_router import route_board_exact dsn = parse_dsn(_slot(far=True)) s = dsn.resolution.value window = IntBox(70000, -70000, 130000, -30000) # the slot only; net M's pins are far off result = route_board_exact( build_board(dsn), trace_width=round(_rule_width_dsn(dsn) * s), clearance=round(_rule_clearance_dsn(dsn) * s), only_nets=frozenset({2}), bounds=window, ).result assert [f[0] for f in result.failed] == [2] assert not result.wires.get(2) and not result.vias.get(2)