Skip to content

PCIe · Module 17

Electrical Concepts — What Is Actually on the Wire

A lane carries a voltage waveform through a frequency-dependent channel, with the clock buried in its own transitions. Differential signalling, embedded timing, loss, reflection and jitter decide whether a symbol survives the trip.

Chapter 17.4 treated the channel as a filter with a pulse response and showed how to undo its memory. It never said what the channel physically is, or why it has a pulse response at all.

This chapter goes underneath. A logical symbol is not what travels. What travels is a voltage difference on a pair of conductors, through copper and connectors and vias, arriving attenuated, delayed, reflected and smeared — and with no clock alongside it, because PCIe does not send one.

What is actually on the wire, why differential signalling helps, where the timing information lives, and which physical effects decide whether the receiver can still tell one symbol from another?

1. The Verified Structure

2. The Boundary, One Last Time

3. Differential Signalling

A lane's transmit side is two conductors carrying opposite-going voltages, and the receiver responds to the difference between them.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
TX+   ▔▔╲__________╱▔▔▔▔
TX−   __╱▔▔▔▔▔▔▔▔▔▔╲____
 
difference = TX+ − TX−   ← what the receiver decides on

A lane is bidirectional using two pairs — §1's source: "a pair of differential transmit signals and a pair of differential receive signals."

So a x4 Link is not four wires. It is four transmit pairs and four receive pairs — sixteen conductors — which is the physical cost Chapter 6.8 weighs and the reason width is expensive in a way speed is not.

4. The Clock Is Inside the Data

PCIe does not forward a data clock alongside each lane. The receiver must work out when to sample from the incoming stream itself, using the CDR (Chapter 17.2 §2).

That only works if the stream has transitions to lock to. A long run of identical symbols carries no edges, and a receiver's timing estimate drifts during it.

5. Unit Interval

The UI is the time allocated to one transmitted symbol.

For NRZ signalling, one symbol carries one bit, so one UI is one bit period. For PAM4 one symbol carries two bits (§12), so a UI and a bit period are no longer the same thing — which is exactly why the distinction is worth naming.

And it is why PCIe rates are quoted in GT/s rather than Gb/s. Transfers per second is a statement about symbols; bits per second depends additionally on how many bits a symbol carries and what the encoding overhead is. Chapter 6.7 does that arithmetic; this chapter only insists on the distinction.

Illustrative, to give the number a feel: at 8 GT/s the UI is 125 ps; at 32 GT/s it is 31.25 ps. A signal travels a few millimetres of PCB in that time, and every effect in §§6–9 has to fit inside it.

6. Channel Loss

A PCB trace, a via, a connector and a package are not transparent. They attenuate, and they attenuate more at higher frequencies.

A sharp edge is made of high-frequency content. Remove that content and the edge slows down — which is Chapter 17.4 §2's pulse spreading, seen from its cause rather than its effect.

Where the loss comes from, without deriving any of it: conductor loss rises with frequency as current crowds toward the surface; dielectric loss rises with frequency as the board material absorbs energy; and vias, connectors and stubs add discontinuities that behave badly at the frequencies where their dimensions become significant relative to the wavelength.

7. Reflection and Impedance

A signal travelling down a controlled-impedance path continues cleanly only while the impedance stays constant. At any discontinuity, part of the energy reflects and travels back.

Sources of discontinuity: vias and their stubs, connector transitions, package-to-board transitions, changes in trace geometry, and imperfect termination at the far end.

Why reflections close the eye: reflected energy arrives later, superimposed on symbols that are still to come. That is indistinguishable from ISI at the sampling instant — same effect, different cause.

Which is why termination matters and is specified. §1's source: the receiver provides "on-die differential termination … programmed to 85Ω, 100Ω, or 150Ω", and the transmitter drives "a 100Ω differential load." The receiver presents a matched impedance so the arriving energy is absorbed rather than sent back — and the programmability exists because the right value depends on the channel it is terminating.

Do not read those numbers as PCIe requirements. They are that device's options, quoted because they make the mechanism concrete.

8. Crosstalk

A lane's electrical quality is not independent of what is routed near it.

An adjacent aggressor couples energy into a victim through the fields around both. The coupling depends on separation, run length, geometry and the frequencies involved — and it is worst when many nearby lanes switch simultaneously, which on a wide Link is the normal case rather than a corner.

9. Jitter

Jitter is variation in when edges occur — and its consequence is horizontal rather than vertical: it eats the timing margin the receiver has to work within.

