Fixed tracks and vias only kept other nets away, so a pin fanned out to its plane was routed to the plane again, beside its own fan-out. Pins, zones and planes that fixed copper of their net touches now form one connected group: a fanned-out supply pin needs nothing more, a fanned-out signal routes from its via, and copper touching no pin or zone adds nothing to route.
94 lines
4.3 KiB
Python
94 lines
4.3 KiB
Python
"""Tracks and vias already on the board (the DSN's wiring) are fixed copper."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from freeroute.board import build_board
|
|
from freeroute.dsn import parse_dsn
|
|
from freeroute.route.pipeline import route_dsn_board_exact
|
|
|
|
_DSN = """(pcb wired (parser) (resolution um 10) (unit um)
|
|
(structure
|
|
(layer F.Cu (type signal)) (layer B.Cu (type signal))
|
|
(boundary (path pcb 0 0 0 30000 0 30000 -20000 0 -20000 0 0))
|
|
(via "Via[0-1]_600:300_um") (rule (width 250) (clearance 200)))
|
|
(placement (component S (place A1 5000 -10000 front 0) (place A2 25000 -10000 front 0)))
|
|
(library
|
|
(image S (pin smd 1 0 0))
|
|
(padstack smd (shape (rect F.Cu -400 -400 400 400)) (attach off))
|
|
(padstack "Via[0-1]_600:300_um" (shape (circle F.Cu 600)) (shape (circle B.Cu 600)) (attach off)))
|
|
(network (net SIG (pins A1-1 A2-1)) (net GND))
|
|
(wiring
|
|
(wire (path F.Cu 400 15000 0 15000 -20000) (net GND) (type route))
|
|
(via "Via[0-1]_600:300_um" 8000 -14000 (net GND) (type route))))"""
|
|
|
|
|
|
def test_wiring_is_read_with_its_nets():
|
|
dsn = parse_dsn(_DSN)
|
|
assert [(w.net, w.layer, w.width) for w in dsn.wires] == [("GND", "F.Cu", 400.0)]
|
|
assert [(v.net, v.x, v.y) for v in dsn.vias] == [("GND", 8000.0, -14000.0)]
|
|
|
|
|
|
def test_wiring_is_fixed_copper_and_not_a_connection_to_route():
|
|
board = build_board(parse_dsn(_DSN))
|
|
copper = board.get_fixed_copper()
|
|
gnd = next(n.net_number for n in board.nets if n.name == "GND")
|
|
assert copper and all(net == gnd for net, _, _ in copper)
|
|
assert not any(gnd in p.net_nos for p in board.get_pins())
|
|
|
|
|
|
def test_other_nets_route_around_a_fixed_track():
|
|
dsn = parse_dsn(_DSN)
|
|
exact, scale, names = route_dsn_board_exact(dsn, resolution=2)
|
|
sig = next(i + 1 for i, n in enumerate(dsn.nets) if n.name == "SIG")
|
|
assert sig in exact.result.routed_net_numbers
|
|
assert exact.drc_clean
|
|
wall = 15000 * scale
|
|
for layer, pts in exact.result.wires.get(sig, []):
|
|
if names[layer] != "F.Cu":
|
|
continue
|
|
for p, q in zip(pts, pts[1:], strict=False):
|
|
assert not (min(p.x, q.x) < wall < max(p.x, q.x)), "crossed the GND track on F.Cu"
|
|
|
|
|
|
# --- fixed wiring as connectivity ------------------------------------------
|
|
|
|
_FANOUT = """(pcb fanout (parser) (resolution um 10) (unit um)
|
|
(structure
|
|
(layer F.Cu (type signal)) (layer In1.Cu (type power)) (layer In2.Cu (type power))
|
|
(layer B.Cu (type signal))
|
|
(boundary (path pcb 0 0 0 30000 0 30000 -20000 0 -20000 0 0))
|
|
(plane GND (polygon In1.Cu 0 500 -500 29500 -500 29500 -19500 500 -19500 500 -500))
|
|
(via "Via[0-3]_600:300_um") (rule (width 150) (clearance 150)))
|
|
(placement (component S (place G1 5000 -10000 front 0) (place A 10000 -10000 front 0)
|
|
(place B 25000 -11500 back 0)))
|
|
(library
|
|
(image S (pin smd 1 0 0))
|
|
(padstack smd (shape (rect F.Cu -300 -300 300 300)) (attach off))
|
|
(padstack "Via[0-3]_600:300_um" (shape (circle F.Cu 600)) (shape (circle B.Cu 600)) (attach off)))
|
|
(network (net GND (pins G1-1)) (net SIG (pins A-1 B-1)))
|
|
(wiring
|
|
(wire (path F.Cu 150 5000 -10000 5000 -11500) (net GND) (type route))
|
|
(via "Via[0-3]_600:300_um" 5000 -11500 (net GND) (type route))
|
|
(wire (path F.Cu 150 10000 -10000 10000 -11500) (net SIG) (type route))
|
|
(via "Via[0-3]_600:300_um" 10000 -11500 (net SIG) (type route))
|
|
(wire (path F.Cu 300 2000 -2000 8000 -2000) (net GND) (type route))))"""
|
|
|
|
|
|
def test_a_pad_fanned_out_to_its_plane_needs_no_route():
|
|
dsn = parse_dsn(_FANOUT)
|
|
exact, _scale, _names = route_dsn_board_exact(dsn, resolution=2)
|
|
gnd = next(i + 1 for i, n in enumerate(dsn.nets) if n.name == "GND")
|
|
assert exact.result.failed == []
|
|
assert not exact.result.vias.get(gnd) and not exact.result.wires.get(gnd)
|
|
|
|
|
|
def test_a_fanned_out_signal_is_routed_from_its_via():
|
|
# B is on the bottom face, level with A's fan-out via: starting from that
|
|
# via on B.Cu is the cheapest way, and needs no via of its own
|
|
dsn = parse_dsn(_FANOUT)
|
|
exact, scale, names = route_dsn_board_exact(dsn, resolution=2)
|
|
sig = next(i + 1 for i, n in enumerate(dsn.nets) if n.name == "SIG")
|
|
assert sig in exact.result.routed_net_numbers
|
|
assert not exact.result.vias.get(sig), "the fan-out via is already there"
|
|
assert {names[layer] for layer, _ in exact.result.wires[sig]} == {"B.Cu"}
|