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.
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:
| Signal | Meaning |
|---|---|
start | Begin. Accepted only in the step's idle state. |
done | The step terminated. Says nothing about success. |
failed | It terminated without a usable result. |
trained_valid | It 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?
| Step | Device mode | Needs a working read path? | Needs a trained gate? | Produces |
|---|---|---|---|---|
| 21.1 Write levelling | MR1 A7 = 1 | No — §4 | No — §4 | Strobe-to-CK delay |
| 21.3 Gate training | MPR + MR4 A10 = 1 | No — presence only | No — it produces it | gate_offset, gate_trained |
| 21.2 Read levelling | MPR | It is the read path | Yes | Read sampling delay |
| 21.4 Read centring | MPR | Yes | Yes | Read delay + PHY reference |
| 21.4 Write centring | MR6 A7 = 1 | Yes — §4 | Yes | Write 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:
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 writesThe 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 freedom | Range of legitimate choice |
|---|---|
| Who executes the flow | Firmware on a core, a hardware engine, or the controller — 19.1 §3 |
| Granularity | Per rank, per byte lane, per bit (21.2 §8) |
| Search strategy within a step | Linear, bisecting, coarse-then-fine, coordinate descent |
| Probe counts and filters | PROBES, REPEATS, agreement thresholds |
| Selection policies | Offsets, guards, minimum widths, rounding |
| Which steps repeat periodically | §8 |
| Extra steps not in this module | Command/address training, DRAM internal calibrations |
| Whether results are stored and restored | Fast 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
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.
| Step | Measures | Drifts with | Typically |
|---|---|---|---|
| Write levelling | Strobe-to-CK at the device | Temperature, voltage | Once at bring-up; occasionally revisited |
| Gate training (coarse) | A round-trip cycle count | Little — it is quantised | Once |
| Gate training (fine) | Sub-cycle gate position | Temperature, voltage | Periodically |
| Read levelling / centring | Sampling position, reference | Temperature, voltage | Periodically |
| Write centring | Write position, device VrefDQ | Temperature, voltage | Periodically |
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 step | Most likely meaning |
|---|---|
| Write levelling | Strobe not arriving, mode not entered, or delay range too short |
| Gate training | Round trip outside the domain, or strobe not toggling |
| Read levelling | Gate wrong, burst misassembled, or no open region |
| Centring | Seed 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
// ---------------------------------------------------------------------
// 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);
endmoduleTwo 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
// 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
| Case | Behaviour | Why |
|---|---|---|
Step 0 fails with MAX_RETRY = 0 | calib_failed, failed_step = 0 | No partial flow; the interface never becomes operational |
| Step fails, retry succeeds | Flow continues; the retry cover fires | Legitimate, and the retry count is published as a diagnostic |
retrain_req while in S_RUN | Ignored until S_DONE | A retrain must not interrupt a step mid-sweep |
retrain_req after a failure | Ignored; S_FAILED goes to S_IDLE | There is nothing to preserve |
PERIODIC_MASK = 0 on a retrain | Every step skipped; reaches S_DONE | Vacuously successful, and honest: nothing was re-measured |
mode_ack never returns | Flow waits in S_PREP | A mode that cannot be set is not a condition to guess past |
Two steps assert done | err_multi_done | The one-hot start contract was violated |
A step asserts done and failed | err_done_and_failed | Each step's own P3 forbids it locally |
start_flow during a flow | Ignored outside S_IDLE | Would interleave two flows' records |
| Retrain after success | ok_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.
// 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| Check | What it establishes |
|---|---|
All five pass; expect calib_done and step_ok all ones | The 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 != 0 | Bounded retry |
Step k fails twice with MAX_RETRY = 1; expect calib_failed | The 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 asserts | P3, 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 entry | P6, 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:
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
| Symptom | Likely cause | How to confirm |
|---|---|---|
calib_failed, failed_step = 0 | Write levelling — 21.1 §12 | Strobe arriving? MR1 A7 actually set? |
calib_failed, failed_step = 1 | Gate training — 21.3 §12 | Round trip within MAX_OFFSET? Modes entered? |
calib_failed, failed_step = 2 | Read levelling — 21.2 §15 | Gate trained? Burst framing? Serial format? |
calib_failed, failed_step = a centring step | Seed or a narrow axis — 21.4 §14 | Which axis's width failed |
Flow hangs in S_PREP | mode_ack not returning | Whoever issues MRS commands is stalled |
Flow hangs in S_RUN | A step's done never asserts | That step's own probe path — each chapter's last debug row |
| Flow passes, traffic fails immediately | §11's callout — passing is not margin | Compare per-step region widths against expectations |
| Flow passes, traffic fails after warm-up | Settings drifted; retrain policy too sparse | Check PERIODIC_MASK against §8's table |
| Retrain fails, interface degrades | §13's mixed-state case | Read step_ok alongside each search's trained_valid |
err_multi_done | Integration: more than one step wired to respond | The one-hot start contract |
| Works with saved settings, fails on a full flow | A step's search is broken, not the settings | The 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
-
Set
PERIODIC_MASKto 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? -
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.
-
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.
-
PERIODIC_MASK = 0on a retrain reachesDONEhaving measured nothing, and P1 holds. Argue whether that should instead be a failure, and write the property that would enforce your answer. -
Using §13's stubs, construct the sequence that makes the retry cover fire on step 3 while
failed_stepends up reporting step 4. What does the existence of that sequence tell you about interpretingretries_usedafter a failure? -
§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.
-
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.
-
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/validcontract 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
Related tutorials
- Related topic
PHY Calibration
Reset release is not readiness. A PHY becomes usable only after clocks settle, initialisation completes and calibration establishes settings that cannot be computed — some of which stop being correct while the system runs.
- Related topic
Mobile-Power Management
LPDDR4X lowers two rails and deletes a third. Two of four LPDDR5X rails go unused in self refresh — so gating them saves nothing in the memory, and a bad shutdown order survives ten thousand clean power cycles.
- Related topic
Calibration Failures
Five failures hide behind one done flag — never started, never converged, never committed, no longer valid, and valid but insufficient. One of them has no digital evidence at all.
- Related topic
The Refresh Requirement
Leakage produces a rule about the passage of time rather than about any operation. What the maintenance operation actually does, why it costs device availability, and how a digital design tracks a deadline, arbitrates it against traffic, and proves it never silently drops the obligation.
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.
