Skip to content

PCIe · Module 18

Polling — Can We Read Each Other?

Detect proved somebody is there. Polling proves the two ports can actually communicate — bit lock, alignment, and consecutively recognised training sets, with no acknowledgement mechanism available to confirm any of it.

Chapter 18.2 established that somebody is there. That is a much weaker statement than it sounds.

A receiver can be electrically present and the lane still unusable: the channel may be too lossy to sample reliably (Chapter 17.5), the receiver may not find where units begin (Chapter 17.2 §3), or the far end may be transmitting something this port cannot recognise.

Polling is where those possibilities are eliminated. It is the first stage in which the two ports actually communicate — and the first in which signal quality stops being theoretical.

How do two ports get from "a receiver exists" to "we can reliably interpret each other's training stream", and why is that two independent problems rather than one?

1. The Verified Substates

2. Present Is Not Communicating

The gap between Detect's answer and Polling's requirement.

Detect establishedPolling must establish
Questionis a receiver terminated there?can I read what it sends?
Evidencean electrical measurementrecovered, aligned, recognised data
Depends onconnectivitysignal quality, equalization, the channel
Fails becauseno card, no power, bad contactloss, jitter, ISI, marginal margin

3. Lock and Alignment Are Generation-Specific

The registry names this chapter's purpose as bit-lock and symbol-lock, and that phrasing is exactly right for one generation family and wrong for another.

GenerationEncodingWhat "aligned" means
Gen1 / Gen28b/10b (5.1, 5.2)symbol boundary within the code stream
Gen3 – Gen5128b/130b (5.3)block boundary and its framing
Gen6changed again (5.6)changed again

Two things are common to all of them, and this chapter uses the general terms.

Timing recovery — the CDR extracts a sampling relationship from the stream's own transitions (Chapter 17.2 §2). This is the same problem in every generation.

Boundary recovery — deciding where a meaningful unit starts, by searching and confirming (Chapter 17.2 §§3–5). The mechanism differs; the necessity does not.

So this chapter says "lock" and "alignment" rather than committing to symbol or block language, and marks the generation where the distinction matters. "Polling equals comma alignment" is true for Gen1 and Gen2 and false from Gen3 onward — §17's misconception list, and a real source of confusion when a design that works at the base rate fails at a higher one for reasons that have nothing to do with commas.

4. Two Problems, Two Failures

The chapter's central separation, and the one that pays off in the lab.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
PHY lock and alignment        →  can I sample and frame the incoming stream?
Training recognition          →  does the framed stream contain valid training information?

5. What Training Ordered Sets Accomplish

TS1 and TS2 are the mechanism by which two ports communicate before any packet layer exists.

What they are for, at the level this chapter owns:

PurposeWhy it needs a dedicated mechanism
be recognisablea receiver searching for alignment needs something with known structure to find
carry training contextlink and lane identity, and control information the partner must agree on
be repeatedconfidence comes from repetition, because nothing can be acknowledged (§7)
signal intentcontrol flags let a port indicate what it is trying to do

TS1 and TS2 are distinct, and the distinction carries progress: a port transmits one kind while it is still establishing something, and the other once it is satisfied — so seeing the second kind from a partner is itself information about the partner's state.

6. Training Ordered Sets Are Not Packets

A recurring misconception with real consequences, worth stating flatly.

TS1 / TS2TLPDLLP
LayerPhysicalTransactionData Link
Scopetwo adjacent portsend to endone Link
Routed by a Switchnoyesno
Acknowledgednovia the Link's ACK
Carries a Requester IDnoyesno
Exists before a trained Linkyes — that is the pointnono

Every mechanism this curriculum built for reliability assumes a working Link. Sequence numbers (Chapter 14.5), ACK and NAK (Chapter 14.2), replay (Chapter 14.4) — none of it exists yet. Training ordered sets are what two ports have instead.

And a Switch does not forward them. Chapter 17.3 §15: each Link trains independently, and training state is never propagated. A Switch's downstream port trains against the Endpoint while its upstream port trains against the Root Complex, as two separate negotiations.

7. Confidence by Repetition

Polling does not progress because one observation looked correct.

Why not: with no reliable channel and no acknowledgement, a single correct-looking pattern could be a coincidence — random data that happened to match, or a momentarily-correct alignment on a marginal lane. Repetition is the only evidence available.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
qualifying observation  → count up
disqualifying observation → count resets
count reaches threshold → this is trustworthy

