Closed general

6-layer boards (0.7.15 drops In3/In4, PR 9 refuses); PCG residual stalls without refinement; via rings lost at 0.2 mm

John Lauer · 7d ago ·closed by John Lauer

Found while running the AI Flow current/thermal screen on a 6-layer board (OCP NIC 3.0 to OCuLink riser, KiCad 10.0.5, JLC06161H-3313 stackup). Tested on the distributed-plated-wall-coupling branch (PR 9, which adom-aiflow 0.1.91's field check requires).

1. Six-layer boards: released 0.7.15 silently drops In3/In4; PR 9 refuses them. 0.7.15 reports layers: [F.Cu, In1.Cu, In2.Cu, B.Cu] for a 6-layer board, all at 35 um, so copper on In3/In4 is missing from the current solve, with no warning. The PR 9 branch refuses with supported copper stacks are F.Cu/B.Cu or F.Cu/In1.Cu/In2.Cu/B.Cu. The solver already takes spec.stackupMm (one dielectric per interval) and copperUmByLayer, and the rest of the code iterates the board's actual layers. What was needed:

  • Board::validate_layers: accept F.Cu, In1.Cu .. In<n>.Cu, B.Cu in order, with an even layer count; dielectric_mm already enforces stackupMm length.
  • Sort layer_names into physical order after parsing: KiCad 9+ files list F.Cu, B.Cu, In1.Cu, ..., so the parse order is not the stack order.
  • A test: 6-layer ordered passes, out-of-order fails, 3-layer still fails.

2. PCG's recursive residual drifts on badly conditioned nets; the true residual stalls at 1e-4. Copper conductance (~2000 for 35 um) next to the 1e-9 leak gives a huge condition number. The CG recursion reports res < tol after ~1000-2000 iterations while the recomputed b - Ax is 1e-4 to 1e-5, so validate_current_residual (1e-6) fails whatever dcMaxIterations is. Iterative refinement fixes it: after each CG pass, solve A dx = b - Ax and add, until the true residual meets tol. On the riser, +3V3 went from 1.4e-4 to 7.4e-8.

3. At 0.2 mm pitch the drill void erases a 0.4/0.2 via's ring, splitting a net. A 0.4 mm via with a 0.2 mm drill leaves a 0.1 mm annulus. With 0.1 mm fine cells (pitch 0.2, pool 2), the drill disc removes nearly all ring cells. A via whose only planar contact is its ring overlapping a pad edge then comes out disconnected. On the riser, +3V3 split into two components at R3 (via at (4.9, 55.7) on the pad edge), and the solve could not converge (the injected current had no path). At --pitch 0.1 the ring resolves, the net is one component, and the solve converges. Suggestion: a connectivity check before solving that names the split and the nearest gap (the residual alone does not say why), and treat a via ring's coverage as at least one cell so a plated via never disconnects at coarse pitch.

I have the patch for 1 and 2 (about 60 lines plus the test) and can open a PR against the PR 9 branch if that is the right base.

1 Reply

John Lauer · 6d ago

Fixed in 0.7.16:

  1. Two-, four- and six-layer boards. The copper stack is checked in physical order (F.Cu, In1 … In, B.Cu, an even count), more than six layers is refused by name, and stackupMm is one value per real gap. Tested on the 6-layer LED grid tile: all six layers solve and converge.
  2. CG now refines until the true residual meets the target.
  3. The drill voids exist only on the PR 9 branch, not main, so main's via rings are intact. On main, copper pieces with terminals but no path to the rest are now referenced on their own and counted in the log rather than stalling the solve.

Log in to reply.