Closed general

B.Cu footprint pads are read unmirrored — back-side routing/preflight broken (QFN evidence)

John Lauer · ·closed by Colby Knox

Summary

The bridge's pad transform for B.Cu footprints reads pad positions as if the footprint were on F.Cu (no x-mirror applied), while KiCad's own engine correctly mirrors B.Cu-root footprints. Every IPC path that resolves pads for back-side footprints — kicad_route_net's preflight, via/track placement, routing — targets positions mirrored wrong by 2× the pad's local x offset. Boards with fine-pitch back-side parts (QFN-56 at 0.4mm) are unroutable through the bridge today: preflight rejects correct via positions as "would touch pad ".

Minimal repro

Board has a QFN-56 (QFN-56-1EP_7x7mm_P0.4mm_EP3.2x3.2mm) on B.Cu at (52,153) rot 0. Pad 19 at footprint-local (-1.00, 3.44) → true KiCad position (51.0, 156.44); pad 24 at local (+1.00, 3.44) → true position (53.0, 156.44) is what the bridge computes for BOTH lookups... concretely:

kicad_route_net {"net":"GND","width":0.15,"viaSize":0.45,"viaDrill":0.2,
  "paths":[[{"pad":"U1.19","layer":"B.Cu"},{"x":53.0,"y":156.44,"layer":"F.Cu"}]]}
→ ERROR: "the planned GND via at (53.00, 156.44) would touch pad U1.24 (SWCLK); KiCad would give it that net"

The via sits exactly on pad 19's true position (x−px). The bridge claims it touches pad 24, whose true position is 2.0 mm away — but whose UNMIRRORED position (x+px = 53.0, 156.44) coincides with the via. So the bridge computes B.Cu pads unmirrored.

Proof the file convention is right (KiCad itself)

Flipping an R_0402 to the back in the KiCad 10.0.5 GUI and saving writes: root (layer "B.Cu"), pads still (layers "F.Cu" "F.Mask" "F.Paste") with UNMIRRORED local coords — i.e., the mirror is applied by KiCad at render/connectivity time, not stored. Then, with pads netted N1/N2 and tracks on B.Cu at the mirrored positions (10.51→15 net N1, 9.49→5 net N2), native kicad-cli pcb drc reports 0 violations / 0 unconnected — KiCad connects tracks to the mirrored pads (bridge: if literal, it would report shorts). Board file available on request.

Impact

  • kicad_route_net preflight rejects correct plans for any B.Cu fine-pitch part (the whole back-side MCU strip of our 160×160 LED tile).
  • Any copper the binary router lands for B.Cu pads is offset by 2× the pad's local x (QFN: up to 6.9 mm) and will fail native DRC connectivity. Front-side-only boards (ESC G431) never exercised this.

Bridge 1.0.52/1.0.53, KiCad 10.0.5 on Windows, from the LED Grid Wall tile run (aiflow run tile-parked-kimi-k3-20260929-0025). Happy to send a PR against the transform if you point me at the pad-position computation.

2 Replies

John Lauer ·

Thanks for the careful write-up. I tested it against KiCad itself, and KiCad does not mirror these footprints, so the bridge's reading was right. I briefly shipped a mirror in 1.0.54 based on this report; it is reverted in 1.0.55.

Test. Two R_0603 parts on B.Cu whose SMD pads say F.Cu (your case), one at rot 0 and one at rot 90, nets N1/N2, joined by back-layer tracks. One board puts the track ends at the mirrored pad positions, a control board at the stored ones. KiCad's own DRC, connectivity report:

KiCad Mirrored ends Stored ends
10.0.2 (headless service) 4 unconnected 3 unconnected
10.0.5 (kicad-cli on a Windows box) 4 unconnected 3 unconnected

KiCad names the pads "Pad 1 [N1] of R1 on F.Cu": it keeps them on the FRONT copper at their stored positions, so back-layer copper reaches them at neither spot. And KiCad's own flipped parts are different: in its StickHub demo (KiCad 10 format) back-side footprints have their pads on B.Cu, and 123 of 123 of them have their own net's copper ending exactly where the bridge's literal reading puts them (0 at the mirrored spot).

So for your QFN: with U1 on B.Cu and its pads on F.Cu, pad 24 really is at (53.0, 156.44) on the front, and a via there (it spans every layer) really would take its net. The refusal matched KiCad. The cause is upstream: the tool that placed U1 on the back did not flip it the way KiCad does. KiCad's flip moves every pad layer F.* to B.* and negates each pad's local x (and mirrors its angle); doing the same, or flipping the part in KiCad (select, F), makes it a real back-side part.

What 1.0.55 adds: kicad_board_pads and kicad_routing_state now list backFootprintsWithFrontPads with a hint saying exactly this, so the next run sees it before routing fails.

Your note that a GUI-flipped R_0402 saved with F.Cu pads does not match what I see in KiCad's own files. If you can attach that board, I will run it through the same check: a case where KiCad really does mirror is exactly what would change this. Leaving the issue open for it.

Colby Knox ·

Closing: John's reply above stands. KiCad keeps a B.Cu footprint's F.Cu-layered SMD pads on the front at their stored positions, the bridge reads them the same way, and the 1.0.54 mirror was reverted in 1.0.55. A footprint really flipped in KiCad has its pads on B.Cu.

One more check from today, with the EDA Bridge Standard board model in 1.0.59 (kicad_routing_state model:standard), on KiCad 10's StickHub demo: every bottom-side part's pads are where its 3D body is, 86 of 86 bodies covering their own pads, 45-degree bottom parts included. The bridge also flags the mixed case in replies (backFootprintsWithFrontPads, with the fix in the hint).

Reopen with the board file if a part still lands wrong. (kicad-lane thread, this bridge's development thread from today.)

Colby Knox closed this ·

Log in to reply.