8. The Polling Subtree

The Polling subtree. Entry from Detect leads to Polling Active, where training ordered sets are exchanged and lock and alignment are established. From Polling Active the machine reaches Polling Configuration once qualification is satisfied, and from there proceeds toward the Configuration state. Polling Compliance is a separate branch from Polling Active used for compliance pattern transmission rather than normal link establishment. A failure in Polling returns toward Detect.fromDetectPolling.ActivePolling.ConfigurationtoConfigurationPolling.Complianceback toDetectreceiver presentreceiver presentqualifiedqualifiedproceedproceedcompliance pathcompliance pathfailurefailurefailurefailure
Figure 1 — the Polling subtree only. Polling.Active is where training ordered sets are exchanged; Polling.Configuration is reached once the port is satisfied and leads onward to Configuration. Polling.Compliance is an alternative path for compliance testing rather than normal Link establishment. Substate entry criteria, repetition counts and timeouts are not shown — they are not published in this chapter, and the full LTSSM map belongs to Chapter 18.1.

Three things to read, and one not to.

Polling.Active is where the work happens — §1's source describes it as the substate in which training ordered sets are transmitted, and it is where lock, alignment and qualification all occur.

Polling.Compliance is a branch, not a step. §1's source: "Transmitting compliance pattern." It exists for compliance testing rather than for establishing a working Link, which is why it is drawn to the side and gets one paragraph rather than a section.

Failure returns toward Detect, not onward. Same principle as Chapter 18.2 §6: a state machine that cannot establish what it needs goes back rather than proceeding on unfounded assumptions.

What not to read into it: the transition criteria are absent, deliberately (§1). This is a topology of the Polling subtree — entry conditions, counts and timeouts are not published here.

9. A Trace

Internal teaching signals, not PCIe pins. Illustrative threshold of 4.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
step             1     2     3     4     5     6     7     8     9    10
cdr_lock         0     1     1     1     1     1     1     1     1     1
rx_aligned       0     0     1     1     1     1     1     1     1     1
 
obs_valid        0     0     1     1     1     0     1     1     1     1
obs_kind         -     -   TS1   TS1   BAD    -   TS1   TS1   TS1   TS1
qual_count       0     0     1     2     0     0     1     2     3     4
threshold_met    0     0     0     0     0     0     0     0     0     1
 
polling_done     0     0     0     0     0     0     0     0     0     1
traffic_enabled  0     0     0     0     0     0     0     0     0     0

Read steps 1–3. Lock first, then alignment. These are separate signals because they are separate problems (§4) — and a design that had only one of them could not distinguish the two failures.

Read steps 3–5 — the reset. Two qualifying observations, then a bad one. The count returns to zero, not to one. Consecutive is the requirement (§7), and this is what marginal signal quality looks like: recognition that starts and does not continue.

Read steps 7–10. Four consecutive qualifying observations. threshold_met asserts on the fourth, not the third or the fifth — §15 verified that boundary exhaustively, and §15's counterexample is what happens when it is off by one.

And note traffic_enabled throughout. Zero. Normal traffic is not legal in Polling, and Chapter 18.1 §13's gate enforces it.

10. RTL — Ordered-Set Classifier and Observation Capture

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE. Normalized training-observation representation.
// THE KINDS are canonical (section 5: TS1 and TS2 are distinct ordered
// sets carrying training context). THE FIELD WIDTHS here are INTERNAL
// NORMALIZED widths, not verified normative field widths -- section 1
// declines to publish TS layouts, and Chapter 18.4 goes deeper on the
// link and lane fields.
package polling_pkg;
 
  typedef enum logic [2:0] {
    OS_NONE     = 3'd0,
    OS_TS1      = 3'd1,
    OS_TS2      = 3'd2,
    OS_OTHER    = 3'd3,   // a training set this port does not qualify on
    OS_INVALID  = 3'd4    // structurally malformed
  } os_kind_e;
 
  // INTERNAL normalized widths. A design with verified normative widths
  // substitutes them here and nowhere else.
  localparam int LINK_W = 9;
  localparam int LANE_W = 5;
  localparam int RATE_W = 4;
  localparam int CTRL_W = 8;
 
  typedef struct packed {
    logic               valid;
    os_kind_e           kind;
    logic [LINK_W-1:0]  link_num;
    logic [LANE_W-1:0]  lane_num;
    logic [RATE_W-1:0]  rate;
    logic [CTRL_W-1:0]  ctrl;
  } training_obs_t;
 
  // ONLY TS1 AND TS2 QUALIFY. An unrecognised or malformed training set
  // must never count toward progress (section 12, P5).
  function automatic bit obs_qualifies(input training_obs_t o);
    return o.valid && ((o.kind == OS_TS1) || (o.kind == OS_TS2));
  endfunction
 
  function automatic bit obs_disqualifies(input training_obs_t o);
    return o.valid && ((o.kind == OS_OTHER) || (o.kind == OS_INVALID));
  endfunction
 
