Documentation Index
Fetch the complete documentation index at: https://mintlify.com/KiCad/kicad-source-mirror/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Signal integrity (SI) analysis helps ensure reliable high-speed digital and analog signal transmission. KiCad provides tools for transmission line modeling, impedance control, and IBIS-based simulation.
Transmission Line Theory
Characteristic Impedance
For a PCB trace, the characteristic impedance Z₀ is determined by:
Where:
- L = inductance per unit length
- C = capacitance per unit length
Microstrip Traces
Surface traces with ground plane beneath:
Z₀ ≈ (87/√(εᵣ+1.41)) × ln(5.98h/(0.8w+t))
Where:
- εᵣ = dielectric constant
- h = height above ground plane
- w = trace width
- t = trace thickness
Stripline Traces
Internal traces between ground planes:
Z₀ ≈ (60/√εᵣ) × ln(4h/(0.67π(0.8w+t)))
Stackup Configuration
Define board stackup in the board file (.kicad_pcb):
(stackup
(layer "F.Cu"
(type "copper")
(thickness 0.035)
)
(layer "dielectric 1"
(type "prepreg")
(thickness 0.2104)
(material "FR4")
(epsilon_r 4.5)
(loss_tangent 0.02)
)
(layer "In1.Cu"
(type "copper")
(thickness 0.0152)
)
(layer "dielectric 2"
(type "core")
(thickness 1.065)
(material "FR4")
(epsilon_r 4.6)
(loss_tangent 0.02)
)
(layer "B.Cu"
(type "copper")
(thickness 0.035)
)
)
Impedance Calculator
Use KiCad’s built-in PCB Calculator for impedance:
import math
def microstrip_impedance(width, height, thickness, er):
"""
Calculate microstrip impedance
Args:
width: Trace width (mm)
height: Dielectric height (mm)
thickness: Copper thickness (mm)
er: Relative permittivity
Returns:
Impedance in ohms
"""
# Effective width
if thickness / height < 0.5:
w_eff = width + (thickness / math.pi) * math.log(2 * height / thickness)
else:
w_eff = width + (thickness / math.pi) * math.log(height / thickness + 1)
# Effective permittivity
er_eff = (er + 1) / 2 + (er - 1) / 2 * math.pow(1 + 12 * height / w_eff, -0.5)
# Impedance
if w_eff / height < 1:
z0 = (60 / math.sqrt(er_eff)) * math.log(8 * height / w_eff + w_eff / (4 * height))
else:
z0 = (120 * math.pi) / (math.sqrt(er_eff) * (w_eff / height + 1.393 + 0.667 * math.log(w_eff / height + 1.444)))
return z0
# Example: 50Ω trace on FR4
width = microstrip_width_for_impedance(
target_z0=50,
height=0.2, # 0.2mm dielectric
thickness=0.035, # 1oz copper
er=4.5 # FR4
)
print(f"Trace width for 50Ω: {width:.3f} mm")
Differential Pairs
Coupling and Impedance
For differential pairs:
Z_diff = 2 × Z_odd
Z_odd = Z₀ × √(1 - k)
Z_even = Z₀ × √(1 + k)
Where k is the coupling coefficient.
Design Rules
Set up differential pair rules in PCB:
import pcbnew
def setup_diff_pair_rules(board, net_prefix, target_z_diff=100):
"""
Configure differential pair routing rules
"""
# Get board design settings
settings = board.GetDesignSettings()
# Set track width for single-ended impedance
single_z0 = target_z_diff / 2
track_width = calculate_width_for_impedance(single_z0)
# Set gap between pairs (typically 2-3x trace width)
gap = track_width * 2.5
# Apply to matching nets
for net_code in range(board.GetNetCount()):
net = board.FindNet(net_code)
if net and net.GetNetname().startswith(net_prefix):
# Set netclass properties
netclass = net.GetNetClass()
netclass.SetTrackWidth(int(track_width * 1e6)) # Convert to nm
netclass.SetDiffPairWidth(int(track_width * 1e6))
netclass.SetDiffPairGap(int(gap * 1e6))
# Usage
board = pcbnew.GetBoard()
setup_diff_pair_rules(board, "USB_", 90) # 90Ω USB differential pair
setup_diff_pair_rules(board, "PCIE_", 100) # 100Ω PCIe
IBIS Models
KiCad supports IBIS (I/O Buffer Information Specification) models for signal integrity simulation.
IBIS File Structure
[IBIS Ver] 4.2
[File Name] device.ibs
[File Rev] 1.0
[Date] January 15, 2024
[Component] MyDevice
[Manufacturer] Example Corp
[Pin] signal_name model_name R_pin L_pin C_pin
1 DATA0 Output_3V3 50m 2n 1p
2 DATA1 Output_3V3 50m 2n 1p
3 CLK Clock_3V3 50m 2n 1p
[Model] Output_3V3
Model_type Output
Polarity Non-Inverting
Enable Active-High
Vinl = 0.8V
Vinh = 2.0V
Vmeas = 1.5V
Cref = 50pF
Rref = 50
Vref = 0
[Voltage Range] 3.0V 3.3V 3.6V
[Pulldown]
| Voltage I(typ) I(min) I(max)
-3.3V -50m -45m -55m
0.0V 0 0 0
3.3V 50m 45m 55m
[Pullup]
| Voltage I(typ) I(min) I(max)
-3.3V 50m 45m 55m
0.0V 0 0 0
3.3V -50m -45m -55m
Loading IBIS Models
Programmatic access to IBIS models:
#include "eeschema/sim/kibis/kibis.h"
// Load IBIS file
KIBIS kibis;
if( kibis.Init( "/path/to/device.ibs" ) )
{
// Get component
KIBIS_COMPONENT* comp = kibis.GetComponent( "MyDevice" );
// Get pin models
for( const KIBIS_PIN& pin : comp->GetPins() )
{
std::cout << "Pin: " << pin.m_pinNumber
<< " Model: " << pin.m_modelName << std::endl;
}
}
Reflection Analysis
Reflection Coefficient
Γ = (Z_L - Z₀) / (Z_L + Z₀)
Where:
- Z_L = load impedance
- Z₀ = transmission line impedance
Return Loss
Good design targets:
- RL > 10 dB (acceptable)
- RL > 20 dB (good)
- RL > 30 dB (excellent)
Python Analysis
import numpy as np
import matplotlib.pyplot as plt
def reflection_coefficient(z_load, z0=50):
"""Calculate reflection coefficient"""
return (z_load - z0) / (z_load + z0)
def return_loss(gamma):
"""Calculate return loss in dB"""
return -20 * np.log10(np.abs(gamma))
def vswr(gamma):
"""Calculate voltage standing wave ratio"""
return (1 + np.abs(gamma)) / (1 - np.abs(gamma))
# Analyze impedance mismatch
z_loads = np.linspace(25, 75, 100)
gammas = [reflection_coefficient(z) for z in z_loads]
rl = [return_loss(g) for g in gammas]
plt.figure(figsize=(10, 6))
plt.subplot(2, 1, 1)
plt.plot(z_loads, gammas)
plt.xlabel('Load Impedance (Ω)')
plt.ylabel('Reflection Coefficient')
plt.grid(True)
plt.subplot(2, 1, 2)
plt.plot(z_loads, rl)
plt.xlabel('Load Impedance (Ω)')
plt.ylabel('Return Loss (dB)')
plt.axhline(y=10, color='r', linestyle='--', label='10 dB threshold')
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
Crosstalk Analysis
Near-End and Far-End Crosstalk
def crosstalk_coupling(spacing, height, er=4.5):
"""
Estimate coupling between parallel traces
Args:
spacing: Edge-to-edge spacing (mm)
height: Height above ground plane (mm)
er: Dielectric constant
Returns:
Coupling coefficient (0-1)
"""
# Simplified model
k = np.exp(-np.pi * spacing / height)
# Adjust for dielectric
k_eff = k / np.sqrt(er)
return k_eff
def near_end_crosstalk(coupling, length, rise_time, velocity=1.5e8):
"""
Estimate near-end crosstalk
Args:
coupling: Coupling coefficient
length: Parallel run length (m)
rise_time: Signal rise time (s)
velocity: Propagation velocity (m/s)
Returns:
NEXT as fraction of aggressor amplitude
"""
if rise_time <= 0:
return 0
t_prop = length / velocity
if t_prop < rise_time:
# Short coupled region
next_value = coupling * t_prop / rise_time
else:
# Long coupled region
next_value = coupling
return next_value
# Example: 10cm parallel run
spacing = 0.3 # mm
height = 0.2 # mm
length = 0.1 # m (10cm)
rise_time = 1e-9 # 1ns
k = crosstalk_coupling(spacing, height)
next_val = near_end_crosstalk(k, length, rise_time)
print(f"Coupling coefficient: {k:.4f}")
print(f"NEXT: {next_val*100:.2f}% ({20*np.log10(next_val):.1f} dB)")
Propagation Delay
Delay Calculation
def propagation_delay(length, er_eff):
"""
Calculate signal propagation delay
Args:
length: Trace length (m)
er_eff: Effective dielectric constant
Returns:
Delay in seconds
"""
c = 3e8 # Speed of light (m/s)
velocity = c / np.sqrt(er_eff)
delay = length / velocity
return delay
def delay_per_inch(er_eff):
"""
Standard delay metric
Args:
er_eff: Effective dielectric constant
Returns:
Delay in ps/inch
"""
length_inch = 0.0254 # meters
delay = propagation_delay(length_inch, er_eff)
return delay * 1e12 # Convert to ps
# FR4 typical
er_eff = 3.5
print(f"Delay: {delay_per_inch(er_eff):.1f} ps/inch")
# Length matching for DDR
clock_length = 100e-3 # 100mm
data_length = 105e-3 # 105mm
skew = abs(propagation_delay(clock_length, er_eff) -
propagation_delay(data_length, er_eff))
print(f"Skew: {skew*1e12:.1f} ps")
Design Guidelines
High-Speed Design Rules
| Parameter | Guideline | Typical Value |
|---|
| Trace spacing | 3× trace width | 0.3-0.5 mm |
| Via stub length | < λ/20 | < 50 mm @ 1 GHz |
| Diff pair coupling | 2-3× trace width | 0.2-0.4 mm |
| Return path | Continuous | No gaps |
| Length matching | < 0.1 × rise time | ±5 mm for DDR4 |
Impedance Tolerances
- Single-ended: ±10% (45-55Ω for 50Ω target)
- Differential: ±10% (90-110Ω for 100Ω target)
- Tight control: ±5% for critical signals
Practical Examples
DDR4 Interface
class DDR4_Design:
def __init__(self):
self.target_z_single = 40 # Ω
self.target_z_diff = 100 # Ω
self.max_skew = 5e-12 # 5ps
self.max_length = 0.05 # 50mm
def validate_trace_lengths(self, lengths):
"""Check if trace lengths meet skew requirements"""
delays = [propagation_delay(l, 3.5) for l in lengths]
max_skew = max(delays) - min(delays)
if max_skew > self.max_skew:
print(f"FAIL: Skew {max_skew*1e12:.2f}ps exceeds {self.max_skew*1e12:.2f}ps")
return False
print(f"PASS: Skew {max_skew*1e12:.2f}ps within spec")
return True
# Example usage
ddr4 = DDR4_Design()
data_lengths = [0.048, 0.050, 0.049, 0.051] # meters
ddr4.validate_trace_lengths(data_lengths)
PCIe Gen3
class PCIe_Gen3_Design:
def __init__(self):
self.target_z_diff = 85 # Ω (PCIe spec: 85±15%)
self.max_intra_pair_skew = 1e-12 # 1ps
self.max_lane_skew = 100e-12 # 100ps
def check_differential_pair(self, p_length, n_length):
"""Validate differential pair matching"""
skew = abs(propagation_delay(p_length, 3.5) -
propagation_delay(n_length, 3.5))
if skew > self.max_intra_pair_skew:
print(f"FAIL: Pair skew {skew*1e12:.2f}ps")
return False
print(f"PASS: Pair skew {skew*1e12:.3f}ps")
return True
Via Modeling
Via Impedance Discontinuity
def via_impedance(pad_diameter, barrel_diameter, height, er=4.5):
"""
Estimate via impedance
Args:
pad_diameter: Via pad diameter (mm)
barrel_diameter: Via barrel diameter (mm)
height: Board thickness (mm)
er: Dielectric constant
Returns:
Via impedance (Ω)
"""
# Simplified via model
z_via = (60 / np.sqrt(er)) * np.log(4 * height / barrel_diameter)
# Capacitance from pad
c_pad = (er * 8.85e-12 * np.pi * (pad_diameter/2)**2) / height
return z_via, c_pad
# Example
pad_d = 0.6 # mm
barrel_d = 0.3 # mm
board_h = 1.6 # mm
z_via, c_pad = via_impedance(pad_d, barrel_d, board_h)
print(f"Via impedance: {z_via:.1f}Ω")
print(f"Pad capacitance: {c_pad*1e15:.2f}fF")
Best Practices
- Minimize via stubs: Use back-drilling for high-speed signals
- Control impedance: Maintain ±10% throughout signal path
- Match lengths: Keep clock/data skew within specifications
- Avoid splits: Don’t cross reference plane gaps
- Use ground stitching: Place vias near signal transitions
- Simulate critical nets: Verify timing and integrity before fabrication
- Document stackup: Specify impedance requirements to fabricator
See Also