Keep hole-to-hole spacing between every two vias, one net's included
Every pin of a supply drops its own via to the plane, and nothing kept two of them apart: the flight controller came back with 199 co-located holes and 109 hole-to-hole errors, nearly all VDD_3V3 and GND. A via now keeps its drill plus --hole-to-hole (default 0.25 mm, KiCad's) from every via centre, fixed ones included, except an own-net via at the same cell, which it reuses; such a via is written once, and none is written on top of a via already on the board.
This commit is contained in:
@@ -31,7 +31,7 @@ import math
|
||||
import random
|
||||
|
||||
from freeroute.board.board import BasicBoard
|
||||
from freeroute.board.items import ConductionArea, ObstacleArea, Pin
|
||||
from freeroute.board.items import ConductionArea, ObstacleArea, Pin, Via
|
||||
from freeroute.geometry import IntBox, IntPoint
|
||||
from freeroute.geometry.polyline import corner_box
|
||||
|
||||
@@ -177,6 +177,8 @@ class GridRouter:
|
||||
access_cost: float = 0.0,
|
||||
rollback: bool = True,
|
||||
net_widths: dict[int, int] | None = None,
|
||||
via_drill: int | None = None,
|
||||
hole_to_hole: int = 0,
|
||||
) -> None:
|
||||
self.board = board
|
||||
# Pin access: while a pin still has connections to route, the cells
|
||||
@@ -248,6 +250,23 @@ class GridRouter:
|
||||
self._spread: list[tuple[int, int]] = [] # the current search's
|
||||
self._cur_half = self.half_width
|
||||
self._halo_by_half: dict[int, list[tuple[int, int]]] = {self.half_width: self._halo_offsets}
|
||||
# Hole-to-hole spacing holds between any two vias, of one net or two.
|
||||
# Other nets' vias are kept further apart already; a net's own vias
|
||||
# (every pin of a supply dropping to its plane) are not, so a via
|
||||
# must keep its drill plus the spacing from every via centre, except
|
||||
# an own-net via at the very same cell, which it simply reuses.
|
||||
drill = via_drill if via_drill else 2 * self.via_half
|
||||
need = drill + max(hole_to_hole, 0)
|
||||
reach = math.ceil(need / self.step)
|
||||
self._hole_offsets = [
|
||||
(dx, dy)
|
||||
for dx in range(-reach, reach + 1)
|
||||
for dy in range(-reach, reach + 1)
|
||||
if (dx or dy) and math.hypot(dx, dy) * self.step < need
|
||||
]
|
||||
self._via_nets: dict[Cell, dict[int, int]] = {} # routed via centres
|
||||
self._fixed_vias: dict[Cell, int] = {}
|
||||
self._fixed_via_points: set[tuple[int, IntPoint]] = set()
|
||||
|
||||
if layers is None:
|
||||
layers = [i for i, layer in enumerate(board.layer_structure.arr) if layer.is_signal]
|
||||
@@ -276,6 +295,10 @@ class GridRouter:
|
||||
self._halo: dict[Node, list[int]] = {}
|
||||
self._rasterize_obstacles()
|
||||
self._rasterize_fixed_copper()
|
||||
for item in board.get_items():
|
||||
if isinstance(item, Via) and item.fixed and item.net_nos:
|
||||
self._fixed_vias[self._cell_of(item.location)] = item.net_nos[0]
|
||||
self._fixed_via_points.add((item.net_nos[0], item.location))
|
||||
|
||||
# --- grid helpers -------------------------------------------------------
|
||||
|
||||
@@ -491,7 +514,21 @@ class GridRouter:
|
||||
return False
|
||||
if self._enter_cost_base(c, layer, net_no, False, conns, wire_keepout=False) != 0.0:
|
||||
return False
|
||||
return True
|
||||
return self._holes_clear(cell, net_no)
|
||||
|
||||
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)
|
||||
if fixed is not None:
|
||||
nets.add(fixed)
|
||||
return nets
|
||||
|
||||
def _holes_clear(self, cell: Cell, net_no: int) -> bool:
|
||||
"""No via centre within hole-to-hole reach, but an own-net one here."""
|
||||
if self._via_nets_at(cell) - {net_no}:
|
||||
return False
|
||||
cx, cy = cell
|
||||
return not any(self._via_nets_at((cx + dx, cy + dy)) for dx, dy in self._hole_offsets)
|
||||
|
||||
# --- A* search ----------------------------------------------------------
|
||||
|
||||
@@ -953,6 +990,9 @@ class GridRouter:
|
||||
via_cells.append((path[-1][0], path[-1][1]))
|
||||
conn.trace_cells = [(cell, layer) for layer, cells in wire_segs for cell in cells]
|
||||
conn.via_cells = list(via_cells)
|
||||
for cell in conn.via_cells:
|
||||
count = self._via_nets.setdefault(cell, {})
|
||||
count[conn.net_no] = count.get(conn.net_no, 0) + 1
|
||||
for cell, layer in conn.trace_cells:
|
||||
self._cell_owner[(*cell, layer)] = conn.index
|
||||
conn.half = half = self._conn_half(conn)
|
||||
@@ -1027,6 +1067,10 @@ class GridRouter:
|
||||
del self._halo[node]
|
||||
conn.halo_nodes = []
|
||||
conn.trace_cells = []
|
||||
for cell in conn.via_cells:
|
||||
count = self._via_nets.get(cell)
|
||||
if count and count.get(conn.net_no):
|
||||
count[conn.net_no] -= 1
|
||||
conn.via_cells = []
|
||||
conn.wires = []
|
||||
conn.vias = []
|
||||
@@ -1257,11 +1301,16 @@ class GridRouter:
|
||||
|
||||
def _result_from(self, snapshot, unrouted: int) -> RouteResult:
|
||||
result = RouteResult(self.half_width)
|
||||
written: set[tuple[int, IntPoint]] = set()
|
||||
result.unrouted = unrouted
|
||||
for net_no, wires, vias, ends in snapshot:
|
||||
for layer, points in wires:
|
||||
result.add_wire(net_no, layer, points)
|
||||
for location in vias:
|
||||
spot = (net_no, location)
|
||||
if spot in written or spot in self._fixed_via_points:
|
||||
continue # a via reused by another connection, or already on the board
|
||||
written.add(spot)
|
||||
result.add_via(net_no, location)
|
||||
result.connections.append((net_no, wires, vias))
|
||||
result.connection_ends.append(ends)
|
||||
@@ -1408,6 +1457,8 @@ def route_board(
|
||||
access_cost: float = 0.0,
|
||||
rollback: bool = True,
|
||||
net_widths: dict[int, int] | None = None,
|
||||
via_drill: int | None = None,
|
||||
hole_to_hole: int = 0,
|
||||
) -> RouteResult:
|
||||
"""Convenience wrapper: build a :class:`GridRouter` and route the board."""
|
||||
router = GridRouter(
|
||||
@@ -1428,5 +1479,7 @@ def route_board(
|
||||
access_cost=access_cost,
|
||||
rollback=rollback,
|
||||
net_widths=net_widths,
|
||||
via_drill=via_drill,
|
||||
hole_to_hole=hole_to_hole,
|
||||
)
|
||||
return router.route()
|
||||
|
||||
Reference in New Issue
Block a user