endpackage
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
import polling_pkg::*;
 
// SYNTHESIZABLE. Capture one training observation whole, and hold it while
// the consumer is busy.
// THE PARSER CANNOT BE BACKPRESSURED -- observations arrive from a
// continuously running receive path (Chapter 17.2 section 9). So an
// observation that cannot be held is REPORTED, never silently dropped.
module training_obs_capture (
  input  logic clk,
  input  logic rst_n,
 
  // ---- From the normalized ordered-set parser. NO `ready`. --------------
  input  logic          parse_valid,
  input  os_kind_e      parse_kind,
  input  logic [LINK_W-1:0] parse_link,
  input  logic [LANE_W-1:0] parse_lane,
  input  logic [RATE_W-1:0] parse_rate,
  input  logic [CTRL_W-1:0] parse_ctrl,
 
  // ---- Entering Polling clears any observation from an earlier state ----
  input  logic          state_entry,
 
  // ---- To the progress controller ----------------------------------------
  output training_obs_t obs,
  input  logic          obs_ready,
 
  output logic          obs_overflow
);
 
  training_obs_t o_q;
  logic          ovf_q;
 
  assign obs          = o_q;
  assign obs_overflow = ovf_q;
 
  wire fire     = o_q.valid && obs_ready;
  wire can_take = !o_q.valid || obs_ready;
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      o_q <= '0; ovf_q <= 1'b0;
    end else if (state_entry) begin
      // ==============================================================
      // ENTERING POLLING DISCARDS ANY PENDING OBSERVATION.
      //
      // Checked FIRST, so an observation parsed just before entry cannot
      // count toward this state's qualification. A stale ordered set from
      // a previous state is exactly the kind of evidence that must not
      // contribute (section 15, mutation 6).
      // ==============================================================
      o_q <= '0;
    end else begin
      if (parse_valid && can_take) begin
        // THE WHOLE DESCRIPTOR AT ONCE. Nothing downstream re-reads the
        // parser -- an observation's fields belong to that observation.
        o_q.valid    <= 1'b1;
        o_q.kind     <= parse_kind;
        o_q.link_num <= parse_link;
        o_q.lane_num <= parse_lane;
        o_q.rate     <= parse_rate;
        o_q.ctrl     <= parse_ctrl;
      end else if (fire) begin
        o_q.valid <= 1'b0;
      end
 
      // An observation that could not be held is REPORTED. The receive
      // path did not stop; pretending it did would hide lost evidence.
      if (parse_valid && !can_take) ovf_q <= 1'b1;
    end
  end
 
endmodule

Classification: synthesizable.

Architecture. A one-entry holding stage capturing the whole observation, with entry clearing checked first.

The state-entry clear is the subtle part. Without it, an ordered set parsed in the cycle before Polling was entered could count toward Polling's qualification — evidence gathered in a state whose conditions were different. Checking it before the capture arm makes that unconstructable rather than a matter of timing.

Failure — four. Re-reading parser outputs at consume time attaches the wrong fields to an observation. A ready on the parser side models an interface the receive path does not have. Dropping silently on overflow hides lost evidence. And omitting the entry clear lets a pre-entry observation contribute.

11. RTL — Consecutive Qualifier Counter

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
import polling_pkg::*;
 
