--reserve-escapes: keep a way out open for every fine-pitch pin
Before routing, each crowded pin gets the shortest way from its pad to open board, found against pads, keepouts and earlier escapes. While the pin still has connections to route, no other net may put a trace or a via there, nor within clearance of it; the reservation lifts once the pin is routed.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
"""--reserve-escapes: a crowded pin keeps a way out until it is routed."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from freeroute.board import build_board
|
||||
from freeroute.dsn import parse_dsn
|
||||
from freeroute.route.grid_router import GridRouter
|
||||
from freeroute.route.pipeline import (
|
||||
_rule_clearance_dsn,
|
||||
_rule_width_dsn,
|
||||
build_exact_routing_result,
|
||||
)
|
||||
|
||||
SMD = (Path(__file__).resolve().parent.parent / "dsn" / "fixtures" / "smd_demo.dsn").read_text()
|
||||
|
||||
|
||||
def _router():
|
||||
dsn = parse_dsn(SMD)
|
||||
s = dsn.resolution.value
|
||||
router = GridRouter(
|
||||
build_board(dsn),
|
||||
trace_width=round(_rule_width_dsn(dsn) * s),
|
||||
clearance=round(_rule_clearance_dsn(dsn) * s),
|
||||
orthogonal=True,
|
||||
resolution=2,
|
||||
reserve_escapes=True,
|
||||
)
|
||||
conns = router._build_connections()
|
||||
router._conns = conns
|
||||
router._count_pending(conns)
|
||||
router._reserve_escapes(conns)
|
||||
return router, conns
|
||||
|
||||
|
||||
def test_a_reserved_escape_is_closed_to_other_nets_and_open_to_its_own():
|
||||
router, conns = _router()
|
||||
assert router._reserved
|
||||
node, claims = next(iter(router._reserved.items()))
|
||||
pin_id, owner = claims[0]
|
||||
other = next(c.net_no for c in conns if c.net_no != owner)
|
||||
cell, layer = node[:2], node[2]
|
||||
assert router._enter_cost(cell, layer, other, True, conns) is None # even for rip-up
|
||||
assert router._enter_cost(cell, layer, owner, False, conns) is not None or node in router._keepout
|
||||
|
||||
|
||||
def test_the_escape_is_released_once_its_pin_is_routed():
|
||||
router, conns = _router()
|
||||
node, claims = next(iter(router._reserved.items()))
|
||||
other = next(c.net_no for c in conns if c.net_no != claims[0][1])
|
||||
cell, layer = node[:2], node[2]
|
||||
assert router._enter_cost(cell, layer, other, False, conns) is None
|
||||
for c in conns: # every connection routed: no pin is pending any more
|
||||
router._settle(c, -1)
|
||||
released = router._enter_cost(cell, layer, other, False, conns)
|
||||
reserved, router._reserved = router._reserved, {}
|
||||
assert released == router._enter_cost(cell, layer, other, False, conns)
|
||||
router._reserved = reserved
|
||||
|
||||
|
||||
def test_reserving_escapes_loses_nothing_on_the_demo_board():
|
||||
plain = build_exact_routing_result(parse_dsn(SMD), resolution=2)
|
||||
reserved = build_exact_routing_result(parse_dsn(SMD), resolution=2, reserve_escapes=True)
|
||||
assert len(reserved.unrouted) <= len(plain.unrouted)
|
||||
@@ -158,6 +158,7 @@ def test_cli_supported_combinations_are_accepted(tmp_path):
|
||||
"orders": ["--orders", "2"],
|
||||
"promote": ["--promote-failures", "2"],
|
||||
"access": ["--access-cost", "5"],
|
||||
"escapes": ["--reserve-escapes"],
|
||||
}
|
||||
for engine, options in ENGINE_OPTIONS.items():
|
||||
for option in options:
|
||||
|
||||
Reference in New Issue
Block a user