Moving from one generation of a serial link to the next, the line rate goes from 5 to 8 gigatransfers per second, only 60% faster. Yet the useful bandwidth per lane almost doubles. No trick in the electronics explains it. The difference is entirely in how the bits are encoded.
What do block codes guarantee, and what does each one cost?
Lessons 2.4 and 2.5 left a link needing three guarantees: transitions often enough for clock recovery, a bounded running disparity for AC coupling, and a way to send framing and control information that data can never imitate. Bit stuffing provides the first and third but makes frame length depend on data, which is awkward at high rates. Something with a fixed overhead and fixed-size blocks is needed, and that overhead is paid on every bit forever, so it had better be small.
A block code maps every n data bits to m line bits (m > n). The extra m − n bits buy guarantees. The overhead, as a share of the line rate, is:
The three codes in wide use buy their guarantees differently:
Code | How it works | Guarantees | Overhead |
|---|---|---|---|
8b/10b | A table maps each byte to one of two 10-bit code groups; extra code groups are reserved as control symbols | At most five identical bits in a row; bounded disparity (the encoder chooses the group that restores balance); unique control symbols such as the comma (K28.5) for alignment | 20% |
64b/66b | A 2-bit sync header (01 = data, 10 = control) in front of 64 scrambled bits | A transition in every header; balance and transitions in the payload only statistically, through scrambling | about 3% |
128b/130b | A 2-bit sync header (10 = data block, 01 = ordered-set block) in front of 128 scrambled bits | Same idea as 64b/66b, with half the overhead | about 1.5% |
The model computes each code's overhead, then the payload bandwidth per lane for five PCI Express generations, using their published line rates and codes.
"""Line-code overhead and what it does to payload bandwidth per lane.
Line rates for PCI Express generations: Gen1 2.5, Gen2 5.0, Gen3 8.0, Gen4 16.0,
Gen5 32.0 GT/s per lane. Gen1-2 use 8b/10b; Gen3-5 use 128b/130b.
"""
codes = {"8b/10b": (8, 10), "64b/66b": (64, 66), "128b/130b": (128, 130)}
print("code overhead (share of the line rate)")
for name, (d, t) in codes.items():
print(f"{name:11} {100 * (t - d) / t:5.2f}%")
gens = [("Gen1", 2.5, "8b/10b"), ("Gen2", 5.0, "8b/10b"), ("Gen3", 8.0, "128b/130b"),
("Gen4", 16.0, "128b/130b"), ("Gen5", 32.0, "128b/130b")]
print("\ngeneration line rate code payload per lane")
payload = {}
for g, rate, code in gens:
d, t = codes[code]
payload[g] = rate * d / t
print(f"{g:10} {rate:5.1f} GT/s {code:10} {payload[g]:6.3f} Gb/s")
ratio = payload["Gen3"] / payload["Gen2"]
print(f"\nGen2 -> Gen3: line rate x{8.0 / 5.0:.2f}, payload x{ratio:.2f}")
assert abs(ratio - 1.97) < 0.01
print("PASS: dropping 8b/10b's 20% overhead let Gen3 nearly double payload with a 1.6x line rate")It prints:
code overhead (share of the line rate)
8b/10b 20.00%
64b/66b 3.03%
128b/130b 1.54%
generation line rate code payload per lane
Gen1 2.5 GT/s 8b/10b 2.000 Gb/s
Gen2 5.0 GT/s 8b/10b 4.000 Gb/s
Gen3 8.0 GT/s 128b/130b 7.877 Gb/s
Gen4 16.0 GT/s 128b/130b 15.754 Gb/s
Gen5 32.0 GT/s 128b/130b 31.508 Gb/s
Gen2 -> Gen3: line rate x1.60, payload x1.97
PASS: dropping 8b/10b's 20% overhead let Gen3 nearly double payload with a 1.6x line rateThat is the opening puzzle solved. Going from Gen2 to Gen3, the line rate rose 1.6 times but the payload rose 1.97 times, because 8b/10b's 20% overhead dropped to about 1.5%. The trade is in the guarantees: 8b/10b's run-length and balance limits are hard, while 128b/130b's depend on scrambling, plus the header's guaranteed transition in every block.
Hard versus statistical guarantees. 8b/10b bounds run length and disparity for any data. Scrambled codes make bad patterns extremely unlikely but not impossible, so receivers must tolerate more baseline wander and longer runs.
Control symbols. 8b/10b has spare code groups that data can never produce, which makes framing and alignment simple and robust. Scrambled codes signal control with the header ("this block is control") and a type field inside, which costs design effort.
Latency and granularity. 8b/10b works byte by byte; 128b/130b needs a full block to be assembled, which adds a little latency and forces data into 16-byte units.
Error behaviour. One bit error in an 8b/10b code group usually produces an invalid code, which is detected immediately. In a scrambled code an error stays a simple bit error (but self-synchronising scramblers can multiply it, lesson 2.7).
If you need... | Choose | Because |
|---|---|---|
Hard limits on runs and balance, simple control symbols, moderate rates | 8b/10b | Every guarantee is unconditional; overhead is acceptable below a few Gb/s |
High rates where 20% is too much bandwidth to give away | 64b/66b or 128b/130b | Low overhead; transitions from the header and scrambling |
Very high rates with PAM4 and FEC | Fixed-size flits protected by FEC (lesson 2.10) | The coding and the error correction are designed together |
Protocol | Code |
|---|---|
PCI Express Gen1–2 | 8b/10b |
PCI Express Gen3–5 | 128b/130b, with a 2-bit sync header (10 data, 01 ordered set) that is not scrambled |
PCI Express Gen6 | PAM4 signalling with fixed 256-byte flits and FEC |
10 Gb/s Ethernet (10GBASE-R) | 64b/66b, with sync header 01 for data blocks and 10 for control blocks: the opposite assignment from PCIe's 128b/130b |
A bandwidth estimate for a new system uses the line rate as the payload rate (illustrative): a x4 link at 5 GT/s is assumed to carry 20 Gb/s. With 8b/10b it carries 16 Gb/s before any packet overhead, and the design misses its throughput target by 20% at the first measurement. The fix is a habit: always multiply by n/m first, then subtract framing, header and flow-control overheads (lesson 6.9).
8 × 8 × 128/130 ≈ 63.0 Gb/s, about 7.9 GB/s.
The receiver must find block boundaries by looking for valid headers (01 or 10) before it can descramble anything, and each header must contain a guaranteed transition.
Control symbols that data can never produce, such as the comma used for word alignment, so framing and alignment markers are unambiguous.
A block code maps n data bits to m line bits; overhead is (m − n)/m: 20% for 8b/10b, about 3% for 64b/66b, about 1.5% for 128b/130b.
8b/10b guarantees run length, balance and control symbols for any data; 64b/66b and 128b/130b rely on scrambling plus a 2-bit header.
Always convert line rate to payload rate with n/m before any bandwidth estimate.