// SYNTHESIZABLE. Count CONSECUTIVE qualifying observations.
// THAT PROGRESS REQUIRES REPEATED RECOGNITION is the mechanism (section
// 7). THE THRESHOLD IS A PARAMETER and is ILLUSTRATIVE -- section 1 could
// not source the normative counts, and this chapter publishes none.
module obs_qualifier #(
  parameter int THRESH = 4,
  // GUARDED. $clog2 of a small threshold, never zero-width.
  parameter int CNT_W  = (THRESH <= 1) ? 1 : $clog2(THRESH + 1)
) (
  input  logic clk,
  input  logic rst_n,
 
  input  logic          restart,          // state entry, or a lost precondition
  input  logic          qualifying,
  input  logic          disqualifying,
 
  output logic [CNT_W-1:0] count,
  output logic             threshold_met
);
 
  generate
    if (THRESH < 1) $error("THRESH must be at least 1");
    if (CNT_W  < 1) $error("CNT_W must be at least 1");
    if ((1 << CNT_W) < (THRESH + 1))
      $error("CNT_W too narrow to hold THRESH");
  endgenerate
 
  logic [CNT_W-1:0] cnt_q;
  assign count = cnt_q;
 
  // ==================================================================
  // THE COMPARISON THAT MUST BE EXACTLY RIGHT.
  //
  //   >= THRESH        CORRECT   -- asserts on the THRESH-th observation
  //   >= THRESH-1      ONE EARLY -- and INVISIBLE at THRESH == 1, which
  //                                 is why section 15 requires THRESH >= 2
  //   >  THRESH        ONE LATE  -- never asserts if the counter saturates
  //
  // Section 15 verified this exhaustively against an independent model.
  // ==================================================================
  assign threshold_met = (cnt_q >= CNT_W'(THRESH));
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      cnt_q <= '0;
    end else if (restart) begin
      // Restart has priority: entering the state, or losing a
      // precondition, invalidates the history regardless of this cycle's
      // observation.
      cnt_q <= '0;
    end else if (disqualifying) begin
      // CONSECUTIVE, NOT CUMULATIVE. A bad observation resets to ZERO,
      // not to one. Counting up only would let scattered good
      // observations across a mostly-bad stream reach the threshold --
      // qualifying a lane that is not working (section 7).
      cnt_q <= '0;
    end else if (qualifying) begin
      // SATURATE. A wrapping counter would drop below the threshold after
      // reaching it and de-assert threshold_met while the stream is fine.
      if (cnt_q < CNT_W'(THRESH)) cnt_q <= cnt_q + CNT_W'(1);
    end
  end
 
endmodule

Classification: synthesizable.

Architecture. One saturating counter, with restart before disqualify before qualify — a declared priority, not an accident of if ordering.

Verified exhaustively (§15): against an independent reference — "the last THRESH observations were all qualifying" — across every good/bad sequence up to length 10, for thresholds 1 through 4. Zero mismatches. And the boundary checks hold for thresholds 1 through 8: all-good asserts on exactly the THRESH-th observation, and THRESH − 1 observations never assert.

Failure — five. >= THRESH-1 asserts one observation early — and is invisible at THRESH = 1, which is why §15 requires testing at 2 or more. > THRESH never asserts once the counter saturates. Counting up without resetting on a bad observation qualifies a marginal lane. Resetting to one instead of zero on a bad observation still over-counts. And wrapping de-asserts a threshold that had been met.

12. RTL — Polling Progress Controller

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
import polling_pkg::*;
 
// SYNTHESIZABLE. Progress within Polling only.
// THIS IS NOT THE LTSSM and is not Polling's normative substate encoding
// (Chapter 18.1 section 9). It models the digital progression: wait for
// lock and alignment, qualify observations, report done or failed.
// The timeout limit is a PARAMETER -- no PCIe constant appears here.
module polling_progress #(
  parameter int THRESH = 4,
  parameter int CNT_W  = 24
) (
  input  logic clk,
  input  logic rst_n,
 
  input  logic          state_entry,      // entering Polling
 
  // ---- Preconditions, from the receive path -----------------------------
  // TWO SEPARATE SIGNALS, deliberately (section 4). Collapsing them into
  // one ready bit destroys the ability to say which failed.
  input  logic          cdr_lock,
  input  logic          rx_aligned,
 
  // ---- Observations ------------------------------------------------------
  input  training_obs_t obs,
  output logic          obs_ready,
 
  // ---- Timeout -----------------------------------------------------------
  input  logic [CNT_W-1:0] timeout_limit,
  input  logic             timeout_elapsed,
 
  // ---- Results ------------------------------------------------------------
  output logic          polling_done,
  output logic          polling_fail,
  output logic          waiting_for_lock,
  output logic          qualifying_now
);
 
  typedef enum logic [1:0] { S_WAIT_LOCK, S_QUALIFY, S_DONE, S_FAIL } st_e;
  st_e st_q;
 
  wire lock_ok = cdr_lock && rx_aligned;
 
  assign waiting_for_lock = (st_q == S_WAIT_LOCK);
  assign qualifying_now   = (st_q == S_QUALIFY);
  assign polling_done     = (st_q == S_DONE);
  assign polling_fail     = (st_q == S_FAIL);
 
  // Observations are consumed only while qualifying. In any other state
  // they are not evidence, and accepting them would let a stale stream
  // contribute to a qualification that has not started.
  assign obs_ready = (st_q == S_QUALIFY);
 
  wire qualifying    = (st_q == S_QUALIFY) && obs_qualifies(obs);
  wire disqualifying = (st_q == S_QUALIFY) && obs_disqualifies(obs);
 
  logic threshold_met;
  logic [$clog2(THRESH+1)-1:0] qcount;
 
  obs_qualifier #(.THRESH(THRESH)) u_q (
    .clk, .rst_n,
    // LOSING LOCK INVALIDATES THE HISTORY. Observations qualified under a
    // lock that has since been lost are not evidence about the stream now.
    .restart      (state_entry || !lock_ok),
    .qualifying, .disqualifying,
    .count        (qcount),
    .threshold_met(threshold_met)
  );
 
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      st_q <= S_WAIT_LOCK;
    end else if (state_entry) begin
      // Entry has highest priority: a fresh attempt starts from scratch
      // regardless of anything else happening this cycle.
      st_q <= S_WAIT_LOCK;
    end else begin
      unique case (st_q)
        S_WAIT_LOCK :
          if (lock_ok)              st_q <= S_QUALIFY;
          else if (timeout_elapsed) st_q <= S_FAIL;
 
        S_QUALIFY :
          // ==========================================================
          // SUCCESS BEATS TIMEOUT ON THE SAME CYCLE, declared.
          //
          // A threshold met in the same cycle a timeout elapses is a
          // successful qualification -- the evidence exists. Failing
          // instead would discard a completed qualification because a
          // counter happened to reach its limit simultaneously
          // (section 15, mutation 9).
          // ==========================================================
          if (threshold_met)        st_q <= S_DONE;
          else if (!lock_ok)        st_q <= S_WAIT_LOCK;
          else if (timeout_elapsed) st_q <= S_FAIL;
 
        // Terminal. done and fail are STATES, so they cannot both assert.
        S_DONE, S_FAIL : ;
 
        default : st_q <= S_WAIT_LOCK;
      endcase
    end
  end
 
