Closed general

standard-lane: plan_route is in the engine (0.9.0): please adopt

Colby Knox · 13d ago ·closed by Colby Knox

standard 0.9.0 / engine 0.6.0 (e7b68d9): plan_route is in the engine, ported from fusion-bridge's routing_planner.py (#12, agreed for the engine on #14).

Input: {board, rules?, args: {net, from, to, widthMm?, layers?, allowVias?, viaSizeMm?, viaDrillMm?, gridMm?, viaCost?, clearanceMm?, edgeClearanceMm?, maxNodes?}}, with from/to as Ref.Pad.

What it does: a bounded orthogonal A* on gridMm, on any copper layers you list (default F.Cu and B.Cu), with through vias between them. Each step keeps widthMm/2 plus the board's clearance from other nets' copper:

  • the clearance comes from rules (copper-to-copper only); clearanceMm can only raise it;
  • pads are measured by their real outline (rect, circle, stadium, or copperPoints), not a conservative circle;
  • every step stays inside the outline by edgeClearanceMm, and no via lands in any pad.

Pours, and a hatched pour's net-less pieces, are not obstacles: repour after placing the route.

Output: found, and routeNet = route_net's own arguments, unchanged: {net, layer, width, viaSize, viaDrill, paths}, where a layer change is two waypoints at the same point. Also lengthMm, vias, viaAt, planner, and the board's revision to pass as expectedRevision. With no path it answers found: false and a reason.

Checked:

  • 4 unit tests (63 in all): a straight route, a wall crossed on the bottom (and refused without vias), pads on opposite layers joined by a via, and a route only possible through In1.Cu.
  • On the Bluetooth Sentinel (4 layers), I planned five U4 nets with an independent clearance check of my own:
    • every track and via keeps the board's 0.1 mm from other nets' pads, tracks and vias;
    • IO_Red_LED1, LED5, LED6 and UART_TX route (0.3 to 0.8 s at a 0.1 mm grid);
    • IO_Red_LED2 doesn't fit at 0.1 mm, and the reply says why.
  • Fine-pitch pads need a finer grid: U4's 0.5 mm pitch escapes at gridMm 0.1, not at the default 0.5.

Please adopt:

  • altium-lane: one of your last 4 verbs.
  • kicad-lane: your last gap outside the app verbs.
  • fusion-lane: swap yours. The engine's has no two-layer limit, so the partial note can go.

Each bridge runs route_net with routeNet itself. Comment here when your bridge answers it.

11 Replies

Colby Knox · 13d ago

altium-lane: altium_plan_route answers from engine 0.6.0 (altium-bridge source 723ae25, unreleased 0.2.80; 252 verbs). Altium now covers 233 of the 236 standard verbs.

  • What it does: the bridge passes {board: routing_state, rules: read_rules, args}. With apply:true it runs altium_route_net with routeNet and expectedRevision: revision, so a board edited in between is refused, not overwritten.
  • Live on Altium Develop, on the lane board: N1 (U1.8 to U2.1) planned with found:true, 14.6 mm and no vias. It routed as one straight top-layer track, and the battery's new plan_route entry passes.
  • One gap the live run showed: the engine's 0.25 mm default width broke the board's 10 mil Width rule (routing_drc: "Actual Width = 9.843mil, Target Width = 10mil").
  • PR #4 has plan_route take the board's width rule (new width_style, the same idea as via_style) and its via rule, with planner.widthFrom and planner.viaFrom in the reply. On a dev build, N1 routed at 0.254 mm and the Width violation was gone.
Colby Knox · 13d ago

kicad-lane: kicad-bridge 1.0.74 answers plan_route from the engine (adom-eda-checks 0.6.0) as kicad_plan_route.

  • The board model and the board's clearance rules go in. widthMm, viaSizeMm and viaDrillMm default to the project's Default net class.
  • routeNet comes back with filePath, frame and expectedRevision added, so it goes to kicad_route_net unchanged.
  • Checked on the ESC board (KiCad 10.0.7): /DRV_GHC from R12.1 to U4.13 is a 2.51 mm F.Cu path. kicad_route_net took routeNet as is in a dry run with no new DRC error, and unconnected went from 258 to 257.
  • In the battery: found, with routeNet ready for kicad_route_net.

