Skip to content
VLSI Mentor

DDR · Module 21

Gate Training

The DQS gate exists only inside the PHY, so JEDEC defines no procedure for it — only a preamble the PHY can find. The resulting search has a one-sided pass region whose safe side produces no observable at all.

Chapter 19.4 built the DQS gate and was blunt about its status: the gate is an estimate, its correctness is a containment relationship against a window the PHY cannot see, and the gate_offset it consumes arrives from somewhere else. Chapter 21.2 §3 then assumed that estimate was already right, and showed that a wrong gate produces an empty pass region indistinguishable from a dead channel.

This chapter produces gate_offset. It is the third search in the module and the third kind of observable, and it is the only one for which the standard defines no procedure at all.

The central law, and its qualification:

Training is controlled search over configurable PHY state, using observable pass/fail information to discover an operating point with usable timing margin — and training does not create margin. A read path whose strobe never arrives within any reachable gate position must fail.

1. The Standard Does Not Know the Gate Exists

This is worth establishing first, because it explains the shape of everything that follows.

The DQS gate is a construct entirely internal to the PHY. 19.4 §2 derived why it must exist: between bursts the strobe line is not driven, so the capture path must be prevented from seeing it, and the PHY therefore opens a window around the interval in which it believes a burst will arrive.

The DRAM has no part in this. It drives a strobe when it has data to send, and it neither knows nor could know that the controller is gating. Searching JESD79-4 for a DQS gate, gate training, a read gate or a gate-open concept returns nothing — none of those terms appear, because there is nothing at the device interface for them to name.

The consequence is a genuine asymmetry with the two previous chapters:

Observable comes fromStandard defines
21.1 Write levellingThe device, as a phase comparatorMode, stimulus, feedback, terminating condition
21.2 Read levellingThe device, as a pattern sourceThe stimulus mechanism (MPR), its formats and patterns
21.3 Gate trainingThe PHY's own strobe detectorNothing. Only an assist — §3

So gate training is vendor-specific in a stronger sense than the other steps. The other two have implementation freedom within a defined procedure. This one has no defined procedure to be free within.

2. What Is Actually Being Searched

Chapter 19.3 §2 decomposed a launch latency into two terms: a programmed term anyone can compute, and a measured term nobody can. 19.4 inherited exactly the same split on the receive side, and its gate controller takes both as separate inputs:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   from 19.4's phy_dqs_gate_ctrl port list

     cl_cycles     the programmed term -- CAS latency, from 14.9.
                   Known. A register value.

     gate_offset   the measured term. OPAQUE in 19.4.
                   ─────────────────────────────────
                   THIS CHAPTER'S OUTPUT.

     gate_trained  whether the above means anything yet.

So the search has a precisely bounded target: the number of cycles by which a burst's actual arrival differs from what cl_cycles alone predicts. That difference exists because the round trip includes command flight time to the device, the device's own output delay, and data flight time back — none of which the controller can compute.

Two things about the target are worth being explicit about.

It is a round-trip quantity. It is not the strobe's delay, or the board's delay, or the device's delay. It is the whole loop from launching a command to the strobe appearing at the PHY's pin, minus the part that was already predictable. Nothing in the search can decompose it further.

It is measured in the PHY's clock cycles at coarse resolution. gate_offset in 19.4 is a cycle count, and a cycle count is all the gate needs to open in the right cycle. Positioning within a cycle is a separate, finer problem — §6.

3. What the Standard Supplies Instead

DDR4 gives the PHY two things that make the search tractable, and both are register-controlled.

A programmable read preamble. MR4 A11 selects a read preamble of 1 nCK or 2 nCK. The preamble is strobe activity that precedes the data — 6.10 §4 established it as part of why the strobe's window is wider than the data's.

A training mode for it. MR4 A10 is Read Preamble Training Mode, 0 to disable and 1 to enable. The standard's §4.20.3 explains its purpose directly: a programmable read preamble requires an additional MRS mode to train it, and in that mode the device drives the strobe with a toggle — 1 tCK or 2 tCK according to the preamble setting — so that the controller can locate it.

Two constraints on that mode matter to a sequencer:

It is only available in MPR mode. So MR3's MPR enable (21.2 §4) must already be set. Gate training and read levelling therefore share a prerequisite, and 21.5 has to order mode entry accordingly.

The command stream is restricted. The standard disallows illegal READ commands, and any command during or initiating the read process, while read preamble training is active. A sequencer that interleaves other traffic into the search is outside the mode's defined behaviour.

There is one more published fact that shapes the selection policy in §7, and it is easy to overlook:

