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.
TX+ ▔▔╲__________╱▔▔▔▔
TX− __╱▔▔▔▔▔▔▔▔▔▔╲____
difference = TX+ − TX− ← what the receiver decides onA 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
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
// 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;
endmoduleClassification: 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
// 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;
endmoduleClassification: 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.
| Choice | The alternative, and why it is worse |
|---|---|
| set dominates clear | clear-wins loses faults arriving while software reads — the worst moment |
| saturating counter | wrapping reports zero after 256 losses; indistinguishable from healthy |
| AND of conditions | OR 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
// 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
endpackageClassification: 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 offset | Sampled value | Fraction of peak |
|---|---|---|
| 0 (ideal) | 120 | 100% |
| −1 sub-UI | 76 | 63% |
| +1 sub-UI | 110 | 92% |
| −2 sub-UI | 20 | 17% |
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
// 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_STAGEScycles (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_maskselecting 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_healthyis 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
| # | Mutation | Caught by | Lab symptom |
|---|---|---|---|
| 1 | sticky bit clears itself next cycle | P4 | faults never observed; register always reads clean |
| 2 | clear wins over a same-cycle fault | P5 | faults lost exactly while software is polling |
| 3 | one lane's event sets another lane's bit | P6 | debugging directed at a healthy lane |
| 4 | cdr_lock polarity inverted | P9 and the health test | lane reported healthy when unlocked |
| 5 | health computed with OR instead of AND | P9 | any one condition reports the lane good |
| 6 | edge detected before synchronizing | P1, P2 | pulses occasionally missed; intermittent lost faults |
| 7 | counter wraps instead of saturating | P7 | reads zero after 256 losses — looks perfect |
| 8 | signal detect alone treated as link-ready | P9 | Link reports ready and drops everything |
| 9 | electrical idle marked as a fault | P11 | fault log full of normal power management |
| 10 | equalization failure ignored in health | P9 | lane reported good while failing at rate |
| 11 | one synchronizer chain shared between signals | P1 | correlated metastability; unreproducible glitches |
| 12 | counter increments on the level, not the edge | P8 | counts cycles rather than losses; huge numbers |
20. Debugging
Symptom → domain → instrument → distinguishing experiment. This is where the module's tools finally separate.
Link is down at Gen5, fine at Gen3
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.1 | Serialization | a wide parallel representation becomes a timed stream per lane |
| 17.2 | Deserialization | timing, boundaries, elastic absorption and lane identity recover it |
| 17.3 | Link Training | two ports negotiate a usable width, speed and lane configuration |
| 17.4 | Equalization | three mechanisms undo the channel's memory |
| 17.5 | Electrical Concepts | what 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 behaviours — something 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.