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:
@@ -471,6 +471,7 @@ def route_board_rooms(
|
||||
pack: bool = False,
|
||||
max_shove_depth: int = 5,
|
||||
shove_cap: int = 3,
|
||||
via_half: int | None = None,
|
||||
) -> ExactRouteResult:
|
||||
"""Route ``board`` with the continuous expansion-room track.
|
||||
|
||||
@@ -494,6 +495,9 @@ def route_board_rooms(
|
||||
i for i, layer in enumerate(board.layer_structure.arr) if layer.is_signal
|
||||
]
|
||||
half_width = max(trace_width // 2, 1)
|
||||
# half the via pad's size: a via is not a trace, and sizing it like one
|
||||
# let routes pass within a via's real copper
|
||||
via_half = via_half or half_width
|
||||
margin = clearance + half_width
|
||||
outline = board.bounding_box
|
||||
|
||||
@@ -537,7 +541,7 @@ def route_board_rooms(
|
||||
via_layers,
|
||||
accepted,
|
||||
shove,
|
||||
)
|
||||
via_half=via_half)
|
||||
for net_no in sorted(_routable_nets(board, signal_layers)):
|
||||
if net_no in packed:
|
||||
continue
|
||||
@@ -562,7 +566,7 @@ def route_board_rooms(
|
||||
accepted,
|
||||
max_shove_depth,
|
||||
shove_cap,
|
||||
)
|
||||
via_half=via_half)
|
||||
|
||||
drc_clean = tree.has_violation(clearance) is None
|
||||
return ExactRouteResult(result=result, tree=tree, drc_clean=drc_clean, shoves=shoves)
|
||||
@@ -587,6 +591,7 @@ def _route_connection(
|
||||
accepted=None,
|
||||
max_shove_depth=5,
|
||||
shove_cap=3,
|
||||
via_half=None,
|
||||
):
|
||||
rooms_by_layer = {
|
||||
layer: _free_rooms(tree, outline, layer, net_no, margin, half_width)
|
||||
@@ -607,7 +612,7 @@ def _route_connection(
|
||||
|
||||
if realized is not None:
|
||||
wires, vias = realized
|
||||
boxes = _collect_boxes(wires, vias, half_width, via_layers, outline)
|
||||
boxes = _collect_boxes(wires, vias, half_width, via_layers, outline, via_half=via_half)
|
||||
if boxes is not None and not any(
|
||||
tree.clearance_conflict(box, net_no, layer, clearance)
|
||||
for box, box_layers in boxes
|
||||
@@ -615,7 +620,7 @@ def _route_connection(
|
||||
):
|
||||
_commit(
|
||||
net_no, wires, vias, tree, result, owner, half_width, via_layers, shove, accepted
|
||||
)
|
||||
, via_half=via_half)
|
||||
return 0
|
||||
|
||||
# the net would be dropped: try a transactional shove-in-rooms recovery
|
||||
@@ -634,11 +639,12 @@ def _route_connection(
|
||||
clearance,
|
||||
max_shove_depth,
|
||||
shove_cap,
|
||||
)
|
||||
via_half=via_half)
|
||||
return 0
|
||||
|
||||
|
||||
def _collect_boxes(wires, vias, half_width, via_layers, outline):
|
||||
def _collect_boxes(wires, vias, half_width, via_layers, outline, via_half=None):
|
||||
via_half = via_half or half_width
|
||||
"""The ``(box, layers)`` copper tiles of a realized route, or ``None`` if any
|
||||
tile leaves the board outline (the same early-out as the old inline check)."""
|
||||
boxes: list[tuple[IntBox, frozenset[int]]] = []
|
||||
@@ -649,13 +655,16 @@ def _collect_boxes(wires, vias, half_width, via_layers, outline):
|
||||
boxes.append((box, frozenset({layer})))
|
||||
for loc in vias:
|
||||
vbox = IntBox(
|
||||
loc.x - half_width, loc.y - half_width, loc.x + half_width, loc.y + half_width
|
||||
loc.x - via_half, loc.y - via_half, loc.x + via_half, loc.y + via_half
|
||||
)
|
||||
boxes.append((vbox, via_layers))
|
||||
return boxes
|
||||
|
||||
|
||||
def _commit(net_no, wires, vias, tree, result, owner, half_width, via_layers, shove, accepted):
|
||||
def _commit(
|
||||
net_no, wires, vias, tree, result, owner, half_width, via_layers, shove, accepted, via_half=None
|
||||
):
|
||||
via_half = via_half or half_width
|
||||
"""Insert a verified route's copper and record it in the routed output.
|
||||
|
||||
With ``shove=False`` this is the foundation router's exact commit sequence.
|
||||
@@ -668,7 +677,7 @@ def _commit(net_no, wires, vias, tree, result, owner, half_width, via_layers, sh
|
||||
tree.insert(TreeShape(next(owner), net_no, frozenset({layer}), box, routed=True))
|
||||
for loc in vias:
|
||||
vbox = IntBox(
|
||||
loc.x - half_width, loc.y - half_width, loc.x + half_width, loc.y + half_width
|
||||
loc.x - via_half, loc.y - via_half, loc.x + via_half, loc.y + via_half
|
||||
)
|
||||
tree.insert(TreeShape(next(owner), net_no, via_layers, vbox, routed=True))
|
||||
for layer, corners in wires:
|
||||
@@ -693,7 +702,7 @@ def _commit(net_no, wires, vias, tree, result, owner, half_width, via_layers, sh
|
||||
acc.wire_owners.append(w_owners)
|
||||
for loc in vias:
|
||||
vbox = IntBox(
|
||||
loc.x - half_width, loc.y - half_width, loc.x + half_width, loc.y + half_width
|
||||
loc.x - via_half, loc.y - via_half, loc.x + via_half, loc.y + via_half
|
||||
)
|
||||
oid = next(owner)
|
||||
tree.insert(TreeShape(oid, net_no, via_layers, vbox, routed=True))
|
||||
@@ -854,6 +863,7 @@ def _pack_channels(
|
||||
via_layers,
|
||||
accepted,
|
||||
shove,
|
||||
via_half=None,
|
||||
) -> set[int]:
|
||||
"""Coordinated multi-trace channel packing (opt-in pre-pass).
|
||||
|
||||
@@ -922,7 +932,7 @@ def _pack_channels(
|
||||
spacing_min,
|
||||
via_layers,
|
||||
outline,
|
||||
)
|
||||
via_half=via_half)
|
||||
if placed is None:
|
||||
continue # could not pack every member cleanly -> leave to greedy
|
||||
for net_no, wires in placed:
|
||||
@@ -937,7 +947,7 @@ def _pack_channels(
|
||||
via_layers,
|
||||
shove,
|
||||
accepted,
|
||||
)
|
||||
via_half=via_half)
|
||||
routed.add(net_no)
|
||||
return routed
|
||||
|
||||
@@ -958,6 +968,7 @@ def _pack_group(
|
||||
spacing_min,
|
||||
via_layers,
|
||||
outline,
|
||||
via_half=None,
|
||||
):
|
||||
"""Assign parallel lanes + staggered fan-in trunks to every net crossing one
|
||||
gap, and return ``[(net_no, wires), ...]`` if ALL of them realize exactly
|
||||
@@ -1014,7 +1025,7 @@ def _pack_group(
|
||||
if len(corners) < 2:
|
||||
return None
|
||||
wires = [(layer, corners)]
|
||||
boxes = _collect_boxes(wires, [], half_width, via_layers, outline)
|
||||
boxes = _collect_boxes(wires, [], half_width, via_layers, outline, via_half=via_half)
|
||||
if boxes is None:
|
||||
return None
|
||||
if any(
|
||||
@@ -1049,6 +1060,7 @@ def _shove_recover_room(
|
||||
clearance,
|
||||
max_shove_depth,
|
||||
shove_cap,
|
||||
via_half=None,
|
||||
) -> int:
|
||||
"""Recover a dropped net by nudging the committed trace(s) that block it.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user