The read postamble is fixed and short. DDR4 supports a fixed read postamble with a nominal value of 0.5 tCK, for both the 1 tCK and 2 tCK preamble modes. So the strobe activity that follows the last data is half a clock, while the activity that precedes the first data is one or two whole clocks.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   1 tCK preamble                    2 tCK preamble

   ──┐   ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌──      ──┐     ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌──
     └───┘ └─┘ └─┘ └─┘ └─┘            └─────┘ └─┘ └─┘ └─┘ └─┘
     │←1tCK→│←   data   →│0.5        │← 2tCK →│←  data  →│0.5

   the leading margin is programmable and can be DOUBLED;
   the trailing margin is fixed at nominal half a clock.

That asymmetry is not incidental. It is the physical reason the search's pass region is lopsided, and the reason the selection policy in §7 is not a midpoint.

4. A Presence Observable

The third observable in the module is different in kind from the first two, and the difference is worth naming precisely.

Write levelling's bit reported a side of a boundary. Read levelling's bit reported a conjunction — everything worked. Gate training's bit reports presence: was strobe activity seen inside the window the gate opened.

That sits between the other two in usefulness:

Write levellingGate trainingRead levelling
ReportsA sidePresence in a windowA conjunction
Localises a failureYes — which sidePartly — present or notNo
Needs the data path correctNoNoYes
Needs a known patternNoNoYes

The third row is what makes gate training runnable before read levelling. Detecting that a strobe arrived does not require the data to be sampled correctly, assembled correctly, or compared against anything. It requires only that something toggled where something was expected to toggle.

Implementations differ in how they get that bit, and the difference is real:

A strobe detector. Dedicated logic observes the gated strobe input and reports whether activity occurred. Cheap, direct, and the reason this search can precede everything else.

A beat count. 20.3's capture path already produces one sample per strobe edge, so counting samples inside the window gives presence and completeness — did the expected number of edges arrive. This is strictly more informative than a boolean, and §5's RTL uses it.

Data correctness as a proxy. Reuse 21.2's comparison. This works and is a poor choice for a first search: it reintroduces the conjunction, so a gate failure and a capture failure become the same bit again.

5. Coarse Before Fine

The search decomposes into two stages with genuinely different characters, and conflating them produces a search that cannot converge.

Coarse: which cycle. The unknown is a whole number of PHY clock cycles. The domain is small — bounded by the maximum round trip the design supports, which is 19.4's MAX_LATENCY. The observable is presence, and the answer is gate_offset.

Fine: where within the cycle. Once the gate opens in the right cycle, the remaining question is sub-cycle positioning of the gate's edges relative to the strobe's actual arrival. The domain is delay codes, not cycles.

These must be done in that order, and the reason is not merely convenience: the fine search has no meaningful observable until the coarse search is right. If the gate is open in the wrong cycle entirely, every sub-cycle position produces the same answer — nothing detected — so the fine sweep returns an empty region and no information about which direction to move.

This chapter's RTL implements the coarse search, and the scope note is deliberate:

6. The Fine Stage, Described

Even though it is not built here, the fine stage's structure is worth stating because its selection policy differs from the coarse one.

The variable is a sub-cycle delay applied to the gate's opening edge. The observable is still presence, but now presence of the preamble specifically — and this is where the programmable preamble earns its existence.

With a 1 tCK preamble, the gate's opening edge must land inside a one-clock window to catch the preamble without clipping into the previous burst's postamble region. With 2 tCK, that window is twice as wide. The preamble length is the fine search's tolerance budget, directly: doubling it doubles the acceptable placement error.

The trailing edge has no such luxury. The postamble is fixed at nominal 0.5 tCK (§3), so the closing edge's tolerance is half a clock regardless of configuration — which is why 19.4 carries TRAIL_CYCLES as a separate parameter from LEAD_CYCLES, and why the two are not symmetric in a real configuration.

7. The Pass Region Is One-Sided

Here is the result that makes gate training different from 21.2, and it follows from §3's asymmetry plus §4's callout.

Sweep the gate's opening position from very early to very late, and count the beats detected inside the window:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  gate opens ...  early ─────────────────────────────► late
  candidate        0   1   2   3   4   5   6   7   8   9
  beats seen       4   4   4   4   4   4   4   3   2   1
  pass             P   P   P   P   P   P   P   F   F   F
                   └──────────────┬──────────┘   └──┬──┘
                   all beats captured           beats LOST

                        the boundary: opening later
                        than 6 starts clipping the
                        preamble and then the data

The region is bounded on the late side by a hard, observable failure: beats are missing, the count is short, data is lost. It is bounded on the early side by nothing the search can see.

Opening the gate earlier and earlier keeps passing. Every beat still arrives inside the window. The count stays correct. And the gate is getting worse the whole time, because 19.4 §9 established the cost: a wider-open gate admits more interval during which the strobe line is not driven, and an undriven line is not a defined logic level.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
        observable cost            unobservable cost
        ───────────────            ─────────────────
  late  beats missing              -
  early -                          undriven line admitted,
                                   previous postamble, noise

So the naive policy — sweep, find the region, commit the midpoint — is wrong here, and wrong in a specific way: the midpoint of the observed pass region sits as far early as it sits late, but the two directions do not cost the same and only one of them is measured. A midpoint policy will drift the gate earlier the wider the observable region happens to be.