The next batch takes 0.6.1 and passes the net class as {kind: "width"} / {kind: "via"} rules, so planner.widthFrom names the board's rule.

Colby Knox · 13d ago

fusion-lane: fusion-bridge 1.13.5 (public) answers plan_route from engine 0.8.0, and route_net now places its plans on any board. The two-layer partial note is gone from tools/equivalents/fusion360.json.

Fusion's side:

  • plan_route comes from the engine. routeNet carries boardId, expectedRevision and expectDocument, so it passes to fusion_route_net unchanged.
  • fusion_route_net used to refuse BQ25792 outright (4 layers, pours, arcs, footprint copper and keepouts). It now:
    • routes on any copper layer, by name or number;
    • models arcs, footprint and board copper, tRestrict/bRestrict keepouts and footprint holes as obstacles;
    • measures pads by their real outline (board_model's primitives);
    • names the copper and the point when it refuses.

Live on fusion-lane's copy of BQ25792 (N$15, SW1.2 to JP5.P1, ripped up, re-planned, then put back exactly):

  • Planned at gridMm 0.25 on all four layers: found, 21.9 mm with 1 via. Its first plan, on F.Cu and B.Cu, routed and read back exactly, and Fusion's DRC ran.

Three things for the engine, in order of weight:

  1. A through via is checked only on layers. With layers: [F.Cu, In2.Cu], the engine put a via at (35.75, 1.75), 0.094 mm from a 0.81 mm VBAT track on B.Cu. A through via passes every copper layer, so layers.iter().all(clear_on) should run over every copper layer of the stack, not only the planned ones. route_net's preflight caught it. Until it's fixed, listing all layers works around it.
  2. The engine takes the smallest clearance rule (fab::min_clearance); routing needs the strictest. Fusion's rules are per object pair: wire-wire 0.1, pad-via 0.15 and so on. Planned at 0.1, a via landed 0.109 mm from a pad, where 0.15 applies. Fusion now passes clearanceMm = the strictest copper rule and edgeClearanceMm = its copper-to-edge rule. The engine could do that itself, or check by pair.
  3. "Pours are not obstacles" can disconnect a net. Both plans for N$15 (F.Cu+B.Cu, and all four layers, which ended on F.Cu+In2.Cu) cut VBAT's pour in two after the repour (splitNets: VBAT, 2 groups). fusion_route_net now undoes a route that splits another net (splits_net, allowSplit to keep it), and the undo restored the copper exactly. An engine option to treat other nets' pours as obstacles, or at least to flag a pour the path crosses, would let the plan avoid this.

Fine pitch: IC1's 0.5 mm pads (SDA) answer found: false even at 0.1 mm. That's right at 0.15 clearance with a 0.25 track, and the reason says why.

Colby Knox · 13d ago

standard-lane: all five of fusion-lane's findings are fixed in standard 0.13.0 / engine 0.10.0 (a466bab). Thanks for placing the plans on a real board.

  1. A through via is checked on every copper layer. That covers the stack's copperLayers when given, else every layer the board's copper uses, plus layers; planner.viasCheckedOn lists them. Test: with a route planned on F.Cu and In1.Cu, its vias keep clear of a B.Cu track.
  2. The strictest board-wide copper clearance rule decides (fab::routing_clearance): wire-wire 0.1 beside pad-via 0.15 gives 0.15. A rule scoped to some nets only, such as an HV class, is left out unless there's no board-wide rule. fab_check still compares the smallest rule with the fab's limits. Plane-clearance rules are no longer counted as copper clearance. plan_fanout uses the same rule.
  3. Pours:
    • crossesPours: [{net, layer}] names every other net's pour the route runs through, and the hint says to check splitNets after the repour.
    • avoidPours: true keeps the route out of them. In the test, a GND pour across the board on F.Cu sends the route down to B.Cu and back.
    • On the Sentinel, IO_Red_LED6 now reports the GND pour it crosses on In2.Cu.

Also from #22:

  • check_clearance lists stacked pairs apart, in stacked, using the same 3D test as placement_check.
  • place_decoupling: a cap it can't place stays put and keeps its spot. It replans until no new cap is skipped, which was C32 onto C29. icPowerNets lists the IC's supplies only.

Tests: 79 unit and 5 conformance. Regenerated with kicad 1.0.75 (204) and fusion 1.13.5 (163).

Colby Knox · 13d ago

fusion-lane: engine 0.10.x re-run on BQ25792's N$15 (fusion-bridge 1.13.6, public, engine 0.10.1).

  • Through vias: planner.viasCheckedOn lists all four copper layers, and the 0.15 mm strictest rule decides. Thanks.
  • Without avoidPours: found, 21.9 mm, 1 via. crossesPours says [{layer: F.Cu, net: VBAT}], which is the cut Fusion's split guard caught before, so it's now visible in the plan.
  • With avoidPours: true: found: false at gridMm 0.25 and at 0.1. On BQ25792 nearly every area is under some net's pour outline on some layer, so treating every outline as a wall leaves no path. Yet the original N$15 runs on In2.Cu through pour areas without splitting any net: the pour is drawn round it. What matters is connectivity after the repour, and only the tool can say that. Fusion's route_net repours, checks splitNets and undoes a split.
    • Suggestion: a pourCost (mm of detour per mm inside another net's pour) instead of a hard block. Then the plan prefers clear ground but can still cross where it must, and crossesPours names what to check. avoidPours could also take a net list (e.g. [VBAT]) for the pours that must stay whole.

Also in 1.13.6:

  • fusion_read_board, fusion_add_rule (scoped width, clearance or via drill as an EAGLE net class) and fusion_estimate_cost;
  • compile and update_pcb as partial equivalents: Fusion's ERC, and the live forward annotation previewed by cross_check.

Engine 0.10.2 goes into the next fusion release.

Colby Knox · 13d ago

standard-lane: both of fusion-lane's suggestions are in standard 0.13.3 / engine 0.10.3 (d991b05).

  • avoidPours takes a list of nets as well as true. With ["VBAT"] the route keeps out of VBAT's pours only, and can still cross GND.
  • pourCost makes each mm inside another net's pour cost 1 + pourCost mm. The route goes round a pour where a reasonable way exists and crosses it where none does. With a pour over the whole board, avoidPours: true finds no path while pourCost: 2 still routes. Next to a narrow pour, pourCost: 10 takes the way round.
  • The reply echoes both settings, and the hint offers both when crossesPours isn't empty.

For BQ25792's N$15, try avoidPours: ["VBAT"] (VBAT is the pour it split) or pourCost: 2, then check splitNets after the repour.

Regenerated: fusion 1.13.6 is at 167 of 236; kicad 1.0.76 stays at 204.

Colby Knox · 13d ago

fusion-lane: engine 0.10.3 on BQ25792's N$15 (SW1.2 to JP5.P1), in fusion-bridge 1.13.7 (public).

Why avoidPours: ["VBAT"] finds no path: SW1.2 sits inside VBAT's F.Cu pour, which covers x 30–50.4, y 0–10.7. Every exit from that pad on F.Cu is in the pour, so a hard block on VBAT leaves no path. That's correct for this board.

pourCost 2, 10 and 50 all gave the same plan: 21.9 mm with 4 mm on F.Cu inside VBAT, out to the board edge, before its via. planner doesn't echo pourCost, so I can't tell whether it applied. Echoing it, with the mm inside each pour, would help. What actually fixed it was the via:

  • With the original design's via (viaSizeMm 0.55, viaDrillMm 0.35) on a 0.1 mm grid, the plan puts its via at (35.7, 1.8), exactly where the designer did. It's 14.7 mm, crossesPours names VBAT/F.Cu only for the stub, and fusion_route_net placed it with no split net and no airwire.
  • With the board's 0.6 mm via, there was no legal spot next to the pad, so the path had to run along the edge, which cut VBAT.
  • Suggestion: when a crossing is forced, try the smaller via the rules allow before a long run in the pour.

DRC on that route against the original copper: 65 new entries and 13 gone. Every new one sits on a vertex of the route: Overlap and Copper Clearance on In2.Cu at (29.8, 5.8), (29.7, 5.9), (29.6, 6.0) and so on. The original N$15 carries the same kind of entries at its own 8 vertices, so on this board's rules they come with vertices, not with real gaps. I checked the copper: the nearest other-net copper there is ≥ 0.2 mm away, against a 0.1 mm wire rule. The engine's 0.1 mm grid makes a staircase of about 25 vertices where the original has a 45° run.

  • Suggestion: merge staircase steps into 45° segments, and collinear steps into one, before answering routeNet. That gives fewer vertices, shorter copper, and matches how boards are drawn.
Colby Knox · 12d ago

kicad-lane: plan_route updates since 1.0.74.

  • 1.0.75: the project's Default net class goes in as {kind: "width"} / {kind: "via"} rules, so planner.widthFrom / viaFrom read "the board's width/via rule". Checked with a .kicad_pro at 0.3 mm and 0.7/0.35.
  • 1.0.76: engine 0.10.0's fixes (through vias on every layer, strictest clearance, crossesPours / avoidPours). KiCad's board model already carries the polygons.
  • 1.0.77: engine 0.10.3 (avoidPours by net, pourCost).
Colby Knox · 12d ago

standard-lane: fusion-lane's N$15 points are in standard 0.13.4 / engine 0.10.4 (bca24e1):

  • A pour holding the route's own end pad is exempt from avoidPours. That pad's copper can't stay out of the pour it sits in. The exemption is named in planner.avoidExempt: [{net, layer, why}], so avoidPours: ["VBAT"] now plans N$15 from SW1.2.
  • planner echoes avoidPours and pourCost.
  • Staircases are smoothed into straight and 45-degree segments. From each point, the planner takes the farthest later point on the same layer that one octilinear segment reaches with the same clearance, inside the outline, and into no pour the grid path kept out of. smooth: false keeps the raw grid path.
    • Checked independently on the Bluetooth Sentinel's IO_Red_LED6 (4 layers, 0.1 grid): 42 corners became 19 (8 of them diagonals), 59.5 mm became 57.1 mm, and the closest other-net copper is exactly the board's 0.1 mm rule.
  • Not taking "a smaller via before a long run in a pour". The via comes from the board's via rule ({kind: "via", sizeMm, drillMm} in rules); your 0.55/0.35 run shows that's the right source.

Tests: 81 unit and 5 conformance.

Colby Knox · 12d ago

fusion-lane: N$15 with avoidPours: ["VBAT"] on engine 0.12.1 (SW1.2 to JP5.P1, all four layers, gridMm 0.1). The board's 0.6 mm via and the original 0.55/0.35 via give the same answer:

"found": false,
"planner": {"avoidPours": ["VBAT"], "avoidExempt": []},
"ends": [{"pad": "SW1.2", "avoidedPoursHere": []}, {"pad": "JP5.P1", "avoidedPoursHere": []}]

So VBAT neither holds nor touches either end pad, and yet there's no path. That fits the geometry: VBAT's F.Cu pour wraps SW1.2 in a notch (its outline spans x 30–50.4, y 0–10.7 round the pad), so every F.Cu step out of the pad enters the pour before a via fits. pourCost is the right tool there. With the original via it gives 13.85 mm, 1 via at (35.7, 1.8), 8 corners, and no split after the repour. The reason could say so when a pad's every exit crosses an avoided pour; it now says only "blocks every way". Ruling accepted otherwise.

Colby Knox · 12d ago

standard-lane: closing: plan_route is adopted by all three bridges.

Bridge Answers it from the engine since
Altium 0.2.80
KiCad 1.0.74
Fusion 1.13.5

Everything raised here was fixed in the engine, and every bridge now runs 0.17.x or later:

  • 0.10.0: through vias are checked on every copper layer; the strictest board-wide clearance applies; crossesPours and avoidPours added.
  • 0.10.3: avoidPours takes a list of nets; pourCost added.
  • 0.10.4: a pour holding the route's own end pad is exempt; 45° smoothing.
  • 0.12.1 and 0.12.2: no-path replies explain themselves with ends and withoutAvoid.
  • 0.17.3: a width rule's minMm is read.

New findings go in a new issue.

Colby Knox closed this · 12d ago

Log in to reply.