ASAravind Sivakumaran
Theme

Theme and appearance

Appearance
Palette

Pick any colour. The displayed accent adjusts for readable contrast in light or dark mode.

← PCIe guide

PCIe / PHYSICAL LAYER

How does a PCIe link move 128 billion bits a second down a copper trace?

Picture two people signalling across a valley with a torch. With two brightness levels, off and on, each flash is one bit. Flash faster and the far side starts to blur one flash into the next. Use four brightness levels and each flash carries two bits at the same speed, but in fog the levels are harder to tell apart, so you add a way to repair the odd mistake. That is the story of the PCIe physical layer from Gen 1 to Gen 7: flash faster (Gens 1 to 5), then use four levels and correct errors (Gens 6 and 7).

The simple guide comes first; then a signal scope where you add noise yourself, the line codes, the 256-byte flit and its error correction, link training step by step, and what Linux shows. The second half is for people who bring up boards, write drivers or read lspci for a living.

What claims does this page make? 5 points
  • Spec rule: speeds, codes, the flit layout and link training come from the PCI Express Base Specification (members only, not read here) as its authors and PCI-SIG describe them in public talks and press releases, cited below. Where a public source gives sizes but not positions, the page says so.
  • Simulation: the scope, the flit lab and the training experiments are simplified models (levels, Gaussian noise, a one-byte-a-group code); they show the idea, not a real channel or the real code's maths.
  • Estimate: loss budgets, retry rates and latencies are the cited authors' figures, not measured here.
  • Real hardware: the lspci lines are pciutils' formats; this page measured no link.
  • Linux behaviour is quoted from its source, read on 2 October 2026.

The simple guide, in five ideas

A root port and a card joined by two lanes. Each lane is two pairs of wires, one pair for each direction, and bits travel both ways at once. Root porttransmit, receive Cardreceive, transmit lane 0 lane 1 → one pair out← one pair back
  1. A lane is four wires. Two for each direction, as a differential pair: the receiver reads the difference between the two wires, so noise that hits both cancels. Both directions run at once. A x16 slot has sixteen lanes, and bytes are dealt across them like cards.
  2. There is no clock wire. The receiver recovers the timing from the data itself. That is why the bits are coded (Gens 1 and 2) or scrambled (all generations): a long run of zeros has no edges to lock onto.
  3. GT/s counts bits on one lane, not bytes. 32 GT/s is 32 billion bits a second, each way. From Gen 6 two bits share a symbol, so 64 GT/s is 32 billion symbols. Coding and checks take a share: 20% at Gens 1 and 2, under 2% at Gens 3 to 5, about 5.5% for the flit at Gens 6 and 7.
  4. Gens 6 and 7 send two bits a symbol. PAM4 uses four voltage levels instead of two. Gen 6 doubles the bit rate without making the signal change faster than Gen 5's, but each level sits closer to its neighbours, so errors are expected and corrected.
  5. A link trains itself. At power-on both ends start at 2.5 GT/s, agree on the lanes, then step up to the fastest speed both can hold, tuning their signal at each step. Software only sees the result.

Every other part of PCIe, the configuration reads, the packets and the DMA of the other chapters, rides on top of this and never sees which generation carried it.

Seven generations at a glance