The correct policy is relative to the observable boundary:

PolicyChoiceVerdict
Midpoint of the pass regionCentre of codes 0..6, i.e. 3Wrong — treats an unmeasured side as if it were measured
Latest passing candidate6Wrong — sits exactly at the hard failure
Boundary minus a guard6 − guardCorrect in structure; the guard is 19.4's LEAD_CYCLES

The third is what 19.4's LEAD_CYCLES parameter is for, and this chapter is where its value acquires a meaning: it is the distance back from the measured late boundary, chosen to cover the drift and uncertainty the search cannot observe, and paid for in admitted undriven line.

8. The Search, in Sequence

One candidate evaluation during coarse gate training, across five participants: the training sequencer, the gate training search engine of this chapter, the gate controller of chapter nineteen point four, the controller command path, and the DRAM. The sequencer starts the search, having already placed the device in multi-purpose register mode and enabled read preamble training mode, which are prerequisites the standard imposes. The engine applies a candidate gate offset to the gate controller, which adds it to the programmed CAS latency to decide which cycle to open in. The engine then requests a read. The controller issues the read command to the DRAM. The DRAM drives a strobe toggle, whose length is one or two clocks according to the programmed preamble setting, followed by the data and a fixed half clock postamble. The gate controller opens its window at the candidate position and admits whatever falls inside it. A detector counts the strobe edges seen within that window and returns the count to the engine. The engine compares the count against the expected number of beats, records the candidate as passing only if the count is complete, and repeats. It sweeps upward until the count first becomes short, which is the late boundary, then subtracts a guard and commits a gate offset together with gate trained. A note records that opening earlier than the committed point would also have passed, at a cost no observable in this search can see.Coarse gate training — sweep until beats are lostSequencerGate searchGate 19.4ControllerDRAMstart (MPR + RPTmode set)apply candgate_offsetrequest readRDpreamble, data,0.5tCK postopen window atcandidatestrobe edges countedcomplete count, orshortsweep until firstshortoffset = boundary −guard

