Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Reading Paths
  • Concept Map
  • How to Read the Diagrams
  • Part 0: What Is a Protocol?10
  • Part 1: Timing and Clocking11
  • Part 2: Encoding and Framing12
    • Line Codes
    • Framing Basics
    • Packet Anatomy
    • Bit Stuffing
    • DC Balance and Running Disparity
    • Block Codes
    • Scrambling
    • Alignment and Lock
    • Control Symbols and Ordered Sets
    • Flits, Phits and Packing
    • Protocols Compared: Encoding and Framing
    • Drills: Encoding and Framing
  • Part 3: Handshakes13
  • Part 4: Flow Control17
  • Part 5: Transactions19
  • Part 6: Concurrency and Performance17
  • Part 7: Ordering13
  • Part 8: Sharing and Arbitration12
  • Part 9: Deadlock and Liveness13
  • Part 10: Reliability and Recovery20
  • Part 11: Bring-Up and Link Management10
  • Part 12: Power8
  • Part 13: Shared State and Coherence9
  • Part 14: Security and Isolation7
  • Part 15: Specifying and Verifying11
  • Part 16: Protocols Dissected17
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 2: Encoding and Framing
  1. Topics
  2. First Principles of Hardware Protocols
  3. Part 2: Encoding and Framing
  4. Line Codes

Part 2 · Encoding and Framing · Foundation (F)

Line Codes

How bits become levels on a wire: NRZ, NRZI, Manchester and PAM4, and what each buys in transitions, bandwidth and noise margin.

On this page

    Note

    Level: Foundation (F) · Answers: Where does a message start and end?

    Before this: Part 2 overview, Embedded Clocks and Clock Recovery

    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 answer, and why it breaks

    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.

    The idea: map bits to levels and changes on purpose

    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.

    Build it: one bit stream, three codes

    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.

    tb.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    // 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
    endmodule

    Watch it run

    How 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.

    Worked numbers: bandwidth and margin

    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:

    Diagram
    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).

    The design space

    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

    How real protocols chose

    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.

    What breaks

    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.

    Why this matters

    Senior lens. The line code is where the physical channel meets the protocol, so choose it with the channel's numbers in hand: bandwidth, loss at the Nyquist frequency, noise, and whether it is AC-coupled. Then check what the code leaves undone (NRZ and NRZI both leave run length to someone else) and make sure the next layer, stuffing, a block code or scrambling, really closes it. Moving to more levels, as PAM4 does, is a trade of margin for bandwidth that only works with error correction designed in from the start.

    Interview

    Asked: "Why does PAM4 double the data rate but hurt noise margin?"

    Weak answer: Because it is faster.

    Strong answer: PAM4 sends four levels per symbol, so each symbol carries two bits: the same bit rate needs half the symbol rate, which suits bandwidth-limited channels. But the four levels share the same voltage swing, so the gap between adjacent levels is a third of NRZ's, about 9.5 dB less, and the same noise causes many more errors. That's why PAM4 links rely on forward error correction.

    Follow-ups: (1) Which line code gives a transition in every bit, and what does it cost? (2) Why does USB need bit stuffing on top of NRZI?

    Check yourself In NRZI as USB uses it, which data pattern produces no transitions?

    A run of 1s, because a 1 means "no change". USB stuffs a 0 after six 1s to force a transition.

    Check yourself Why is Manchester always DC-balanced?

    Every bit is one high half and one low half, so the line spends exactly half its time at each level, whatever the data.

    Check yourself A channel loses too much at high frequencies for 32 GBd NRZ. What does PAM4 change?

    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.

    Key takeaways

    • 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.

    Keep going

    Next: Lesson 2.2, Framing Basics. The receiver now has bits. How does it find where a message starts and where it ends?

    Related topics

    • Part 2 overview: Encoding and Framing

    Previous

    26 / 234

    Next: Framing Basics

    Up next

    Up nextFraming Basics

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Packet Anatomy
    • Bit Stuffing
    • DC Balance and Running Disparity
    clkbit_nownrznrzimanch
    Simulation output: the bits 0 1 0 0 0 1 1 1 0 0 in three line codes. Each bit lasts two slots.