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:
2026-09-27 07:26:32 -06:00
parent 96c212466a
commit c1df284147
8 changed files with 303 additions and 32 deletions
+16 -2
View File
@@ -110,6 +110,13 @@ def build_parser() -> argparse.ArgumentParser:
help="disable rip-up-and-retry (grid and exact engines)",
)
parser.add_argument("--layers", help="comma-separated signal layer indices to route on")
parser.add_argument(
"--edge-clearance",
type=float,
metavar="MM",
help="copper to board-edge clearance in mm, e.g. KiCad's min_copper_edge_clearance "
"(the DSN does not carry it); default 0: copper only has to stay inside the outline",
)
return parser
@@ -158,11 +165,18 @@ def main(argv: list[str] | None = None) -> int:
diagonal=args.diagonal,
rip_up=rip_up,
max_passes=max_passes,
edge_clearance_mm=args.edge_clearance,
)
elif args.engine == "room":
result = build_rooms_routing_result(dsn, layers=layers, shove=args.shove, pack=args.pack)
result = build_rooms_routing_result(
dsn, layers=layers, shove=args.shove, pack=args.pack,
edge_clearance_mm=args.edge_clearance,
)
else:
result = build_routing_result(dsn, layers=layers, rip_up=rip_up, max_passes=max_passes)
result = build_routing_result(
dsn, layers=layers, rip_up=rip_up, max_passes=max_passes,
edge_clearance_mm=args.edge_clearance,
)
ses_text = write_ses(dsn, result)