Route to the real board outline, per connection, and out of fine-pitch pins
- The exact engine verified copper per net and dropped a whole net on any conflict: a 25-pin +3V3 net lost every connection to one bad segment. Verify and accept per ratsnest connection instead. - Pad and keepout cells were rounded outward, blocking cells up to half a step clear of the copper; 62 of the 64 pins of a 0.5 mm pitch LQFP could not start a route. Block only cells whose centre is inside the clearance zone, and let a net walk along inside its own pad, which adds no copper. - Routing was bounded by the outline's bounding box, so tracks cut across rounded corners. Keep the DSN boundary as a polygon; the grid blocks cells too close to it (scanline plus per-edge, precomputed for vias) and the exact and room engines reject copper that is. - --edge-clearance MM sets copper-to-edge clearance, e.g. KiCad's min_copper_edge_clearance, which the DSN does not carry. Default 0. On the TerraHawk rc4 power stage the exact engine now matches the FreeRouting JAR: same open count, no DRC errors, with a 0.5 mm edge rule.
This commit is contained in:
@@ -472,6 +472,7 @@ def route_board_rooms(
|
||||
max_shove_depth: int = 5,
|
||||
shove_cap: int = 3,
|
||||
via_half: int | None = None,
|
||||
edge_clearance: int | None = None,
|
||||
) -> ExactRouteResult:
|
||||
"""Route ``board`` with the continuous expansion-room track.
|
||||
|
||||
@@ -498,6 +499,7 @@ def route_board_rooms(
|
||||
# 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
|
||||
edge = (board.outline, 0 if edge_clearance is None else edge_clearance)
|
||||
margin = clearance + half_width
|
||||
outline = board.bounding_box
|
||||
|
||||
@@ -541,7 +543,7 @@ def route_board_rooms(
|
||||
via_layers,
|
||||
accepted,
|
||||
shove,
|
||||
via_half=via_half)
|
||||
via_half=via_half, edge=edge)
|
||||
for net_no in sorted(_routable_nets(board, signal_layers)):
|
||||
if net_no in packed:
|
||||
continue
|
||||
@@ -566,7 +568,7 @@ def route_board_rooms(
|
||||
accepted,
|
||||
max_shove_depth,
|
||||
shove_cap,
|
||||
via_half=via_half)
|
||||
via_half=via_half, edge=edge)
|
||||
|
||||
drc_clean = tree.has_violation(clearance) is None
|
||||
return ExactRouteResult(result=result, tree=tree, drc_clean=drc_clean, shoves=shoves)
|
||||
@@ -592,6 +594,7 @@ def _route_connection(
|
||||
max_shove_depth=5,
|
||||
shove_cap=3,
|
||||
via_half=None,
|
||||
edge=None,
|
||||
):
|
||||
rooms_by_layer = {
|
||||
layer: _free_rooms(tree, outline, layer, net_no, margin, half_width)
|
||||
@@ -612,7 +615,7 @@ def _route_connection(
|
||||
|
||||
if realized is not None:
|
||||
wires, vias = realized
|
||||
boxes = _collect_boxes(wires, vias, half_width, via_layers, outline, via_half=via_half)
|
||||
boxes = _collect_boxes(wires, vias, half_width, via_layers, outline, via_half=via_half, edge=edge)
|
||||
if boxes is not None and not any(
|
||||
tree.clearance_conflict(box, net_no, layer, clearance)
|
||||
for box, box_layers in boxes
|
||||
@@ -639,11 +642,11 @@ def _route_connection(
|
||||
clearance,
|
||||
max_shove_depth,
|
||||
shove_cap,
|
||||
via_half=via_half)
|
||||
via_half=via_half, edge=edge)
|
||||
return 0
|
||||
|
||||
|
||||
def _collect_boxes(wires, vias, half_width, via_layers, outline, via_half=None):
|
||||
def _collect_boxes(wires, vias, half_width, via_layers, outline, via_half=None, edge=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)."""
|
||||
@@ -652,6 +655,8 @@ def _collect_boxes(wires, vias, half_width, via_layers, outline, via_half=None):
|
||||
for box in PolylineShape(Polyline(corners), half_width).tiles():
|
||||
if not outline.contains_box(box):
|
||||
return None
|
||||
if edge is not None and edge[0] is not None and not edge[0].box_clear(box, edge[1]):
|
||||
return None
|
||||
boxes.append((box, frozenset({layer})))
|
||||
for loc in vias:
|
||||
vbox = IntBox(
|
||||
@@ -864,6 +869,7 @@ def _pack_channels(
|
||||
accepted,
|
||||
shove,
|
||||
via_half=None,
|
||||
edge=None,
|
||||
) -> set[int]:
|
||||
"""Coordinated multi-trace channel packing (opt-in pre-pass).
|
||||
|
||||
@@ -932,7 +938,7 @@ def _pack_channels(
|
||||
spacing_min,
|
||||
via_layers,
|
||||
outline,
|
||||
via_half=via_half)
|
||||
via_half=via_half, edge=edge)
|
||||
if placed is None:
|
||||
continue # could not pack every member cleanly -> leave to greedy
|
||||
for net_no, wires in placed:
|
||||
@@ -969,6 +975,7 @@ def _pack_group(
|
||||
via_layers,
|
||||
outline,
|
||||
via_half=None,
|
||||
edge=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
|
||||
@@ -1025,7 +1032,7 @@ def _pack_group(
|
||||
if len(corners) < 2:
|
||||
return None
|
||||
wires = [(layer, corners)]
|
||||
boxes = _collect_boxes(wires, [], half_width, via_layers, outline, via_half=via_half)
|
||||
boxes = _collect_boxes(wires, [], half_width, via_layers, outline, via_half=via_half, edge=edge)
|
||||
if boxes is None:
|
||||
return None
|
||||
if any(
|
||||
@@ -1061,6 +1068,7 @@ def _shove_recover_room(
|
||||
max_shove_depth,
|
||||
shove_cap,
|
||||
via_half=None,
|
||||
edge=None,
|
||||
) -> int:
|
||||
"""Recover a dropped net by nudging the committed trace(s) that block it.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user