Skip to content
VLSI Mentor

DDR · Module 21

Training Algorithms

Four searches exist and nothing has said in what order they run. The order is not a convention: it follows from which observable each step needs — a derivation that predicts the sequence real controllers document.

Four searches are built. 21.1 locates a boundary from a phase report. 21.3 finds a one-sided region from a presence count. 21.2 finds two edges from a correctness comparison. 21.4 descends a two-axis grid.

Not one of them is usable on its own, and the reason is visible in their scope notes: 21.2 §3 cannot start until the gate works, 21.3 §3 needs a device mode that 21.2 §4 also needs, and 21.4 §12 needs a seed that only an earlier search can produce.

This chapter orders them. Its central claim is that the order is not a convention anyone chose:

The training order is determined by which observable each step requires, not by which direction it trains.

That claim is worth making because it turns a memorised sequence into something derivable — and, as §5 shows, it predicts the order that real controller documentation specifies.

1. Training or Calibration?

Chapter 19.5 §3 already warned that two different things get called calibration. This module adds a second overloaded pair, and the honest position is that no universal taxonomy exists — JEDEC, DRAM vendors, PHY vendors, FPGA vendors and SoC teams all use these words slightly differently.

So rather than claim a standard definition, this curriculum uses them operationally, and the distinction is drawn where it is useful:

Training — a procedure that determines an interface setting by interacting with the link and observing the result. Every chapter in this module is training by that definition: each one applies a candidate, stimulates, observes, classifies and commits.

Calibration — the broader establishment and maintenance of PHY operating state, which includes training but also includes things that involve no search at all.

