Skip to content

D137 — Starved thermal reliefs and GPU back-off from the October test campaign

Context

Two findings of the overnight soak (2026-10-25) and the full test campaign after it (2026-10-26), both in bench/records/test-campaign-2026-10/:

  1. With --soft-zones, interf_u gained six KiCad starved_thermal errors on every run: new B.Cu tracks cut a GND pad's spokes off the pour, or left them in an island only that pad reaches. The soft-zone refill (doc 05 §36) counts unconnected items, which do not change, so the router reported nothing and kept the copper.
  2. With both shared GPUs full of other jobs, every cost-to-go field asked the CUDA driver for free memory before falling back to the CPU, and the threads queued on the driver's lock for seconds per field: interf_u took 2,900 s against 20 s with --no-gpu, ErgoDone 1,708 s against 6 s, all boards byte-identical.

Decision

  • drc::starved_thermals follows DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer of KiCad 10.0 on a board with current fills: spokes counted as crossings of the fill with the pad grown by half its thermal gap; spokes into a fill polygon with a single connection (CN_ZONE_LAYER::HasSingleConnection) count only when the pad has no other; tracks touching the pad that run from inside that outline into the fill count as spokes (on any layer, as KiCad); the minimum is the board setup's min_resolved_spokes (read from the project, default 2), and a severity of ignore turns the check off. tracemaker drc prints the pads it finds; the JSON report does not carry them (its types are the parity-checked ones).
  • Soft zones check the reliefs after the refill repair, name the starved pads in the log and put starved_thermals in the summary's refill object. Pads already starved in the input do not count. A thermal repair then runs one transaction per starved pad: the nets whose new copper crosses the pad's spoke lanes (each spoke grown by the zone's minimum width and the clearance, cut back from other nets' pads) lose their new copper and are routed again alone, with every starved pad's lanes as keep-outs; the result is kept only if fewer reliefs are starved and nothing more is unconnected after a refill (rule 4). --no-refill-repair turns it off with the unconnected repair.
  • The after-route passes (power widening, stitching vias) are judged on starved reliefs too when fills are soft.
  • GPU back-off (gpu/backoff.hpp): a CUDA device that refused (has_free_memory false) is not asked again for 30 s, the delay doubling while it keeps refusing (to 15 min) and resetting when it accepts. Fields computed meanwhile on the CPU are the same fields, so outputs do not change.

Alternatives

  • Rip every net that crosses any starved pad's spokes at once (tried first): cured all seven reliefs on interf_u but lost seven connections, so the transaction discarded it. Per pad, two of six were repaired with no connection lost.
  • Protect only the two spokes the fewest new nets cross: those were already free; the starvation came from islands further out, so nothing was ripped and nothing improved.
  • Keep spoke lanes out of the first pass for every thermal pad on a soft zone (prevention instead of repair): would change every soft-zone board and block the channels between pins of a plane net on the plane layer; not tried.
  • Stop asking the GPU for the rest of the run after one refusal: simpler, but a run that starts while another job holds the GPU would never use it once freed. The doubling back-off costs a few probes per hour.
  • Make --no-gpu the default (the campaign found the GPU path slower on short runs even when the GPUs are free): a separate decision, left open.

Consequences

  • interf_u at 3 M work, --soft-zones --si-rules: KiCad's starved_thermal 6 → 4, 140/164 routed and 26 unconnected either way, route time 15 → 76 s (six router runs for the repair). The other seven plane boards and the quick tier are byte-identical.
  • The engine's check finds KiCad's pads on every plane board run (and none where KiCad finds none) with one extra pad on interf_u (U9.C4: three islands, which KiCad does not count as single-connection there); erring that way only costs a repair attempt.
  • Not repaired: spokes into islands bounded by copper that does not cross the spokes themselves (three of the four left on interf_u). Not modelled: custom-pad spoke primitives and min_resolved_spokes constraints in custom rules.
  • Docs: doc 05 §36.1, doc 07 §4; records in bench/records/test-campaign-2026-10/.