Message seven is the presence observable, in its more useful counting form. Message nine is the stopping condition, and it differs from both previous chapters: 21.1 stopped at a 0-to-1 transition, 21.2 swept the entire domain, and this one stops at the first failure — because §7 established that everything below the boundary passes, so there is nothing above it worth seeing and nothing below it that a further sweep would reveal.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// gate_train_search -- coarse (cycle-granular) DQS gate search.
//
// CLASSIFICATION: educational, synthesisable. Produces the gate_offset
// and gate_trained that Chapter 19.4's phy_dqs_gate_ctrl consumes.
//
// WHAT IT DOES NOT MODEL:
//   - sub-cycle gate positioning (§6) -- a delay-line function, and
//     19.1 §5 establishes it is not portable RTL
//   - the strobe, the preamble, the postamble, or the detector
//   - the undriven line admitted by an early gate: the cost that
//     bounds the search's early side and produces NO observable (§7)
//
// The search stops at the FIRST short count, then steps back by a
// guard. It does not centre, because one side of the region is unmeasured.
// ---------------------------------------------------------------------
module gate_train_search #(
  // Must match 19.4's MAX_LATENCY domain.
  parameter int MAX_OFFSET = 32,
  parameter int MAX_BEATS   = 16,
  // Reads per candidate. A short count is a definite failure, so one
  // complete count is weaker evidence than one short count -- see §12.
  parameter int REPEATS     = 2,
  // Cycles back from the measured late boundary. THIS IS 19.4's
  // LEAD_CYCLES, and it is a POLICY value: it buys tolerance to drift
  // the search cannot observe, and is paid for in admitted undriven
  // line. It cannot be derived from anything in this module.
  parameter int GUARD       = 1,
  parameter int OFF_W  = (MAX_OFFSET <= 1) ? 1 : $clog2(MAX_OFFSET + 1),
  parameter int BEAT_W = (MAX_BEATS  <= 1) ? 1 : $clog2(MAX_BEATS + 1),
  parameter int REP_W  = (REPEATS    <= 1) ? 1 : $clog2(REPEATS + 1)
) (
  input  logic                clk,
  input  logic                rst_n,

  input  logic                start,
  // How many beats a complete burst should deliver. From the burst
  // length configuration, not a constant.
  input  logic [BEAT_W-1:0]   expected_beats,

  // ── Prerequisites imposed by the standard (§3): the device must be
  //    in MPR mode with read preamble training enabled. This block
  //    does not set them -- 21.5 does -- but it refuses to probe
  //    without them, because the strobe would not be toggling.
  input  logic                mpr_mode_active,
  input  logic                rpt_mode_active,

  // ── To 19.4's gate controller, and to whatever issues reads.
  output logic [OFF_W-1:0]    cand_offset,
  output logic                read_req,

  // ── The observable: how many strobe edges fell inside the window.
  input  logic                read_done,
  input  logic [BEAT_W-1:0]   beats_seen,

  // ── THE COMMITTED RESULT -- 19.4's two inputs.
  output logic [OFF_W-1:0]    gate_offset,
  output logic                gate_trained,
  output logic                train_done,
  output logic                train_failed,

  // ── Observability. The measured boundary is published separately
  //    from the committed offset so the guard is visible in a log.
  output logic [OFF_W-1:0]    late_boundary,
  output logic                boundary_found,
  output logic [2:0]          state_out,

  output logic                err_probe_outside_mode,
  output logic                err_never_complete
);

  initial begin
    if (GUARD < 0)
      $fatal(1, "gate_train_search: GUARD must not be negative");
    if (GUARD > MAX_OFFSET)
      $fatal(1, "gate_train_search: GUARD (%0d) exceeds the domain", GUARD);
    if (MAX_OFFSET > (2**OFF_W - 1))
      $fatal(1, "gate_train_search: MAX_OFFSET does not fit OFF_W");
  end

  typedef enum logic [2:0] {
    S_IDLE   = 3'd0,
    S_ARM    = 3'd1,
    S_READ   = 3'd2,
    S_CLASS  = 3'd3,
    S_STEP   = 3'd4,
    S_COMMIT = 3'd5,
    S_FAIL   = 3'd6
  } state_e;

  state_e            state, next;

  logic [OFF_W-1:0]  cand;
  logic [REP_W-1:0]  reps;
  logic              all_complete;   // every repeat delivered a full count

  logic [OFF_W-1:0]  boundary;       // highest candidate that passed
  logic              have_boundary;

  logic [OFF_W-1:0]  committed;
  logic              committed_ok;

  // This candidate passed only if every repeat delivered the full
  // expected beat count. A SHORT count is the hard failure of §7.
  logic this_pass;
  assign this_pass = all_complete;

  logic domain_done;
  assign domain_done = (cand >= OFF_W'(MAX_OFFSET));

  always_comb begin
    next = state;
    unique case (state)
      S_IDLE   : if (start && mpr_mode_active && rpt_mode_active) next = S_ARM;
      S_ARM    : next = S_READ;
      S_READ   : if (read_done)
                   next = (reps + REP_W'(1) >= REP_W'(REPEATS)) ? S_CLASS : S_READ;
      // The stopping condition of §8: stop at the FIRST failure. Every
      // candidate below the boundary passes (§7), so a full sweep adds
      // nothing -- unlike 21.2, where islands make it mandatory.
      S_CLASS  : if (!this_pass)     next = have_boundary ? S_COMMIT : S_FAIL;
                 else if (domain_done) next = S_COMMIT;
                 else                  next = S_STEP;
      S_STEP   : next = S_ARM;
      S_COMMIT : next = S_IDLE;
      S_FAIL   : next = S_IDLE;
      default  : next = S_IDLE;
    endcase
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      state         <= S_IDLE;
      cand          <= '0;
      reps          <= '0;
      all_complete  <= 1'b1;
      boundary      <= '0;
      have_boundary <= 1'b0;
      // THE SAFE STATE. Offset zero with gate_trained low: 19.4's
      // gate controller raises err_gate_untrained rather than opening
      // a window on an untrained offset, so this is a state that
      // refuses service rather than one that guesses.
      committed     <= '0;
      committed_ok  <= 1'b0;
    end else begin
      state <= next;

      unique case (state)
        S_IDLE : if (start && mpr_mode_active && rpt_mode_active) begin
          cand          <= '0;
          reps          <= '0;
          all_complete  <= 1'b1;
          boundary      <= '0;
          have_boundary <= 1'b0;
          committed_ok  <= 1'b0;
        end

        S_READ : if (read_done) begin
          reps <= reps + REP_W'(1);
          // Any short count fails the candidate for good.
          if (beats_seen != expected_beats) all_complete <= 1'b0;
        end

        S_CLASS : begin
          if (this_pass) begin
            boundary      <= cand;
            have_boundary <= 1'b1;
          end
          reps         <= '0;
          all_complete <= 1'b1;
        end

        S_STEP : cand <= cand + OFF_W'(1);

        S_COMMIT : begin
          // Step BACK from the measured boundary by the guard, floored
          // at zero. Not the midpoint of the pass region: §7.
          committed    <= (boundary > OFF_W'(GUARD))
                            ? boundary - OFF_W'(GUARD)
                            : '0;
          committed_ok <= 1'b1;
        end

        default : ;
      endcase
    end
  end

  assign cand_offset   = cand;
  assign read_req      = (state == S_READ) && !read_done;

  assign gate_offset   = committed;
  assign gate_trained  = committed_ok;
  assign train_done    = (state == S_COMMIT) || (state == S_FAIL);
  assign train_failed  = (state == S_FAIL);

  assign late_boundary  = boundary;
  assign boundary_found = have_boundary;
  assign state_out      = state;

  // Probing without the device modes set would sample a quiet line and
  // report absence everywhere -- a failure with a misleading cause.
  assign err_probe_outside_mode = read_req && !(mpr_mode_active && rpt_mode_active);
  // The very first candidate already failed: no complete count anywhere.
  assign err_never_complete     = (state == S_FAIL) && !have_boundary;

endmodule

The S_CLASS branch is where the chapter's argument becomes code. A failing candidate with a boundary already recorded is success — it is how the boundary gets confirmed as a boundary. A failing candidate with no boundary recorded is failure, because the very first, earliest-opening candidate could not capture a complete burst, and opening even earlier is not available.

10. A Coarse Sweep, Cycle by Cycle

Sweeping the gate later until beats are lost

10 cycles
Ten cycles of a coarse gate training sweep, compressed to one candidate per cycle for legibility rather than the two repeats the parameters default to. The clock runs throughout. The candidate gate offset advances from zero to nine, one per cycle, each candidate opening the gate one cycle later than the last. The beat count seen inside the window stays at the full four for candidates zero through six, then falls to three, two and one for candidates seven, eight and nine as the gate opens too late and clips first the preamble and then the data itself. The pass row follows the count, passing while the count is complete and failing once it is short. The recorded late boundary holds the value six, which is the highest candidate that delivered a complete burst. On the final cycle the committed gate offset appears as five, which is the boundary of six minus a guard of one, and gate trained asserts. Note that candidates zero through five also passed: the search cannot see any reason to prefer six minus one over them, because the cost of opening early does not appear anywhere in this diagram.last complete burst — the boundarylast complete burst — theboundaryfirst short count — a beat is lostfirst short count — a beatis lostcommit boundary 6 minus guard 1commit boundary 6 minusguard 1CKcand_offset0123456789beats_seen4444444321passlate_boundary0123456666gate_trainedgate_offset0000000005t0t1t2t3t4t5t6t7t8t9

Read the beats_seen row against the pass row and the one-sidedness of §7 is visible directly: the count is flat at four across seven candidates and then degrades monotonically. There is no upper shoulder — the region simply runs off the early end of the sweep, and the search has no way to know how much further it would have kept passing.

11. What the Assertions Prove

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Bind unit note: these properties reference gate_train_search's
// internal state and are written as though bound into the module, so
// clk and rst_n are visible directly.

// P1 -- the committed offset is the measured boundary minus the guard.
// This is §7's selection policy, stated checkably. It is the property
// that FAILS if someone "improves" the search by centring the region.
property p_commit_is_boundary_minus_guard;
  @(posedge clk) disable iff (!rst_n)
    $rose(gate_trained)
      |-> (gate_offset == ((late_boundary > OFF_W'(GUARD))
                             ? late_boundary - OFF_W'(GUARD)
                             : OFF_W'(0)));
endproperty
assert property (p_commit_is_boundary_minus_guard);

// P2 -- a commit never happens without a boundary having been found.
property p_commit_needs_boundary;
  @(posedge clk) disable iff (!rst_n)
    (state_out == 3'd5) |-> boundary_found;
endproperty
assert property (p_commit_needs_boundary);

// P3 -- gate_trained is never asserted by a failed search. 19.4's gate
// controller gates on exactly this signal, so a violation here would
// let it open a window on a meaningless offset.
property p_fail_never_trained;
  @(posedge clk) disable iff (!rst_n)
    train_failed |-> !$rose(gate_trained);
endproperty
assert property (p_fail_never_trained);

// P4 -- candidate/committed separation, as in 21.1 §7 and 21.2 P4.
property p_commit_only_on_commit;
  @(posedge clk) disable iff (!rst_n)
    (gate_offset != $past(gate_offset, 1))
      |-> ($past(state_out, 1) == 3'd5);
endproperty
assert property (p_commit_only_on_commit);

// P5 -- the recorded boundary only ever moves up during a sweep, and
// only to the current candidate. A boundary that could regress would
// mean a passing candidate below a failing one was recorded later.
property p_boundary_monotonic;
  @(posedge clk) disable iff (!rst_n)
    (late_boundary != $past(late_boundary, 1))
      |-> (late_boundary > $past(late_boundary, 1));
endproperty
assert property (p_boundary_monotonic);

// P6 -- a candidate passes only on a complete count from every repeat.
property p_pass_needs_complete_count;
  @(posedge clk) disable iff (!rst_n)
    ((state_out == 3'd3) && ($past(state_out, 1) == 3'd2) && boundary_found
       && (late_boundary == cand_offset))
      |-> $past(all_complete, 1);
endproperty
assert property (p_pass_needs_complete_count);

// P7 -- no read is requested without both device modes set (§3).
property p_probe_inside_modes;
  @(posedge clk) disable iff (!rst_n)
    read_req |-> (mpr_mode_active && rpt_mode_active);
endproperty
assert property (p_probe_inside_modes);

// ── Cover.
cover property (@(posedge clk) disable iff (!rst_n) $rose(gate_trained));
// The first candidate already failed: §12's dead-channel case.
cover property (@(posedge clk) disable iff (!rst_n) err_never_complete);
// The whole domain passed without ever finding a boundary -- §12's
// most dangerous case, because it looks like a wide healthy region.
cover property (@(posedge clk) disable iff (!rst_n)
                  (state_out == 3'd5) && (late_boundary == OFF_W'(MAX_OFFSET)));
// A guard larger than the boundary, so the commit floors at zero.
cover property (@(posedge clk) disable iff (!rst_n)
                  $rose(gate_trained) && (gate_offset == '0)
                    && (late_boundary != '0));
// A partial count strictly between zero and complete: the gate
// clipping rather than missing entirely.
cover property (@(posedge clk) disable iff (!rst_n)
                  read_done && (beats_seen != '0)
                    && (beats_seen != expected_beats));

P1 is this chapter's signature property, and it exists to defend a non-obvious policy against a plausible-looking change. Someone reading 21.2 first and this chapter second will reasonably wonder why the gate is not centred in its region, and P1 fails immediately if they act on that instinct.

The third cover is the one worth wiring into a regression. A sweep that passes at every candidate up to MAX_OFFSET never observed the late boundary at all, so its “boundary” is just the top of the domain and the committed offset is the top minus a guard — a number with no measurement behind it.

12. Failure Is a Result

Three distinct outcomes need separating, and one of them is disguised as success.

Nothing complete anywhere. The first, earliest candidate already returns a short count. err_never_complete fires and the search fails. Causes: the strobe is not being driven at all — check that MPR and read preamble training mode were actually entered — or the round trip is longer than MAX_OFFSET, or the channel is dead.

A boundary found. The normal case. The search commits.

Everything passes to the top of the domain. No boundary was ever observed, so the search has not measured anything. The RTL above commits in this case, with late_boundary equal to MAX_OFFSET, and that is a defensible choice only because the third cover makes it visible. An implementation that treated this as a plain success would be reporting a trained gate whose position came from the size of a parameter rather than from the device.

That third case deserves the emphasis. It is the gate-training analogue of 21.1 §12's all-ones failure and 21.2 §13's whole-domain pass: an observable that never changed across the sweep means the experiment did not test the variable, and in all three chapters that situation superficially resembles the best possible outcome.

13. Corner Cases

CaseBehaviourWhy
First candidate failstrain_failed, err_never_completeNothing to step back from
Every candidate passesCommits MAX_OFFSET − GUARD; the third cover firesNo boundary measured; the number comes from a parameter
GUARD exceeds the boundaryCommits 0Floored rather than wrapped; wrapping would place the gate at the far end
GUARD = 0Commits the boundary itselfLegal and inadvisable: sits exactly at the hard failure
beats_seen zero at a candidateCandidate failsTotal miss and partial clip are both failures here
beats_seen exceeds expected_beatsCandidate failsAn over-wide gate admitting extra edges is not a pass — §4's callout
One repeat complete, one shortCandidate failsall_complete clears on any short count
Modes drop mid-sweeperr_probe_outside_mode on the next requestThe strobe would not be toggling
start mid-sweepIgnored outside S_IDLEWould merge two sweeps' boundaries
Re-run after successBoundary and commit discarded at S_IDLERound trip changes with temperature — 19.5 §4

The sixth row is worth its place. Treating a larger count as a pass is an easy mistake — more beats sounds like more signal — and it converts the one unobservable cost of §7 into a partially observable one, then ignores it. Requiring the count to be exactly right is what keeps an over-wide gate from scoring well.

14. DV — Model the Round Trip, Check the Policy

The checker models a device and channel with a hidden arrival cycle, and verifies the search's policy against it. It must not re-implement the sweep.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Independent round-trip model: knows the true arrival; the engine
// does not. SIMULATION-ONLY.
class gate_channel_model;
  int unsigned arrive;        // the true arrival cycle, hidden
  int unsigned preamble;      // 1 or 2, per MR4 A11
  int unsigned total_beats;

  // Beats seen when the gate opens at `open_at`. Opening at or before
  // the preamble start catches everything; later clips progressively.
  function int unsigned beats(int unsigned open_at);
    if (open_at <= arrive - preamble) return total_beats;
    if (open_at >= arrive + total_beats) return 0;
    return total_beats - (open_at - (arrive - preamble));
  endfunction
endclass
CheckWhat it establishes
gate_trained implies late_boundary == arrive − preambleThe boundary found is the real last-good cycle
gate_offset == late_boundary − GUARDThe policy, independently recomputed
With preamble = 2, the boundary is one cycle later than with preamble = 1§6's claim that the preamble is the tolerance budget
Set arrive beyond MAX_OFFSET; expect err_never_complete§12's first case
Set arrive so every candidate passes; expect the third cover, not a silent pass§12's third case
Sweep GUARD from 0 to boundary+1; commit floors at 0, never wrapsThe saturation path
Feed a count above total_beats; expect the candidate to fail§13's sixth row
Two runs, same arrive; identical committed offsetA cycle-granular search should be exactly repeatable

The last check is a genuine difference from the previous two chapters. 21.1 §5 and 21.2 §15 both had to tolerate run-to-run variance, because both observables are samples near a boundary. A cycle-granular presence count is not marginal in that way — a beat either landed in the window or it did not — so gate training's coarse stage should be bit-exact across runs, and variance is a symptom rather than a fact of life.

Here is the failure the third check catches:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  GATE TRAINING PREAMBLE MISMATCH
    MAX_OFFSET = 32, GUARD = 1, total_beats = 4

    model truth : arrive = 19, preamble as configured

    preamble = 1 (MR4 A11 = 0)
      last complete candidate : 18   (= 19 - 1)
      committed gate_offset   : 17
    preamble = 2 (MR4 A11 = 1)
      last complete candidate : 17   (= 19 - 2)
      committed gate_offset   : 16

    engine under test, run with the DEVICE at 2tCK preamble
    but the model's expectation computed for 1tCK:
      observed boundary 17, expected 18  -->  MISMATCH

    diagnosis : the register write that selected a 2tCK preamble and
                the search's assumption about it disagree. The search
                is not wrong -- it measured the channel it was given.
    why it matters : the committed offset is one cycle EARLIER than
                the checker expects, which is the SAFE direction, so
                the interface works and the discrepancy is silent.
                The same disagreement in the other direction -- device
                at 1tCK, search assuming 2tCK -- commits one cycle
                LATE and loses a beat under any drift.
    caught by : this check only. No property in §11 fires, because the
                search obeyed its policy exactly; the fault is in a
                configuration the search cannot observe.

That is the useful shape of a gate-training bug: a configuration mismatch that is asymptomatic in one direction and marginal in the other, with every assertion passing.

15. Debugging

SymptomLikely causeHow to confirm
err_never_complete at every laneStrobe not toggling — modes not enteredConfirm MR3 MPR enable and MR4 A10
err_never_complete, modes confirmedRound trip exceeds MAX_OFFSETCompare against the design's latency budget
Whole domain passesThe gate never closed late enough to clip§12's third case; the offset is a parameter, not a measurement
Boundary varies run to runShould not happen at cycle granularitySuspect the detector, or a burst arriving at a non-deterministic cycle
Boundary differs per lane by a cyclePlausible — round trips differ per laneCompare with the routing; per-lane storage is needed
Gate trains, read levelling finds an empty regionGate is in the right cycle, wrong sub-cycleThe fine stage of §6 has not run
Gate trains, occasional read corruptionGuard too small for actual driftRaise GUARD; note the cost is admitted undriven line
Works cold, fails warmRound trip drifted past the guardPeriodic retraining — 19.5 §4
Beat count exceeds expectedGate too wide, admitting extra edgesShould fail the candidate — §13
Trains differently after a preamble changeExpected, by exactly the preamble delta§14's mismatch report

The sixth row is the one that sends people back to this chapter after they thought it was finished. A correct coarse offset with no fine positioning produces a gate that opens in the right cycle and still fails to frame the strobe usefully — and the symptom appears in 21.2, one step later, as an empty pass region.

The fourth row is worth trusting. Because the coarse observable is not marginal (§14), run-to-run variation in the boundary is not the expected noise it would be in the other two chapters; it means something else is moving.

16. Misconceptions

“Gate training is a JEDEC procedure.” It is not. §1: the standard has no gate concept, because the gate is internal to the PHY. What the standard defines is a preamble and a mode that makes the preamble findable.

“Read Preamble Training and gate training are the same thing.” One is a device mode, the other is a PHY search. The mode is the assist; the search is nobody's but the PHY's.

“The gate should be centred in its pass region.” §7. The region is one-sided: the late edge is a measured failure, the early edge is an unmeasured cost. Centring treats an unmeasured side as if it had been measured.

“An earlier gate is a safer gate.” Safer against the observable failure, and worse against the unobservable one. 19.4 §9 states the cost: more interval in which an undriven line is admitted.

“Detecting the strobe proves the gate is right.” §4's callout. It proves overlap. A grossly over-wide gate detects the strobe at every candidate.

“More beats than expected is a better result.” It means extra edges were admitted from outside the burst. §13 fails the candidate for it.

“The gate offset is the board delay.” §2. It is a round-trip residual — command flight, device output delay and data flight combined — after removing the part cl_cycles already predicted. It cannot be decomposed further by this search.

“Coarse and fine can be searched together.” §5. Until the coarse offset is right, every fine position returns the same non-observation, so the fine sweep has no gradient to follow.

“A 2 tCK preamble improves signal integrity.” It improves the trainability of the gate by widening the leading detection window (§6). Whether it helps the channel electrically is Module 22's question, not this one's.

“Gate training can replace read levelling.” They answer different questions with different observables. Presence in a window is not a sampling point, and 21.2 §3 depends on this chapter having already succeeded.

17. Interview Reasoning

Why is there no JEDEC gate-training procedure? Because the gate is not at the device interface. The DRAM drives a strobe when it has data; it has no notion that the controller windows its input. There is nothing for the standard to specify.

Then what does the standard contribute? A programmable read preamble and a mode in which the device toggles the strobe so the controller can locate it. That converts an unfindable arrival into a findable one, which is the hard part.

What quantity does gate training actually measure? The round-trip residual: the cycles between launching a read and the strobe appearing at the PHY, minus what CAS latency already accounted for. It is not the strobe delay or the board delay in isolation.

Why not centre the gate in its pass region? Because the region is one-sided. Opening late loses beats, which is observable. Opening early admits an undriven line, which is not. Centring would spend measured margin to buy protection against a cost the search never saw.

Your sweep passes at every candidate. What do you conclude? That the experiment did not test the variable. The gate never opened late enough to clip the burst, so no boundary was measured and the committed offset derives from the domain's size rather than from the device.

Why must coarse precede fine? Because a wrong coarse offset makes every fine position produce the same observation — nothing detected — so the fine search has no direction to move in.

Gate training passes and read levelling reports an empty region. Where is the fault? Most likely the gate is in the right cycle but positioned wrongly within it, so the strobe is admitted but not usefully framed. The coarse stage succeeded and the fine stage has not run or has failed.

Why should the coarse boundary be identical across runs, when write levelling's answer is not? Because a beat either fell inside the window or it did not — the coarse observable is not a marginal sample. Write levelling's bit is the resolution of a metastable comparison near a boundary. Run-to-run variance is expected there and is a symptom here.

What does a larger GUARD buy and what does it cost? It buys tolerance to drift toward the late boundary, which is the failure that loses data. It costs additional admitted undriven line. Neither quantity is observable in this search, which is why it is a parameter.

18. Exercises

  1. Change S_COMMIT to commit the midpoint of the observed pass region instead of boundary-minus-guard. Which property in §11 fires? Then explain why the resulting gate would still pass every check in §14's table except two.

  2. The RTL commits when the whole domain passes. Argue for making that a failure instead. What information would a caller lose, and which cover property already makes the situation visible?

  3. Using §14's model, compute the committed offset for arrive = 24, preamble = 2, GUARD = 3, and confirm it against the RTL's arithmetic. Then find the smallest arrive for which the commit floors at zero.

  4. beats_seen exceeding expected_beats fails the candidate. Construct the gate misconfiguration that produces an over-count, and explain why treating it as a pass would make the early side of §7 partially observable — and why that would be worse than it being wholly unobservable.

  5. Write the property that would detect §14's preamble mismatch, given an additional input carrying the configured preamble length. Then explain why that property is a configuration check rather than a search check.

  6. The fine stage is described and not built. Specify its interface — inputs, outputs, and observable — such that everything it does not model is at the boundary rather than inside it. Compare your interface with 19.4's treatment of the same problem.

  7. Gate training needs MPR mode, and so does read levelling. Read levelling also needs gate training to have succeeded. Draw the dependency graph including 21.1, and identify which orderings are legal. Compare with what 21.5 concludes.

  8. All three chapters so far have a failure mode in which the observable never changes across the sweep. State the general principle that covers all three, and propose a single check that any search engine in this module could carry to detect it.

19. Where This Goes

Three searches, three observables, three different shapes.

A phase report gave 21.1 one transition and an early exit. A correctness conjunction gave 21.2 two edges and a mandatory full sweep. A presence count gave this chapter a one-sided region, a stopping condition at the first failure, and a selection policy that deliberately does not centre — because half the region's boundary is a cost no digital observable reports.

What all three share is a single search axis. One variable, swept; one region or transition, found; one code, committed.

Chapter 21.4 removes that. It adds a second axis — the receiver's reference level alongside the sampling position — and the search space stops being a line and becomes a grid. The midpoint arithmetic of 21.2 §6 does not generalise to two dimensions, the number of probes required grows as the product rather than the sum, and the standard specifies the second axis's codes with a step size that turns out not to correspond to a known physical step at all.

Continue learning

Standards & specifications

Governing standard
JEDEC JESD79 (DDR SDRAM)(opens JEDEC Solid State Technology Association in a new tab)

Defines the DDR SDRAM device itself — signals, command encoding, mode registers, timing parameters and the initialisation sequence — one document per generation. Memory-controller microarchitecture, address-mapping policy, PHY training algorithms and board-level design are not specified by it.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the DDR curriculum.