Each generation doubled the bandwidth. Gen 3 got its doubling partly from a lighter code; Gen 6 from PAM4. Bandwidth here is one direction of a x16 link after line coding (and, for Gens 6 and 7, after the flit's error-checking bytes), in GB/s. PCI-SIG's press releases quote both directions before coding: 128 GB/s for Gen 5, 256 for Gen 6, 512 for Gen 7.

GenYearGT/sCodingSignalNyquist, GHzx16, GB/s each way
120032.58b/10bNRZ1.254.0
2200758b/10bNRZ2.58.0
320108128b/130bNRZ415.8
4201716128b/130bNRZ831.5
5201932128b/130bNRZ1663.0
6202264flitPAM416121.0
72025128flitPAM432242.0

NRZ and PAM4 on a scope

The same bits, sent at the same symbol rate, two ways. NRZ (non-return to zero) has two levels: one bit a symbol. PAM4 (pulse amplitude modulation, four levels) has four: two bits a symbol, Gray coded so neighbouring levels differ in one bit (00, 01, 11, 10 from the bottom). Add noise and watch where the receiver, deciding at the dashed thresholds, gets a symbol wrong (red dots).

Two traces of the same bits: NRZ on top with two levels, PAM4 below with four. Dots mark where the receiver sampled each symbol; red dots are wrong decisions. NRZ: 2 levels, 1 bit a symbol PAM4: 4 levels, 2 bits a symbol

NRZ eye: one big opening
PAM4 eye: three openings, each a third as tall

The eye is what an oscilloscope shows when it overlays every symbol on one interval: the open space in the middle is the receiver's margin. PAM4 fits three eyes into the swing NRZ used for one, so each is a third as tall: about 9.5 dB less signal-to-noise (20·log103). Raise the noise until NRZ starts to fail; PAM4 failed long before. That is the price of Gen 6, and why it was the first generation to need forward error correction: its target is a first-bit error rate (FBER) of 10−6, where Gens 3 to 5 aimed at 10−12.

Two further tricks from Gen 6 do not show here: precoding, which stops one bad decision in an equalizer from turning into a burst, and the Gray code itself, which makes the usual mistake (a neighbouring level) cost one bit, not two.

Line codes: 8b/10b, 128b/130b, and none

8b/10b (Gens 1, 2)128b/130b (Gens 3-5)Flit mode (Gens 6, 7)
What is senteach byte as a 10-bit symbol from a tablea 2-bit sync header, then 128 scrambled bits256-byte flits, scrambled; no per-block header
Overhead20%1.5%the flit's 8-byte CRC and 6-byte FEC, about 5.5% (242 of 256 bytes carry packets and link information)
Clock recoveryat most 5 equal bits in a row, by the codeby the scrambler (statistically)by the scrambler
Controlspecial symbols (K codes), such as COMheader 01: an ordered set; 10: dataordered sets between flits
Errors caught bypacket CRCs (LCRC, ECRC)packet CRCsthe flit's FEC and CRC

The 256-byte flit and its error correction

A flit (flow control unit) is the fixed-size block every Gen 6 and Gen 7 link sends: 236 bytes of packets, 6 of link information, an 8-byte CRC and 6 bytes of FEC. The FEC is three interleaved codes; each repairs one bad byte. Bytes are dealt to the three groups in turn, so a burst of up to three neighbouring bad bytes puts one in each group, and all three are repaired. Press bytes to corrupt them (the arrow keys move between them), or send a burst:

A 256-byte flit: 236 bytes of packets, 6 of link information, an 8-byte CRC and 6 bytes of FEC. Below it, bytes are dealt to the three FEC groups 0, 1, 2, 0, 1, 2 in turn. TLP · 236 bytesthe packets: the cargo DLP · 6link info CRC · 8the seal FEC · 6the repair kit Not to scale: the first part is really 92% of the flit. 012012012 Bytes go to FEC groups 0, 1, 2, 0, 1, 2 … in turn. A burst of 3 neighbours puts one bad byte in each group, so all three are repaired.

Pick a byte to see its part here.

Try a burst of 3, then 4. Four bad bytes in a row put two in one group; the FEC cannot repair it and the CRC catches it (an 8-byte CRC detects any 8 bad bytes for certain). The receiver then asks for the flit again. The numbers that made this design work, from the specification's lead author:

The sizes and their order are published; the byte positions of the packet and link bytes (0-235, 236-241) are inferred from them, and the lab's "byte mod 3" grouping is a simplification of the real interleave. CXL 3's 256-byte flit keeps this CRC and FEC but lays out the rest its own way: a 2-byte header, then CXL.io packets or CXL.cache and CXL.mem slots (see the CXL chapter).

The LTSSM: Detect, then Polling, then Configuration, then L0, the working state. From L0 a link goes to Recovery to change speed or retrain, and to the power states L0s, L1 and L2. Detectanyone there? Pollingbit and symbol lock Configurationlink and lanes L0s, L1, L2power saving L0data flows Recoveryspeed, equalization

Experiments

One link, from power-on to L0 and back: the link training state machine (LTSSM) as both ends run it.

Experiment

    The link

      What the two ends do

        Notes

          For professionals: equalization, channel loss and retimers

          Copper loses high frequencies more than low ones, so a sharp edge arrives smeared into the next symbol. Equalization undoes that: the transmitter pre-shapes its signal (de-emphasis, a weaker level after a run of equal bits, and preshoot, a stronger one before an edge), and the receiver adds its own filters (CTLE, DFE). Gen 3 introduced a three-tap transmitter filter tuned during training; Gen 6 uses four taps.

          Training starts from one of eleven presets, P0 to P10 (P10 is for compliance testing). The values below are the ones pciutils decodes. The presets a port starts training with are in its Lane Equalization Control registers (the Secondary PCI Express capability for 8 GT/s, the 16 and 32 GT/s Physical Layer capabilities above that). LnkCtl2: … Compliance Preset/De-emphasis in lspci -vv is something else: the preset a port sends while a compliance test runs, not the one the live link uses.

          PresetP0P1P2P3P4P5P6P7P8P9
          De-emphasis, dB−6−3.5−4.4−2.5000−6−3.50
          Preshoot, dB000001.92.53.53.53.5
          1. Phase 0: the downstream port (the root port side) tells the device which preset to start with.
          2. Phase 1: both ends send with those presets until the link works well enough to talk.
          3. Phase 2: the upstream end's receiver asks the downstream port's transmitter for changes, over and over, until its errors are low enough.
          4. Phase 3: the same in the other direction.

          The conventional, full sequence equalizes each rate from 8 GT/s up in turn, which is why Linux has a separate capability for each (PCI_EXT_CAP_ID_PL_16GT, _32GT, _64GT, each with its lane equalization registers). PCIe 5.0 added two optional shortcuts that both ends must support: an equalization bypass that goes from 2.5 GT/s straight to the highest common rate and equalizes only there, and no equalization needed, for a channel short and clean enough to skip it (lspci: Physical Layer 32.0 GT/s, Phy32Cap: EqualizationBypass+ NoEqualizationNeeded+). Equalization is the step that fails on a poor channel; the second experiment above shows the result.

          The channel loss budget says how much a link may lose end to end at its Nyquist frequency: 36 dB at 16 GHz for Gen 5 (about 9 dB for the processor package, 16 for the board, 1.5 for the connector and 9.5 for the card), and 32 dB at 16 GHz for Gen 6, because PAM4's smaller eyes need more of the signal to arrive. When a trace is too long, a retimer recovers the bits and sends them again freshly equalized; a link may have at most two, and lspci's LnkSta2 reports them (Retimer+ 2Retimers-). A redriver only amplifies, noise included.

          Both ends also need the same idea of time: a 100 MHz reference clock, shared (CommClk+ in lspci) or separate, often with spread-spectrum clocking that wobbles it by up to 0.5% at about 30 kHz to spread its radio emissions.

          Gen 6, Gen 7, optics and Gen 8

          What Linux shows

          The link's state lives in the device's PCI Express capability. lspci decodes it (pciutils 3.15):

          LnkCap:	Port #0, Speed 32GT/s, Width x16, ASPM L1, Exit Latency L1 <16us
          LnkSta:	Speed 16GT/s (downgraded), Width x16
          LnkCap2: Supported Link Speeds: 2.5-32GT/s, Crosslink- Retimer+ 2Retimers+ DRS-
          LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete+ EqualizationPhase1+
          	 EqualizationPhase2+ EqualizationPhase3+ LinkEqualizationRequest-
          	 Retimer+ 2Retimers- CrosslinkRes: unsupported, FltMode-

          sysfs has the same in words: /sys/bus/pci/devices/0000:01:00.0/current_link_speed reads 16.0 GT/s PCIe, max_link_speed 32.0 GT/s PCIe, and current_link_width 16. When a device is held back by a slower link anywhere above it, the kernel says so once at boot (__pcie_print_link_status() in drivers/pci/pci.c):

          pci 0000:01:00.0: 126.016 Gb/s available PCIe bandwidth, limited by 8.0 GT/s PCIe x16 link at 0000:00:01.0 (capable of 252.048 Gb/s with 16.0 GT/s PCIe x16 link)

          Computed from the kernel's code for a Gen 4 x16 card behind a Gen 3 x16 port, not captured from hardware; the addresses are examples. A link in flit mode adds , in Flit mode.

          Sources