Model pads and vias as KiCad places them
Measured against KiCad's own pads on the TerraHawk rc4 power stage: - Back-side parts kept their pads on F.Cu (32 pads). Mirror the copper layer for a back-side part. - Pad outlines were copied unrotated (29 pads on rotated parts). Place the outline through the same chain as the pin: pin rotation, back-side mirror, part rotation. Oval pads, a path with a width, came out as a zero-height sliver; pad them out by the aperture. - Multi-shape padstacks used the first shape; use the largest. - Footprint-level keepouts were parsed but never placed. KiCad exports a mounting hole's plated ring that way, so the router could not see it. - Vias were sized like a trace (0.25 mm square for a 0.6 mm via), and every remaining short and clearance error was a via. Read the size from the DSN's via padstack and use it in all three engines; the grid engine now reserves the cells a via's copper reaches. With these the exact and room engines route that board with no shorts, clearance or other DRC errors. Pins now carry their component reference.
This commit is contained in:
@@ -90,6 +90,7 @@ def route_board_exact(
|
||||
diagonal: bool = False,
|
||||
max_shove_depth: int = 5,
|
||||
shove_cap: int = 3,
|
||||
via_half: int | None = None,
|
||||
) -> ExactRouteResult:
|
||||
"""Route ``board`` orthogonally and verify the result with exact geometry.
|
||||
|
||||
@@ -102,6 +103,7 @@ def route_board_exact(
|
||||
i for i, layer in enumerate(board.layer_structure.arr) if layer.is_signal
|
||||
]
|
||||
half_width = max(trace_width // 2, 1)
|
||||
via_half = via_half or half_width
|
||||
step = trace_width + clearance
|
||||
|
||||
result = route_board(
|
||||
@@ -113,6 +115,7 @@ def route_board_exact(
|
||||
max_passes=max_passes,
|
||||
rip_up=rip_up,
|
||||
orthogonal=True,
|
||||
via_half=via_half,
|
||||
)
|
||||
|
||||
tree = ShapeSearchTree()
|
||||
@@ -141,7 +144,7 @@ def route_board_exact(
|
||||
accepted: dict[int, _Accepted] = {}
|
||||
|
||||
for net_no in sorted(set(result.wires) | set(result.vias)):
|
||||
boxes = _net_boxes(result, net_no, half_width, via_layers)
|
||||
boxes = _net_boxes(result, net_no, half_width, via_layers, via_half)
|
||||
if any(
|
||||
tree.clearance_conflict(box, net_no, layer, clearance)
|
||||
for box, box_layers in boxes
|
||||
@@ -543,15 +546,14 @@ def _rollback(shoved, accepted, tree, owner) -> int: # noqa: ARG001
|
||||
# --- helpers -----------------------------------------------------------------
|
||||
|
||||
|
||||
def _net_boxes(result, net_no, half_width, via_layers):
|
||||
def _net_boxes(result, net_no, half_width, via_layers, via_half=None):
|
||||
via_half = via_half or half_width
|
||||
boxes: list[tuple[IntBox, frozenset[int]]] = []
|
||||
for layer, points in result.wires.get(net_no, []):
|
||||
for box in PolylineShape(Polyline(points), half_width).tiles():
|
||||
boxes.append((box, frozenset({layer})))
|
||||
for loc in result.vias.get(net_no, []):
|
||||
vbox = IntBox(
|
||||
loc.x - half_width, loc.y - half_width, loc.x + half_width, loc.y + half_width
|
||||
)
|
||||
vbox = IntBox(loc.x - via_half, loc.y - via_half, loc.x + via_half, loc.y + via_half)
|
||||
boxes.append((vbox, via_layers))
|
||||
return boxes
|
||||
|
||||
|
||||
Reference in New Issue
Block a user