Route with copper zones, and let vias into no-tracks areas

freeroute ignored the DSN's planes (KiCad's zones), so it never knew that
pads in a pour are joined by it. On the rc4 power stage those pads sit
inside "keep router off" areas as well, and every connection to them was
dropped without a word.

- Read (plane <net> <shape>) and put each zone on the board as a
  ConductionArea of its net. Zones are not obstacles: KiCad refills them
  around other nets' copper.
- The grid groups each net's pads and zone pieces that copper already
  joins (a pad in a zone, two zones sharing a pad) and routes only between
  groups. A connection may end on a zone by a via inside its copper, or by
  a track where tracks are allowed.
- wire_keepout now blocks traces only, as KiCad's "no tracks" rule area
  does; via_keepout only vias; keepout both, in the grid and in the exact
  verifier.

rc4 power stage, exact engine, resolution 2: 2 unconnected, down from 8
(FreeRouting JAR: 12), no DRC errors.
This commit is contained in:
2026-09-27 11:54:04 -06:00
parent 83b2248a97
commit 354c89a8d5
9 changed files with 313 additions and 39 deletions
+42 -4
View File
@@ -31,7 +31,7 @@ from freeroute.geometry.polygon_shape import PolygonShape
from .board import BasicBoard
from .clearance import ClearanceMatrix
from .items import ObstacleArea, Pin
from .items import ConductionArea, ObstacleArea, Pin
from .layer import Layer, LayerStructure
from .net import Nets
from .transform import CoordinateTransform
@@ -55,6 +55,7 @@ def build_board(dsn: DsnBoard) -> BasicBoard:
_insert_pins(dsn, board, transform, terminal_to_net)
_insert_keepouts(dsn, board, transform)
_insert_image_keepouts(dsn, board, transform)
_insert_zones(dsn, board, transform, nets)
return board
@@ -330,6 +331,8 @@ def _insert_keepouts(dsn: DsnBoard, board: BasicBoard, transform: CoordinateTran
name=keepout.area.name,
layer=layer_index,
tiles=tiles,
blocks_wires=keepout.kind != "via_keepout",
blocks_vias=keepout.kind != "wire_keepout",
)
)
@@ -402,13 +405,17 @@ def _insert_image_keepouts(dsn: DsnBoard, board: BasicBoard, transform: Coordina
image = images.get(placement.lib_name)
if image is None:
continue
areas = [*image.keepouts, *image.via_keepouts, *image.wire_keepouts]
areas = [
*((a, True, True) for a in image.keepouts),
*((a, False, True) for a in image.via_keepouts),
*((a, True, False) for a in image.wire_keepouts),
]
if not areas:
continue
for place in placement.places:
if place.x is None or place.y is None:
continue
for area in areas:
for area, blocks_wires, blocks_vias in areas:
border = area.border
if border is None:
continue
@@ -423,7 +430,12 @@ def _insert_image_keepouts(dsn: DsnBoard, board: BasicBoard, transform: Coordina
top, bottom = min(signal_layers), max(signal_layers)
layers = [top + bottom - layer for layer in layers]
for layer_index in layers:
board.insert(ObstacleArea(name=area.name, layer=layer_index, tiles=tiles))
board.insert(
ObstacleArea(
name=area.name, layer=layer_index, tiles=tiles,
blocks_wires=blocks_wires, blocks_vias=blocks_vias,
)
)
def _placed_area_tiles(shape: Shape, place, transform: CoordinateTransform) -> list:
@@ -447,3 +459,29 @@ def _placed_area_tiles(shape: Shape, place, transform: CoordinateTransform) -> l
if len(pts) < 3:
return []
return [tile for tile in PolygonShape(pts).split_to_convex() if not tile.is_empty()]
def _insert_zones(dsn: DsnBoard, board: BasicBoard, transform: CoordinateTransform, nets) -> None:
"""Each DSN plane as a ConductionArea of its net on its layer."""
layer_no = {layer.name: i for i, layer in enumerate(dsn.layers)}
numbers = {net.name: net.net_number for net in nets}
for plane in dsn.planes:
shape = plane.shape
if plane.net not in numbers or shape.layer not in layer_no:
continue
if not isinstance(shape, (Polygon, Path)):
continue
c = shape.coords
pts = [transform.dsn_to_board_point(c[i], c[i + 1]) for i in range(0, len(c) - 1, 2)]
if len(pts) < 3:
continue
polygon = PolygonShape(pts)
tiles = [t for t in polygon.split_to_convex() if not t.is_empty()]
if not tiles:
continue
board.insert(
ConductionArea(
net_nos=[numbers[plane.net]], name=plane.net, layer=layer_no[shape.layer],
tiles=tiles, polygon=polygon, blocks_wires=False, blocks_vias=False,
)
)