Two families, at the level this chapter needs. Deterministic components come from identifiable mechanisms — data-dependent jitter from ISI, periodic jitter from a coupled interferer, duty-cycle distortion. They are bounded: there is a worst case, and it can be characterised. Random components arise from noise processes and are unbounded in principle — they are described statistically, and the question is not "how bad can it get" but "how often does it exceed X."

That distinction is why link budgets are probabilistic, and why §13's BER is a rate rather than a yes/no.

The RTL-facing consequence is simple even though the analysis is not: jitter moves the optimal sampling instant, and the CDR must track it while the eye must stay open long enough for the residual error to be tolerable. Chapter 17.4's data-dependent jitter is the part equalization can attack, because ISI is deterministic and computable. The random part cannot be equalized away.

10. Common Mode and Coupling

A differential receiver decides on the difference, but it is a real circuit with a finite input range. §1's figure names VICM — an input common-mode voltage — beside the termination and the CTLE, which is that circuit's operating point.

So the two ends must agree about more than the difference. The transmitter's common-mode level and the receiver's acceptable range must overlap, or the receiver is being asked to operate outside its design point regardless of how good the differential signal is.

Capacitive coupling is one way to decouple them. §1's source: "Supports AC and DC coupling modes with configurable transmit common-mode voltage." Series capacitors pass the signal's transitions and block the DC level, so each end can sit at whatever common-mode voltage suits it.

11. Detecting Nothing At All

Before any of this matters, a receiver must be able to tell whether anything is present.

§1's source gives two distinct mechanisms. Signal detect"identifies whether the signal level present at the receiver input buffer is above the signal detect threshold voltage needed to trip or activate the receiver input, which prevents false activity on the receiver path." And loss of signal detection, "included within the receiver path."

And a transmitter must be able to go quiet deliberately: "The output buffer also has electrical idle capabilities used to orderly quiet the link transmitters."

12. NRZ and PAM4

NRZ has two levels and one decision threshold. A symbol is above or below it, and one symbol carries one bit.

PAM4 has four levels and three thresholds. A symbol carries two bits, so the same symbol rate moves twice the data — which is how Gen6 (Chapter 5.6) increases throughput without doubling the symbol rate.

13. Error Rate, and What Replay Cannot Do

At this layer, errors are a rate rather than an event. A physical link does not "work" or "fail" — it has a probability of misinterpreting a symbol, and the design target is to keep that probability low enough that the layers above are not overwhelmed.

PCIe has real recovery mechanisms above the PHY. LCRC detects a corrupted TLP and the Link retries it (Chapter 14.3); Gen6 adds forward error correction below that.

14. The Chain, End to End

Digital transmit logic feeds a serializer, which feeds a transmit driver with configurable swing and emphasis. The signal passes through the package, the printed circuit board channel and a connector, then more board and package on the far side, reaching a receiver front end with termination and continuous-time equalization. From there it goes to clock and data recovery with decision feedback, then to the deserializer and the digital receive logic.TX digitalChapters 17.1Driver andemphasisswing, de-emphasis —analogPackage, PCB,connectorloss, reflection,crosstalkRX front endtermination, VICM,CTLECDR and slicertiming recovery, DFERX digitalChapter 17.2waveformdegraded12
Figure 1 — one lane's transmit path to the far receiver, marking where each domain's responsibility begins and ends. The digital blocks at either end are ordinary synchronous logic. Everything between the driver and the receiver front end is analog and physical: this chapter's subject, and the part no RTL simulator can predict.

Three things to read out of the figure.

Only the two end blocks are RTL. Everything between them is analog, physical, or dedicated circuitry — §2's boundary, drawn one final time for the whole module.

The channel block is where margin is spent, and it is one block only for drawing convenience: package, board, connector, board and package again are five transitions, each with its own loss and its own discontinuity (§§6–7).

And the two arrows either side of the channel are drawn differently. What leaves the driver is a designed waveform; what arrives is a degraded one, and everything from the front end onward is an attempt to recover the decision that was originally encoded in it.