The clearest example of the second category is already in the curriculum. ZQ calibration (Chapter 7.9) tunes output driver and termination impedance against an external precision resistor. There is no candidate, no observation loop and no pass/fail classification visible to the controller — the device does it internally against a physical reference. It is calibration and it is not training.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   CALIBRATION  (19.5's lifecycle owns the whole box)
   ┌──────────────────────────────────────────────────┐
   │  internally referenced, no search                │
   │    ZQ calibration -- against a real resistor     │
   │    internal delay-line calibration               │
   │                                                  │
   │  TRAINING -- search against an observable        │
   │  ┌────────────────────────────────────────────┐  │
   │  │  21.1 write levelling    phase report      │  │
   │  │  21.3 gate training      presence count    │  │
   │  │  21.2 read levelling     correctness       │  │
   │  │  21.4 centring           correctness, 2-D  │  │
   │  └────────────────────────────────────────────┘  │
   └──────────────────────────────────────────────────┘

This is not a vocabulary correction aimed at anyone else's documentation. It is a statement of what this curriculum means, connected explicitly to 19.5 so the two modules do not quietly disagree.

2. The Uniform Step Contract

All four searches were built to the same shape, and that was deliberate. Each one takes a start, holds the device in whatever mode it needs, sweeps, and terminates in exactly one of two ways:

SignalMeaning
startBegin. Accepted only in the step's idle state.
doneThe step terminated. Says nothing about success.
failedIt terminated without a usable result.
trained_validIt terminated with one.

Chapter 21.1 §7 established why done and valid are separate signals, and every subsequent chapter kept the separation. The sequencer depends on it completely: a step that merely reported completion would give the sequencer no basis for deciding whether to advance.

The uniformity is what makes a single sequencer possible despite the four searches having nothing else in common — different observables, different region shapes, different stopping conditions, different selection policies. From above, they are interchangeable.

3. What Each Step Needs

The dependency structure comes from one question asked of each step: what must already work for this step's observable to mean anything?

StepDevice modeNeeds a working read path?Needs a trained gate?Produces
21.1 Write levellingMR1 A7 = 1No — §4No — §4Strobe-to-CK delay
21.3 Gate trainingMPR + MR4 A10 = 1No — presence onlyNo — it produces itgate_offset, gate_trained
21.2 Read levellingMPRIt is the read pathYesRead sampling delay
21.4 Read centringMPRYesYesRead delay + PHY reference
21.4 Write centringMR6 A7 = 1Yes — §4YesWrite delay + device VrefDQ

Read the third column down and the order falls out. Only two steps can run before a read path exists, and one of them is the thing that creates it.

4. The Two Facts That Fix the Order

Two observations, both established earlier from primary sources, do all the work.

Write levelling needs neither the gate nor the read path. Chapter 21.1 §3 quoted the standard on this: the device provides write-levelling feedback on the DQ bits asynchronously, and there are no read strobes needed for those DQ bits. No strobe means nothing to gate; no gating and no capture means the read path is uninvolved. The PHY samples a level with its own clock after waiting out tWLO.

So write levelling is a write-direction step that runs first — which looks backwards until you notice that its observable is the only one that bypasses the read path entirely.

Every other write-side step needs the read path. Checking a write means reading it back and comparing. There is no other observable: the device does not report on the quality of data it received. So write DQ centring and device VrefDQ training are both gated on a working read path, and therefore come last.

Together these give:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   write levelling       observable bypasses the read path entirely
        │                (JEDEC: no read strobes needed)

   gate training         observable is PRESENCE -- needs no correct
        │                data, and produces what read capture needs

   read levelling        first step whose observable is correctness;
        │                needs the gate from above

   read centring         needs read levelling's result as a SEED
        │                (21.4 §12: a bad seed stalls the descent)

   write centring        needs the read path above to verify a write
                         -- the only observable available for writes

The ordering principle stated once more, because it is the chapter's point: a step can run as soon as its observable is available, and not before. Direction is irrelevant. The first and last steps in that list are both write-direction steps, separated by three read-direction steps, because their observables are as far apart as any two in the module.

5. What Real Flows Do

The derivation above is this chapter's own. It is worth checking it against documentation, and the check passes.

Public controller documentation for DDR4 specifies a training sequence in which the logic performs write levelling, then DQS gate training, then alignment of the read DQ bits, then alignment of read DQS to DQ, after which write calibration is performed and normal operation begins. The same source is where §4's callout about clearing MR1 bit 7 before gate training comes from.

That is §4's order, step for step: write levelling first, gate training second, the read-side alignment steps third and fourth, write calibration last.

6. Where Implementations Legitimately Differ

The order is constrained. A great deal else is not, and it is worth being precise about which is which, because “training is vendor-specific” is true in a way that is easy to over-apply.

Constrained by the observables (§4): the relative order of the five steps. Any flow that trains write DQ before the read path works has no way to check its result.

Constrained by the standard: the modes, the register fields, the stimulus mechanisms, and the restriction that read preamble training is available only in MPR mode.

Free, and genuinely variable between implementations:

Degree of freedomRange of legitimate choice
Who executes the flowFirmware on a core, a hardware engine, or the controller — 19.1 §3
GranularityPer rank, per byte lane, per bit (21.2 §8)
Search strategy within a stepLinear, bisecting, coarse-then-fine, coordinate descent
Probe counts and filtersPROBES, REPEATS, agreement thresholds
Selection policiesOffsets, guards, minimum widths, rounding
Which steps repeat periodically§8
Extra steps not in this moduleCommand/address training, DRAM internal calibrations
Whether results are stored and restoredFast boot by replaying saved settings, with revalidation

The last row is worth a sentence because it is a real architectural option. A system that saved a previous boot's settings can restore them and run a much shorter validation pass instead of a full flow. That trades boot time against the risk that conditions have changed since the values were measured — and the settings' validity is exactly what 19.5 §4 argued cannot be assumed to persist.

7. The Sequencer State Machine

The training sequencer state machine, with seven states. The machine starts in idle, where no step is running and no settings are valid. On a start request it moves to prep, which establishes the device mode the next step requires and, critically, leaves whatever mode the previous step was using. From prep it moves to run step, where exactly one of the module's four searches is active and the sequencer waits on that step's done signal. Run step has two exits. If the step reported failure it moves to failed, which holds a defined safe state, asserts calibration failed to the lifecycle block of chapter nineteen point five, and does not advance. If the step succeeded it moves to advance, which records that step's committed result and decides what comes next. Advance has three exits. If steps remain it returns to prep for the next one. If all steps have passed it moves to done, where calibration done is asserted and the interface is finally usable. From done, when the retrain interval elapses, it moves to retrain hold, which is a distinct state because a retraining pass must not discard the settings currently in use; from there it returns to prep to re-run only the periodic subset of steps. Failed returns to idle so that a whole flow can be retried from the beginning.IDLEPREPMODERUN STEPADVANCEDONEFAILEDRETRAINHOLDstart flowstart flowdevice mode setdevice mode setstep passedstep passedstep failedstep failedmore steps remainmore steps remainall steps passedall steps passedretrain interval elapsedretrain interval elapsedretraininterval…re-run periodic subsetre-run periodic subsetre-runperiodic…safe state, calib_failedsafe state,calib_failed

Three structural points, each of which is a decision rather than a detail.

PREP sits between every pair of steps. §4's callout is the reason: mode exit is part of advancing, and a state that both leaves the old mode and establishes the new one makes that impossible to skip.

FAILED does not advance. There is no path from FAILED to ADVANCE. A failed step fails the flow — §9 is about why the alternative is worse than it sounds.

RETRAIN HOLD is a separate state from IDLE. This is the subtle one, and it comes straight from 19.5 §6: during a recalibration pass the previously trained settings remain valid and in use, because the old values stay usable until new ones replace them. Routing a retrain through IDLE would clear them, so the machine has a distinct entry point that preserves them.

8. Which Steps Repeat

Chapter 19.5 §4 established the lifecycle shape: some things happen once, some happen continuously. Applying that to the five steps requires asking what each one measured and whether the measured quantity drifts.

StepMeasuresDrifts withTypically
Write levellingStrobe-to-CK at the deviceTemperature, voltageOnce at bring-up; occasionally revisited
Gate training (coarse)A round-trip cycle countLittle — it is quantisedOnce
Gate training (fine)Sub-cycle gate positionTemperature, voltagePeriodically
Read levelling / centringSampling position, referenceTemperature, voltagePeriodically
Write centringWrite position, device VrefDQTemperature, voltagePeriodically

The second row is the interesting one and it follows from 21.3 §14: a coarse gate observable is not marginal — a beat either landed inside the window or it did not — so the coarse cycle count is exactly repeatable and does not need periodic re-measurement. Its fine counterpart does, because sub-cycle position is a continuous quantity.

That is a useful general rule for retraining policy: quantised measurements are stable; continuous ones drift. A search whose answer is a whole number of cycles can be trusted across temperature in a way that a search whose answer is a delay code cannot.

9. Failure Propagation

A step reports failed. There are three things a sequencer could do, and only one of them is defensible.

Skip the step and continue. The remaining steps then run against an untrained setting. Worse, later steps' observables depend on earlier steps' results: read levelling with an untrained gate returns an empty region (21.2 §3), and centring with a bad seed stalls (21.4 §12). So skipping does not produce a partially trained interface — it produces a cascade of failures whose first cause is now several steps back.

Retry the step. Legitimate, and bounded retries are common in real flows. But a retry that succeeds after an identical failure is information: the observable is marginal, which is precisely 21.1 §5's uncertain band. A retry count is worth recording rather than hiding.

Fail the flow. The interface is not usable, calib_failed is asserted, and phy_operational never rises. 19.5's command stage then refuses traffic rather than issuing it into an untrained interface.

The third is what §7's machine does, and the reason is the one this module has repeated in every chapter: training does not create margin. A flow that cannot find a setting has discovered something true about the channel, and reporting it is the only correct response.

What makes failure useful rather than merely correct is reporting which step failed, because the four searches fail for very different reasons:

Failing stepMost likely meaning
Write levellingStrobe not arriving, mode not entered, or delay range too short
Gate trainingRound trip outside the domain, or strobe not toggling
Read levellingGate wrong, burst misassembled, or no open region
CentringSeed outside the region, or one axis too narrow

A sequencer that reports only “training failed” throws away the single most valuable diagnostic it has.

10. The Sequencer

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// training_sequencer -- orders the module's searches, propagates their
// results, and supplies the calib_done / calib_failed that Chapter
// 19.5's phy_calib_lifecycle consumes.
//
// CLASSIFICATION: educational, synthesisable.
//
// WHAT IT DOES NOT MODEL:
//   - any search: steps are opaque start/done/failed contracts (§2)
//   - mode register writes: it requests a mode and waits for an ack
//   - any physical quantity whatsoever
//
// The step ORDER is fixed by §4's observable-availability argument and
// is encoded as a constant, not as a configurable list, because an
// arbitrary order is not merely suboptimal -- it is unobservable.
// ---------------------------------------------------------------------
module training_sequencer #(
  parameter int NUM_STEPS = 5,
  // Bounded retries per step (§9). Zero means fail on first failure.
  parameter int MAX_RETRY = 1,
  // Which steps re-run on a periodic retrain (§8), as a bit mask with
  // bit i set for step i. POLICY: derived from what each step measures
  // and whether that quantity drifts.
  parameter logic [NUM_STEPS-1:0] PERIODIC_MASK = 5'b11100,
  parameter int STP_W = (NUM_STEPS <= 1) ? 1 : $clog2(NUM_STEPS),
  parameter int RTY_W = (MAX_RETRY <= 0) ? 1 : $clog2(MAX_RETRY + 1)
) (
  input  logic                   clk,
  input  logic                   rst_n,

  // ── From the lifecycle block of 19.5.
  input  logic                   start_flow,
  input  logic                   retrain_req,

  // ── Mode management. The sequencer asks for the mode the next step
  //    needs; whatever issues MRS commands acknowledges. §4's callout:
  //    establishing a mode includes LEAVING the previous one, so this
  //    is one request carrying a target, not a set and a clear.
  output logic [STP_W-1:0]       mode_for_step,
  output logic                   mode_req,
  input  logic                   mode_ack,

  // ── The step interface. One-hot start; the addressed step replies.
  output logic [NUM_STEPS-1:0]   step_start,
  input  logic [NUM_STEPS-1:0]   step_done,
  input  logic [NUM_STEPS-1:0]   step_failed,

  // ── To 19.5's phy_calib_lifecycle. These are the two inputs that
  //    chapter declared opaque and this one supplies.
  output logic                   calib_done,
  output logic                   calib_failed,

  // ── Observability. WHICH step failed is the flow's most valuable
  //    diagnostic (§9), so it is published rather than reduced.
  output logic [STP_W-1:0]       cur_step,
  output logic [STP_W-1:0]       failed_step,
  output logic                   have_failure,
  output logic [NUM_STEPS-1:0]   step_ok,        // per-step success
  output logic [RTY_W-1:0]       retries_used,
  output logic                   retrain_active,
  output logic [2:0]             state_out,

  output logic                   err_multi_done,
  output logic                   err_done_and_failed
);

  initial begin
    if (NUM_STEPS < 1)
      $fatal(1, "training_sequencer: NUM_STEPS must be at least 1");
    if (MAX_RETRY < 0)
      $fatal(1, "training_sequencer: MAX_RETRY must not be negative");
  end

  typedef enum logic [2:0] {
    S_IDLE   = 3'd0,
    S_PREP   = 3'd1,
    S_RUN    = 3'd2,
    S_ADV    = 3'd3,
    S_DONE   = 3'd4,
    S_FAILED = 3'd5,
    S_HOLD   = 3'd6
  } state_e;

  state_e               state, next;

  logic [STP_W-1:0]     step;
  logic [RTY_W-1:0]     retry;
  logic [NUM_STEPS-1:0] ok_mask;
  logic [STP_W-1:0]     fail_at;
  logic                 fail_seen;
  logic                 in_retrain;

  // The addressed step's replies, selected down to one bit each.
  logic this_done, this_failed;
  assign this_done   = step_done[step];
  assign this_failed = step_failed[step];

  logic last_step;
  assign last_step = (step >= STP_W'(NUM_STEPS - 1));

  logic retry_available;
  assign retry_available = (retry < RTY_W'(MAX_RETRY));

  // On a retrain pass, steps outside the periodic mask are skipped
  // rather than re-run (§8).
  logic step_in_scope;
  assign step_in_scope = !in_retrain || PERIODIC_MASK[step];

  always_comb begin
    next = state;
    unique case (state)
      S_IDLE   : if (start_flow) next = S_PREP;
      // A skipped step needs no mode change, so PREP passes straight
      // through for steps outside a retrain's scope.
      S_PREP   : if (!step_in_scope) next = S_ADV;
                 else if (mode_ack)  next = S_RUN;
      S_RUN    : if (this_done)
                   next = this_failed ? (retry_available ? S_PREP : S_FAILED)
                                      : S_ADV;
      S_ADV    : next = last_step ? S_DONE : S_PREP;
      // DONE is persistent: the interface is usable and stays usable
      // until a retrain is requested. It does NOT fall back to IDLE,
      // because IDLE clears the committed record (§7).
      S_DONE   : if (retrain_req) next = S_HOLD;
      S_HOLD   : next = S_PREP;
      S_FAILED : 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;
      step       <= '0;
      retry      <= '0;
      ok_mask    <= '0;
      fail_at    <= '0;
      fail_seen  <= 1'b0;
      in_retrain <= 1'b0;
    end else begin
      state <= next;

      unique case (state)
        S_IDLE : if (start_flow) begin
          step       <= '0;
          retry      <= '0;
          ok_mask    <= '0;      // a fresh flow trusts nothing
          fail_seen  <= 1'b0;
          in_retrain <= 1'b0;
        end

        S_RUN : if (this_done) begin
          if (this_failed) begin
            if (retry_available) begin
              retry <= retry + RTY_W'(1);
            end else begin
              fail_at   <= step;
              fail_seen <= 1'b1;
            end
          end else begin
            ok_mask[step] <= 1'b1;
            retry         <= '0;
          end
        end

        S_ADV : if (!last_step) step <= step + STP_W'(1);

        S_HOLD : begin
          // A retrain restarts the step walk WITHOUT clearing ok_mask
          // or the searches' committed results (§8's callout). Each
          // periodic step replaces its own result as it succeeds.
          step       <= '0;
          retry      <= '0;
          in_retrain <= 1'b1;
        end

        default : ;
      endcase
    end
  end

  // One-hot start, asserted for exactly one cycle as PREP hands over.
  always_comb begin
    step_start = '0;
    if (state == S_PREP && step_in_scope && mode_ack) step_start[step] = 1'b1;
  end

  assign mode_for_step  = step;
  assign mode_req       = (state == S_PREP) && step_in_scope;

  assign calib_done     = (state == S_DONE);
  assign calib_failed   = (state == S_FAILED);

  assign cur_step       = step;
  assign failed_step    = fail_at;
  assign have_failure   = fail_seen;
  assign step_ok        = ok_mask;
  assign retries_used   = retry;
  assign retrain_active = in_retrain;
  assign state_out      = state;

  // Two steps reporting completion at once means the one-hot start
  // contract was violated somewhere.
  assign err_multi_done      = ($countones(step_done) > 1);
  // A step must not claim both outcomes -- 21.1 P3 forbids it locally,
  // and this checks it at the integration level.
  assign err_done_and_failed = |(step_done & step_failed & ~step_start);

endmodule

Two lines carry most of the chapter's argument. S_HOLD restarting the walk without clearing ok_mask is §8's callout in code. And S_DONE having no path back to S_IDLE is what keeps a working interface working — the machine sits in DONE indefinitely, because IDLE is where the record gets discarded.

11. What the Assertions Prove

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

// P1 -- THE flow-level safety property. calib_done is asserted only
// when every step in scope has actually succeeded. If this holds, no
// combination of step failures can produce a usable-looking flow.
property p_done_needs_all_ok;
  @(posedge clk) disable iff (!rst_n)
    calib_done |-> ((step_ok | (retrain_active ? ~PERIODIC_MASK
                                               : {NUM_STEPS{1'b0}}))
                      == {NUM_STEPS{1'b1}});
endproperty
assert property (p_done_needs_all_ok);

// P2 -- done and failed are mutually exclusive at the flow level, as
// they are at the step level in every chapter of this module.
property p_done_xor_failed;
  @(posedge clk) disable iff (!rst_n)
    !(calib_done && calib_failed);
endproperty
assert property (p_done_xor_failed);

// P3 -- a step is never started without its mode acknowledged (§4's
// callout). Starting a step against the previous step's device mode is
// the integration bug this property exists to forbid.
property p_no_start_without_mode;
  @(posedge clk) disable iff (!rst_n)
    (|step_start) |-> mode_ack;
endproperty
assert property (p_no_start_without_mode);

// P4 -- exactly one step is ever started at a time.
property p_start_is_onehot;
  @(posedge clk) disable iff (!rst_n)
    (|step_start) |-> $onehot(step_start);
endproperty
assert property (p_start_is_onehot);

// P5 -- the step walk only ever moves forward within a flow. A
// sequencer that could step backwards could re-run a step whose mode
// has already been left.
property p_step_advances_only;
  @(posedge clk) disable iff (!rst_n)
    ((step != $past(step, 1)) && ($past(state_out, 1) == 3'd3))
      |-> (step == $past(step, 1) + 1);
endproperty
assert property (p_step_advances_only);

// P6 -- a retrain preserves the previous flow's record (§8). ok_mask
// must not be cleared on entry to a retrain pass.
property p_retrain_preserves_record;
  @(posedge clk) disable iff (!rst_n)
    ($past(state_out, 1) == 3'd6) |-> (step_ok == $past(step_ok, 1));
endproperty
assert property (p_retrain_preserves_record);

// P7 -- a failure is recorded with its step number before the flow
// fails, so the §9 diagnostic is never lost.
property p_failure_is_attributed;
  @(posedge clk) disable iff (!rst_n)
    $rose(calib_failed) |-> have_failure;
endproperty
assert property (p_failure_is_attributed);

// P8 -- retries are bounded. An unbounded retry would hang the flow
// on a marginal channel rather than reporting it.
property p_retries_bounded;
  @(posedge clk) disable iff (!rst_n)
    (retries_used <= RTY_W'(MAX_RETRY));
endproperty
assert property (p_retries_bounded);

// ── Cover.
cover property (@(posedge clk) disable iff (!rst_n) $rose(calib_done));
cover property (@(posedge clk) disable iff (!rst_n) $rose(calib_failed));
// A retry succeeded after a failure: §9's marginal-observable case,
// which is worth knowing happened.
cover property (@(posedge clk) disable iff (!rst_n)
                  (state_out == 3'd3) && (retries_used != '0));
// A full retrain pass completed, skipping the non-periodic steps.
cover property (@(posedge clk) disable iff (!rst_n)
                  calib_done && retrain_active);
// Each step was the failing one at least once, across the regression.
cover property (@(posedge clk) disable iff (!rst_n)
                  $rose(calib_failed) && (failed_step == '0));
cover property (@(posedge clk) disable iff (!rst_n)
                  $rose(calib_failed) && (failed_step == STP_W'(NUM_STEPS - 1)));

P1 is the property that matters most, and it is the flow-level counterpart of the done-versus-valid separation every step carries. P3 is the one that catches the integration bug §4's callout describes, and it is not checkable inside any individual step — only here, where mode and step meet.

P6 deserves its place because the behaviour it protects is easy to break with a well-intentioned tidy-up. Clearing state on entry to a pass looks like good hygiene and would discard the working settings of a live interface.

12. Corner Cases

CaseBehaviourWhy
Step 0 fails with MAX_RETRY = 0calib_failed, failed_step = 0No partial flow; the interface never becomes operational
Step fails, retry succeedsFlow continues; the retry cover firesLegitimate, and the retry count is published as a diagnostic
retrain_req while in S_RUNIgnored until S_DONEA retrain must not interrupt a step mid-sweep
retrain_req after a failureIgnored; S_FAILED goes to S_IDLEThere is nothing to preserve
PERIODIC_MASK = 0 on a retrainEvery step skipped; reaches S_DONEVacuously successful, and honest: nothing was re-measured
mode_ack never returnsFlow waits in S_PREPA mode that cannot be set is not a condition to guess past
Two steps assert doneerr_multi_doneThe one-hot start contract was violated
A step asserts done and failederr_done_and_failedEach step's own P3 forbids it locally
start_flow during a flowIgnored outside S_IDLEWould interleave two flows' records
Retrain after successok_mask preserved, periodic steps re-run§8's callout

The fifth row is worth stating explicitly rather than treating as degenerate. A retrain with an empty periodic mask reaches DONE without measuring anything, and P1 still holds because the non-periodic steps are excused by the mask. That is correct and it is also a configuration worth noticing in review — it is a flow that reports success for doing nothing.

13. DV — Model the Steps, Check the Policy

The checker replaces all five searches with programmable stubs and verifies the sequencer's policy. That is the natural decomposition: each search already has its own independent checker in its own chapter, and what remains unverified is the ordering and propagation.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Programmable step stub. The testbench decides each step's outcome,
// so every ordering and failure path is reachable deterministically.
// SIMULATION-ONLY.
class step_stub;
  int unsigned latency;       // cycles from start to done
  bit          will_fail;     // outcome
  int unsigned fail_times;    // fail this many attempts, then pass
  int unsigned attempts;

  function bit outcome();
    attempts++;
    return (attempts <= fail_times) ? 1'b1 : will_fail;
  endfunction
endclass
CheckWhat it establishes
All five pass; expect calib_done and step_ok all onesThe nominal flow
Step k fails with MAX_RETRY = 0; expect failed_step == k for every k§9's attribution, at every position
Step k fails once with MAX_RETRY = 1; expect success and retries_used != 0Bounded retry
Step k fails twice with MAX_RETRY = 1; expect calib_failedThe bound is actually enforced
Record the order of step_start pulses; expect strictly 0,1,2,3,4§4's order, independently observed
Withhold mode_ack; expect no step_start ever assertsP3, driven rather than assumed
Assert retrain_req mid-S_RUN; expect it to take effect only after DONE§12's third row
Run a retrain; expect step_start only for steps in PERIODIC_MASK§8's scoping
Run a retrain; expect step_ok unchanged at entryP6, independently checked
Fail a step during a retrain; expect calib_failed and check what step_ok holds§8's callout — the interesting case

The last check is the one that exposes a genuine design question rather than a bug:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  RETRAIN FAILURE -- WHAT SHOULD SURVIVE?
    flow 1 : all five steps pass. calib_done. interface working.
    PERIODIC_MASK = 5'b11100 (steps 2, 3, 4 re-run)

    retrain pass : step 2 passes and REPLACES its committed result.
                   step 3 FAILS twice with MAX_RETRY = 1.

    sequencer under test:
      calib_failed asserted, failed_step = 3
      step_ok      = 5'b11111  (preserved from flow 1)
      step 2's search has ALREADY overwritten its committed value
      step 3's search holds its own safe state -- trained_valid LOW

    the resulting interface state:
      step 0, 1, 4 settings : from flow 1, still valid
      step 2 setting        : NEW, from the retrain, validated
      step 3 setting        : INVALID -- its search failed

    diagnosis : the interface is now in a state that NEITHER flow
                produced. Step 2's new value was validated against
                step 3's OLD value, and step 3 no longer has one.
    why no assertion fires : every property in §11 holds. P6 preserved
                the record, P7 attributed the failure, P1 is not
                claiming done. The sequencer did exactly its job.
    the real question : whether a failed retrain should ROLL BACK the
                steps that already succeeded within it. This RTL does
                not, and that is a policy choice rather than a bug --
                but it must be a CONSCIOUS one, because the resulting
                mixed state is not a state any complete flow produced.

That report is the most useful thing the sequencer's testbench produces. It is not a defect in the RTL — it is a consequence of retraining a subset of a flow whose steps are not independent, and a system that cares must either roll back or re-run the whole flow rather than the periodic subset. Naming it is worth more than silently picking one side, and it is the kind of finding that only appears when the ordering is verified separately from the searches.

14. Debugging

SymptomLikely causeHow to confirm
calib_failed, failed_step = 0Write levelling — 21.1 §12Strobe arriving? MR1 A7 actually set?
calib_failed, failed_step = 1Gate training — 21.3 §12Round trip within MAX_OFFSET? Modes entered?
calib_failed, failed_step = 2Read levelling — 21.2 §15Gate trained? Burst framing? Serial format?
calib_failed, failed_step = a centring stepSeed or a narrow axis — 21.4 §14Which axis's width failed
Flow hangs in S_PREPmode_ack not returningWhoever issues MRS commands is stalled
Flow hangs in S_RUNA step's done never assertsThat step's own probe path — each chapter's last debug row
Flow passes, traffic fails immediately§11's callout — passing is not marginCompare per-step region widths against expectations
Flow passes, traffic fails after warm-upSettings drifted; retrain policy too sparseCheck PERIODIC_MASK against §8's table
Retrain fails, interface degrades§13's mixed-state caseRead step_ok alongside each search's trained_valid
err_multi_doneIntegration: more than one step wired to respondThe one-hot start contract
Works with saved settings, fails on a full flowA step's search is broken, not the settingsThe saved values came from a flow that worked once

The first four rows are §9's argument paying off: because the sequencer publishes failed_step, each failure routes directly to one chapter's debugging table instead of to a general investigation.

The last row is a genuinely useful diagnostic. A system that boots fine from restored settings and fails when it trains from scratch has isolated the fault to the search, not the channel — because settings that work are proof a valid operating point exists.

15. Misconceptions

“The training order is a vendor convention.” §4. It follows from which observable each step needs. A flow that trains write DQ before the read path works has no way to check its own result.

“Write-direction steps come after read-direction steps.” Write levelling comes first, because its observable is the only one that bypasses the read path — the standard specifies no read strobes for its feedback.

“Training and calibration are the same thing.” §1. This curriculum treats training as search against an observable, and calibration as the wider establishment of operating state — which includes internally referenced procedures like ZQ calibration that involve no search.

“A failed step can be skipped.” §9. Later steps' observables depend on earlier steps' results, so skipping produces a cascade whose first cause is now several steps back.

“Retries are a sign of a robust flow.” A retry that succeeds after an identical failure means the observable was marginal. It is worth recording, not hiding.

“Retraining should start from a clean slate.” §8. Clearing state discards working settings on a live interface. trained_valid is separate from phy_operational in 19.5 precisely so a retrain can be abandoned safely.

“Every step needs periodic retraining.” §8. A coarse gate offset is a quantised cycle count and is exactly repeatable; continuous quantities drift and need revisiting.

“A passing flow means the interface has margin.” §11's callout. It means each search found a setting defensible on its own terms with the observables it had.

“Saved settings make training unnecessary.” They make a full flow unnecessary at boot, and they were measured under conditions that may no longer hold. Revalidation is the trade.

“A failed retrain leaves the previous working state.” §13 shows it need not: steps that already succeeded within the retrain have replaced their values, so the interface can end in a mixed state no complete flow produced.

16. Interview Reasoning

Why does write levelling run first when it is a write-direction procedure? Because its observable bypasses the read path entirely — the device drives feedback on DQ asynchronously with no read strobe — so it is the only write-side step that needs nothing trained beforehand.

Why must write DQ centring run last? Because the only way to observe whether a write landed correctly is to read it back, so it depends on the entire read path being trained.

Why does gate training precede read levelling? Gate training's observable is presence, which needs no correct data. Read levelling's observable is correctness, which needs the strobe admitted. And a wrong gate makes read levelling report an empty region indistinguishable from a dead channel.

What is the difference between training and calibration in this curriculum? Training determines a setting by interacting and observing. Calibration is the broader establishment of operating state and includes internally referenced procedures — ZQ calibration against a real resistor — that have no search at all.

A step fails. Why not skip it and continue? Because later steps depend on earlier results. Skipping does not give a partially trained interface; it gives a cascade of failures whose root cause is now several steps upstream.

Why is failed_step worth publishing? Because the four searches fail for entirely different reasons, and the step number routes the investigation immediately instead of leaving “training failed” as the only clue.

Which steps need periodic retraining, and why not all of them? The ones whose measured quantity is continuous. A coarse gate offset is a whole number of cycles — a beat either landed in the window or not — so it is stable; delay codes and reference levels drift with temperature and voltage.

Why does a retrain not route through IDLE? Because IDLE clears the record. During a recalibration pass the previous settings must remain valid and in use, which is exactly why 19.5 separates trained_valid from phy_operational.

A retrain fails halfway. What state is the interface in? Possibly one no complete flow produced: steps that already re-ran hold new values, the failed step holds none, and the rest hold values from the original flow. Whether to roll back is a policy decision that has to be made deliberately.

The system boots fine from saved settings and fails when trained from scratch. What does that tell you? That a valid operating point exists, so the fault is in the search rather than in the channel.

17. Exercises

  1. Set PERIODIC_MASK to include step 0 and run a retrain in which step 0 fails. Compare the resulting interface state with §13's report. Does including write levelling in the periodic set make the mixed-state problem better or worse, and why?

  2. Add a rollback: on a failed retrain, restore each periodic step's previous committed value. Specify the interface each search would need for that, and identify what the searches in 21.1 through 21.4 would have to store that they currently do not.

  3. The step order is a compile-time constant. Write the property that would detect an illegal order if it were made configurable — using only the dependency facts of §3 — and explain why an assertion is a weaker safeguard here than a constant.

  4. PERIODIC_MASK = 0 on a retrain reaches DONE having measured nothing, and P1 holds. Argue whether that should instead be a failure, and write the property that would enforce your answer.

  5. Using §13's stubs, construct the sequence that makes the retry cover fire on step 3 while failed_step ends up reporting step 4. What does the existence of that sequence tell you about interpreting retries_used after a failure?

  6. §8 claims quantised measurements are stable and continuous ones drift. Test the claim against the coarse gate offset: identify a physical change that would shift it by a whole cycle, and say what that implies for how often it should be revalidated.

  7. The sequencer contains no training. List everything it would need to absorb to also perform step 0's search, and argue from 19.1 §3 whether a combined block would be a better or worse fit for real implementations.

  8. Every search in this module can fail in a way where the observable never changed across its sweep. Design a single flow-level check the sequencer could apply to all five steps that would flag this, given only the done/failed/valid contract of §2. What would each step have to add to its interface?

18. Module 21, Complete

Five chapters, one question: how does a system discover settings that make a DDR interface work?

The observable determines the algorithm. Chapter 21.1 had the best one — a device acting as a phase comparator, reporting which side of CK the strobe landed on — and got the simplest search: one transition, an early exit. It also established the machinery the rest of the module reuses: the experiment loop, the filter over repeated probes that makes a metastable boundary survivable, and the candidate-versus-committed discipline that keeps an experimental setting out of the production datapath.

A correctness bit is much weaker than it looks. 21.2 substituted “did the data match” for a phase report and paid for it everywhere: two edges instead of one transition, a mandatory full-domain sweep because the pass region may be fragmented, a minimum-width filter, and an empty region that six unrelated faults produce identically. It also found, in the four published MPR patterns, a complete binary lane identifier hiding in plain sight — and the fact that the read format decides whether a pattern can train timing at all.

Some searches have no standard at all. 21.3 searches for a window the DRAM does not know exists; JESD79-4 contains no gate concept, and what it offers instead is a preamble the PHY can find. That search's pass region is one-sided — bounded on one side by lost beats and on the other by a cost no digital observable reports — which is why its selection policy deliberately does not centre, and why 19.4's LEAD_CYCLES is a purchase rather than a computation.

A second axis breaks the arithmetic. 21.4 turned a line into a grid, and with it the probe count from a sum into a product and the notion of a centre into something undefined — because no conversion exists between a delay code and a reference-level code. It is also where the module's honesty was most tested and best supported: the Vref code table is exact to a hundredth of a percent while the standard pins the physical step it maps to only to a 0.5%–0.8% band, the two ranges share no reachable voltage at all, and JEDEC says of its own receiver mask that it is not the valid data eye.

And the order is derivable. This chapter's claim is that the sequence is not a convention: a step runs as soon as its observable is available. That puts a write-direction step first, because write levelling's feedback needs no read strobe, and a write-direction step last, because checking a write means reading it back. The derivation predicts the order real controller documentation specifies.

The discipline every chapter repeated, and the reason this module could not be written any other way: training does not create margin. It finds a setting inside margin that already exists. Every search here can be correct, terminate, commit a value and assert validity — and still be pointing at a setting that fails, because the quantity that decides is physical and no property in five chapters reaches it. 20.3 §12 remains the concrete form: every observable correct, the data wrong, nothing firing.

What Module 21 did not do is explain why any pass region is the shape it is. Every chapter observed shapes — narrow regions, fragmented regions, tilted grids, per-lane spread, one-sided boundaries — and each one handed the cause onward.

Module 22 takes it up: ODT, termination, impedance, reflections and the signal-integrity reasoning that explains why the region a training flow measures is narrower in a real system than in any datasheet — and why it sometimes has holes in it.

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.