Read the DSN's wiring as fixed copper of its nets

Tracks and vias already on the board were ignored, so other nets could
be routed straight through them (the flight controller's 82 GND
stitching vias and its guard tracks). They are now copper of their net:
other nets keep a pad's distance from them in the grid, the exact check
and the room engine, and they add no connections to route.
This commit is contained in:
2026-09-28 21:06:35 -06:00
parent 0f6a2a0812
commit 8a50bde531
9 changed files with 198 additions and 2 deletions
+43 -1
View File
@@ -31,7 +31,7 @@ from freeroute.geometry.polygon_shape import PolygonShape
from .board import BasicBoard
from .clearance import ClearanceMatrix
from .items import ConductionArea, ObstacleArea, Pin
from .items import ConductionArea, ObstacleArea, Pin, Trace, Via
from .layer import Layer, LayerStructure
from .net import Nets
from .transform import CoordinateTransform
@@ -56,6 +56,7 @@ def build_board(dsn: DsnBoard) -> BasicBoard:
_insert_keepouts(dsn, board, transform)
_insert_image_keepouts(dsn, board, transform)
_insert_zones(dsn, board, transform, nets)
_insert_wiring(dsn, board, transform, nets)
return board
@@ -471,6 +472,47 @@ def _placed_area_tiles(shape: Shape, place, transform: CoordinateTransform) -> l
return [tile for tile in PolygonShape(pts).split_to_convex() if not tile.is_empty()]
def _insert_wiring(dsn: DsnBoard, board: BasicBoard, transform: CoordinateTransform, nets) -> None:
"""Tracks and vias already on the board, as fixed copper of their nets."""
layer_no = {layer.name: i for i, layer in enumerate(dsn.layers)}
signal_layers = [i for i, layer in enumerate(dsn.layers) if layer.is_signal]
numbers = {net.name: net.net_number for net in nets}
for wire in dsn.wires:
if wire.net not in numbers or wire.layer not in layer_no:
continue
c = wire.coords
corners = [transform.dsn_to_board_point(c[i], c[i + 1]) for i in range(0, len(c) - 1, 2)]
if len(corners) < 2:
continue
board.insert(
Trace(
net_nos=[numbers[wire.net]], fixed=True, layer=layer_no[wire.layer],
half_width=max(round(transform.dsn_to_board(wire.width / 2)), 1), corners=corners,
)
)
for via in dsn.vias:
padstack = dsn.padstack(via.padstack)
if via.net not in numbers or padstack is None or not padstack.shapes:
continue
layers: set[int] = set()
for shape in padstack.shapes:
if shape.layer in ("signal", "pcb"):
layers.update(signal_layers)
elif shape.layer in layer_no:
layers.add(layer_no[shape.layer])
location = transform.dsn_to_board_point(via.x, via.y)
pad = _pad_tile(max(padstack.shapes, key=_shape_extent), transform)
box = pad.bounding_box()
board.insert(
Via(
net_nos=[numbers[via.net]], fixed=True, padstack_name=via.padstack,
location=location, layers=sorted(layers or signal_layers),
shape=IntBox(box.ll.x + location.x, box.ll.y + location.y,
box.ur.x + location.x, box.ur.y + location.y),
)
)
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)}