15. RTL — PHY Status Monitor

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE. Capture and synchronize PHY status for one lane.
// THE STATUS SIGNALS ARE VENDOR-VERIFIED as things a PHY reports
// (section 1: signal detect above a threshold, loss-of-signal detection,
// electrical idle). The synchronizer depth and the sticky policy are
// ILLUSTRATIVE IMPLEMENTATION POLICY.
module phy_status_sync #(
  // Two flops is the usual teaching minimum; real designs choose this
  // from their MTBF target and the technology.
  parameter int SYNC_STAGES = 2
) (
  input  logic clk,                  // local digital domain
  input  logic rst_n,
 
  // ---- LEVEL status from the PHY macro domain ---------------------------
  // LEVELS ONLY. A two-flop synchronizer is safe for a level that is held
  // long enough to be sampled; it is NOT safe for an arbitrary one-cycle
  // pulse, which can be missed entirely (section 21).
  input  logic phy_cdr_lock,
  input  logic phy_signal_detect,
  input  logic phy_electrical_idle,
 
  output logic cdr_lock,
  output logic signal_detect,
  output logic electrical_idle,
 
  // Edges derived AFTER synchronization, so they are single-cycle pulses
  // in the local domain and safe for downstream capture logic.
  output logic cdr_lock_lost,
  output logic signal_lost
);
 
  generate
    if (SYNC_STAGES < 2) $error("SYNC_STAGES must be at least 2");
  endgenerate
 
  logic [SYNC_STAGES-1:0] lock_sync, sd_sync, ei_sync;
  logic                   lock_q, sd_q;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      lock_sync <= '0; sd_sync <= '0; ei_sync <= '0;
      lock_q <= 1'b0; sd_q <= 1'b0;
    end else begin
      // Shift registers, one per signal. Never share a synchronizer chain
      // between signals -- correlated metastability is not a thing you can
      // reason about afterwards.
      lock_sync <= {lock_sync[SYNC_STAGES-2:0], phy_cdr_lock};
      sd_sync   <= {sd_sync  [SYNC_STAGES-2:0], phy_signal_detect};
      ei_sync   <= {ei_sync  [SYNC_STAGES-2:0], phy_electrical_idle};
 
      // Previous values, for edge detection in the LOCAL domain.
      lock_q <= lock_sync[SYNC_STAGES-1];
      sd_q   <= sd_sync  [SYNC_STAGES-1];
    end
  end
 
  assign cdr_lock        = lock_sync[SYNC_STAGES-1];
  assign signal_detect   = sd_sync  [SYNC_STAGES-1];
  assign electrical_idle = ei_sync  [SYNC_STAGES-1];
 
  // FALLING EDGES ONLY. A loss is the event worth capturing; a
  // reacquisition is visible from the level itself.
  assign cdr_lock_lost = lock_q && !cdr_lock;
  assign signal_lost   = sd_q   && !signal_detect;
 
endmodule

Classification: synthesizable.

Architecture. One independent synchronizer chain per signal, plus locally-derived edges.

Why the edges are derived after synchronization, not before: an edge detected in the PHY domain would be a one-cycle pulse that must then cross domains — and a two-flop synchronizer can miss a pulse entirely if it does not span a sampling edge. Synchronizing the level first and detecting the edge locally makes the pulse a local-domain object by construction.

Failure — four. Sharing one synchronizer chain between signals. Detecting edges before synchronizing and then crossing a pulse. A single flop rather than two or more. And treating electrical_idle as an error rather than as a state (§11).

