Keep a route's own vias apart while it is searched

Committed vias were checked for hole spacing, but a route's own vias are
not committed until it is done, so a route hopping layers (more of them
on 6 layers) could put two of its vias 0.41 mm apart, as th-eda's
6-layer flight controller showed. A via is now also checked against
layer changes earlier on its own route.
This commit is contained in:
2026-09-29 03:10:52 -06:00
parent 6e852a929b
commit 83ad0f8ef7
2 changed files with 34 additions and 0 deletions
+24
View File
@@ -538,6 +538,26 @@ class GridRouter:
return False
return self._holes_clear(cell, net_no)
def _near_path_via(self, node: Node, cell: Cell, came_from: dict) -> bool:
"""A layer change earlier on the route that reaches ``node`` sits
within hole spacing of ``cell`` (and is not the same through via).
Committed vias are checked elsewhere; a route's own are not yet
committed while it is searched. Looks back far enough to cover the
hole spacing twice over, not along the whole route."""
limit = 4 * (math.ceil(self._hole_need / self.step) + 1)
need = self._hole_need / self.step
steps = 0
while node in came_from and steps < limit:
prev = came_from[node]
changes_layer = prev[:2] == node[:2] and prev[2] != node[2]
if changes_layer and node[:2] != cell and (
math.hypot(node[0] - cell[0], node[1] - cell[1]) < need
):
return True
node = prev
steps += 1
return False
def _via_nets_at(self, cell: Cell) -> set[int]:
nets = {net for net, n in self._via_nets.get(cell, {}).items() if n > 0}
fixed = self._fixed_vias.get(cell)
@@ -664,6 +684,8 @@ class GridRouter:
pin = ends.get(node)
if ok and isinstance(pin, Pin) and self._walled(node):
ok = self._via_fits_at(pin.location, net_no)
if ok and self._near_path_via(source, cell, came_from):
ok = False # too close to a via earlier on this same route
if ok:
self._relax(source, target, g_via, goal_cell, g_score, came_from, open_heap)
continue
@@ -675,6 +697,8 @@ class GridRouter:
ok = via_ok.get(cell)
if ok is None:
ok = via_ok[cell] = self._via_placeable(cell, net_no, conns)
if ok and self._near_path_via(current, cell, came_from):
ok = False
else:
ok = True
if ok:
+10
View File
@@ -124,3 +124,13 @@ def test_hole_spacing_from_a_fixed_via_is_measured_from_where_it_really_is():
assert not router._holes_clear(cell, 1), f"{d / s:.0f} um from the fixed via"
elif 560 * s < d < 700 * s and cell not in router._tht_block:
assert router._holes_clear(cell, 1)
def test_a_route_keeps_its_own_vias_apart():
# a route that changed layer at (10, 10) may not change layer again one
# cell away; the same cell is the same through via, and far is fine
router, _s = _router(_THT)
came_from = {(10, 10, 1): (10, 10, 0), (11, 10, 1): (10, 10, 1), (12, 10, 1): (11, 10, 1)}
assert router._near_path_via((12, 10, 1), (11, 10), came_from)
assert not router._near_path_via((12, 10, 1), (10, 10), came_from)
assert not router._near_path_via((12, 10, 1), (40, 10), came_from)