Prove channel packing: connects all where greedy drops, feasibility-bounded

Add channel_pack.dsn: three nets whose pads spread wider than a wall gap, so they
must converge and pack into lanes. Plain room routing (shove on or off) drops >= 1
net under every net ordering; the packer connects all three under every ordering,
DRC-clean (search tree and independent emitted-copper check).

The falsifiable boundary test sweeps the gap width for N=3 and N=4 and asserts the
packer connects all N iff the gap meets the geometric feasibility width
(N-1)*(width+clearance) + 2*(half_width+clearance), never routes fewer than greedy
below it, and never false-packs. Determinism, endpoints-on-pads, valid SES, and
pack=False-equals-default (additive) are covered.
This commit is contained in:
Ryan Malloy 2026-07-13 12:27:31 -06:00
parent c28223ea9c
commit 7f81c1f4ce
2 changed files with 308 additions and 0 deletions

View File

@ -0,0 +1,75 @@
(pcb "channel_pack.dsn"
(parser
(string_quote ")
(space_in_quoted_tokens on)
(host_cad "freeroute-test")
(host_version "1.0")
)
(resolution um 10)
(unit um)
(structure
(layer F.Cu
(type signal)
(property
(index 0)
)
)
(layer B.Cu
(type power)
(property
(index 1)
)
)
(boundary
(path pcb 0 0 0 200000 0 200000 -80000 0 -80000 0 0)
)
(keepout "wall_left"
(rect F.Cu 0 -42000 92000 -38000)
)
(keepout "wall_right"
(rect F.Cu 108000 -42000 200000 -38000)
)
(rule
(width 2000)
(clearance 2000)
)
)
(library
(padstack Rect_Pad
(shape (rect F.Cu -1000 -1000 1000 1000))
(attach off)
)
(image PAD
(pin Rect_Pad 1 0 0)
)
)
(placement
(component PAD
(place P0a 70000 -10000 front 0)
(place P0b 70000 -70000 front 0)
(place P1a 100000 -10000 front 0)
(place P1b 100000 -70000 front 0)
(place P2a 130000 -10000 front 0)
(place P2b 130000 -70000 front 0)
)
)
(network
(net NET0
(pins P0a-1 P0b-1)
)
(net NET1
(pins P1a-1 P1b-1)
)
(net NET2
(pins P2a-1 P2b-1)
)
(class default
(rule
(width 2000)
(clearance 2000)
)
)
)
(wiring
)
)

View File

@ -0,0 +1,233 @@
"""Tests for coordinated multi-trace channel packing on the room router.
``pack=True`` is a third opt-in room-track upgrade, independent of ``shove``. The
skeptic's finding that motivated it: shove recovers ordering drops but does not
create density; the real densifier is lane packing. When several 2-pin nets must
cross a common obstacle gap, greedy per-net routing lets each net grab a gate
independently, the paths collide, and some nets drop even though the gap is
physically wide enough for all of them (``channel_pack.dsn`` -- plain routing,
shove on or off, drops a net under *every* net ordering). The packer assigns each
net a parallel lane across the gap's usable width (pad-ordered, spaced >= width +
clearance) with staggered fan-in trunks, routes them together, and connects all
of them DRC-clean.
The win is bounded honestly by geometry: the packer succeeds iff the gap is at
least ``(N-1)*(width+clearance) + 2*(half_width+clearance)`` wide -- the true
feasibility width for N lanes -- and never regresses below greedy below it.
"""
from __future__ import annotations
import itertools
from pathlib import Path
import pytest
from freeroute.dsn import parse_dsn
from freeroute.dsn.sexp import parse
from freeroute.geometry import Polyline, PolylineShape
from freeroute.route import route, route_dsn_board_rooms
from freeroute.route.pipeline import _rule_clearance_dsn
FIXTURES = Path(__file__).resolve().parent.parent / "dsn" / "fixtures"
CHANNEL_PACK = FIXTURES / "channel_pack.dsn"
SIMPLE = FIXTURES / "simple_2net.dsn"
CROSSING = FIXTURES / "crossing_2net.dsn"
def _clearance(dsn, scale):
return round(_rule_clearance_dsn(dsn) * scale)
def _emitted_drc_violations(result, clearance):
"""Independent DRC over the emitted route: reconstruct every wire's copper
tiles and return different-net, same-layer pairs closer than ``clearance``.
Does not consult the search tree, so it catches copper emitted but (via a bug)
absent from the tree."""
items = []
for net_no, segments in result.wires.items():
for layer, corners in segments:
for box in PolylineShape(Polyline(corners), result.half_width).tiles():
items.append((net_no, layer, box))
bad = []
for i in range(len(items)):
n1, l1, b1 = items[i]
expanded = b1.offset(clearance)
for j in range(i + 1, len(items)):
n2, l2, b2 = items[j]
if n1 == n2 or l1 != l2:
continue
if b2.overlaps(expanded):
bad.append((n1, n2))
return bad
def _net_blocks(text):
import re
return re.findall(r" \(net [^\n]*\n \(pins [^\n]*\n \)\n", text)
def _reorder(text, perm):
blocks = _net_blocks(text)
return text.replace("".join(blocks), "".join(blocks[i] for i in perm), 1)
def _wire_key(result):
return {
n: [(layer, [(p.x, p.y) for p in pts]) for layer, pts in segs]
for n, segs in result.result.wires.items()
}
def _routed_counts(text, n, *, pack, shove):
return [
len(
route_dsn_board_rooms(parse_dsn(_reorder(text, p)), shove=shove, pack=pack)[
0
].routed_net_numbers
)
for p in itertools.permutations(range(n))
]
# --- the genuine gap: current router drops a net under every ordering ---------
def test_channel_pack_plain_drops_a_net_under_every_ordering():
"""Bar (same methodology as true_density): the current router, shove on OR
off, drops >= 1 of the three nets under EVERY net ordering -- an
order-independent drop, even though the gap physically fits three lanes."""
text = CHANNEL_PACK.read_text()
plain = _routed_counts(text, 3, pack=False, shove=False)
shoved = _routed_counts(text, 3, pack=False, shove=True)
assert all(k < 3 for k in plain)
assert all(k < 3 for k in shoved)
# shove buys nothing here: it relocates blockers, it does not pack lanes
assert shoved == plain
def test_channel_pack_connects_all_drc_clean():
"""The packer connects all three nets and the emitted copper is exactly
DRC-clean, verified both through the search tree and independently."""
dsn = parse_dsn(CHANNEL_PACK.read_text())
on, scale, _ = route_dsn_board_rooms(dsn, pack=True)
clearance = _clearance(dsn, scale)
assert on.routed_net_numbers == {1, 2, 3}
assert on.drc_clean
assert on.tree.has_violation(clearance) is None
assert _emitted_drc_violations(on.result, clearance) == []
def test_channel_pack_connects_all_under_every_ordering():
counts = _routed_counts(CHANNEL_PACK.read_text(), 3, pack=True, shove=False)
assert counts == [3, 3, 3, 3, 3, 3]
def test_channel_pack_is_deterministic():
a, _, _ = route_dsn_board_rooms(parse_dsn(CHANNEL_PACK.read_text()), pack=True)
b, _, _ = route_dsn_board_rooms(parse_dsn(CHANNEL_PACK.read_text()), pack=True)
assert _wire_key(a) == _wire_key(b)
assert a.routed_net_numbers == b.routed_net_numbers
def test_channel_pack_endpoints_stay_on_pads():
dsn = parse_dsn(CHANNEL_PACK.read_text())
board_pins: dict[int, set[tuple[int, int]]] = {}
from freeroute.board import build_board
for pin in build_board(dsn).get_pins():
for net_no in pin.net_nos:
board_pins.setdefault(net_no, set()).add((pin.location.x, pin.location.y))
on, _, _ = route_dsn_board_rooms(dsn, pack=True)
for net_no, segments in on.result.wires.items():
pts = [(p.x, p.y) for _, run in segments for p in run]
assert {pts[0], pts[-1]} == board_pins[net_no]
def test_channel_pack_valid_ses():
ses = route(CHANNEL_PACK.read_text(), engine="room", pack=True)
top = parse(ses)
assert top.head == "session"
nets = top.child("routes").child("network_out").children("net")
assert {n.values()[0].text for n in nets} == {"NET0", "NET1", "NET2"}
# --- additive: pack must not disturb the room-default / non-channel tracks ----
@pytest.mark.parametrize("fixture", [SIMPLE, CROSSING, CHANNEL_PACK])
def test_pack_off_matches_default(fixture):
"""pack defaults off; passing pack=False must be byte-identical to the
foundation room route (no channel behaviour leaks into the default track)."""
dsn = parse_dsn(fixture.read_text())
default, _, _ = route_dsn_board_rooms(dsn)
off, _, _ = route_dsn_board_rooms(dsn, pack=False)
assert _wire_key(default) == _wire_key(off)
def test_pack_on_non_channel_board_is_noop():
"""On a board with no obstacle gap to pack, pack=True changes nothing: the
pre-pass finds no channel group and the greedy loop runs exactly as before."""
dsn = parse_dsn(SIMPLE.read_text())
off, _, _ = route_dsn_board_rooms(dsn, pack=False)
on, _, _ = route_dsn_board_rooms(dsn, pack=True)
assert _wire_key(off) == _wire_key(on)
# --- falsifiable: pack succeeds iff the gap meets the geometric feasibility ----
def _channel_dsn(n, gap, spread, *, width=200000, height=80000):
gx0 = (width - gap) // 2
gx1 = (width + gap) // 2
cx = width // 2
xs = [int(round(cx + (i - (n - 1) / 2) * spread)) for i in range(n)]
places = "".join(
f" (place P{i}a {x} -10000 front 0)\n (place P{i}b {x} -70000 front 0)\n"
for i, x in enumerate(xs)
)
nets = "".join(f" (net NET{i}\n (pins P{i}a-1 P{i}b-1)\n )\n" for i in range(n))
return f"""(pcb "sweep.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 {width} 0 {width} -{height} 0 -{height} 0 0))
(keepout "wl" (rect F.Cu 0 -42000 {gx0} -38000))
(keepout "wr" (rect F.Cu {gx1} -42000 {width} -38000))
(rule (width 2000) (clearance 2000)))
(library
(padstack Rect_Pad (shape (rect F.Cu -1000 -1000 1000 1000)) (attach off))
(image PAD (pin Rect_Pad 1 0 0)))
(placement (component PAD
{places} ))
(network
{nets} (class default (rule (width 2000) (clearance 2000))))
(wiring )
)
"""
@pytest.mark.parametrize("n", [3, 4])
def test_channel_pack_boundary_is_geometric_feasibility(n):
"""Sweep the gap width: the packer connects all N nets iff the gap is at least
the geometric feasibility width for N lanes, and never routes fewer than the
greedy baseline below it. width=2000, clearance=2000 -> half_width=1000, so
lanes need >= 4000 apart and >= 3000 from each gap edge."""
width, clearance, half_width = 2000, 2000, 1000
feasibility = (n - 1) * (width + clearance) + 2 * (half_width + clearance)
spread = 40000
step = 1000
for gap in range(feasibility - 4 * step, feasibility + 4 * step + 1, step):
text = _channel_dsn(n, gap, spread)
plain = len(route_dsn_board_rooms(parse_dsn(text), pack=False)[0].routed_net_numbers)
packed_res = route_dsn_board_rooms(parse_dsn(text), pack=True)[0]
packed = len(packed_res.routed_net_numbers)
assert packed_res.drc_clean
assert packed >= plain # never a regression
if gap >= feasibility:
assert packed == n # packs everything at/above feasibility
else:
assert packed < n # cannot (and does not falsely) pack below it