DDR · Module 28
DDR Silicon Bring-Up
Every investigation in this module was expensive because many variables had changed before anyone looked. Staging is the discipline that makes the suspect list short by construction.
Every chapter in this module began the same way: a system already running, a failure already present, and a suspect list containing everything.
Chapter 28.1 needed five experiments to establish that a bitmap meant anything. Chapter 28.2 reconstructed a violated contract from history that might have been recorded from the wrong signal. Chapter 28.5 had sixteen axes because one wrong byte is consistent with all of them.
Those investigations were expensive largely because many variables had changed at once before anyone looked.
Bring-up is the discipline that prevents it, and the central question is not how do I make it work. It is:
HOW DO YOU MOVE A DDR SYSTEM FROM UNKNOWN BEHAVIOUR TO LOCALIZED, REPRODUCIBLE, EXPLAINABLE BEHAVIOUR?
The answer is staging: establish each layer, believe it for a stated reason, and only then let the next layer depend on it. When a failure appears at stage seven, the suspect list is stage seven plus whatever stage six's gate failed to establish — short by construction rather than by investigation.
1. Why Order Is the Whole Method
State the argument once, precisely.
A DDR subsystem is a stack of dependencies. Training depends on calibration; a meaningful transaction depends on training; address expansion depends on the map being right; refresh interaction depends on traffic working at all.
So a failure at any layer has a suspect list that includes every layer beneath it — unless those layers were established and believed first.
| Approach | Suspect list when a failure appears at stress traffic |
|---|---|
| Configure everything, run random traffic | clocks, reset, configuration, PHY, calibration, training, map, bursts, banks, refresh, frequency, channel |
| Staged, each gate satisfied | stress traffic, plus whatever the previous gate failed to establish |
That difference is worth days per failure, and it compounds: every failure found during bring-up is found with a short list, so each is cheaper than the last would have been.
And the second column's caveat is the honest part. A stage gate establishes something for a stated reason, and the reason is always partial. §3 is about what each gate actually establishes, because a gate believed to prove more than it does reintroduces the long suspect list silently.
2. The Stages
The order, with the DDR-specific gate at each. CURRICULUM-DERIVED from the owners named in each row.
| # | Stage | Gate — what must be true to proceed | Owner |
|---|---|---|---|
| 0 | Clocks, reset, register access | a known register reads its known reset value, and a written value reads back | 6.8 |
| 1 | Controller configuration | every timing and geometry register reads back what provenance says it should — §6 | Module 17 |
| 2 | PHY initialisation and calibration | 19.5's five events completed; 28.4's four fields clean | 19.5 |
| 3 | Training | every step started, converged, committed, and its window is not one tap wide | Module 21, 28.1 |
| 4 | One deterministic transaction | one write, one read, one address, one pattern — and it matches | Module 27 |
| 5 | Address expansion | a walking-bit address sweep, checked against the map's inversion | 8.6 |
| 6 | Lane, burst and pattern variation | every lane, every beat position, several transition densities | Module 12, 28.6 §4 |
| 7 | Bank and rank expansion | multi-bank interleaving, then multiple ranks | Module 16, 5.4 |
| 8 | Refresh, idle and power interaction | refresh running with traffic; idle and power transitions exercised | Module 15, 28.3 |
| 9 | Frequency expansion | each supported frequency, retrained, with provenance re-verified | 28.4 §5 |
| 10 | Stress traffic | randomised, concurrent, long-duration | Module 23 |
Stage 4 is the pivot and deserves its own emphasis. Everything before it establishes that the machinery exists; stage 4 is the first moment anything is known to work. A bring-up that reaches stage 10 without ever having done stage 4 in isolation has no baseline at all.
And stage 10 is last for a reason that is often reversed in practice. Random stress traffic finds failures efficiently — and it finds them with the longest possible suspect list, which is the opposite of what bring-up is for. Its place is to find what the staged sequence missed, not to be the sequence.
3. What a Gate Actually Establishes
The most dangerous thing in a staged bring-up is a gate believed to prove more than it does.
| Gate | Establishes | Does not establish |
|---|---|---|
| Register reads back | the access path works | the value is correct — §6 |
Calibration done | a flag is set | that it started, converged or committed — 28.4 §1 |
| Training passed | a setting was committed | that the window is wide — 28.1 §5 |
| One transaction matched | one address, one pattern, one lane set works | anything about other addresses |
| Address sweep clean | the map is self-consistent over that sweep | that the map matches the device's geometry |
| All lanes exercised | each lane carried data | that each has margin |
| Refresh running | commands are issuing | that deadlines are met — 28.3 §3 |
| A frequency passes | that frequency works now | that its settings are not stale — 28.4 §5 |
Row two is the one that costs the most. Chapter 28.4 showed that done implemented as !busy reads true from reset — so a stage-2 gate that checks only done lets an entire uncalibrated PHY through, and the failure surfaces at stage 3 or 4 with training or the first transaction blamed.
Row three is the second most expensive. A committed setting at the centre of a one-tap window passes every digital check and fails under real traffic. Chapter 28.1 §5 is explicit that a one-tap window means the window is at or below the step size, and the gate must therefore check the width, not merely that a setting was chosen.
So a gate is a statement with a stated limit, and §15's ledger records both — because a bring-up that reaches stage 10 needs to know, when something fails, exactly what each earlier gate did and did not establish.
4. The One-Variable Rule
The rule that makes every stage transition interpretable.
Change exactly one controlled variable between runs. The difficulty is that one variable is not obvious, and several bring-up actions change more than one.
| Action | Variables actually changed |
|---|---|
| Raise the frequency | period, every derived timing count, calibration validity, channel behaviour, accesses per second |
| Enable a second rank | loading, termination configuration, address space, arbitration |
| Retrain | every committed setting, and the state the device is left in |
| Enable refresh | traffic interruptions, bus occupancy, thermal profile |
| Switch the address map | which bank each address hits, row-hit rate, bank parallelism |
| Enable a wider burst | data phases per command, bus occupancy, some spacing feasibility |
Not one of those is a single variable, and that is the point: "change one thing" is a goal that requires decomposition rather than discipline alone.
The practical technique is to change the compound variable and then, if the result is surprising, decompose it. Raising the frequency is one bring-up step; if it fails, the decomposition is: re-derive the timing counts by hand (28.2 §11), retrain at the new frequency (28.4 §5), and re-measure per access (28.6 §2) — three sub-experiments that isolate three of the five things the one step changed.
And the rule has a corollary that is easy to violate under pressure: do not change a variable and a stage at the same time. Moving from stage 7 to stage 8 while also raising the frequency produces a failure whose suspect list is both, and the whole method collapses for that failure.
5. Configuration Provenance as a Bring-Up Artefact
Chapter 28.2 §11 walks the chain from a device figure to an applied count, and treats it as a debugging procedure. During bring-up it is cheaper and more valuable: build the chain once, as a record, before stage 1's gate.
| Link | What the record must contain |
|---|---|
| Device specification figure | the figure, its units, and its condition — including whether it is a max(nCK, ns) form |
| Frequency bin | which bin this run uses |
| Conversion | the arithmetic, and the rounding direction used |
| Register encoding | the encoded value and the decode back to a count |
| Readback | the value read from the register after configuration |
| Interpretation | command clocks or controller clocks |
Stage 1's gate is that every row's readback matches its conversion, which turns 28.2's most tedious investigation into a one-time table.
CURRICULUM-DERIVED from 28.2 §10: a minimum time requirement must round up, and a floor is invisible wherever the division is exact. So the provenance record must state the rounding direction explicitly — because a run that happens to use a frequency where every division is exact will pass a readback gate with a rounding bug present.
The bring-up payoff is specific and large. Every later chapter's investigation asks is the applied value right? A provenance table answers it in seconds and, more importantly, answers it the same way every time — which is what makes 28.6 §1's exclusion recordable rather than re-argued.
6. Stage 4 — The First Transaction
The most important stage, and the one most often skipped because it looks trivial.
One write, one read, one address, one pattern, one burst, one bank. Everything else disabled or minimised.
What makes it valuable is not that it passes. It is that it is the only configuration in which a failure has almost no suspect list — and that it establishes a baseline every later stage is compared against.
| Choose | Why |
|---|---|
| An address whose map decomposition you can state | so a failure names a bank, row and column — 8.6 |
| A pattern with a known transition count | so 28.6 §4's axis has a defined value |
| The shortest legal burst | so a beat-position failure has fewer positions — 12.1 |
| One bank, kept open | so no activate/precharge timing is exercised yet |
| Refresh disabled if legal for the duration | so 28.3's axis is out of the picture |
And run it enough times to establish a rate, not once. Chapter 28.5 §3's argument applies here first: a single passing transaction and a transaction that fails one time in ten thousand are indistinguishable in one attempt. The gate is a rate over a stated count, and that number becomes the baseline every later stage's rate is compared against.
7. Stages 5 to 7 — Expanding Along One Axis at a Time
Each expansion adds one dimension of 28.5 §2's axis table, deliberately.
Stage 5 — address expansion. A walking-bit sweep exercises each address bit independently. CURRICULUM-DERIVED from 8.6, which owns the map and its inversion: the gate is that every accessed address decomposes to the bank, row and column you predicted, verified by the inversion rather than by the data matching.
That distinction matters. Data matching proves the round trip worked; the inversion proves the map is what you think it is — and a map that is wrong in a self-consistent way passes a data check and fails at stage 7 when banks start interleaving.
Stage 6 — lane, burst and pattern. Three sub-axes, and the order within them matters: lanes first (each lane must carry data at all), then beat positions (via burst length variation — 12.1), then transition density (28.6 §4's profile at several levels).
Stage 7 — banks and ranks. Multi-bank interleaving first, because it exercises Module 16's parallelism and every cross-bank timing rule (14.7, 14.8) without adding electrical load. Ranks last within the stage, because adding a rank changes loading and termination as well as address space — §4's second row.
what each expansion ADDS to the suspect list, and what it
therefore tests
stage 5 address bits, the map, its inversion
stage 6 per-lane paths, beat positions, transition density
stage 7 cross-bank timing, bank-group rules, then rank loading
a failure appearing at stage 6 and not stage 5 implicates the
axis stage 6 added -- which is three sub-axes, and the sub-order
within the stage narrows it to one.
this is the entire value of staging, in three lines.8. Stages 8 and 9 — Interactions and Frequency
The two stages where previously-established things stop being true.
Stage 8 — refresh, idle and power. Refresh introduces traffic interruptions (28.3 §6's drain), idle introduces the long-gap cases (28.2 §12's stale history and counter wrap), and power transitions introduce the restore-versus-re-establish question (28.4 §5).
| Sub-stage | Gate | What it catches |
|---|---|---|
| Refresh with traffic | zero deadline misses and a clean error histogram by window position | 28.3 §9's restart bug |
| Long idle, then traffic | no violation on the first command after the gap | 28.2 §12's stale history |
| Power-state entry and exit | settings re-established, provenance tag matching | 28.4 §5 |
Stage 9 — frequency expansion, and this is the stage that invalidates the most. Every timing count changes, calibration validity changes, and the channel behaves differently.
So the gate has three parts and skipping any of them is how a frequency bug reaches production: the provenance table re-derived for the new bin (§5), a full retrain with its own window-width check, and a re-run of stages 4 through 8 at the new frequency.
That last requirement is the expensive one and it is not negotiable. A frequency that passes stage 10 without having passed stage 4 at that frequency has no baseline at that frequency, and every failure found there inherits the long suspect list the whole method exists to avoid.
9. A Stage Gate Failing
The moment the method earns its cost. ILLUSTRATIVE stage indices.
A gate fails, evidence is captured, and the system does not advance
10 cyclesadvance tracks gate_pass exactly, and that is the structural commitment. A bring-up in which advancing is a human decision rather than a gate result will advance past a marginal gate at four in the afternoon, and the cost lands three stages later.
evidence rises before recovery, and the ordering is the rule this chapter cares most about. §14 is about why.
And blocked staying high is what preserves the finding. A system that retried automatically and succeeded would leave no record that stage 3 ever failed — which is the module's fourth law in hardware form.
10. Evidence Capture Before Recovery
The rule most often broken, and the one with the highest cost when it is.
When something fails in a lab, the instinct is to recover: reset, reboot, retrain, power-cycle. Every one of those destroys the state that would explain the failure.
| Recovery action | Destroys |
|---|---|
| Reset | every debug register, all captured state, the failing configuration |
| Reboot | the above, plus the software state that produced the stimulus |
| Retrain | the committed settings that were in use when it failed — 28.1 |
| Power-cycle | all of the above, plus the thermal state |
So the rule is: capture first, recover second, and the capture list is short enough to be a checklist:
The first-error snapshots. Chapter 28.2 §15's violation snapshot, 28.5 §13's first-error capture, 28.1 §14's ledger. All three are designed to survive until an explicit clear, and that design exists for this moment.
The full configuration. Not the intended configuration — the configuration as read back, per §5.
The stage and the gate results. Which stage, which gate, and what every earlier gate established.
The rate and the duration. Chapter 28.5 §3's arithmetic requires both, and neither is recoverable after a reboot.
And the reason this is hard is worth naming. The pressure to recover is highest exactly when the failure is most interesting — a rare failure after eleven hours. A written capture checklist is what survives that pressure, and §15's ledger exists so that most of the list is captured automatically rather than remembered.
11. Minimal Reproducer Reduction, During Bring-Up
Chapter 28.5 §15 owns reduction as a post-failure technique. During bring-up it is cheaper, because the staged sequence has already done most of the work.
A failure at stage N is already minimal in every axis stage N had not yet added. That is the staging dividend: a stage-6 failure needs no address-range reduction, because stage 5's sweep is the range and stage 6 added lanes, beats and patterns.
| Failure appears at | Already minimal in | Reduce within |
|---|---|---|
| Stage 4 | everything | pattern, then burst length |
| Stage 5 | lanes, beats, banks, refresh, frequency | the address range |
| Stage 6 | banks, refresh, frequency | lane, then beat, then transition density |
| Stage 7 | refresh, frequency | banks, then ranks |
| Stage 8 | frequency | which interaction — refresh, idle, or power |
| Stage 10 | nothing | everything — which is why stage 10 is last |
The bottom row is the argument for the whole method in one line. A failure found by random stress traffic is minimal in nothing, and reducing it is the expensive investigation every earlier chapter described. A failure found at stage 6 arrives pre-reduced in five axes.
12. The Bring-Up Ledger
// ---------------------------------------------------------------------
// bringup_ledger -- CLASSIFICATION: synthesisable, BINDABLE.
//
// WHAT IT DOES: records the staged sequence -- stage, gate verdicts,
// evidence-before-recovery ordering, and the number of variables
// changed per stage transition.
//
// WHY advance IS GATED BY gate_pass (§9): a bring-up in which
// advancing is a human decision will advance past a marginal gate at
// four in the afternoon, and the cost lands three stages later. The
// ledger records any advance that happened WITHOUT a passing gate, so
// the violation is a number rather than a memory.
//
// WHY THE FIRST FAILING STAGE IS LATCHED: Module 28's second law. A
// later stage's failure is usually a consequence of an earlier gate
// that was not really satisfied, so the FIRST failing stage is the
// evidence.
//
// WHY recovery_without_evidence IS COUNTED (§14): reset, reboot,
// retrain and power-cycle each destroy the state that explains the
// failure. The pressure to recover is highest exactly when the
// failure is most interesting, and a counter survives that pressure
// better than a checklist.
//
// WHY vars_changed (§4): "change one thing" requires decomposition,
// and a transition that changed several variables makes the next
// failure's suspect list long again. Recording it makes the method's
// own violations visible.
//
// WHAT IT CANNOT TELL YOU: whether a gate's CRITERION was strong
// enough (§3). No register detects a gate that proves too little.
//
// SYNTHESIS: a stage register, counters, and a small per-stage array.
// ---------------------------------------------------------------------
module bringup_ledger #(
parameter int STAGES = 11, // §2's table, 0..10
parameter int VAR_W = 8
)(
input logic clk,
input logic rst_n,
input logic gate_eval,
input logic gate_pass,
input logic advance,
input logic [$clog2(STAGES)-1:0] stage,
input logic [VAR_W-1:0] vars_changed, // §4
input logic evidence_captured,
input logic recovery,
input logic clear,
output logic blocked,
output logic [15:0] gates_evaluated,
output logic [15:0] gates_passed,
output logic [15:0] gates_failed,
output logic [$clog2(STAGES)-1:0] first_fail_stage,
output logic first_fail_valid,
output logic [15:0] advance_without_pass,
output logic [15:0] recovery_without_evidence,
output logic [15:0] multi_var_transitions,
output logic [$clog2(STAGES)-1:0] max_stage_reached,
output logic [15:0] gate_result [STAGES]
);
// $clog2(1) is 0, making the stage vector illegal as [-1:0]. A
// single-stage bring-up is also not a staged bring-up.
initial begin
if (STAGES < 2) $fatal(1, "bringup_ledger: STAGES must be >= 2 (got %0d)", STAGES);
if (VAR_W < 1) $fatal(1, "bringup_ledger: VAR_W must be >= 1");
end
logic ev_seen_this_failure;
always_ff @(posedge clk) begin
if (!rst_n || clear) begin
blocked <= 1'b0;
gates_evaluated <= '0;
gates_passed <= '0;
gates_failed <= '0;
first_fail_stage <= '0;
first_fail_valid <= 1'b0;
advance_without_pass <= '0;
recovery_without_evidence <= '0;
multi_var_transitions <= '0;
max_stage_reached <= '0;
ev_seen_this_failure <= 1'b0;
for (int s = 0; s < STAGES; s++) gate_result[s] <= '0;
end else begin
if (gate_eval) begin
if (gates_evaluated != 16'hFFFF) gates_evaluated <= gates_evaluated + 1'b1;
if (gate_pass) begin
if (gates_passed != 16'hFFFF) gates_passed <= gates_passed + 1'b1;
// Per-stage tally: a stage whose gate was evaluated many
// times before passing is a stage that was nearly failed,
// which §3 says is worth knowing later.
if (gate_result[stage] != 16'hFFFF)
gate_result[stage] <= gate_result[stage] + 1'b1;
end else begin
if (gates_failed != 16'hFFFF) gates_failed <= gates_failed + 1'b1;
blocked <= 1'b1;
// §9, and the module's second law: the FIRST failing stage
// is the evidence. A later failure is usually a consequence.
if (!first_fail_valid) begin
first_fail_valid <= 1'b1;
first_fail_stage <= stage;
end
ev_seen_this_failure <= 1'b0;
end
end
// §9: advancing without a passing gate is the method's own
// violation, recorded rather than prevented -- this block
// observes a controller, it does not drive one.
if (advance && !(gate_eval && gate_pass)
&& advance_without_pass != 16'hFFFF)
advance_without_pass <= advance_without_pass + 1'b1;
if (advance) begin
if (stage > max_stage_reached) max_stage_reached <= stage;
// §4: a transition that changed more than one variable makes
// the next failure's suspect list long again.
if (vars_changed > 1 && multi_var_transitions != 16'hFFFF)
multi_var_transitions <= multi_var_transitions + 1'b1;
end
if (evidence_captured) ev_seen_this_failure <= 1'b1;
// §14: recovery before capture destroys the explanation. Counted
// per recovery event, and only while blocked -- a recovery
// during normal operation is not a violation of this rule.
if (recovery && blocked && !ev_seen_this_failure
&& recovery_without_evidence != 16'hFFFF)
recovery_without_evidence <= recovery_without_evidence + 1'b1;
// A recovery while blocked clears the block so the sequence can
// be retried -- but first_fail_stage and every counter survive,
// which is what makes the retry interpretable afterwards.
if (recovery && blocked) blocked <= 1'b0;
end
end
endmodulefirst_fail_stage latches and survives every recovery, which is the module's second law in one register. A bring-up that failed at stage 3, was recovered, and then reached stage 10 has a different meaning from one that reached stage 10 first time — and after the recovery nothing else in the system remembers the difference.
advance_without_pass records the method's own violation rather than preventing it. This block observes a controller; it does not drive one. Making the violation a number is what allows a post-mortem to ask whether the method was actually followed, which is otherwise a matter of recollection.
And recovery_without_evidence is counted only while blocked. A recovery during normal operation is not a violation of §14's rule, and counting it would make the number meaningless through noise.
13. What the Assertions Prove
// Bound to §15's ledger. Every property carries `disable iff`, and
// every antecedent is covered below -- 27.2 measured this curriculum
// at 78.53% implications, so a silent pass is otherwise
// indistinguishable from an unbound block.
module bringup_ledger_sva #(
parameter int STAGES = 11
)(
input logic clk, rst_n, clear,
input logic gate_eval, gate_pass, advance, evidence_captured, recovery, blocked,
input logic [$clog2(STAGES)-1:0] stage, first_fail_stage, max_stage_reached,
input logic first_fail_valid,
input logic [15:0] gates_evaluated, gates_passed, gates_failed,
input logic [15:0] advance_without_pass, recovery_without_evidence
);
// ---- P1. FORBIDDEN. A failing gate always blocks. §9: advancing
// past a marginal gate is how a cost lands three stages later.
property p_fail_blocks;
@(posedge clk) disable iff (!rst_n)
(gate_eval && !gate_pass) |=> blocked;
endproperty
assert property (p_fail_blocks)
else $error("ledger: a failing gate did not block");
// ---- P2. The first failing stage is latched and never changes
// without a clear. Module 28's second law.
property p_first_fail_sticky;
@(posedge clk) disable iff (!rst_n)
(first_fail_valid && !clear) |=> (first_fail_stage == $past(first_fail_stage));
endproperty
assert property (p_first_fail_sticky)
else $error("ledger: first_fail_stage changed after being latched");
// ---- P3. FORBIDDEN. A recovery does not clear the first-failure
// record. §14: recovery destroys everything else, and this must
// survive it.
property p_recovery_preserves_first_fail;
@(posedge clk) disable iff (!rst_n)
(first_fail_valid && recovery && !clear) |=> first_fail_valid;
endproperty
assert property (p_recovery_preserves_first_fail)
else $error("ledger: a recovery cleared the first-failure record");
// ---- P4. PARTITION. Passes plus failures equal evaluations. A gate
// that was evaluated and produced neither is a verdict nobody
// recorded.
property p_gate_partition;
@(posedge clk) disable iff (!rst_n)
(gates_passed + gates_failed == gates_evaluated);
endproperty
assert property (p_gate_partition)
else $error("ledger: %0d passed + %0d failed != %0d evaluated",
gates_passed, gates_failed, gates_evaluated);
// ---- P5. INVARIANT. Evaluations only advance on an evaluation.
property p_eval_only_on_eval;
@(posedge clk) disable iff (!rst_n)
(!gate_eval) |=> (gates_evaluated == $past(gates_evaluated));
endproperty
assert property (p_eval_only_on_eval)
else $error("ledger: gates_evaluated advanced with no evaluation");
// ---- P6. FORBIDDEN. An advance with no passing gate in the same
// cycle is recorded. §9 -- the method's own violation, made a number.
property p_advance_without_pass_counted;
@(posedge clk) disable iff (!rst_n)
(advance && !(gate_eval && gate_pass) && advance_without_pass != 16'hFFFF)
|=> (advance_without_pass == $past(advance_without_pass) + 1);
endproperty
assert property (p_advance_without_pass_counted)
else $error("ledger: an ungated advance was not recorded");
// ---- P7. FORBIDDEN. A recovery while blocked with no evidence
// captured is recorded. §14's rule, as a counter.
property p_recovery_without_evidence_counted;
@(posedge clk) disable iff (!rst_n)
(recovery && blocked && !evidence_captured && !$past(evidence_captured)
&& recovery_without_evidence != 16'hFFFF)
|=> (recovery_without_evidence >= $past(recovery_without_evidence));
endproperty
assert property (p_recovery_without_evidence_counted)
else $error("ledger: a recovery before capture was not recorded");
// ---- P8. INVARIANT. max_stage_reached is monotone. A high-water
// mark that fell would erase how far the bring-up actually got.
property p_max_stage_monotone;
@(posedge clk) disable iff (!rst_n)
(!clear) |=> (max_stage_reached >= $past(max_stage_reached));
endproperty
assert property (p_max_stage_monotone)
else $error("ledger: max_stage_reached decreased");
// ---- P9. INVARIANT. The stage index stays in range.
property p_stage_in_range;
@(posedge clk) disable iff (!rst_n)
(stage < STAGES);
endproperty
assert property (p_stage_in_range)
else $error("ledger: stage %0d outside STAGES", stage);
// ---- P10. INVARIANT. max_stage_reached bounds the current stage
// whenever an advance has occurred.
property p_max_bounds_stage;
@(posedge clk) disable iff (!rst_n)
(max_stage_reached >= stage) || (max_stage_reached == 0);
endproperty
assert property (p_max_bounds_stage)
else $error("ledger: stage exceeds max_stage_reached");
// ---- P11. A recovery while blocked releases the block, so the
// sequence can be retried. A block with no release would make the
// ledger a one-shot.
property p_recovery_releases_block;
@(posedge clk) disable iff (!rst_n)
(recovery && blocked) |=> !blocked;
endproperty
assert property (p_recovery_releases_block)
else $error("ledger: a recovery did not release the block");
// ---- P12. FORBIDDEN. Clear releases everything including the
// first-failure record -- and clear is the ONLY thing that does.
property p_clear_releases_all;
@(posedge clk) disable iff (!rst_n)
clear |=> (!first_fail_valid && !blocked && gates_evaluated == 16'd0);
endproperty
assert property (p_clear_releases_all)
else $error("ledger: clear did not release the record");
// ---- 27.2 §7: publish every antecedent.
cover property (@(posedge clk) disable iff (!rst_n) gate_eval);
cover property (@(posedge clk) disable iff (!rst_n) gate_eval && gate_pass);
cover property (@(posedge clk) disable iff (!rst_n) gate_eval && !gate_pass);
cover property (@(posedge clk) disable iff (!rst_n) advance);
cover property (@(posedge clk) disable iff (!rst_n) blocked);
cover property (@(posedge clk) disable iff (!rst_n) evidence_captured);
cover property (@(posedge clk) disable iff (!rst_n) recovery);
cover property (@(posedge clk) disable iff (!rst_n) recovery && blocked);
cover property (@(posedge clk) disable iff (!rst_n) clear);
cover property (@(posedge clk) disable iff (!rst_n) first_fail_valid);
cover property (@(posedge clk) disable iff (!rst_n) advance_without_pass != 16'd0);
cover property (@(posedge clk) disable iff (!rst_n) recovery_without_evidence != 16'd0);
cover property (@(posedge clk) disable iff (!rst_n) stage == STAGES - 1);
cover property (@(posedge clk) disable iff (!rst_n) max_stage_reached == STAGES - 1);
endmodule14. DV — Testing the Ledger
// SIMULATION-ONLY. Independent reference: it replays the event stream
// into a QUEUE and derives every count by rescanning, rather than
// accumulating incrementally. A different algorithm, so agreement is
// evidence -- 27.4 §15's argument.
class bringup_reference;
typedef struct {
int stage; bit eval; bit pass; bit adv;
bit evid; bit rec; int vars;
} ev_t;
ev_t log[$];
function void push(int stage, bit eval, bit pass, bit adv,
bit evid, bit rec, int vars);
ev_t e;
e.stage = stage; e.eval = eval; e.pass = pass; e.adv = adv;
e.evid = evid; e.rec = rec; e.vars = vars;
log.push_back(e);
endfunction
function int gates_evaluated();
int n = 0; foreach (log[i]) if (log[i].eval) n++; return n;
endfunction
function int gates_failed();
int n = 0; foreach (log[i]) if (log[i].eval && !log[i].pass) n++; return n;
endfunction
// Module 28's second law: the FIRST failing stage. Returns -1 when
// none failed, so "never failed" is distinct from "failed at 0".
function int first_fail_stage();
foreach (log[i]) if (log[i].eval && !log[i].pass) return log[i].stage;
return -1;
endfunction
function int advance_without_pass();
int n = 0;
foreach (log[i]) if (log[i].adv && !(log[i].eval && log[i].pass)) n++;
return n;
endfunction
function int multi_var_transitions();
int n = 0; foreach (log[i]) if (log[i].adv && log[i].vars > 1) n++; return n;
endfunction
// §14: a recovery is a violation only if it happens while blocked
// and no evidence was captured since the failure that blocked it.
function int recovery_without_evidence();
int n = 0; bit blocked = 0, evid = 0;
foreach (log[i]) begin
if (log[i].eval && !log[i].pass) begin blocked = 1; evid = 0; end
if (log[i].evid) evid = 1;
if (log[i].rec && blocked) begin
if (!evid) n++;
blocked = 0;
end
end
return n;
endfunction
function int max_stage_reached();
int m = 0; foreach (log[i]) if (log[i].adv && log[i].stage > m) m = log[i].stage;
return m;
endfunction
endclass| Check | What it establishes |
|---|---|
| Replay §9's trace through both; compare every output | Incremental ledger and rescanning reference agree |
| 30,000 random event streams through both | Two algorithms agree on all seven counts |
| No gate ever fails | Reference first_fail_stage() returns −1; ledger's first_fail_valid is 0 |
| Fail at stage 3, recover, reach stage 10 | first_fail_stage stays 3 — P2, P3 |
| Fail at stage 3, then at stage 7 | First record stays 3 — the module's second law |
| Advance with no passing gate | advance_without_pass rises — P6 |
Advance with vars_changed = 3 | multi_var_transitions rises — §4 |
| Recover while blocked, no evidence | recovery_without_evidence rises — P7 |
| Recover while blocked, evidence first | Counter unchanged — §14's rule satisfied |
| Recover while not blocked | Counter unchanged — not a violation of this rule |
Assert clear | Everything releases, including first_fail_valid — P12 |
STAGES = 1 | Elaboration fails — a single stage is not a staged bring-up |
Drive stage = STAGES | P9 fires |
| 70,000 gate evaluations | Counters saturate; partition still holds — P4 |
| Run with the bring-up controller disconnected | All 12 properties pass; all 14 covers empty |
Two reports close the module, and they are the two ways a bring-up looks finished and is not:
TWO PASSING BRING-UP REPORTS
(A) the ledger was never connected
all 12 properties PASS
gates_evaluated 0
gates_passed 0
gates_failed 0
first_fail_valid 0
max_stage_reached 0
------------------------------------------------
cover gate_eval 0 hits
... all 14 covers 0 hits
seven of the twelve properties are implications and never
armed. The five INVARIANTS -- the gate partition, the
monotone high-water mark, the stage range, the bound, and the
evaluation counter -- all pass on zeros.
"bring-up completed with no gate failures" is produced by this
run AND by a flawless one. gates_evaluated is the field that
separates them, and it is the first one to read.
(B) every gate passed and the method was never followed
gates_evaluated 11
gates_passed 11
gates_failed 0
first_fail_valid 0
max_stage_reached 10
------------------------------------------------
advance_without_pass 0
multi_var_transitions 7 <-- seven of ten transitions
recovery_without_evidence 0
every gate passed, nothing was ever blocked, and the system
reached stage 10. By the headline numbers this is a textbook
bring-up.
and SEVEN of the ten stage transitions changed more than one
variable. §4 says that makes each subsequent failure's suspect
list long again -- so the staging dividend §11 describes was
never actually earned, and a stage-6 failure here would NOT
arrive pre-reduced in five axes.
the gates also each passed first time, which §3 says is
exactly when a gate's criterion deserves scrutiny: a gate that
never nearly failed may be one that could not fail.
diagnosis : (A) is the vacuity case, and it is the module's last
appearance of a pattern that has appeared in all seven
chapters. (B) is subtler and more common: the ceremony of
staging without its substance. Both report success.
the fix : (A) read gates_evaluated. (B) read
multi_var_transitions -- and treat a gate that has never once
failed across a programme as a gate to review rather than a
gate to trust.Report (B) is the honest closing note for this module. A bring-up can satisfy every property, pass every gate, reach the final stage, and still have produced none of the localization the method exists to produce — because the value was never in the stages. It was in changing one variable at a time so that each failure's suspect list stayed short.
15. Proving a Repair
The last step, and the one the module's third law exists for.
A configuration change that makes a failure disappear is an experiment. Turning it into a repair requires three things, and the third is the one usually skipped.
A mechanism. Not what I changed, but why that change addresses the cause — stated in terms of the contract that was violated. Chapter 28.2's six fields, 28.3's three questions, 28.4's five failures: each gives a form the mechanism can be stated in.
A demonstration that the mechanism was present. Ideally: reintroduce the defect and show the failure returns. A repair whose defect cannot be reintroduced is a correlation, and it is worth saying so in the report rather than claiming more.
A regression at the right duration and scope. Chapter 28.5 §3's arithmetic sets the duration: long enough that the baseline rate would have produced enough failures for zero to be surprising. And the scope must be expanded, not the reproducer — because the reproducer was minimised, and a repair verified only on the minimal case has been verified on the case least likely to expose a side effect.
| Regression scope | Why |
|---|---|
| The minimal reproducer | proves the specific failure is gone |
| Every stage from 4 onward | proves the change did not break an earlier gate |
| Every frequency | §8 — a change may be right at one bin and wrong at another |
| The stress stage, at duration | proves it survives the case with no suspect list |
Row two is the one that catches a repair which fixed one stage and broke another, and it is cheap precisely because the staged sequence already exists as a runnable artefact. That is the final dividend of the method: the bring-up sequence is also the regression suite.
16. Corner Cases
| Case | Behaviour | Why |
|---|---|---|
STAGES = 1 | Elaboration fails | $clog2(1) is 0, and one stage is not a staged bring-up |
| Gate fails | blocked rises, advance stops | P1 — §9's structural commitment |
| Second failure after the first | First stage retained | P2 — the module's second law |
| Recovery while blocked | Block releases, record survives | P3, P11 |
| Recovery while not blocked | Not counted as a violation | §12 — otherwise the number is noise |
| Recovery before evidence capture | Counted | P7 — §14's rule |
| Advance with no passing gate | Counted, not prevented | §12 — the block observes, it does not drive |
| Transition changing 3 variables | multi_var_transitions rises | §4 |
| Every gate passes first time | Worth reviewing | §3, report (B) — a gate that never nearly failed |
| Stage 10 reached, 7 multi-variable transitions | Staging dividend not earned | report (B) |
| Frequency raised without re-running stage 4 | No baseline at that frequency | §8 |
gates_evaluated zero | The ledger was never connected | report (A) |
| 70,000 evaluations | Counters saturate; partition holds | P4 |
clear asserted | Everything releases | P12 |
Rows nine and ten are the two findings that only a ledger produces. Neither is a failure, and both mean the method delivered less than its report suggests.
17. Misconceptions
“Bring-up is a checklist.” §1. It is a dependency order with a gate at each step, and the gates are what make a later failure cheap to localize.
“Start with stress traffic to find bugs fast.” §2. It finds them with the longest possible suspect list. Its place is last, to find what the staged sequence missed.
“The gate passed, so that layer is established.” §3. A gate establishes something for a stated reason, and every reason is partial. Calibration done is the expensive example.
“I only changed one thing.” §4. Raising the frequency changes five things; enabling a rank changes four. One action is not one variable.
“Provenance is a debugging activity.” §5. Built once before stage 1, it answers every later chapter's is the applied value right? in seconds.
“One passing transaction means stage 4 is done.” §6. A single pass and a one-in-ten-thousand failure are indistinguishable in one attempt. The gate is a rate over a stated count.
“Reset it and try again.” §10. Reset destroys every debug register, the captured state and the failing configuration. Capture first.
“It passed on the retry, so we are fine.” The module's fourth law, and §12's first_fail_stage exists so the retry does not erase the record.
“The frequency passed stress traffic, so it is good.” §8. Without stage 4 at that frequency there is no baseline, and every failure there inherits the long list.
“The fix works — the failure is gone.” §15. A mechanism, a demonstration that the mechanism was present, and a regression at the right duration and expanded scope. Absence of the symptom is the weakest of the three.
“Verify the repair on the minimal reproducer.” §15. That is the case least likely to expose a side effect. Expand the scope, do not minimise it.
18. Interview Reasoning
Why stage a bring-up at all? Because a failure's suspect list is every layer beneath it. Staging makes the list short by construction rather than by investigation.
What is the first stage and its gate? Clocks, reset and register access — a known register reads its reset value and a written value reads back. Without it nothing else is observable.
Why is one deterministic transaction its own stage? It is the first moment anything is known to work, and it is the only configuration where a failure has almost no suspect list. It is also the baseline every later stage is compared against.
Why is stress traffic last? It finds failures efficiently and with the longest possible suspect list. Its job is to find what the staged sequence missed.
Give an example of a gate that proves less than it appears to. Calibration done. Implemented as !busy it reads true from reset, so a stage gate checking only that lets an entirely uncalibrated PHY through.
What counts as one variable? Less than one action. Raising the frequency changes the period, every derived timing count, calibration validity, channel behaviour and the access rate — five things.
How do you handle a compound step like a frequency change? Take it as one bring-up step, and if it fails, decompose: re-derive the timing counts, retrain at the new frequency, and re-measure per access.
Something fails after eleven hours. What do you do first? Capture. The first-error snapshots, the configuration as read back, the stage and gate results, and the rate with its duration. Every recovery action destroys some of that.
Why is the reproducer already minimal when a staged bring-up finds a failure? Because the stage that found it is minimal in every axis the earlier stages had not yet added. A stage-6 failure needs no address reduction.
How do you prove a repair? State the mechanism in terms of the violated contract, show the failure returns when the defect is reintroduced, and regress at the duration the baseline rate demands — across expanded scope, not the minimal reproducer.
19. Exercises
-
§2 gives eleven stages. Choose two adjacent ones and argue for swapping them, then say what the swap costs when a failure appears at the later of the two.
-
§3 lists eight gates and what each fails to establish. For the address-sweep gate, construct the map error that passes it and fails at stage 7.
-
§4 decomposes a frequency change into five variables. Design the smallest set of sub-experiments that isolates each, and state the total cost.
-
§5 requires the rounding direction in the provenance record. Construct the frequency at which a
floorbug is invisible for a given parameter, and say how you would choose a bin to expose it. -
§6's gate is a rate over a stated count. Derive the count needed to distinguish a working path from one failing at 10⁻⁶ with reasonable confidence, and state your assumption.
-
§12 counts
advance_without_passrather than preventing it. Argue for making the ledger drive the controller instead, then decide. -
Report (B) has seven multi-variable transitions and no failures. Write the paragraph you would put in the bring-up report, and say what you would do differently next time.
-
§15 requires reintroducing the defect. Construct the repair for which that is impossible, and say what the report should claim instead.
20. Where This Goes
Bring-up is the discipline that makes every other chapter in this module cheap. Stage the system so each layer is established and believed before the next depends on it; know what each gate does and does not establish; change one controlled variable at a time, remembering that one action is rarely one variable; build configuration provenance before the first transaction rather than during the first investigation; and capture evidence before recovering, because every recovery action destroys the state that would explain the failure.
Four results carry forward. Stage 4 is the pivot — one transaction, one address, one pattern, run enough times to establish a rate — and a bring-up that never did it in isolation has no baseline. A gate believed to prove more than it does reintroduces the long suspect list silently, and calibration done is the standing example. A failure found at stage N arrives pre-reduced in every axis stage N had not yet added, which is the staging dividend. And the bring-up sequence is also the regression suite, which is what makes §15's expanded-scope requirement affordable.
Module 28 as a whole has one result. Every chapter took a signature that looked like a verdict and turned it into evidence: a pass bitmap that is not an eye, a timing violation whose rule may be the wrong rule, a refresh correlation with four mechanisms, a done flag covering five failures, a corruption structure that correction can rewrite, and an escalation criterion with two clauses rather than one. In every case the instrument's most valuable output was a statement about its own limits — an unknown, a refusal, a count of what it did not check.
And the through-line from Module 27 is now complete. That module built instruments that report what they did not check. This module showed why: because the thing they did not check is where the next hypothesis lives, and an investigation that cannot see its own blind spots spends its time in them.
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
Training Failures
A pass bitmap is a projection of four independent failure domains onto one bit per setting. Eleven signatures, each consistent with several causes, and the experiment that separates them.
- Related topic
Timing Violations
A violation names its own evidence: two commands, one shared resource, a required separation and an actual one. Reconstructing those four facts is the whole investigation.
- Related topic
Refresh Issues
Three investigations wearing one name: a missed obligation, late service, or something else that merely correlates with refresh. Separating them is most of the work.
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.
