Window boards: never search a connection ending outside the window
With items away from the window left off its board, a stub from the window edge out to a rerouted net's far pin crossed copper the exact check never saw: 38 shorts on th-eda's flight controller. A connection with an end outside the bounds is now left unrouted, and a window whose new copper leaves the window is refused.
This commit is contained in:
@@ -211,6 +211,10 @@ class GridRouter:
|
||||
# these nets get connections, and the grid covers only ``bounds``
|
||||
# (cells outside it do not exist, so nothing is routed there).
|
||||
self.only_nets = frozenset(only_nets) if only_nets is not None else None
|
||||
self._bounds = bounds
|
||||
# connections with an end outside ``bounds``: never searched, since
|
||||
# the grid has no cells to reach that end by
|
||||
self._outside: set[int] = set()
|
||||
# Multi-pin nets: pair a net's pin groups by a minimum spanning tree,
|
||||
# and let a connection start or end on copper its net already routed
|
||||
# (a T into the tree), not only on one chosen pin.
|
||||
@@ -717,6 +721,8 @@ class GridRouter:
|
||||
def _search(
|
||||
self, conn: _Connection, allow_ripup: bool, conns: list[_Connection]
|
||||
) -> list[Node] | None:
|
||||
if conn.index in self._outside:
|
||||
return None
|
||||
half = self._conn_half(conn)
|
||||
self._cur_half, self._spread = half, self._spread_for(half)
|
||||
try:
|
||||
@@ -1872,6 +1878,11 @@ class GridRouter:
|
||||
self._net_conns: dict[int, list[_Connection]] = {}
|
||||
for conn in conns:
|
||||
self._net_conns.setdefault(conn.net_no, []).append(conn)
|
||||
if self._bounds is not None:
|
||||
self._outside = {
|
||||
c.index for c in conns
|
||||
if not (_group_inside(c.a_group, self._bounds) and _group_inside(c.b_group, self._bounds))
|
||||
}
|
||||
return conns
|
||||
|
||||
def _fixed_pieces(self, net_no: int) -> list[_FixedPiece]:
|
||||
@@ -2017,6 +2028,19 @@ def _group_label(group) -> str:
|
||||
return "/".join(parts) or "?"
|
||||
|
||||
|
||||
def _group_inside(group, bounds: IntBox) -> bool:
|
||||
"""A group has a pin inside ``bounds``, or a zone reaching into it."""
|
||||
pins, zones = group
|
||||
if any(bounds.ll.x <= p.location.x <= bounds.ur.x and bounds.ll.y <= p.location.y <= bounds.ur.y
|
||||
for p in pins):
|
||||
return True
|
||||
for zone in zones:
|
||||
box = zone.bounding_box()
|
||||
if box.ll.x <= bounds.ur.x and bounds.ll.x <= box.ur.x and box.ll.y <= bounds.ur.y and bounds.ll.y <= box.ur.y:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _ends_of(conn) -> tuple[str, str]:
|
||||
"""A readable description of what a connection joins."""
|
||||
return _group_label(conn.a_group), _group_label(conn.b_group)
|
||||
|
||||
@@ -206,6 +206,11 @@ def window_reroute(
|
||||
after = _open(f for f in sub.failed if f[0] in affected)
|
||||
if len(after) >= len(before):
|
||||
continue
|
||||
# the window board left out what lies away from it, so copper
|
||||
# reaching out of the window was never checked against it
|
||||
if any(not _inside((sub.wires.get(n, []), sub.vias.get(n, [])), window)
|
||||
for n in affected if sub.wires.get(n) or sub.vias.get(n)):
|
||||
continue
|
||||
# a net keeping some of its copper is written at one width: the
|
||||
# window's new copper has to have gone in at that width too
|
||||
if any(
|
||||
|
||||
@@ -110,3 +110,19 @@ def test_window_board_keeps_far_items_of_the_rerouted_nets_only():
|
||||
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 freeroute.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)
|
||||
|
||||
Reference in New Issue
Block a user