endmodule

Classification: synthesizable (progress within Polling only — not the LTSSM).

Architecture. Four states, with lock and alignment kept as separate inputs and qualification history invalidated when lock is lost.

Three declared priorities, each with a worse alternative:

PriorityThe alternative, and why it is worse
entry beats everythinga fresh attempt inheriting old history qualifies on stale evidence
losing lock restarts qualificationobservations counted under a lock that no longer holds
success beats same-cycle timeouta completed qualification discarded because a counter coincided

Failure — four. Merging lock and alignment into one input loses §4's diagnostic. Not restarting on lock loss accumulates evidence across an interruption. Accepting observations outside S_QUALIFY lets a stale stream contribute. And done and fail as flags rather than states permits both.

13. Assertions

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SVA over training_obs_capture, obs_qualifier and polling_progress.
// These assert the LOCAL progression contract. They assert NOTHING about
// the partner transmitting TS, about lock being achievable, or about
// Polling succeeding -- all of which depend on a partner and a channel
// this design does not control.
 
// ---- ENVIRONMENT ------------------------------------------------------
// A1: the parser's outputs are stable while parse_valid.
assume property (@(posedge clk) disable iff (!rst_n)
  parse_valid |-> !$isunknown({parse_kind, parse_lane, parse_link}));
// A2: state_entry is a single-cycle pulse.
assume property (@(posedge clk) disable iff (!rst_n)
  state_entry |=> !state_entry);
 
// ---- OBSERVATION OWNERSHIP --------------------------------------------
 
// P1: an observation's fields are stable while it waits.
property p_obs_stable;
  @(posedge clk) disable iff (!rst_n)
  (obs.valid && !obs_ready && !state_entry)
    |=> (obs.valid && $stable(obs.kind) && $stable(obs.lane_num)
                   && $stable(obs.link_num));
