Add rip-up-and-retry to the maze router
Restructures the router so occupancy is tracked per connection, then adds a rip-up-and-retry loop that recovers from bad greedy net orderings. When a connection cannot reach its target through free space, a rip-up search may pass through other nets' traces at an escalating penalty; the router rips up the connections that path crosses (fully removing their occupancy), routes the failing connection, and re-queues the ripped connections. Passes iterate up to max_passes, keeping the best (fewest-unrouted) result and stopping on full success or when a pass changes nothing. Thrash prevention: each connection may be ripped at most rip_cap times, and the rip penalty escalates with a connection's rip count, so repeatedly-ripped connections harden into walls. Fully deterministic (sorted rip sets, connection-creation order, tuple-keyed A* — no RNG). route()/route_dsn_board()/route_board() take rip_up and max_passes; rip_up=False reproduces the previous greedy single-pass behaviour for comparison. RouteResult now reports the unrouted connection count.
This commit is contained in:
@@ -44,23 +44,37 @@ def _via_padstack(dsn: DsnBoard) -> str:
|
||||
|
||||
|
||||
def route_dsn_board(
|
||||
dsn: DsnBoard, *, layers: list[int] | None = None
|
||||
dsn: DsnBoard,
|
||||
*,
|
||||
layers: list[int] | None = None,
|
||||
rip_up: bool = True,
|
||||
max_passes: int = 10,
|
||||
) -> tuple[RouteResult, int, list[str]]:
|
||||
"""Route a parsed :class:`DsnBoard`; return the board-unit result, the scale,
|
||||
and the layer names. ``layers`` restricts routing to those signal-layer
|
||||
indices (default: all signal layers, i.e. multi-layer with vias)."""
|
||||
indices (default: all signal layers, i.e. multi-layer with vias); ``rip_up``
|
||||
toggles rip-up-and-retry (``False`` = greedy single pass)."""
|
||||
board = build_board(dsn)
|
||||
scale = max(dsn.resolution.value, 1)
|
||||
width_board = round(_rule_width_dsn(dsn) * scale)
|
||||
clearance_board = round(_rule_clearance_dsn(dsn) * scale)
|
||||
result = route_board(board, trace_width=width_board, clearance=clearance_board, layers=layers)
|
||||
result = route_board(
|
||||
board,
|
||||
trace_width=width_board,
|
||||
clearance=clearance_board,
|
||||
layers=layers,
|
||||
rip_up=rip_up,
|
||||
max_passes=max_passes,
|
||||
)
|
||||
return result, scale, [layer.name for layer in dsn.layers]
|
||||
|
||||
|
||||
def build_routing_result(dsn: DsnBoard, *, layers: list[int] | None = None) -> RoutingResult:
|
||||
def build_routing_result(
|
||||
dsn: DsnBoard, *, layers: list[int] | None = None, rip_up: bool = True
|
||||
) -> RoutingResult:
|
||||
"""Route ``dsn`` and convert the board-unit paths + vias to a DSN-unit
|
||||
:class:`~freeroute.ses.RoutingResult` for SES emission."""
|
||||
route, scale, layer_names = route_dsn_board(dsn, layers=layers)
|
||||
route, scale, layer_names = route_dsn_board(dsn, layers=layers, rip_up=rip_up)
|
||||
width_dsn = _rule_width_dsn(dsn)
|
||||
via_name = _via_padstack(dsn)
|
||||
# net_number is assigned in DSN order by build_board, so index i -> number i+1
|
||||
|
||||
Reference in New Issue
Block a user