The router began as a port of FreeRouting and has grown its own engine (negotiated congestion, tree nets, exact checks, axis escapes), and 'freeroute' was one letter from the project it no longer follows. The package, module and command are now rpmroute; the freeroute command stays as an alias, and the RPMROUTE / RPMROUTE_ORACLE_JAVA variables fall back to the old FREEROUTE names. Version 2026.10.07. Routing is unchanged: a PCBench board routes to a byte-identical session either way.
66 lines
2.5 KiB
Python
66 lines
2.5 KiB
Python
"""--negotiate: PathFinder-style rounds on the empty board before routing."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from rpmroute.board import build_board
|
|
from rpmroute.dsn import parse_dsn
|
|
from rpmroute.route.grid_router import GridRouter
|
|
from rpmroute.route.pipeline import _rule_clearance_dsn, _rule_width_dsn, route_dsn_board
|
|
|
|
FIXTURES = Path(__file__).resolve().parent.parent / "dsn" / "fixtures"
|
|
|
|
|
|
def _router(name, negotiate):
|
|
dsn = parse_dsn((FIXTURES / name).read_text())
|
|
s = dsn.resolution.value
|
|
return GridRouter(
|
|
build_board(dsn),
|
|
trace_width=round(_rule_width_dsn(dsn) * s),
|
|
clearance=round(_rule_clearance_dsn(dsn) * s),
|
|
negotiate=negotiate,
|
|
)
|
|
|
|
|
|
def test_negotiation_leaves_the_board_empty_and_the_ends_as_they_were():
|
|
router = _router("true_density.dsn", 3)
|
|
conns = router._conns = router._build_connections()
|
|
ends = [(c.a_end, c.b_end) for c in conns]
|
|
router._negotiate(list(conns), conns)
|
|
assert router._history # a dense board has contested cells
|
|
assert not router._cell_owner and not router._halo and not router._via_claims
|
|
assert not any(c.routed for c in conns)
|
|
assert [(c.a_end, c.b_end) for c in conns] == ends
|
|
assert router._present is None
|
|
|
|
|
|
def test_sharing_is_priced_only_for_other_nets():
|
|
router = _router("true_density.dsn", 1)
|
|
node = (3, 4, 0)
|
|
router._history[node] = 2.0
|
|
router._present, router._present_cost = {node: {7: 1, 9: 2}}, 0.5
|
|
assert router._congestion(node, 7) == pytest.approx(2.5) # one other net
|
|
assert router._congestion(node, 5) == pytest.approx(3.0) # two other nets
|
|
router._present = None
|
|
assert router._congestion(node, 5) == pytest.approx(2.0) # history only, after
|
|
|
|
|
|
@pytest.mark.parametrize("name", ["ripup_needed.dsn", "true_density.dsn", "kicad_routable.dsn"])
|
|
def test_negotiated_routing_keeps_the_occupancy_honest(name):
|
|
"""Only routed connections hold cells afterwards, as without negotiation."""
|
|
router = _router(name, 3)
|
|
router.route()
|
|
conns = router._conns
|
|
assert all(conns[i].routed for i in router._cell_owner.values())
|
|
assert all(conns[i].routed for claims in router._halo.values() for i in claims)
|
|
|
|
|
|
def test_option_reaches_the_router_and_is_off_by_default():
|
|
text = (FIXTURES / "kicad_routable.dsn").read_text()
|
|
plain, _, _ = route_dsn_board(parse_dsn(text))
|
|
again, _, _ = route_dsn_board(parse_dsn(text), negotiate=0)
|
|
assert plain.unrouted == again.unrouted
|