endproperty
a_stable : assert property (p_obs_stable);
 
// P2: STATE ENTRY DISCARDS a pending observation -- evidence from before
// this attempt must not contribute.
property p_entry_clears;
  @(posedge clk) disable iff (!rst_n)
  state_entry |=> !obs.valid;
endproperty
a_entry : assert property (p_entry_clears);
 
// P3: an observation that could not be held is REPORTED, never silent.
property p_overflow_reported;
  @(posedge clk) disable iff (!rst_n)
  (parse_valid && obs.valid && !obs_ready && !state_entry) |=> obs_overflow;
endproperty
a_ovf : assert property (p_overflow_reported);
 
// ---- QUALIFICATION ----------------------------------------------------
 
// P4: the count increments ONLY on a qualifying observation.
property p_count_only_on_qualify;
  @(posedge clk) disable iff (!rst_n)
  (count > $past(count)) |-> $past(qualifying);
endproperty
a_inc : assert property (p_count_only_on_qualify);
 
// P5: ONLY TS1 AND TS2 QUALIFY. An unrecognised or malformed training set
// must never contribute to progress.
property p_only_ts_qualifies;
  @(posedge clk) disable iff (!rst_n)
  (obs.valid && (obs.kind inside {OS_OTHER, OS_INVALID, OS_NONE}))
    |-> !obs_qualifies(obs);
endproperty
a_kind : assert property (p_only_ts_qualifies);
 
