Two engineers look at the same scope trace from a serial link. One reads the bits as the line's level: high is 1, low is 0. The other insists the bits are in the changes: a change is 0, no change is 1. They decode different data from the same wire, and they are both following a real standard. The line code is the first layer of meaning a protocol puts on a wire, and it is not always the obvious one.
How do bits become levels on a wire, and why would anyone not send the bit as the level?
The obvious code is NRZ (non-return-to-zero): drive the line high for a 1 and low for a 0, for the whole bit. It uses the least bandwidth and is trivial to build. It breaks in two ways that Part 1 already exposed. A long run of identical bits has no transitions, so a receiver recovering its clock from the data drifts (lesson 1.4). And a long run of identical bits pushes the line's average away from the middle, which an AC-coupled link cannot carry (lesson 2.5). The line code is the first place a protocol can fix both.
Code | Rule | What it buys | What it costs |
|---|---|---|---|
NRZ | Level = bit | Lowest bandwidth; simplest | No transitions in long runs; average drifts with the data |
NRZI | A change marks one value, no change the other (in USB, a 0 is a change) | Immune to swapped wires (only changes matter); runs of the "change" value are full of transitions | Runs of the other value still have none; needs stuffing or coding for those |
Manchester | Every bit has a transition in its middle; the direction gives the value | A transition in every bit; perfectly balanced (always one high half, one low half) | Twice the bandwidth of NRZ for the same bit rate |
PAM4 | Four levels, each carrying two bits | Twice the bits per symbol: half the symbol rate for the same bit rate | Levels are closer together: much less noise margin, so more bit errors (Part 10) |
Notice that NRZI fixes only half of NRZ's problem: whichever value is "no change" can still form a long run. That is why USB pairs NRZI with bit stuffing (lesson 2.4), which breaks up long runs of that value.
The testbench sends ten bits in NRZ, NRZI and Manchester side by side, with the clock at twice the bit rate so Manchester's mid-bit transition is visible. In this listing a Manchester 1 is low-then-high and a 0 is high-then-low; standards differ on that convention. A checker decodes all three back to the original bits.
// One bit stream in three line codes. clk runs at twice the bit rate, so each bit
// lasts two slots and Manchester can change level in the middle of a bit.
// NRZ: the line level is the bit.
// NRZI: a 0 toggles the line, a 1 leaves it (the convention USB uses).
// Manchester: every bit has a transition in its middle; here a 1 is low-then-high
// and a 0 is high-then-low (conventions differ between standards).
// A checker decodes all three and compares them with the original bits.
module tb;
logic clk = 0;
always #5 clk = ~clk;
localparam int N = 10;
localparam logic [N-1:0] BITS = 10'b0011100010; // sent from bit 0 upwards
logic half = 0; // 0 = first half of a bit, 1 = second half
int i = 0; // index of the bit being sent
logic bit_now, nrz, nrzi, manch;
logic nrzi_prev = 1; // line level during the previous bit
logic started = 0;
assign bit_now = BITS[i];
assign nrz = bit_now;
assign manch = bit_now ^ ~half; // 1: 0 then 1; 0: 1 then 0
assign nrzi = nrzi_prev ^ ~bit_now; // a 0 changes the level, a 1 keeps it
always @(posedge clk) begin
if (!started) started <= 1;
else begin
half <= ~half;
if (half) begin
if (i < N - 1) i <= i + 1;
end
end
end
always @(posedge clk) if (started && half) nrzi_prev <= nrzi; // remember this bit's level
// Checker: decode each code at the end of every bit
logic manch_first;
int errors = 0, checked = 0;
always @(posedge clk) if (started) begin
if (!half) manch_first <= manch;
else begin
if (nrz != bit_now) errors++;
if ((nrzi == nrzi_prev) != bit_now) errors++; // no change = 1, change = 0
if ({manch_first, manch} != (bit_now ? 2'b01 : 2'b10)) errors++;
checked++;
end
end
initial begin
$dumpfile("wave.vcd"); $dumpvars(0, tb);
#210;
if (errors == 0 && checked >= N - 1) $display("PASS: NRZ, NRZI and Manchester all decode back to the same %0d bits", checked);
else $display("FAIL: %0d errors over %0d bits", errors, checked);
$finish;
end
endmoduleHow to read it (bit k occupies slots 2k and 2k + 1):
NRZ (nrz) follows the bit. The run of three 0s (slots 4 to 9) and three 1s (slots 10 to 15) produces no transitions at all inside each run.
NRZI (nrzi) starts from 1 and toggles on every 0: bit 0 takes it to 0, bit 1 (a 1) holds it, bits 2, 3 and 4 (0s) toggle it three times, so the run of 0s is full of transitions. The run of three 1s (slots 10 to 15) has none.
Manchester (manch) changes level in the middle of every bit, slots 2k to 2k + 1, whatever the data: high-to-low for each 0, low-to-high for each 1. It is busy all the time, which is exactly why it is easy to recover a clock from it and why it needs twice the bandwidth.
For a link carrying 1 Gb/s (illustrative), compare the symbol rate on the wire and the spacing between levels for the same maximum voltage swing:
Code symbols per second levels level spacing (share of full swing)
NRZ 1.0 G 2 1 (100%)
Manchester 2.0 G (two per bit) 2 1 (100%)
PAM4 0.5 G (two bits each) 4 1/3 (33%: about 9.5 dB less signal between levels)PAM4 halves the symbol rate, which matters when the channel loses high frequencies, but shrinks the gap between adjacent levels to a third, so the same noise causes far more errors. That is why links that adopted PAM4 also adopted forward error correction (lesson 10.6).
If your channel... | Lean towards | Because |
|---|---|---|
Is short and has plenty of bandwidth | NRZ, with a block code or scrambling for transitions | Lowest symbol rate; transitions come from coding |
Is low-rate, needs simple clock recovery and balance | Manchester | Transitions and balance for free; the bandwidth cost is affordable at low rates |
Has wires that may be swapped, or uses differential signalling with uncertain polarity | NRZI (with stuffing) | Only changes carry meaning |
Is bandwidth-limited at very high rates | PAM4, with FEC | Half the symbol rate; FEC buys back the lost margin |
Protocol | Line code |
|---|---|
USB 2.0 | NRZI (a 0 is a transition, a 1 is no transition) with a 0 stuffed after six 1s, so runs of 1s cannot starve the receiver of transitions. |
PCI Express Gen1–5 | NRZ signalling, with transitions and balance provided by 8b/10b (Gen1–2) or by a sync header and scrambling (Gen3–5). |
PCI Express Gen6 | PAM4 signalling, with fixed-size flits protected by FEC. |
A board swaps the two wires of a differential pair by mistake (illustrative). On an NRZ link every bit is inverted, and the link fails unless the receiver can detect and correct polarity. On an NRZI link nothing changes: a change is still a change. Many serial protocols therefore either use codes that tolerate inversion or detect polarity during training (Part 11). The deeper lesson: a line code decides not only bandwidth and transitions but also which wiring mistakes the system survives.
A run of 1s, because a 1 means "no change". USB stuffs a 0 after six 1s to force a transition.
Every bit is one high half and one low half, so the line spends exactly half its time at each level, whatever the data.
The same bit rate needs only 16 GBd, halving the highest frequencies the channel must carry, at the cost of a third of the level spacing, which must be recovered with FEC.
A line code maps bits to levels or changes: NRZ (level), NRZI (change or not), Manchester (mid-bit direction), PAM4 (four levels, two bits each).
NRZ and NRZI leave long runs without transitions; Manchester always has them at twice the bandwidth.
PAM4 halves the symbol rate but cuts the level spacing to a third, so it needs forward error correction.