KiCad Stackup Impedance Calculator
Drop a KiCad .kicad_pcb file or the whole project folder to read its physical stackup and get the trace width for 50 Ω single-ended and the width and gap for 90 Ω and 100 Ω differential pairs on every signal layer, with the loss per inch, from a 2D field solver. The routing check then measures the width, gap, length and skew of every differential pair as routed and checks its impedance on each layer against the interface it carries. The files are read in your browser and not uploaded.
Method, formulas and references
What is read from the file
A .kicad_pcb file is a text file of nested lists (s-expressions). The page reads it in your browser, in a background worker, and looks only at the top-level items it needs: the file version, (general (thickness …)), the (layers …) list, the (stackup …) section inside (setup …), the Edge.Cuts outline graphics, the header and outline of every (zone …), every track segment and arc (start, mid and end point, width, layer, net) and every via (position, layers, net). Footprints, fill polygons and everything else are skipped, so a board of tens of megabytes is read in well under a second. Nets are named by their (net N "name") declarations in KiCad 6 to 9 and by (net "name") on each item from KiCad 10. From a .kicad_pro file, dropped with the board or found in the project folder, only the net classes are read; everything else in the folder is ignored.
The board you load becomes the active KiCad project of this browser, shared with the KiCad project page and the calculators that read it, and it stays loaded after a reload. Its files are kept only in this browser, until you close the project on that page; its method section says what is stored and how to remove it.
Each stackup item is (layer "name" (type "…") …) with (thickness mm [locked]), (material "…"), (epsilon_r …) and (loss_tangent …); a dielectric made of several plies lists them as sublayers separated by the bare token addsublayer. Every sublayer becomes its own ply here, so stacked prepregs with different Dk are modelled as they are. Where a value is missing, KiCad's own default is used and listed under the table: Dk 4.5 and loss tangent 0.02 for a dielectric, Dk 3.3 for solder mask, 0.035 mm copper. A mask without a loss tangent uses 0.027, a typical LPI mask. A dielectric with exactly Dk 4.5 and loss tangent 0.02 is flagged, because those are the values KiCad gives every new dielectric.
Plane layers
KiCad does not mark which copper layers are planes, so they are inferred. For each copper layer, the zone outlines are clipped to the board's bounding box and their area is added up per net. The board area is the largest closed Edge.Cuts outline (or its bounding box). A layer is taken as a plane when
- it is declared a power layer in Board Setup → Board Editor Layers, or
- zones of at most 3 nets cover at least 50 % of the board, and the layer carries less than 20 % of the track length of the most-routed layer, or
- on a two-layer board, zones cover at least 50 % of one side and less than that of the other.
A ground pour on a busy signal layer therefore stays a signal layer. Each layer's detected role and the reason are shown under Layer roles in the Properties panel, where you can change it. Zone outlines are used, not the filled copper, so antipads and clearances do not reduce the coverage.
Impedance and loss
The stackup is converted to the site's stackup model and every signal layer is handed to the same code as the Layer Stack Manager: the cross-section runs from the trace to the nearest plane above and below; a signal layer passed on the way counts as dielectric (dual stripline). Widths are solved with the site's 2D field solver at normal accuracy with a 0.5 mil trapezoidal etch, so the numbers equal the Layer Stack Manager's impedance view for the same stackup and pair gap. The solves run in parallel on the solver worker pool and are cached for the visit.
Loss per inch is the conductor loss (skin effect with Hammerstad roughness for the chosen foil) plus the dielectric loss from the field solution, as on the Trace Loss and Interface Rules pages. Each ply keeps its own Dk and loss tangent from the file; Dk is moved to the loss frequency with the Djordjevic–Sarkar wideband model, taking the file's values as given at 1 GHz (KiCad's default specification frequency) unless the file states another frequency. For a differential pair the loss is that of the odd mode, which is the differential insertion loss per inch.
Routing check
Net classes. They are read from net_settings in the .kicad_pro file and resolved as the KiCad version that wrote the file does (the schema version is in net_settings.meta.version). KiCad 6 lists the member nets of each class by name. KiCad 7 and 8 use an explicit netclass_assignments entry, else the first matching netclass_patterns entry, else Default. KiCad 9 and 10 apply every assigned and every matching class; the one with the lowest priority number decides and missing values come from the next. A pattern matches when it matches the whole net name either as a regular expression or as a wildcard (* any run, ? one character), case-sensitive, as KiCad's net-class matcher does; vector buses such as D[0..7] are expanded. Without a project file every net is in Default.
Differential pairs. KiCad's own rule: reading the net name backwards, digits and underscores are skipped and the next character must be + or P (positive) or - or N (negative); the partner has that character swapped. So USB_D+/USB_D-, PCIE_TX0_P/PCIE_TX0_N and LVDS_P1/LVDS_N1 are pairs; lower-case p/n are not.
Width and gap. On each layer the width is the length-weighted most common track width of each net, with the range when it varies. Arcs count with their true length. For the gap, every piece of one trace is compared with the pieces of the other trace on the same layer that run within 10° of parallel; a uniform grid of the pieces keeps this fast on large boards. Where the two overlap when projected onto each other (as in KiCad's diff-pair DRC), the centre distance is taken at the middle of the overlap and the gap is that distance less half of each width; where several pieces overlap, the nearest counts. Arcs are cut into chords of at most 2°. The gap reported is the length-weighted most common gap (in 1 µm steps). A section whose gap is more than 3 × that gap (and more than 25 µm above it), or that has no parallel partner at all, such as the breakout to pads and vias, is uncoupled: it is left out of the gap and shown as the difference between coupled and total length. The coupled length is the mean of what is measured along each of the two traces.
Impedance. The measured width and gap of each layer are analysed with the same 2D field solver, cross-section, 0.5 mil etch and accuracy as the width table above, so a width designed for a target and then analysed gives the target back. A pair whose two traces differ in width is solved with their mean. A layer is checked when it has at least 3 mm of coupled length; shorter sections are breakout near pads and vias (and electrically short) and are listed, not checked. A layer with less than 1 mm coupled over more than 5 mm of routing is a warning: the two traces run apart there. The solves run on the solver worker pool and are cached by layer, width and gap.
Length, skew and loss. The length of a net is the sum of its segments and arcs on all layers; the via count is the number of vias on the net. Intra-pair skew is the difference of the two routed lengths; via barrels, pad-to-die (package) lengths and any length-tuning settings in KiCad are not included. It is turned into picoseconds with the delay of the layer that carries the pair (from the solver's effective permittivity of the odd mode). Loss is each layer's routed length times its loss per inch at the interface's Nyquist frequency (the page's loss frequency for a plain impedance target), from the same loss model as the width table and the Interface Rules page.
Targets and results. Each net class, split by the interface its names suggest, is matched to a target: an interface from the Interface Rules data, or a plain impedance. The suggestion comes from the class and net names (USB, HDMI, PCIe, SATA, MDI/ETH, MIPI/CSI/DSI, …); an impedance written in the class name, such as 85Ohm_PCIE, is the designer's target: the interface is suggested when that impedance lies inside its range and within 10 % of its nominal impedance, otherwise the class's impedance is suggested as a plain target. Change any of them under Routing targets; the choice is kept in the page address. An interface target uses its impedance range from the specification or vendor guide; a plain or custom target allows ±10 %. Skew is checked against the interface's intra-pair limit (a time limit through the layer delay), length against its maximum length and loss against its loss budget (over 80 % of the budget is a warning), where the data has them. ✓ passes, ⚠ needs a look (no reference plane, long uncoupled sections, a tight loss budget), ✗ fails. When the impedance of one net differs by more than 10 % between layers, or one layer passes and another fails, the layer change is flagged: a width that is right on an outer layer is often wrong on an inner one.
Limits
- The routing check measures tracks, not pads: the length inside pads, via barrels and package lengths are not counted, and a track ending on a pad is not joined to the next one. Coplanar ground beside a pair and the copper of other nets near it do not enter the impedance.
- Custom DRC rules (the .kicad_dru file) are not read. Bus groups such as
{A B}in net-class patterns are not expanded. - KiCad 5 and older files have no stackup section (only a board thickness), so no impedance can be given. Open them in KiCad 6 or later, check the physical stackup and save, or enter the layers in the Layer Stack Manager.
- Solder mask is taken as a uniform coating over the outer layers with the thickness in the file over the laminate and half of it over the trace. Switch it off for mask-free areas.
- Coplanar ground beside the trace, plane cut-outs and the copper roughness of each layer are not read from the file.
- KiCad's default stackup values are a placeholder; get the pressed thicknesses and laminate Dk/Df from your fabricator.
Sources
- KiCad source, board_stackup.cpp: BOARD_STACKUP::FormatBoardStackup (how the stackup is written) and the defaults of a new stackup item.
- KiCad source, pcb_io_kicad_sexpr_parser.cpp: PCB_IO_KICAD_SEXPR_PARSER::parseBoardStackup (how KiCad reads it back).
- KiCad source, pcb_io_kicad_sexpr.h: the board file format versions.
- KiCad developer documentation, board file format.
- KiCad source, net_settings.cpp: NET_SETTINGS, the net classes in the project file, their schema migrations (version 2 in KiCad 6, 3 in KiCad 7 and 8, 4 in KiCad 9, 5 in KiCad 10) and GetEffectiveNetClass.
- KiCad source, eda_pattern_match.cpp: EDA_COMBINED_MATCHER for net-class patterns (anchored regular expression or anchored wildcard).
- KiCad source, board.cpp: BOARD::MatchDpSuffix, the differential-pair naming rule.
- KiCad source, drc_test_provider_diff_pair_coupling.cpp: how KiCad's DRC finds the coupled sections of a pair (parallel projection of segment pairs).