16. RTL — Sticky Fault Capture and Lane Health

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE. Per-lane sticky fault capture with an explicit clear.
// THE FAULT SOURCES are vendor-verified conditions (section 1). The
// sticky-dominates-clear policy, the counter saturation and the health
// reduction are ILLUSTRATIVE IMPLEMENTATION POLICY.
module phy_fault_capture #(
  parameter int LANES  = 16,
  parameter int CNT_W  = 8,
  // GUARDED. $clog2(1) is zero; LANES = 1 is a legal configuration.
  parameter int LANE_W = (LANES <= 1) ? 1 : $clog2(LANES)
) (
  input  logic clk,
  input  logic rst_n,
 
  // ---- Per-lane events, already synchronized (section 15) ---------------
  input  logic [LANES-1:0] cdr_lock,
  input  logic [LANES-1:0] cdr_lock_lost,
  input  logic [LANES-1:0] signal_detect,
  input  logic [LANES-1:0] signal_lost,
  input  logic [LANES-1:0] eq_ok,
  input  logic [LANES-1:0] eq_fail,
 
  // ---- Software-style clear ---------------------------------------------
  input  logic             clear_valid,
  input  logic [LANES-1:0] clear_mask,
 
  // ---- Sticky status -----------------------------------------------------
  output logic [LANES-1:0] sticky_lock_lost,
  output logic [LANES-1:0] sticky_signal_lost,
  output logic [LANES-1:0] sticky_eq_fail,
  output logic [CNT_W-1:0] lock_loss_count [LANES],
 
  // ---- Local diagnostics -------------------------------------------------
  output logic [LANES-1:0] lane_healthy,
  output logic             phy_healthy
);
 
  generate
    if (LANES  < 1) $error("LANES must be at least 1");
    if (CNT_W  < 1) $error("CNT_W must be at least 1");
    if (LANE_W < 1) $error("LANE_W must be at least 1");
  endgenerate
 
  logic [LANES-1:0] lk_q, sl_q, ef_q;
  logic [CNT_W-1:0] cnt_q [LANES];
 
  assign sticky_lock_lost   = lk_q;
  assign sticky_signal_lost = sl_q;
  assign sticky_eq_fail     = ef_q;
  assign lock_loss_count    = cnt_q;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      lk_q <= '0; sl_q <= '0; ef_q <= '0;
      for (int i = 0; i < LANES; i++) cnt_q[i] <= '0;
    end else begin
      for (int i = 0; i < LANES; i++) begin
        // ============================================================
        // SET DOMINATES CLEAR, DECLARED.
        //
        // A fault arriving in the same cycle as its clear must SURVIVE.
        // The opposite policy loses exactly the events that occur while
        // software is reading the register -- which is when faults are
        // most likely to be arriving, and makes the register untrustworthy
        // precisely when it is being used (section 20, mutation 2).
        // ============================================================
        if (cdr_lock_lost[i])                      lk_q[i] <= 1'b1;
        else if (clear_valid && clear_mask[i])     lk_q[i] <= 1'b0;
 
        if (signal_lost[i])                        sl_q[i] <= 1'b1;
        else if (clear_valid && clear_mask[i])     sl_q[i] <= 1'b0;
 
        if (eq_fail[i])                            ef_q[i] <= 1'b1;
        else if (clear_valid && clear_mask[i])     ef_q[i] <= 1'b0;
 
        // SATURATING, NOT WRAPPING. A counter that wraps to zero reports
        // "no losses" after exactly 256 of them, which is the reading an
        // engineer is least able to distinguish from a healthy lane.
        if (cdr_lock_lost[i]) begin
          if (cnt_q[i] != {CNT_W{1'b1}}) cnt_q[i] <= cnt_q[i] + CNT_W'(1);
        end else if (clear_valid && clear_mask[i]) begin
          cnt_q[i] <= '0;
        end
      end
    end
  end
 
  // ==================================================================
  // LOCAL DIAGNOSTICS ONLY -- NOT A LINK STATE.
  //
  // "Healthy" here means this lane's local physical conditions are all
  // satisfied. It does NOT mean the Link is trained, configured, or in a
  // state where traffic is legal. Chapter 18.1 owns Link state, and P9
  // asserts that this block never claims otherwise.
  // ==================================================================
  always_comb
    for (int i = 0; i < LANES; i++)
      // AND, not OR. Every condition is necessary; none is sufficient.
      lane_healthy[i] = cdr_lock[i] && signal_detect[i] && eq_ok[i];
 
  assign phy_healthy = &lane_healthy;
 
endmodule

Classification: synthesizable.

Architecture. Per-lane sticky bits with set dominating clear, a saturating loss counter, and a health reduction that is an AND of necessary conditions.

The three policy choices are all deliberate, and each has an opposite that is worse.

ChoiceThe alternative, and why it is worse
set dominates clearclear-wins loses faults arriving while software reads — the worst moment
saturating counterwrapping reports zero after 256 losses; indistinguishable from healthy
AND of conditionsOR reports a lane healthy on signal detect alone (§11)

Failure — five. Clear winning loses concurrent faults. A wrapping counter hides sustained trouble. OR instead of AND treats presence as readiness. Indexing with an unchecked lane id marks the wrong lane. And exporting phy_healthy as a Link state confuses local diagnostics with Chapter 18.1's LTSSM.

17. Verification Model — Sampling a Real Pulse

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// VERIFICATION-ONLY. Sampled channel response and jittered sampling.
// Reuses the Q1.7 fixed-point discipline of Chapter 17.4 section 15 --
// signed types, a widened accumulator, one rounding point.
package si_model_pkg;
 
  localparam int W    = 8;
  localparam int FRAC = 7;
  localparam int ONE  = 1 << FRAC;
  localparam int ACC_W = 2*W + 3;
 
  typedef logic signed [W-1:0]     samp_t;
  typedef logic signed [ACC_W-1:0] acc_t;
 
  function automatic samp_t rnd(input acc_t a);
    acc_t r;
    r = (a + acc_t'(1 << (FRAC-1))) >>> FRAC;   // ARITHMETIC shift
    if (r >  acc_t'( (1<<(W-1)) - 1 )) return samp_t'( (1<<(W-1)) - 1 );
    if (r <  acc_t'(-(1<<(W-1))     )) return samp_t'(-(1<<(W-1)));
    return samp_t'(r);
  endfunction
 
  // The pulse response sampled at UI/4 resolution, so a sampling instant
  // displaced by jitter lands on a DIFFERENT value -- which is the whole
  // point of the model. Index 4 is the ideal centre.
  // ILLUSTRATIVE VALUES, not a measured channel.
  localparam int SUB = 4;                        // sub-samples per UI
  localparam int PLEN = 12;
 
  function automatic samp_t pulse_at(input int idx);
    // A rising edge, a peak at the centre, and a decaying tail.
    case (idx)
      0,1     : return samp_t'(  2);
      2       : return samp_t'( 20);
      3       : return samp_t'( 76);
      4       : return samp_t'(120);             // ~0.94 -- the eye centre
      5       : return samp_t'(110);
      6       : return samp_t'( 70);
      7       : return samp_t'( 38);
      8       : return samp_t'( 20);
      9,10,11 : return samp_t'(  8);
      default : return samp_t'(  0);
    endcase
  endfunction
 
  // ==================================================================
  // JITTER MOVES THE SAMPLING INSTANT, NOT THE SIGNAL.
  //
  // This is the physical content of "horizontal margin": the waveform is
  // what it is, and the question is how much the sampling point can move
  // before the sampled value stops being decodable.
  // ==================================================================
  function automatic samp_t sample_with_jitter(input int ideal_idx,
                                               input int jitter_sub);
    int idx;
    idx = ideal_idx + jitter_sub;
    if (idx < 0)     idx = 0;
    if (idx >= PLEN) idx = PLEN - 1;
    return pulse_at(idx);
  endfunction
 
endpackage

Classification: verification-only.

The sub-sample resolution is the point. Sampling the pulse only at UI centres cannot show a horizontal effect at all — a model with one sample per UI can represent ISI but not jitter. Four sub-samples per UI make a displaced sampling instant land on a genuinely different value.

Worked numbers from this model, with the ideal centre at index 4:

Sampling offsetSampled valueFraction of peak
0 (ideal)120100%
−1 sub-UI7663%
+1 sub-UI11092%
−2 sub-UI2017%

Read the asymmetry. Sampling early costs far more than sampling late on this pulse shape, because the rising edge is steeper than the decay. A CDR that locks slightly early on this channel loses margin much faster than one that locks slightly late — which is a real effect and one no amount of vertical margin compensates for.

And that is the concrete meaning of horizontal eye closure: the range of sampling offsets over which the value remains decodable.

18. Assertions

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SVA over phy_status_sync and phy_fault_capture. These assert the
// DIGITAL WRAPPER's contract. They assert NOTHING about eye margin,
// jitter, BER, CDR lock time or channel behaviour -- none of which is
// modelled and none of which RTL can predict (section 2).
 
// ---- ENVIRONMENT ------------------------------------------------------
// A1: the PHY status inputs are LEVELS held long enough to be sampled by
// a two-flop synchronizer. A one-cycle pulse would need a different
// mechanism (section 21).
assume property (@(posedge clk) disable iff (!rst_n)
  $rose(phy_cdr_lock) |=> phy_cdr_lock);
// A2: clear_mask names existing lanes.
assume property (@(posedge clk) disable iff (!rst_n)
  clear_valid |-> ((clear_mask & ~{LANES{1'b1}}) == '0));
 
// ---- SYNCHRONIZATION --------------------------------------------------
 
// P1: the synchronized output is the input delayed by the chain depth --
// never a direct combinational path from the PHY domain.
property p_synchronized_not_direct;
  @(posedge clk) disable iff (!rst_n)
  cdr_lock == $past(phy_cdr_lock, SYNC_STAGES);
endproperty
a_sync : assert property (p_synchronized_not_direct);
 
// P2: the loss pulse is exactly one cycle, in the LOCAL domain.
property p_loss_is_single_cycle;
  @(posedge clk) disable iff (!rst_n)
  cdr_lock_lost |=> !cdr_lock_lost;
endproperty
a_pulse : assert property (p_loss_is_single_cycle);
 
// P3: a loss pulse corresponds to an actual falling edge.
property p_loss_on_falling_edge;
  @(posedge clk) disable iff (!rst_n)
  cdr_lock_lost |-> ($past(cdr_lock) && !cdr_lock);
endproperty
a_edge : assert property (p_loss_on_falling_edge);
 
// ---- STICKY CAPTURE ---------------------------------------------------
 
// P4: a sticky bit PERSISTS until explicitly cleared. The whole point of
// the register: an event that has already passed must still be readable.
generate for (genvar i = 0; i < LANES; i++) begin : g_sticky
  property p_sticky_persists;
    @(posedge clk) disable iff (!rst_n)
    (sticky_lock_lost[i] && !(clear_valid && clear_mask[i]))
      |=> sticky_lock_lost[i];
  endproperty
  a_persist : assert property (p_sticky_persists);
 
  // P5: SET DOMINATES CLEAR. A fault arriving in the same cycle as its
  // clear survives -- section 16's declared policy, and the one that
  // matters because software reads while faults arrive.
  property p_set_beats_clear;
    @(posedge clk) disable iff (!rst_n)
    (cdr_lock_lost[i] && clear_valid && clear_mask[i])
      |=> sticky_lock_lost[i];
  endproperty
  a_set_wins : assert property (p_set_beats_clear);
 
  // P6: a lane's event only ever sets that lane's bit.
  property p_no_cross_lane;
    @(posedge clk) disable iff (!rst_n)
    (!cdr_lock_lost[i] && !$past(sticky_lock_lost[i]))
      |-> !sticky_lock_lost[i];
  endproperty
  a_lane : assert property (p_no_cross_lane);
end endgenerate
 
// P7: the counter SATURATES rather than wrapping -- it never decreases
// except on a clear.
generate for (genvar i = 0; i < LANES; i++) begin : g_cnt
  property p_counter_saturates;
    @(posedge clk) disable iff (!rst_n)
    (lock_loss_count[i] < $past(lock_loss_count[i]))
      |-> $past(clear_valid && clear_mask[i]);
  endproperty
  a_sat : assert property (p_counter_saturates);
end endgenerate
 
// P8: the counter increments only on a represented event.
generate for (genvar i = 0; i < LANES; i++) begin : g_inc
  property p_counter_event_only;
    @(posedge clk) disable iff (!rst_n)
    (lock_loss_count[i] > $past(lock_loss_count[i])) |-> $past(cdr_lock_lost[i]);
  endproperty
  a_inc : assert property (p_counter_event_only);
end endgenerate
 
// ---- HEALTH -----------------------------------------------------------
 
// P9: THE SCOPE PROPERTY. Lane health requires EVERY condition -- signal
// detect alone is never sufficient (section 11).
generate for (genvar i = 0; i < LANES; i++) begin : g_health
  property p_health_requires_all;
    @(posedge clk) disable iff (!rst_n)
    lane_healthy[i] |-> (cdr_lock[i] && signal_detect[i] && eq_ok[i]);
  endproperty
  a_health : assert property (p_health_requires_all);
end endgenerate
 
// P10: this block NEVER drives Link state. Local diagnostics only --
// Chapter 18.1 owns the LTSSM, and a refactor that wires these together
// is caught here rather than discovered in the lab.
property p_health_is_not_link_state;
  @(posedge clk) disable iff (!rst_n)
  $changed(phy_healthy) |-> $stable(dut_ltssm.state);
endproperty
a_scope : assert property (p_health_is_not_link_state);
 
// P11: electrical idle is a STATE, not a fault. It must not set any
// sticky bit on its own (section 11).
property p_idle_is_not_a_fault;
  @(posedge clk) disable iff (!rst_n)
  (electrical_idle && !cdr_lock_lost && !signal_lost && !eq_fail)
    |=> ($stable(sticky_lock_lost) && $stable(sticky_signal_lost));
endproperty
a_idle : assert property (p_idle_is_not_a_fault);
 
// P12: reset clears all diagnostic history.
property p_reset;
  @(posedge clk)
  !rst_n |=> ((sticky_lock_lost == '0) && (sticky_signal_lost == '0)
           && (sticky_eq_fail == '0));
endproperty
a_reset : assert property (p_reset);

P5 is the property that makes the register trustworthy. A fault arriving in the same cycle as its clear must survive — because software reads these registers precisely when faults are arriving, and a clear-wins policy silently discards exactly the sample the engineer was trying to take.

P7 and P8 are the counter pair. P7 forbids the wrap that would report zero after 256 losses; P8 forbids counting anything that was not a represented event, which catches a counter wired to a level rather than an edge.

P10 is a cross-module scope property, in the style Chapter 15.5 §11 established. It cannot be written inside either module, and it is what prevents a later refactor from quietly making a local diagnostic drive Link state.

No electrical property is asserted anywhere, and that is the honest position: this RTL observes an analog macro and reports what it says. It cannot verify the analog behaviour and does not pretend to.

19. Verification and Fault Injection

The scoreboard maintains its own sticky-bit and counter model from observed events, and never reads the DUT's registers as its expected value.

Synchronization

  • Level asserted and held — verify it appears after exactly SYNC_STAGES cycles (P1).
  • A falling edge — verify a single-cycle local pulse (P2, P3).
  • Rapid toggling faster than the chain depth — verify no spurious pulses and no direct path.
  • SYNC_STAGES = 2 and 3.

Sticky capture

  • One fault per lane, each lane in turn — verify only that lane's bit sets (P6).
  • Fault, then clear — verify the bit clears.
  • Fault and clear in the same cycle — verify the fault survives (P5). Required.
  • Two lanes faulting in the same cycle — verify both set.
  • clear_mask selecting a subset — verify unselected lanes retain their bits.
  • Counter to saturation — verify it holds at all-ones rather than wrapping (P7). Required.
  • Reset with faults set (P12).

Health

  • Signal detect only — verify not healthy (P9). Required, and the mutation-8 test.
  • Signal detect and CDR lock, no eq_ok — verify not healthy.
  • All three — verify healthy.
  • One lane unhealthy on x8 — verify phy_healthy is low.
  • Electrical idle asserted with no fault — verify no sticky bit sets (P11).
  • LANES = 1, 2, 4, 8 — including the $clog2(1) corner.

Numerical model

  • Sample at the ideal instant — verify the peak value.
  • Offsets of ±1 and ±2 sub-UI — verify the values in §17's table, and verify the early/late asymmetry.
  • A flat pulse (no channel) — verify jitter has little effect, which is the control case.

Mutations

#MutationCaught byLab symptom
1sticky bit clears itself next cycleP4faults never observed; register always reads clean
2clear wins over a same-cycle faultP5faults lost exactly while software is polling
3one lane's event sets another lane's bitP6debugging directed at a healthy lane
4cdr_lock polarity invertedP9 and the health testlane reported healthy when unlocked
5health computed with OR instead of ANDP9any one condition reports the lane good
6edge detected before synchronizingP1, P2pulses occasionally missed; intermittent lost faults
7counter wraps instead of saturatingP7reads zero after 256 losses — looks perfect
8signal detect alone treated as link-readyP9Link reports ready and drops everything
9electrical idle marked as a faultP11fault log full of normal power management
10equalization failure ignored in healthP9lane reported good while failing at rate
11one synchronizer chain shared between signalsP1correlated metastability; unreproducible glitches
12counter increments on the level, not the edgeP8counts cycles rather than losses; huge numbers

20. Debugging

Symptom → domain → instrument → distinguishing experiment. This is where the module's tools finally separate.

Margin, before anything else. §6: the frequency content that matters roughly doubles with the rate, so a channel with margin at Gen3 may have none at Gen5.

Inspect: the equalization result (Chapter 17.4), CDR lock stability, the eye monitor if the device exposes one, and the negotiated speed.

The distinguishing experiment: change the channel — shorter cable, different slot. If Gen5 comes up, the design is working and the channel was the limit.

One lane on x8 repeatedly drops

Lane-specific, which eliminates everything Link-wide.

Inspect: that lane's sticky_lock_lost and lock_loss_count (§16 exists for exactly this), and its equalization setting relative to its neighbours'.

The distinguishing experiment: move the device to a different slot or reverse the lane order if the board permits. If the fault follows the physical position, it is channel, connector or crosstalk (§8). If it follows the logical lane, it is the control plane (Chapter 17.4 §19).

Replay rate rises under thermal stress

Electrical margin degrading — not a Data Link Layer implementation bug.

Inspect PHY error counters and lock-loss counts before the replay logic. §13: replay is designed for rare errors, and a rising rate means the channel is producing more than rare.

The distinguishing experiment: correlate the replay rate against temperature while watching the PHY counters. If PHY errors rise with temperature and replays follow, the cause is physical. If replays rise while PHY error counters stay flat, then — and only then — look at the reliability logic.

Protocol traces clean, BER measurement poor

Different observation domains, and the answer is that both are correct.

A protocol analyzer only sees packets that were received well enough to be decoded. A marginal channel that is being successfully corrected produces clean traces and a poor raw error rate simultaneously.

This is a warning, not a contradiction. The system works now, with no margin — and margin is what absorbs temperature, ageing, voltage variation and unit-to-unit spread. A design that ships at zero margin fails in the field, and the protocol trace will never say so.

21. Clock Domains

§15 uses a two-flop synchronizer, and it is correct for what it carries — levels.

A level that is held for many cycles will be sampled. Metastability may delay the observed transition, but the value is stable long enough that the resolved output is right within a cycle or two.

22. Module 17 Complete

Five chapters, one path.

17.1Serializationa wide parallel representation becomes a timed stream per lane
17.2Deserializationtiming, boundaries, elastic absorption and lane identity recover it
17.3Link Trainingtwo ports negotiate a usable width, speed and lane configuration
17.4Equalizationthree mechanisms undo the channel's memory
17.5Electrical Conceptswhat is actually on the wire, and what consumes the margin

The through-line is margin. Serialization and deserialization are about preserving information across a boundary; training is about discovering how much the physical reality supports; equalization is about recovering what the channel took; and this chapter is about where it goes.

And one boundary held throughout. The digital side owns framing, distribution, buffering, control and status. Dedicated circuitry owns the conversion, the recovery and the analog behaviour. Every chapter said so in the same words, because a tutorial that blurs it teaches something that will not survive contact with a real PHY.

What Module 17 never answered is who decides. Detection, training, normal operation, recovery, power states, disable and reset are all behaviourssomething must decide which one the Link is doing right now, and make sure the others are not.

Module 18 is that something.

23. Common Misconceptions

  • "Bits travel as perfect zeros and ones." A voltage waveform travels; the receiver decides (§1's model).
  • "Differential signalling rejects all noise." Rejection is good and finite, and depends on the pair being genuinely matched (§3).
  • "CDR is just a clock divider." It extracts timing from the data's own transitions, tracking ppm offset and spread spectrum (§4).
  • "Every bit must contain an edge." Encoding and scrambling bound run length; they do not eliminate runs (§4).
  • "Eye opening is a protocol field." It is a measured property of a waveform, not something in a packet (§20).
  • "Equalization fixes any channel." It recovers deterministic ISI. It cannot create margin that loss and noise have removed (Chapter 17.4 §7).
  • "Longer trace alone determines failure." Material, vias, connectors and reference continuity all spend from the same budget (§6).
  • "BER and replay count are the same." Raw symbol error rate versus a Link-layer consequence of it (§13).
  • "Signal detect means the Link is in L0." It means something is above a threshold. Everything else remains to be established (§11).
  • "An RTL simulator can predict eye margin." It cannot; §17 is illustrative arithmetic and says so (§2).
  • "x16 electrical quality is x1 repeated sixteen times." Neighbouring lanes are aggressors; margin is not uniform across the bundle (§8).
  • "PAM4 has the same vertical margin as NRZ." Four levels in a comparable swing means substantially smaller spacing per decision (§12).
  • "Electrical idle is a fault." It is a deliberate, orderly quiet (§11, P11).
  • "A clean protocol trace means the channel is healthy." It means errors are being corrected — possibly at zero margin (§20).

24. Understanding Check

25. What's Next

The wire carries a waveform, not bits. Differential signalling buys real but finite immunity; the clock lives inside the data and must be extracted while tracking ppm offset and deliberate spreading; and loss, reflection, crosstalk and jitter each spend from a margin budget nothing above the Physical Layer can replenish.

Module 17 is complete — from a parallel word to a lane and back, through a channel that fights the whole way.

Chapter 18.1 — LTSSM Overview opens the module that answers the question all five of these chapters left open: detection, training, normal operation, recovery, power states, disable and reset are behaviours — something has to decide which one the Link is doing, and enforce that the others are not.

The idea to carry forward: every layer above spends margin it cannot create.