// P6: A DISQUALIFYING OBSERVATION RESETS TO ZERO, not to one.
// Consecutive is the requirement (section 7).
property p_bad_resets_fully;
  @(posedge clk) disable iff (!rst_n)
  disqualifying |=> (count == '0);
endproperty
a_reset : assert property (p_bad_resets_fully);
 
// P7: THE THRESHOLD CANNOT ASSERT EARLY. Restated independently of the
// module's own comparison, so an off-by-one fails rather than agreeing
// with itself.
property p_no_early_threshold;
  @(posedge clk) disable iff (!rst_n)
  threshold_met |-> (count >= CNT_W'(THRESH));
endproperty
a_early : assert property (p_no_early_threshold);
 
// P7b: and it DOES assert once the count reaches the threshold -- the
// complementary half, which an off-by-one in the other direction fails.
property p_no_late_threshold;
  @(posedge clk) disable iff (!rst_n)
  (count >= CNT_W'(THRESH)) |-> threshold_met;
endproperty
a_late : assert property (p_no_late_threshold);
 
// P8: the counter SATURATES -- it never exceeds the threshold and never
// wraps below it once reached.
property p_saturates;
  @(posedge clk) disable iff (!rst_n)
  count <= CNT_W'(THRESH);
endproperty
a_sat : assert property (p_saturates);
 
// P9: LOSING LOCK INVALIDATES the qualification history.
property p_lock_loss_restarts;
  @(posedge clk) disable iff (!rst_n)
  (!cdr_lock || !rx_aligned) |=> (count == '0);
endproperty
a_lock : assert property (p_lock_loss_restarts);
 
// ---- PROGRESSION ------------------------------------------------------
 
// P10: POLLING CANNOT COMPLETE WITHOUT BOTH lock/alignment AND
// qualification. The chapter's central progression property.
property p_done_needs_both;
  @(posedge clk) disable iff (!rst_n)
  $rose(polling_done) |-> ($past(cdr_lock) && $past(rx_aligned)
                                           && $past(threshold_met));
endproperty
a_done : assert property (p_done_needs_both);
 
// P11: done and fail are mutually exclusive.
property p_done_xor_fail;
  @(posedge clk) disable iff (!rst_n)
  !(polling_done && polling_fail);
endproperty
a_excl : assert property (p_done_xor_fail);
 
// P12: SUCCESS BEATS A SAME-CYCLE TIMEOUT -- the declared priority.
property p_success_beats_timeout;
  @(posedge clk) disable iff (!rst_n)
  (qualifying_now && threshold_met && timeout_elapsed) |=> polling_done;
endproperty
a_priority : assert property (p_success_beats_timeout);
 
// P13: observations are consumed only while qualifying -- a stale stream
// outside that window is not evidence.
property p_obs_only_when_qualifying;
  @(posedge clk) disable iff (!rst_n)
  obs_ready |-> qualifying_now;
endproperty
a_window : assert property (p_obs_only_when_qualifying);
 
// ---- SCOPE ------------------------------------------------------------
 
// P14: NO NORMAL TRAFFIC IN POLLING. Bound against Chapter 18.1's gate.
property p_no_traffic;
  @(posedge clk) disable iff (!rst_n)
  (dut_ltssm.cur_state == ST_POLLING) |-> !dut_gate.traffic_enabled;
endproperty
a_no_traffic : assert property (p_no_traffic);
 
// P15: reset and entry clear the qualification history.
property p_reset;
  @(posedge clk)
  !rst_n |=> ((count == '0) && !polling_done && !polling_fail && !obs.valid);
endproperty
a_reset : assert property (p_reset);

P7 and P7b are the off-by-one pair, and both are needed. P7 forbids early assertion; P7b forbids late. A >= THRESH-1 implementation fails P7; a > THRESH implementation fails P7b. Either alone would pass one of the two classic mutations.

P6 is the consecutive-versus-cumulative property, and it asserts reset to zero specifically — a design that reset to one would satisfy a looser property and still over-count.

P10 is the progression property, and it requires both preconditions explicitly rather than a single merged ready — which is §4's separation made checkable.

No liveness is asserted. "The partner eventually transmits TS1" is an environment property, and asserting it would assume away exactly the failure this state exists to detect.

14. Verification and Fault Injection

The scoreboard runs its own observation sequence and its own consecutive-count model, and never reads cnt_q or calls obs_qualifies.

Qualification — the boundary set

  • THRESH = 1, 2, 3, 4, 8. THRESH ≥ 2 is required, because the >= THRESH-1 off-by-one is invisible at 1 (§11).
  • Exactly THRESH consecutive qualifying observations — verify assertion on the THRESH-th, not earlier (P7).
  • THRESH − 1 observations — verify no assertion.
  • THRESH + 1 — verify saturation, no wrap (P8).
  • A disqualifying observation at count THRESH − 1 — verify reset to zero (P6). Required.
  • Lock lost mid-qualification — verify restart (P9).
  • TS1 and TS2 both qualifying; OS_OTHER and OS_INVALID not (P5).

Observation capture

  • Consumer stalled across observations — verify stability, then overflow reported (P1, P3).
  • state_entry with a pending observation — verify it is discarded (P2). Required, and the mutation-6 test.
  • An observation in the same cycle as state_entry — verify it does not count toward the new attempt.

Progression

  • Lock, then alignment, then qualification — verify polling_done requires all three (P10).
  • Alignment without lock, and lock without alignment — verify neither progresses.
  • Timeout while waiting for lock — verify polling_fail.
  • Threshold met and timeout in the same cycle — verify done (P12). Required.
  • Reset mid-qualification (P15).

Mutations

#MutationCaught byLab symptom
1count increments on every ordered setP4, P5qualifies on a stream carrying no valid training
2TS2 classified as TS1scoreboard kind checkprogress on the wrong evidence; Configuration entered early
3bad observation does not reset the countP6marginal lane qualifies; fails later in Configuration
4threshold >= THRESH-1P7 at THRESH ≥ 2Configuration entered one TS too early
5threshold > THRESHP7bnever completes; looks like a dead partner
6pre-entry observation countedP2qualification succeeds on evidence from another state
7counter wrapsP8threshold_met de-asserts after being reached
8lock ignored in the completion conditionP10Polling completes on an unlocked lane
9timeout beats same-cycle successP12intermittent failure at exactly the timeout boundary
10normal traffic enabled in PollingP14packets into a Link that is still training
11OS_INVALID accepted as qualifyingP5malformed sets count as progress
12reset leaves the countP15second attempt qualifies immediately
13observation fields re-read at consumeP1lane and link fields from a different observation
14lock loss does not restart qualificationP9evidence accumulated across an interruption

15. Model Verification

The qualifier was executed before publication, not reasoned about.

Exhaustive equivalence. Every good/bad observation sequence up to length 10, for thresholds 1 through 4 — 4,092 sequences — compared against an independent reference defined as "the first index at which the previous THRESH observations were all qualifying." Zero mismatches.

Boundary checks for thresholds 1 through 8: a run of qualifying observations asserts on exactly the THRESH-th, and a run of THRESH − 1 never asserts.

And both classic off-by-ones were executed to produce §14's table rather than described from memory — including the finding that >= THRESH-1 is indistinguishable from correct at THRESH = 1, which is why the required test set starts at 2.

16. Debugging

Symptom → which of the three failures → signal → distinguishing experiment.

This is the physical-quality stage (§2). The card is there and the channel is the question.

Read three signals, in this order — they correspond to §4's three failures:

  1. cdr_lock — if it never asserts, timing cannot be recovered. Signal level, channel loss, equalization (Chapter 17.4).
  2. rx_aligned — if lock is high and alignment is not, the receiver cannot find unit boundaries (Chapter 17.2 §5).
  3. qual_count — if both are high and the count never accumulates, training sets are arriving unrecognised or intermittently.

Three readings, three different investigations. A merged "polling failed" signal would leave all three open.

CDR lock is high but there is no progress

Alignment or recognition, and the count tells you which (§4).

If rx_aligned is low: the boundary search is failing — and note this is generation-specific (§3), so a design that assumed comma alignment on a Gen3+ Link is looking for the wrong evidence entirely.

If alignment is high and qual_count oscillates: training sets are being recognised and interrupted. That pattern — starting and resetting — is the signature of marginal signal quality, not of a logic fault, because a logic fault would produce zero recognition rather than intermittent recognition.

If qual_count is flat at zero with alignment high: the content is not recognised at all. The partner may be in Polling.Compliance (§1), may be transmitting at a different rate, or the classifier may be wrong.

Works at the base rate and fails at a higher one

Margin, and the comparison is the diagnostic (Chapter 17.4 §2).

The distinguishing experiment: change the channel — shorter cable, different slot. If the higher rate then polls successfully, the logic is fine and the channel was the limit.

And do not carry Gen1 assumptions forward (§3). The alignment mechanism is different from Gen3 onward, and a design or a debug expectation built around commas is looking for something that is not there.

Qualification reaches the threshold and Configuration fails immediately

Suspect the threshold arithmetic (§14's counterexample) before suspecting Configuration.

The distinguishing experiment: raise the threshold parameter and re-run. If the Link becomes stable, Polling was completing on insufficient evidence — and the real fault is here, one state earlier than the symptom.

17. Common Misconceptions

  • "Polling means software polls the device." A name collision with a software idiom (Chapter 18.1 §4).
  • "Polling starts before receiver presence is known." Detect precedes it, and for good reason (Chapter 18.2 §6).
  • "CDR lock alone means Polling is complete." Lock is one of three conditions; alignment and repeated recognition are the others (§4, P10).
  • "One valid training set is enough." Confidence comes from consecutive repetition (§7).
  • "TS1 and TS2 are TLPs." Physical-layer ordered sets, existing precisely because no packet layer works yet (§6).
  • "A Switch forwards training ordered sets." Each Link trains independently (Chapter 17.3 §15).
  • "Comma alignment describes all PCIe generations." 8b/10b symbol alignment and Gen3+ block alignment are different mechanisms (§3).
  • "Polling negotiates the bus number." Software assigns bus numbers, much later (Chapter 7.4).
  • "Link number means bus number." Different layers entirely — Chapter 18.4 §5.
  • "Training sequence numbers are DLL sequence numbers." No relationship (Chapter 14.5).
  • "Polling success means traffic can flow." Configuration comes next, and L0 after that (P14).
  • "A Polling timeout is a transaction timeout." Different layer, different mechanism (Chapter 12.1).
  • "A bad observation should decrement the count." It resets it — consecutive is the requirement (§7, P6).

18. Understanding Check

19. What's Next

Polling is where "somebody is there" becomes "we can read each other" — and it is the first stage a marginal channel fails.

Two independent problems, three distinct failures. Lock and alignment must both succeed, and the recovered stream must carry consecutively recognised training information. Keeping those as separate signals is what makes "stuck in Polling" a three-way diagnosis rather than a dead end.

And confidence comes from repetition, because nothing here can be acknowledged — with a threshold comparison that must be exactly right, and an off-by-one that hides at the one parameter value most likely to be tested.

Chapter 18.4 — Configuration takes the next question. Trained lanes are not yet a Link. Which of them belong together, what their logical identities are, how wide the result is, and — the part that decides whether everything above works — when a discovery becomes a commitment.

The idea to carry forward: evidence gathered under conditions that no longer hold is not evidence.