Check plane and in-pad vias where they will actually go
A start that was already a plane end joined the plane at once, with no check that its via fits: under a sensor's via keepout the via went in the pad and the exact check refused it. A via leaving or reaching a walled-in pin goes in that pin's pad, but was only checked at the grid cell's centre; it is now checked exactly at the pin against via keepouts and other nets' pads and fixed copper.
This commit is contained in:
@@ -311,6 +311,9 @@ class GridRouter:
|
|||||||
self._via_edge_block: set[Cell] = set()
|
self._via_edge_block: set[Cell] = set()
|
||||||
# wire keepouts as boxes, to check a pin stub the way the exact engine will
|
# wire keepouts as boxes, to check a pin stub the way the exact engine will
|
||||||
self._keepout_boxes: dict[int, list[tuple[IntBox, bool]]] = {}
|
self._keepout_boxes: dict[int, list[tuple[IntBox, bool]]] = {}
|
||||||
|
# via keepouts as boxes, and other copper a via in a pad must clear
|
||||||
|
self._via_keepout_boxes: list[tuple[IntBox, bool]] = []
|
||||||
|
self._static_copper: list[tuple[IntBox, int]] | None = None
|
||||||
# pin end nodes no wire can reach without entering a keepout
|
# pin end nodes no wire can reach without entering a keepout
|
||||||
self._via_only: set[Node] = set()
|
self._via_only: set[Node] = set()
|
||||||
if outline is not None:
|
if outline is not None:
|
||||||
@@ -360,6 +363,8 @@ class GridRouter:
|
|||||||
self._keepout.add((*cell, item.layer))
|
self._keepout.add((*cell, item.layer))
|
||||||
if item.blocks_vias:
|
if item.blocks_vias:
|
||||||
self._via_keepout.add((*cell, item.layer))
|
self._via_keepout.add((*cell, item.layer))
|
||||||
|
if item.blocks_vias:
|
||||||
|
self._via_keepout_boxes.append((box, item.clearance))
|
||||||
|
|
||||||
def _rasterize_fixed_copper(self) -> None:
|
def _rasterize_fixed_copper(self) -> None:
|
||||||
"""Tracks and vias already on the board: copper of their net, which
|
"""Tracks and vias already on the board: copper of their net, which
|
||||||
@@ -452,7 +457,14 @@ class GridRouter:
|
|||||||
goals = self._group_ends(conn.b_group, conn.net_no, conns)
|
goals = self._group_ends(conn.b_group, conn.net_no, conns)
|
||||||
if not starts or not goals:
|
if not starts or not goals:
|
||||||
return None
|
return None
|
||||||
common = sorted(set(starts) & set(goals))
|
# A start that is already an end joins at once, except that ending on
|
||||||
|
# a plane there needs a via: one that cannot be placed (a via keepout,
|
||||||
|
# another net's pad) is no end, and the search goes on from there.
|
||||||
|
common = [
|
||||||
|
n for n in sorted(set(starts) & set(goals))
|
||||||
|
if not isinstance(goals[n], ConductionArea)
|
||||||
|
or self._via_placeable((n[0], n[1]), conn.net_no, conns)
|
||||||
|
]
|
||||||
if common:
|
if common:
|
||||||
conn.a_end, conn.b_end = starts[common[0]], goals[common[0]]
|
conn.a_end, conn.b_end = starts[common[0]], goals[common[0]]
|
||||||
return [common[0]]
|
return [common[0]]
|
||||||
@@ -485,6 +497,12 @@ class GridRouter:
|
|||||||
ok = via_ok.get(cell)
|
ok = via_ok.get(cell)
|
||||||
if ok is None:
|
if ok is None:
|
||||||
ok = via_ok[cell] = self._via_placeable(cell, net_no, conns)
|
ok = via_ok[cell] = self._via_placeable(cell, net_no, conns)
|
||||||
|
# a via leaving or reaching a walled-in pin goes in its pad
|
||||||
|
# (see _commit), so it has to fit there exactly
|
||||||
|
for node, ends in ((source, starts), (target, goals)):
|
||||||
|
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:
|
if ok:
|
||||||
self._relax(source, target, g_via, goal_cell, g_score, came_from, open_heap)
|
self._relax(source, target, g_via, goal_cell, g_score, came_from, open_heap)
|
||||||
continue
|
continue
|
||||||
@@ -548,6 +566,29 @@ class GridRouter:
|
|||||||
# copper too, or a stub ends up closer than clearance to a trace in the
|
# copper too, or a stub ends up closer than clearance to a trace in the
|
||||||
# cell beside it.
|
# cell beside it.
|
||||||
|
|
||||||
|
def _walled(self, node: Node) -> bool:
|
||||||
|
"""A pin end no wire may leave or reach: only a via in its pad will do."""
|
||||||
|
return node in self._keepout or node in self._via_only
|
||||||
|
|
||||||
|
def _via_fits_at(self, point: IntPoint, net_no: int) -> bool:
|
||||||
|
"""A via centred exactly on ``point`` clears every via keepout and all
|
||||||
|
other nets' pads and fixed copper (what a via in a pad must meet)."""
|
||||||
|
via = IntBox(point.x - self.via_half, point.y - self.via_half,
|
||||||
|
point.x + self.via_half, point.y + self.via_half)
|
||||||
|
grown = via.offset(self.clearance)
|
||||||
|
for box, needs in self._via_keepout_boxes:
|
||||||
|
if box.overlaps(grown if needs else via):
|
||||||
|
return False
|
||||||
|
if self._static_copper is None:
|
||||||
|
copper = [
|
||||||
|
(p.shape.bounding_box(), p.net_nos[0] if p.net_nos else -1)
|
||||||
|
for p in self.board.get_pins()
|
||||||
|
if p.shape is not None and any(layer in self.layers for layer in p.layers)
|
||||||
|
]
|
||||||
|
copper += [(box, net) for net, _, box in self.board.get_fixed_copper()]
|
||||||
|
self._static_copper = copper
|
||||||
|
return not any(net != net_no and box.overlaps(grown) for box, net in self._static_copper)
|
||||||
|
|
||||||
def _stub_cells(self, pin_location: IntPoint, cell: Cell) -> list[Cell]:
|
def _stub_cells(self, pin_location: IntPoint, cell: Cell) -> list[Cell]:
|
||||||
"""Cells where another net's trace would come too close to the stub."""
|
"""Cells where another net's trace would come too close to the stub."""
|
||||||
from freeroute.board.outline import _point_segment
|
from freeroute.board.outline import _point_segment
|
||||||
|
|||||||
Reference in New Issue
Block a user