DDR · Module 33
PHY Review Checklist
A PHY's correctness lives in registers whose contents were measured rather than written, so every item is a question about provenance: what measured this, against what, when, and what happens when that measurement stops being true.
Chapter 33.2 asked what each line does. This gate asks about a value that no line contains.
A controller's correctness is in its code. A PHY's correctness is in its registers — and the contents of those registers were measured, not written. Chapter 19.5 §2 states the consequence in one sentence, and it is the reason this gate exists at all: “A DDR PHY is not usable when reset is released. It becomes usable after a sequence of steps that establish values which cannot be calculated — and some of those values stop being correct while the system is running.”
What measured this value, against what, when — and what happens to the part when that measurement stops being true?
Every item below is a review question whose answer is not in the RTL. The code that applies a trained delay is trivial and correct in every build in this chapter. What is wrong is the value, its provenance, its lifetime, or the design's belief about it — and none of those is visible in the line that uses it.
This changes what a review can conclude. At 33.2's gate, five of nine items were decided by reading source text. Here, one is — and the rest require the reviewer to demand an artifact the design does not contain: a provenance table, a reset-domain map, a measured window width, a drift budget.
1. How To Use This Chapter
Each of the nine review items answers the same eight questions.
| Facet | What it settles |
|---|---|
| Under review | the value, flag or search being examined |
| Invariant at risk | the property that breaks if it is wrong |
| Where it lives | the register, and the step that wrote it |
| Evidence to demand | the artifact the reviewer asks for by name |
| What escapes | the bug that reaches silicon |
| How DV proves it | the stimulus that would falsify it |
| Telemetry | what exposes it after tapeout |
| Misleading evidence | what makes the broken value look correct |
The misleading evidence at this gate has a shape all its own, and it is the most dangerous of the eight. At the architecture gate it is a reassuring document; at the RTL gate a reassuring waveform. Here it is a part that boots.
CURRICULUM-DERIVED from 28.4 §2, which names it precisely: a calibration done flag can mean five different things, and the worst of the five “produces a system which boots, passes its own self-checks, and corrupts data hours later.” Every item below has a build that boots. Seven of the nine have a build that boots, trains, reports success, and passes a functional test.
2. The One-Sentence Model
A PHY review is sound when every quantity in the timing path is classified as calculated or measured with no third category, when every completion flag's five possible meanings are distinguishable, when every trained value gates its own consumer closed by default, when each trained register's surviving reset matches the retraining that reset triggers, when every periodic recalibration's preconditions are enforced rather than assumed, when every search can tell an exhausted range from an absent margin, when every selection rule matches the shape of its pass region, when every margin names the measurement it came from, and when every level and delay is assigned to a direction and a side — and “training passed” is bit 0.
3. What This Chapter Owns
| Ground | Owner |
|---|---|
| The controller / PHY / DRAM ownership split, and the placement rule | 19.1 §3, 30.7 |
| The PHY lifecycle, and one-time versus continuous | 19.5 |
| Every training algorithm and the sequencer | Module 21 |
| Published window figures and the setup/hold trade | 20.5 |
| What a training failure signature does and does not support | 28.1 |
The five meanings of one done flag, and the unverifiable result | 28.4 |
| Why an eye is the shape it is | Module 22 |
| Reviewing the code that applies a trained value | 33.2 |
| Reviewing the provenance, lifetime and belief attached to a measured value | this chapter |
Two boundaries need stating precisely, because this gate shares vocabulary with chapters on both sides of it.
With 33.2 §12: that item asks does any parameter name something outside the design — a question about source text, answered by grepping a parameter list, and its verdict is flag it. This chapter's §6 asks a different question about the same line: for every quantity in the timing path, which side of the calculated/measured line is it on, and who is authoritative? That is a census, its pass criterion is completeness, and it cannot be answered by grep because the entries the census is looking for are the ones that are missing.
With 28.1 and 28.4: those chapters own the interpretation of a failure that has already happened — eleven bitmap signatures, five done meanings, and the discriminating experiments. This chapter asks whether the design was built so that the discrimination is possible at all. 28.1's finding is “five causes, one signature.” This gate's item 6 is the review question that would have produced five signatures.
4. Teaching-Model Boundary And Source Discipline
Every SystemVerilog block below is an educational model. None is a PHY, none contains a delay line, and none should be taken as an implementation.
CURRICULUM-DERIVED from 20.3's standing statement that sample quality is not digitally observable: a model written in SystemVerilog cannot represent an eye, a phase, or a volt. What these models do represent is the digital belief the design holds about a physical quantity — a register, a validity flag, a reset domain, a selection rule, a range bound. Those are exactly the things a review can inspect, and exactly the things that go wrong.
| Claim class | What it means here | Example below |
|---|---|---|
| Structural | a real, documented mechanism | mode registers, done flags, reset domains, sweep ranges |
| Curriculum-derived | follows from a cited chapter of this track | every item's pass criterion, each cited by section |
| Derived | computed in this chapter from the models below | every gap figure in §16 |
| Illustrative | a chosen number that makes a mechanism visible | window widths, tap counts, drift rates, tick intervals |
Every number that stands for a physical quantity in this chapter is ILLUSTRATIVE, and labelled. Window widths, delay-tap resolutions, drift-per-degree figures and recalibration intervals are chosen to make an argument legible in simulation. CURRICULUM-DERIVED from 20.5, which owns the published figures: a real window comes from a device datasheet at a stated speed bin, and this chapter derives none.
No external source was consulted. Every structural claim traces to a chapter of this track, cited inline. Where this chapter needs a physical fact it does not own, it names the chapter that owns it and takes the fact as given — the discipline 19.3 §3 uses for CL and CWL, and the reason its models are about relationships rather than magnitudes.
5. The Line This Whole Gate Is About
One distinction organises all nine items, and 19.3 §2 owns it: a PHY latency decomposes into a part that is known from the configuration and a part that is only knowable by measurement.
Every defect in this chapter is a value on the wrong side of that line, or a belief about a value that outlived its measurement.
| Side | Property | What goes wrong |
|---|---|---|
| Calculated | reproducible from programmed state; identical on every part | a measured quantity is computed instead — works on one part |
| Measured | discovered by search; differs per part, per board, per temperature | a calculated quantity is trained — wastes boot time, hides a config error |
| The line itself | who is authoritative for each quantity | the census is incomplete, and nobody notices the missing row |
The third row is the one this gate exists for. CURRICULUM-DERIVED from 30.7 §2's placement rule and its recorded consequence: six rows of 19.1's ownership table are implementation-dependent and one is strongly so, so “an architecture diagram claiming one boundary is describing a particular product, not DDR.” A review that checks the rows that are present, against a table that is incomplete by construction, has checked nothing about the rows that are absent.
And there is a second organising fact, from 19.5 §4: the measured side splits again into one-time and continuous. A value established once at boot and a value re-established every few milliseconds fail in completely different ways — the first fails at the next reset, the second fails between two updates — and items 4, 8 and 9 below are the three review questions that follow from that split.
6. Review Item 1 — Which Side of the Calculated / Measured Line Is This Quantity On?
| Facet | Content |
|---|---|
| Under review | every quantity that contributes to a PHY timing decision |
| Invariant at risk | a measured quantity is never recomputed from configuration |
| Where it lives | the expression that produces the launch or capture offset |
| Evidence to demand | a provenance census: one row per quantity, with its side of the line and its authority |
| What escapes | a part that works at the frequency and on the board the constant was derived at |
| How DV proves it | two configurations whose calculated terms agree and whose measured terms differ |
| Telemetry | a read-back of the offset actually in use, beside the offset training produced |
| Misleading evidence | the expression is dimensionally correct and its result is right on the bench |
The pass criterion is completeness, not correctness. Every quantity feeding a timing decision must appear in the census with a side and an authority. A census with nine correct rows and one missing row fails this item, and the missing row is the finding.
That criterion is 33.1 §12's, reused deliberately. That item's commitment ledger passes only when every decision committed in silicon is listed, and its weak build failed on one undocumented commitment out of a correct set. DERIVED: a census judged on completeness has the same failure mode at both gates — the row that is absent is the row nobody argued about — and this is the second of the two places in this module where a review's verdict turns on an omission rather than on an error.
// ILLUSTRATIVE quantities. ROBUST BUILD: provenance is a declared
// property of each quantity, and the launch offset is the sum of a
// calculated term and a measured term -- 19.3 section 2's decomposition,
// made structural so a review can audit it.
module quantity_provenance #(
parameter int OFF_W = 8,
// CALCULATED side: reproducible from programmed state.
parameter int CWL_CYCLES = 9, // 14.10's programmed value
parameter int SER_CYCLES = 2, // 19.2's serialiser depth
// MEASURED side: MUST arrive as an input. There is no default,
// because a default is a calculated value wearing a measured name.
parameter bit ALLOW_CALC_FLIGHT = 1'b0
)(
input logic clk,
input logic rst_n,
// programmed configuration
input logic [3:0] cfg_cwl,
input logic [1:0] cfg_ser_depth,
// measured, from training
input logic [OFF_W-1:0] trained_flight,
input logic trained_flight_valid,
// a quantity the reviewer must classify: does it belong to either side?
input logic [OFF_W-1:0] board_trace_delay, // never used: see below
output logic [OFF_W-1:0] launch_offset,
output logic launch_offset_valid,
// the census, exposed so a review can read it rather than infer it
output logic [2:0] calc_terms,
output logic [2:0] meas_terms,
output logic [2:0] unclassified_terms
);
// Provenance enum, structural: 0 = calculated, 1 = measured.
localparam int NQ = 3;
localparam bit [NQ-1:0] PROVENANCE = 3'b100; // {flight, ser, cwl}
logic [OFF_W-1:0] calc_part, meas_part;
always_comb begin
// CALCULATED: from programmed registers, not from parameters. A
// parameter here would make the term un-reprogrammable, which is
// 33.2 section 12's finding in a different costume.
calc_part = OFF_W'(cfg_cwl) + OFF_W'(cfg_ser_depth);
// MEASURED: from the training input, and ONLY from it.
meas_part = trained_flight;
end
// The census. Counted from PROVENANCE so it cannot drift from the
// declaration, and unclassified is computed as a residue -- the whole
// point of section 5's third row.
always_comb begin
calc_terms = 3'(NQ - $countones(PROVENANCE));
meas_terms = 3'($countones(PROVENANCE));
// board_trace_delay contributes to a timing decision and appears on
// neither side. In the robust build it is REFUSED rather than used.
unclassified_terms = (board_trace_delay != '0) ? 3'd1 : 3'd0;
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
launch_offset <= '0;
launch_offset_valid <= 1'b0;
end else begin
launch_offset <= calc_part + meas_part;
// Refuses while the measured term has no provenance.
launch_offset_valid <= trained_flight_valid && !ALLOW_CALC_FLIGHT;
end
end
endmoduleThe weak build is the one that exists in real code, and it does not look like a shortcut.
// INTENTIONALLY DEFECTIVE. WEAK BUILD: the measured term is computed.
// Set ALLOW_CALC_FLIGHT = 1 and the module derives flight time from the
// clock period and a trace length -- dimensionally correct, reproducible,
// and wrong on every board but the one it was characterised on.
//
// FLIGHT_NS = TRACE_MM * PROP_NS_PER_MM // ILLUSTRATIVE
// flight_cyc = FLIGHT_NS / CLK_PERIOD_NS
//
// The expression is defensible on a whiteboard. That is the problem:
// it makes `trained_flight` redundant, so nobody notices it is unread.
//
// CONTRACT VIOLATED: 19.3 section 2 -- the measured term is the part that
// is "only knowable by measurement". A formula is not a measurement.
//
// WHY IT SURVIVES: on the characterisation board the two agree to within
// one tap, the census reports three classified terms and zero
// unclassified, and `launch_offset_valid` asserts without training.
// The part boots FASTER than the robust build.
//
// TRACE (ILLUSTRATIVE, PROP_NS_PER_MM = 6.5e-3, CLK_PERIOD_NS = 0.625):
// board A: TRACE_MM = 48 -> flight_cyc = 0.499 -> 0 trained = 0 SAME
// board B: TRACE_MM = 48 (assumed) but actual 96
// -> flight_cyc = 0 trained = 1 DIFFER
// -> launch_offset low by 1 cycle on every write on board B.The escape is specific and it is not “the formula is wrong.” The formula is right. What escapes is that the formula's inputs are assumptions about a board, and the design has no way to notice that the board changed. CURRICULUM-DERIVED from 22.3 §3: board and package design are separate disciplines “with their own tools, their own measurement methods and their own literature” — so a trace length in RTL is a number transcribed across a discipline boundary, and nothing in the RTL domain can revalidate it.
Misleading evidence, precisely. The weak build's census reports three classified terms, zero unclassified — a clean audit. It reports that because the census counts the declaration, and the declaration was edited to match the code. DERIVED: a self-reported provenance census is evidence about the declaration, not about the code, which is why this item's evidence to demand is the census plus the read-back of §6's telemetry row — two artifacts that disagree when the formula path is active.
And the unread input is the tell, again. trained_flight is declared and never read in the weak build. CURRICULUM-DERIVED from 33.2 §8: eight of the track's eighteen documented defects present this way. This item is the PHY-side reason the tell is so common — a formula that reproduces a measurement makes the measurement look redundant.
7. Review Item 2 — Which of Five Things Does This done Flag Mean?
| Facet | Content |
|---|---|
| Under review | every completion, convergence or readiness flag in the PHY |
| Invariant at risk | a flag that gates consumption means the value is usable, not the step returned |
| Where it lives | the assignment that raises the flag, and every consumer that reads it |
| Evidence to demand | for each flag, what it would report in each of 28.4's five cases |
| What escapes | a system that boots, self-checks clean, and corrupts data hours later |
| How DV proves it | five stimuli, one per case, each checked for a distinct flag pattern |
| Telemetry | a separate bit per case, not one flag |
| Misleading evidence | the flag is high, and it is high for the correct reason on the bench |
CURRICULUM-DERIVED from 28.4 §2, which owns the five-way split and states the consequence: five failures, one flag. This item does not re-derive the five. It demands that the design distinguish them.
// ROBUST BUILD: completion is FOUR separate facts, because collapsing
// them is 28.4's finding. The names are chosen to be un-aliasable.
module completion_semantics #(
parameter int VAL_W = 8,
parameter int TIMEOUT = 64, // ILLUSTRATIVE
parameter int STALE_AT = 512 // ILLUSTRATIVE ticks
)(
input logic clk,
input logic rst_n,
input logic start,
// from the search engine
input logic engine_returned, // the step finished running
input logic engine_converged, // it found a point
input logic [VAL_W-1:0] engine_value,
input logic engine_in_range, // the point was inside the sweep
input logic tick, // ILLUSTRATIVE staleness clock
input logic env_changed, // e.g. a thermal event
// four distinct outputs, deliberately not one
output logic ran, // case 1 vs 2: did it even run?
output logic converged, // case 3: did it find a point?
output logic in_range, // case 4: was the point reachable?
output logic trusted, // case 5: is it still true?
output logic [VAL_W-1:0] value,
output logic [2:0] fail_case // 0 = none; 1..5 = 28.4's cases
);
logic [$clog2(TIMEOUT+1)-1:0] busy_cnt;
logic [$clog2(STALE_AT+1)-1:0] age;
logic running;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
ran <= 1'b0; converged <= 1'b0; in_range <= 1'b0; trusted <= 1'b0;
value <= '0; fail_case <= 3'd0; busy_cnt <= '0; age <= '0;
running <= 1'b0;
end else begin
if (start) begin
running <= 1'b1;
busy_cnt <= '0;
ran <= 1'b0; converged <= 1'b0;
in_range <= 1'b0; trusted <= 1'b0;
fail_case <= 3'd0;
end
if (running) begin
busy_cnt <= busy_cnt + 1'b1;
if (engine_returned) begin
running <= 1'b0;
ran <= 1'b1;
converged <= engine_converged;
in_range <= engine_in_range;
value <= engine_value;
age <= '0;
// trusted requires ALL of the preceding facts, and nothing else
// may raise it.
trusted <= engine_converged && engine_in_range;
if (!engine_converged && !engine_in_range) fail_case <= 3'd4;
else if (!engine_converged) fail_case <= 3'd3;
else fail_case <= 3'd0;
end else if (busy_cnt == TIMEOUT[$clog2(TIMEOUT+1)-1:0]) begin
// Case 2: the step never completed. Distinguished from case 1
// (never started) by `ran` staying low with `busy_cnt` high.
running <= 1'b0;
ran <= 1'b1;
fail_case <= 3'd2;
end
end
// Case 5: it converged, it was in range, and it STOPPED being true.
// 19.5 section 4's continuous side: a value can become invalid without
// anything failing.
if (trusted) begin
if (env_changed) begin
trusted <= 1'b0;
fail_case <= 3'd5;
end else if (tick) begin
if (age == STALE_AT[$clog2(STALE_AT+1)-1:0]) begin
trusted <= 1'b0;
fail_case <= 3'd5;
end else begin
age <= age + 1'b1;
end
end
end
end
end
endmodule// INTENTIONALLY DEFECTIVE. WEAK BUILD: one flag, raised on return.
//
// always_ff @(posedge clk or negedge rst_n)
// if (!rst_n) calib_done <= 1'b0;
// else if (engine_returned) calib_done <= 1'b1; // <-- the defect
//
// CONTRACT VIOLATED: 28.4 section 2 -- `done` must not mean "returned".
//
// WHY IT SURVIVES: on the bench the engine converges, in range, and
// nothing changes thermally during the test. `calib_done` is high for
// the correct reason, so every consumer behaves correctly. The flag is
// not wrong; it is UNDERDETERMINED.
//
// TRACE, five stimuli (ILLUSTRATIVE):
// case 1 never started robust: ran=0 weak: done=0 SAME
// case 2 hung, timed out robust: ran=1 fc=2 weak: done=1 DIFFER
// case 3 returned, no point robust: converged=0 fc=3 weak: done=1 DIFFER
// case 4 point out of range robust: in_range=0 fc=4 weak: done=1 DIFFER
// case 5 converged, drifted robust: trusted=0 fc=5 weak: done=1 DIFFER
//
// distinguishable cases: robust 5 of 5, weak 1 of 5.The measured gap is 5 versus 1, and the shape of the error matters more than the count. Four of the five cases are indistinguishable from success in the weak build, and case 5 is indistinguishable from success forever — because nothing in the weak build ever lowers the flag. CURRICULUM-DERIVED from 19.5 §2: “some of those values stop being correct while the system is running” — a flag that is monotonic cannot represent a value that is not.
Note what this item does not claim. It does not claim the weak build's calib_done is a bug in the sense of a wrong assignment. The assignment is exactly what its name says. The defect is that a consumer reads it as a statement about the value's usability, and the flag was never that. DERIVED: the review question is not is the flag correct but what does a consumer believe when it is high, and is that belief warranted by the assignment.
8. Review Item 3 — Does This Consumer Refuse to Operate Untrained?
| Facet | Content |
|---|---|
| Under review | every consumer of a trained value |
| Invariant at risk | the default state of a trained-value gate is closed |
| Where it lives | the reset branch of the gate, and the polarity of its enable |
| Evidence to demand | the value the gate takes at reset, read out of the RTL, not asserted in review |
| What escapes | a window of traffic driven with reset-value delays, before training runs |
| How DV proves it | drive traffic between reset release and training start, and check it is refused |
| Telemetry | a counter of operations attempted while untrained — must read exactly zero |
| Misleading evidence | the boot sequence never drives traffic early, so the window is never entered |
The pass criterion is a polarity, and it is the cheapest item in this chapter. CURRICULUM-DERIVED from 19.5 §2: a PHY is not usable when reset is released. So the correct reset value of every trained-value consumer's enable is the one that refuses, and a reviewer reads the reset branch.
// ROBUST BUILD: refusal is the reset state, and the gate is closed by
// a value that cannot be produced by a stuck-at-zero fault on the
// validity input.
module untrained_refusal #(
parameter int OFF_W = 8
)(
input logic clk,
input logic rst_n,
input logic [OFF_W-1:0] trained_offset,
input logic trained_valid,
input logic op_request,
output logic op_grant,
output logic [OFF_W-1:0] offset_in_use,
output logic gate_open,
// telemetry: 19.5's lifecycle, made countable
output logic [15:0] untrained_attempts,
output logic [15:0] granted_ops
);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
// The whole item is these three lines.
gate_open <= 1'b0;
offset_in_use <= '0;
untrained_attempts <= '0;
granted_ops <= '0;
op_grant <= 1'b0;
end else begin
// The gate tracks validity in BOTH directions -- 28.4's case 5
// means a trained value can stop being trusted, so the gate must
// be able to reclose.
gate_open <= trained_valid;
if (trained_valid) offset_in_use <= trained_offset;
op_grant <= op_request && gate_open;
if (op_request && !gate_open) untrained_attempts <= untrained_attempts + 1'b1;
if (op_request && gate_open) granted_ops <= granted_ops + 1'b1;
end
end
endmodule// INTENTIONALLY DEFECTIVE. WEAK BUILD: the gate opens at reset and
// closes on invalidity.
//
// if (!rst_n) gate_open <= 1'b1; // <-- the defect
// else gate_open <= trained_valid;
//
// CONTRACT VIOLATED: 19.5 section 2 -- a PHY is not usable at reset release.
//
// WHY IT SURVIVES: `gate_open` is corrected on the first clock after
// reset, so the window is ONE CYCLE WIDE, and the boot ROM does not
// issue a transaction in it. The defect is invisible unless something
// changes the boot sequence -- a faster ROM, a warm reset that does not
// re-run the ROM, or a debug flow that drives traffic early.
//
// TRACE (ILLUSTRATIVE, 4 requests at t = 0, 1, 40, 41; training at t = 20):
// robust: untrained_attempts = 2 granted_ops = 2 -> refused both early
// weak: untrained_attempts = 1 granted_ops = 3 -> ONE early op granted
// with offset_in_use = 0
// gap: one operation, executed with a reset-value delay, no error raised.One operation is the whole finding, and the reason it matters is not the operation. DERIVED: the weak build's untrained_attempts reads 1 rather than 2, so its own telemetry under-reports by exactly the number of escapes. A reviewer who checks the counter and finds a non-zero value has found a problem; a reviewer who checks the counter and finds it lower than the number of early requests has found this one.
And the misleading evidence is a property of the environment, not the design. CURRICULUM-DERIVED from 30.7 §10's finding that an environment sharing the design's wrong constant cannot detect it — here the environment shares the design's schedule: a testbench that models the boot ROM's timing never enters the window the ROM never entered. The stimulus that falsifies this item is the one nobody writes, because it is not a legal boot sequence.
9. Review Item 4 — Which Reset Does This Trained Register Survive, and Does That Match the Retraining It Triggers?
| Facet | Content |
|---|---|
| Under review | every trained register's reset domain, and every reset's retraining trigger |
| Invariant at risk | a reset that clears a trained value must start the training that replaces it |
| Where it lives | the sensitivity list of the trained register, and the condition on the retrain request |
| Evidence to demand | a two-column map: register → clearing reset, reset → retraining triggered |
| What escapes | a part that boots perfectly and fails after a warm reset |
| How DV proves it | assert a warm reset after successful training and check either survival or retraining |
| Telemetry | a training-run counter and a reset-cause register, read together |
| Misleading evidence | every cold boot works, and cold boot is what the bring-up test does |
CURRICULUM-DERIVED from 19.5 §2, which owns this hazard by name: “the ordering hazards that make cold boot and warm reset behave differently.” This item is that hazard turned into a review artifact — and the artifact is two columns, because the defect is a mismatch between them rather than an error in either.
This is the one item in this chapter that earns a diagram, and the reason is specific: the defect is a wiring topology, not a value. Both builds have correct registers and a correct sequencer. What differs is which reset line reaches which register, and prose describes that badly.
The reset-domain map the review demands.
Robust: the reset that clears a trained register is the reset that starts the retraining which rewrites it. Weak: the functional reset gains an arrow into trained and gains no sibling arrow into seq.
Read the diagram as a rule: every arrow that clears a trained register must have a sibling arrow into the retrain trigger.
// ROBUST BUILD: trained state lives in the POR domain, and the POR
// domain is the one that triggers retraining. The two facts are tied
// together by construction, not by a comment.
module reset_domain_map #(
parameter int OFF_W = 8
)(
input logic clk,
input logic rst_por_n, // cold: clears trained state
input logic rst_func_n, // warm: does not
input logic [OFF_W-1:0] trained_offset_in,
input logic training_done,
input logic op_request,
output logic retrain_req,
output logic [OFF_W-1:0] trained_offset,
output logic trained_valid,
output logic op_grant,
output logic [7:0] training_runs,
output logic [7:0] ops_with_stale_offset
);
// TRAINED STATE: POR domain only. A warm reset does not appear in
// this sensitivity list, which is the entire mechanism.
always_ff @(posedge clk or negedge rst_por_n) begin
if (!rst_por_n) begin
trained_offset <= '0;
trained_valid <= 1'b0;
training_runs <= '0;
end else begin
if (training_done) begin
trained_offset <= trained_offset_in;
trained_valid <= 1'b1;
training_runs <= training_runs + 1'b1;
end
end
end
// RETRAIN TRIGGER: also POR domain. Same reset, so the clear and the
// replacement cannot get out of step.
always_ff @(posedge clk or negedge rst_por_n) begin
if (!rst_por_n) retrain_req <= 1'b1; // cleared -> retrain
else if (training_done) retrain_req <= 1'b0;
end
// FUNCTIONAL STATE: warm domain. Traffic is re-gated on the warm
// reset, but the trained value it uses is untouched.
always_ff @(posedge clk or negedge rst_func_n) begin
if (!rst_func_n) begin
op_grant <= 1'b0;
ops_with_stale_offset <= '0;
end else begin
op_grant <= op_request && trained_valid;
// Telemetry the reviewer asked for: an operation granted while the
// offset is the reset value is the escape, counted.
if (op_request && trained_valid && (trained_offset == '0))
ops_with_stale_offset <= ops_with_stale_offset + 1'b1;
end
end
endmodule// INTENTIONALLY DEFECTIVE. WEAK BUILD: trained state moves into the
// functional domain, and the retrain trigger does not follow it.
//
// always_ff @(posedge clk or negedge rst_func_n) begin // <-- moved
// if (!rst_func_n) begin
// trained_offset <= '0;
// trained_valid <= 1'b0; // cleared by a WARM reset
// end ...
// always_ff @(posedge clk or negedge rst_por_n)
// if (!rst_por_n) retrain_req <= 1'b1; // <-- not moved
//
// CONTRACT VIOLATED: 19.5 section 2's cold-boot / warm-reset divergence.
//
// WHY IT SURVIVES: cold boot asserts BOTH resets, so `trained_valid`
// clears and `retrain_req` rises together. Every cold boot is perfect.
// Bring-up tests cold-boot. So does the regression.
//
// TRACE (ILLUSTRATIVE):
// cold boot: trained_valid 0 -> retrain_req 1 -> train -> valid 1
// robust and weak IDENTICAL.
// warm reset after training:
// robust: trained_valid stays 1, trained_offset preserved,
// training_runs = 1, ops_with_stale_offset = 0
// weak: trained_valid -> 0, trained_offset -> 0, retrain_req
// stays 0 (POR did not fire), and `trained_valid` re-raises
// only if `training_done` ever pulses again -- it does not.
// -> the gate stays CLOSED and the interface is dead,
// OR, if the gate defaults open (item 3's weak build),
// every subsequent operation uses offset 0.
//
// gap: cold boots to failure = 0; warm resets to failure = 1.Two failure modes, and which one you get depends on item 3 — which is why these two items must be reviewed together. DERIVED: with item 3's robust gate, the weak reset map produces a dead interface after warm reset — bad, loud, and found in an hour. With item 3's weak gate, it produces a live interface running on reset-value delays — silent, and 28.4's “boots, self-checks clean, corrupts data hours later.” The combination of two individually survivable defects is worse than either, and no single-item review finds the combination.
The headline number is the one to remember: zero cold boots fail, one warm reset does. CURRICULUM-DERIVED from 28.1 §4's failure-domain split, which lists stale-state defects as one of its six domains — and this is the mechanism that produces them: not a bad measurement, but a good measurement cleared by a reset that did not ask for a new one.
10. Review Item 5 — Are This Recalibration's Preconditions Enforced, or Assumed?
| Facet | Content |
|---|---|
| Under review | every periodic or triggered recalibration |
| Invariant at risk | a measurement is taken only under the conditions its result is valid for |
| Where it lives | the condition on the start pulse, not the interval counter |
| Evidence to demand | the list of preconditions, and the line of RTL enforcing each |
| What escapes | a periodic update that overwrites a good value with a worse one |
| How DV proves it | fire the interval timer during traffic and check the start is held off |
| Telemetry | recalibrations started, and recalibrations deferred — the second is the useful one |
| Misleading evidence | the recalibration runs on schedule, converges, and commits — every time |
CURRICULUM-DERIVED from 19.5 §4's one-time-versus-continuous split. A one-time value's risk is that it goes stale (item 4). A continuous value's risk is the opposite and less obvious: that it is refreshed badly.
The review question is not whether the cadence is implemented. It is whether the preconditions are. A recalibration is a measurement, and a measurement taken while the thing being measured is in use measures something else.
// ROBUST BUILD: the interval timer REQUESTS; the preconditions GRANT.
// Separating them is the whole item.
module recal_preconditions #(
parameter int INTERVAL = 256, // ILLUSTRATIVE ticks
parameter int QUIET_FOR = 8, // ILLUSTRATIVE idle cycles required
parameter int VAL_W = 8
)(
input logic clk,
input logic rst_n,
input logic tick,
input logic bus_busy,
input logic thermal_settling, // a second precondition
input logic [VAL_W-1:0] measured_value,
input logic measurement_done,
output logic recal_start,
output logic [VAL_W-1:0] committed_value,
output logic [15:0] recals_started,
output logic [15:0] recals_deferred,
output logic [15:0] commits_under_traffic
);
logic [$clog2(INTERVAL+1)-1:0] ivl;
logic [$clog2(QUIET_FOR+1)-1:0] quiet;
logic pending, running;
// Preconditions, named once and used once. A reviewer can point at
// this expression and at the list it came from.
wire preconditions_met = (quiet == QUIET_FOR[$clog2(QUIET_FOR+1)-1:0])
&& !bus_busy
&& !thermal_settling;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
ivl <= '0; quiet <= '0; pending <= 1'b0; running <= 1'b0;
recal_start <= 1'b0; committed_value <= '0;
recals_started <= '0; recals_deferred <= '0;
commits_under_traffic <= '0;
end else begin
recal_start <= 1'b0;
// Quiet-window tracker.
if (bus_busy) quiet <= '0;
else if (quiet != QUIET_FOR[$clog2(QUIET_FOR+1)-1:0]) quiet <= quiet + 1'b1;
// The timer only ever sets `pending`. It never starts anything.
if (tick) begin
if (ivl == INTERVAL[$clog2(INTERVAL+1)-1:0]) begin
ivl <= '0;
pending <= 1'b1;
end else begin
ivl <= ivl + 1'b1;
end
end
// Deferral is counted, because an unbounded deferral is its own
// defect and a reviewer must be able to see it.
if (pending && !running) begin
if (preconditions_met) begin
pending <= 1'b0;
running <= 1'b1;
recal_start <= 1'b1;
recals_started <= recals_started + 1'b1;
end else if (tick) begin
recals_deferred <= recals_deferred + 1'b1;
end
end
if (running && measurement_done) begin
running <= 1'b0;
// Commit only if the preconditions held for the WHOLE measurement.
if (preconditions_met) committed_value <= measured_value;
else commits_under_traffic <= commits_under_traffic + 1'b1;
end
end
end
endmodule// INTENTIONALLY DEFECTIVE. WEAK BUILD: the timer starts the measurement.
//
// if (tick && (ivl == INTERVAL)) begin
// ivl <= '0; recal_start <= 1'b1; running <= 1'b1; // <-- the defect
// end
// if (running && measurement_done) committed_value <= measured_value;
//
// CONTRACT VIOLATED: the measurement's own validity conditions. Not a
// timing rule, not a protocol rule -- a MEASUREMENT rule, which is why
// no protocol checker catches it.
//
// WHY IT SURVIVES: it is more robust-looking than the robust build. It
// never defers, never stalls, never reports a deferral, and always
// commits on schedule. `recals_started` matches the expected count
// exactly, which is what a reviewer checks.
//
// TRACE (ILLUSTRATIVE, 8 intervals, bus busy during 3 of them):
// robust: recals_started = 5 recals_deferred = 3
// commits_under_traffic = 0 committed_value: always valid
// weak: recals_started = 8 recals_deferred = 0
// commits_under_traffic = 3 committed_value: 3 of 8 measured
// under traffic and committed anyway
//
// gap: 3 of 8 commits (37.5%) are measurements of the wrong thing.The measured gap is 3 of 8, and the direction of the error is what makes this item hard to review. DERIVED: the weak build's telemetry is better-looking than the robust build's — eight starts, zero deferrals, a perfect schedule. The robust build reports three deferrals, which reads like a design that is struggling.
CURRICULUM-DERIVED from 28.4 §2's standing rule: a failure signature is evidence, not a verdict — “a done flag is a signature. So is a converged result. Neither is a statement about margin.” This item is the corollary for the schedule: a recalibration that always completes on time is a signature too, and it is consistent with a design that never checks whether completing on time was possible.
And the escape has a characteristic silicon signature worth naming: a value that gets worse over hours of load and recovers when the system goes idle. Each recalibration under traffic commits a slightly worse value; each recalibration at idle commits a good one. The part appears to be thermally sensitive when it is actually load-sensitive, and the two are distinguished by 28.6's methods, not by this gate.
11. Review Item 6 — Can This Search Tell an Exhausted Range From an Absent Margin?
| Facet | Content |
|---|---|
| Under review | every training search's failure report |
| Invariant at risk | no passing point in the range searched and no passing point are different results |
| Where it lives | the failure assignment at the end of the sweep, and the range bounds |
| Evidence to demand | the sweep bounds, and the physically possible range they are supposed to cover |
| What escapes | a margin investigation that starts from a configuration error |
| How DV proves it | a stimulus whose answer lies one tap outside the range, checked for a distinct code |
| Telemetry | whether the passing region touched either end of the sweep |
| Misleading evidence | an all-zeros bitmap, which is a real signature of a real failure — just not this one |
CURRICULUM-DERIVED from 28.1 §2, which owns the interpretation problem and states it exactly: “A bitmap of all zeros is consistent with a dead lane, a stuck classifier, a mis-programmed sweep range, an unfinished prerequisite step, and a genuine margin failure. Five causes, one signature, and nothing in the bitmap distinguishes them.”
28.1 owns what to do when you are handed that bitmap. This item owns the review question that would have prevented it: does the search distinguish the causes it can distinguish? Three of the five are digitally decidable by the search itself — a mis-programmed range, an unfinished prerequisite, and a classifier that never changes state. A search that reports one code for all five has thrown away information it had.
// ROBUST BUILD: one sweep, five distinguishable outcomes.
module search_range_adequacy #(
parameter int TAPS = 32, // ILLUSTRATIVE sweep width
parameter int TAP_W = 5,
parameter int HW_TAPS = 64 // ILLUSTRATIVE hardware range
)(
input logic clk,
input logic rst_n,
input logic start,
input logic prereq_done, // 21.5's dependency structure
input logic probe_valid,
input logic probe_pass,
input logic classifier_alive, // did the observable ever move?
output logic [TAP_W-1:0] tap,
output logic probe_req,
output logic done,
output logic found,
output logic [TAP_W-1:0] best_tap,
// the five outcomes, as separate bits
output logic err_prereq, // cause: unfinished prerequisite
output logic err_classifier, // cause: stuck observable
output logic range_exhausted, // cause: mis-programmed range
output logic edge_at_low, // pass region touched tap 0
output logic edge_at_high, // pass region touched TAPS-1
output logic [TAP_W:0] pass_count
);
logic sweeping;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
tap <= '0; probe_req <= 1'b0; sweeping <= 1'b0;
done <= 1'b0; found <= 1'b0; best_tap <= '0;
err_prereq <= 1'b0; err_classifier <= 1'b0;
range_exhausted <= 1'b0; edge_at_low <= 1'b0; edge_at_high <= 1'b0;
pass_count <= '0;
end else begin
probe_req <= 1'b0;
if (start) begin
done <= 1'b0; found <= 1'b0; pass_count <= '0;
err_prereq <= 1'b0; err_classifier <= 1'b0;
range_exhausted <= 1'b0; edge_at_low <= 1'b0; edge_at_high <= 1'b0;
// The prerequisite check is BEFORE the sweep, so a prerequisite
// failure cannot masquerade as a margin failure.
if (!prereq_done) begin
err_prereq <= 1'b1;
done <= 1'b1;
end else begin
tap <= '0;
sweeping <= 1'b1;
end
end
if (sweeping && !probe_valid) probe_req <= 1'b1;
if (sweeping && probe_valid) begin
if (probe_pass) begin
pass_count <= pass_count + 1'b1;
if (!found) begin found <= 1'b1; best_tap <= tap; end
if (tap == '0) edge_at_low <= 1'b1;
if (tap == TAP_W'(TAPS-1)) edge_at_high <= 1'b1;
end
if (tap == TAP_W'(TAPS-1)) begin
sweeping <= 1'b0;
done <= 1'b1;
// Stuck observable: not one probe ever differed from another.
if (!classifier_alive) err_classifier <= 1'b1;
// Range exhausted: nothing passed AND the hardware has range
// this sweep did not use. That second clause is the item --
// "nothing passed" alone does not distinguish the causes.
else if (!found && (TAPS < HW_TAPS)) range_exhausted <= 1'b1;
end else begin
tap <= tap + 1'b1;
end
end
end
end
endmodule// INTENTIONALLY DEFECTIVE. WEAK BUILD: one failure bit.
//
// if (tap == TAPS-1) begin
// done <= 1'b1;
// failed <= !found; // <-- the defect: one code, five causes
// end
//
// and the prerequisite is checked NOWHERE, so an unfinished prerequisite
// runs the sweep and reports a margin failure.
//
// CONTRACT VIOLATED: 28.1 section 4's failure-domain split -- a search
// defect, an observation defect, a configuration defect, a stale-state
// defect, an ordering defect and a margin problem are six domains, and
// `failed` is one bit.
//
// WHY IT SURVIVES: `failed` is CORRECT. Nothing passed, so the search
// failed. The defect is not a wrong value; it is a report with less
// information than the design had. And on the bench, where the range is
// right and the prerequisites run in order, `failed` never asserts.
//
// TRACE (ILLUSTRATIVE, five stimuli):
// prereq not done robust: err_prereq=1 weak: failed=1
// classifier stuck robust: err_classifier=1 weak: failed=1
// range 32 of 64 taps robust: range_exhausted=1 weak: failed=1
// genuine no-margin robust: all clear, found=0 weak: failed=1
// pass region at edge robust: found=1 edge_at_high=1 weak: found=1
//
// distinguishable causes: robust 4 of 4 failures; weak 1 of 4.
// And row 5: the weak build reports SUCCESS with the answer against
// the end of the sweep -- a pass region that is probably wider than
// the range, i.e. an unmeasured margin reported as a measured one.Row 5 is the finding that is worth more than the other four, and it is a success case. DERIVED: a passing region that touches an end of the sweep means the sweep did not contain the region's boundary, so the margin reported is a lower bound of unknown looseness. The weak build calls that success. CURRICULUM-DERIVED from 20.5's setup-versus-hold trade: choosing a sample point is a trade between two margins, and you cannot trade what you did not measure.
And the pass criterion for this item is a comparison, not a check. The reviewer needs two numbers: the sweep's range and the hardware's range. A sweep of 32 taps is correct or a configuration error depending entirely on the second number, and the second number is not in the RTL — which is why this item's evidence to demand is an artifact and not a line.
12. Review Item 7 — Does This Selection Rule Match the Shape of Its Pass Region?
| Facet | Content |
|---|---|
| Under review | the rule that picks an operating point from a pass region |
| Invariant at risk | a two-sided region is centred; a one-sided region is offset |
| Where it lives | the arithmetic that converts a pass region into a chosen tap |
| Evidence to demand | per search, whether its pass region is one-sided or two-sided, and why |
| What escapes | an operating point placed half-way into the failing side |
| How DV proves it | a one-sided pass region, and a check that the choice is not its midpoint |
| Telemetry | the chosen tap and both region edges, read together |
| Misleading evidence | “we centre the window” is correct practice, cited correctly, applied to the wrong search |
CURRICULUM-DERIVED from 21.3 §2, which owns this and states it without hedging: the gate's pass region is one-sided and therefore not centred. And from 21.4 §2, which owns the two-sided case: the eye is searched on two axes and a centre is selected in both.
Two searches in the same PHY, two different correct selection rules. The defect is a single rule applied to both — and it is the most defensible-sounding defect in this chapter, because centring is what a careful engineer does.
// ROBUST BUILD: the selection rule is a parameter of the SEARCH, and
// each search declares its region shape.
module selection_rule_shape #(
parameter int TAPS = 32,
parameter int TAP_W = 5,
// 0 = two-sided (centre it); 1 = one-sided (offset from the edge)
parameter bit ONE_SIDED = 1'b0,
parameter int LEAD_TAPS = 3 // ILLUSTRATIVE, 19.4's LEAD_CYCLES analogue
)(
input logic clk,
input logic rst_n,
input logic [TAPS-1:0] pass_map,
input logic map_valid,
output logic [TAP_W-1:0] chosen_tap,
output logic chosen_valid,
output logic [TAP_W-1:0] region_low,
output logic [TAP_W-1:0] region_high,
output logic chosen_in_region,
output logic [TAP_W:0] margin_to_fail
);
logic [TAP_W-1:0] lo, hi;
logic any;
always_comb begin
lo = '0; hi = '0; any = 1'b0;
for (int i = 0; i < TAPS; i++) begin
if (pass_map[i]) begin
if (!any) begin lo = TAP_W'(i); any = 1'b1; end
hi = TAP_W'(i);
end
end
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
chosen_tap <= '0; chosen_valid <= 1'b0;
region_low <= '0; region_high <= '0;
chosen_in_region <= 1'b0; margin_to_fail <= '0;
end else if (map_valid) begin
region_low <= lo;
region_high <= hi;
chosen_valid <= any;
if (any) begin
if (ONE_SIDED) begin
// The region has ONE real edge. The other end is not a
// boundary -- it is where the sweep stopped. So offset a
// documented lead from the real edge, and do not average.
chosen_tap <= lo + TAP_W'(LEAD_TAPS);
margin_to_fail <= (TAP_W+1)'(LEAD_TAPS);
end else begin
// Two real edges: the midpoint maximises the distance to both.
chosen_tap <= TAP_W'((lo + hi) >> 1);
margin_to_fail <= (TAP_W+1)'((hi - lo) >> 1);
end
end
end
end
// Traceability: the choice must land inside the region it came from.
always_comb chosen_in_region = chosen_valid
&& (chosen_tap >= region_low)
&& (chosen_tap <= region_high);
endmodule// INTENTIONALLY DEFECTIVE. WEAK BUILD: one rule, both searches.
//
// chosen_tap <= (lo + hi) >> 1; // <-- always centre
//
// with ONE_SIDED removed from the parameter list entirely, so no
// instantiation can express the distinction.
//
// CONTRACT VIOLATED: 21.3 section 2 -- the gate's pass region is one-sided
// and therefore not centred.
//
// WHY IT SURVIVES: on a two-sided region it is IDENTICAL to the robust
// build. The eye-centring instance is correct. Only the gate instance is
// wrong, and only when the region's high end is the sweep's end rather
// than a real edge.
//
// TRACE (ILLUSTRATIVE, TAPS = 32, LEAD_TAPS = 3):
// two-sided map, pass on taps 9..23:
// robust (ONE_SIDED=0): chosen 16 margin 7 -- centred
// weak: chosen 16 margin 7 IDENTICAL
// one-sided map, pass on taps 6..31 (31 is the sweep end):
// robust (ONE_SIDED=1): chosen 9 margin_to_fail 3 in_region 1
// weak: chosen 18 margin_to_fail 12 in_region 1
//
// gap: 9 taps. And the weak build's reported margin, 12, is LARGER --
// it is measuring distance to tap 31, which is not a failing tap.
// It is the end of the sweep.The gap is nine taps, and the weak build reports a better margin than the robust one. DERIVED: 12 versus 3. That inversion is the item's whole difficulty — a reviewer comparing reported margins picks the wrong build.
What the weak build actually measured is the distance to the edge of its own search. CURRICULUM-DERIVED from 21.3 §2's reason the gate region is one-sided: a strobe that arrives after the gate opens is still captured, so the far side of the pass region is bounded by the sweep and not by physics. Averaging two numbers where only one is a measurement produces a number that is neither.
And this item composes with item 6 in a way that is worth stating. Item 6's row 5 flags a pass region touching an end of the sweep. Item 7 is what that flag is for: it is the input to the selection rule, and a region with an artificial edge must not be centred. DERIVED: item 6 without item 7 produces a warning nobody acts on; item 7 without item 6 cannot tell which regions are one-sided.
13. Review Item 8 — Does This Margin Name the Measurement It Came From?
| Facet | Content |
|---|---|
| Under review | every guard band, lead, trail, or margin constant |
| Invariant at risk | a margin is derived from a measured window and a documented drift budget |
| Where it lives | the parameter declaration, and the expression that uses it |
| Evidence to demand | the measured window width, the drift budget, and the arithmetic between them |
| What escapes | a margin that exceeds the window on a narrow part, placing the point outside it |
| How DV proves it | a narrow window, and a check that the margin still fits |
| Telemetry | margin requested versus margin available, both read out |
| Misleading evidence | the margin is generous, and generous margins are good engineering |
This is the item 33.2 §24 handed forward, and the hand-off was explicit: at the RTL gate a tolerance parameter is a defect; in a PHY a margin is the point. They are the same line of Verilog.
The distinction is provenance, and it is not in the code. CURRICULUM-DERIVED from 21.4 §2, whose one-sentence model settles what a margin can and cannot be: training does not create margin — it discovers an operating point with usable margin, and “a grid with no passing cell must fail.” A margin constant that exceeds what the grid offered is not conservative. It is a request the physics cannot fill.
// ROBUST BUILD: the margin is computed from the measured width, and the
// design checks that the arithmetic is satisfiable before committing.
module margin_provenance #(
parameter int TAP_W = 6,
// The drift budget is a DECLARED fraction of the measured window,
// not an absolute tap count. Provenance: 19.5 section 4's continuous
// side -- the window narrows with temperature, and the budget is the
// allowance for that narrowing.
parameter int DRIFT_NUM = 1,
parameter int DRIFT_DEN = 4, // ILLUSTRATIVE: 25% of the window
parameter int MIN_USABLE = 4 // ILLUSTRATIVE taps
)(
input logic clk,
input logic rst_n,
input logic [TAP_W-1:0] window_low, // measured
input logic [TAP_W-1:0] window_high, // measured
input logic window_valid,
output logic [TAP_W-1:0] chosen_tap,
output logic chosen_valid,
output logic [TAP_W:0] margin_required,
output logic [TAP_W:0] margin_available,
output logic margin_satisfiable,
output logic [7:0] margin_refusals
);
logic [TAP_W:0] width;
always_comb begin
width = (TAP_W+1)'(window_high) - (TAP_W+1)'(window_low) + 1'b1;
// The margin is a FUNCTION of the measurement. It cannot exceed the
// window, because it is a fraction of it.
margin_required = (TAP_W+1)'((width * DRIFT_NUM) / DRIFT_DEN);
margin_available = (width >> 1);
// The satisfiability test is the item. A margin that does not fit is
// refused, not truncated.
margin_satisfiable = window_valid
&& (width >= (TAP_W+1)'(MIN_USABLE))
&& (margin_required <= margin_available);
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
chosen_tap <= '0; chosen_valid <= 1'b0; margin_refusals <= '0;
end else if (window_valid) begin
if (margin_satisfiable) begin
chosen_tap <= TAP_W'((window_low + window_high) >> 1);
chosen_valid <= 1'b1;
end else begin
// 21.4's rule, enforced: a grid with no usable cell must FAIL.
chosen_valid <= 1'b0;
margin_refusals <= margin_refusals + 1'b1;
end
end
end
endmodule// INTENTIONALLY DEFECTIVE. WEAK BUILD: the margin is a constant.
//
// parameter int GUARD_TAPS = 6; // "measured on the bench"
// ...
// chosen_tap <= window_low + GUARD_TAPS;
// chosen_valid <= window_valid; // <-- no satisfiability test
//
// CONTRACT VIOLATED: 21.4 section 2 -- training does not create margin,
// and a constant margin asserts an amount of margin the window may not
// contain.
//
// WHY IT SURVIVES: GUARD_TAPS = 6 was measured. On the characterisation
// part the window was 24 taps wide, 6 is 25% of it, and the arithmetic
// is exactly what the robust build computes. The constant is not a
// guess -- it is a correct answer, frozen.
//
// TRACE (ILLUSTRATIVE, DRIFT 1/4):
// wide part, window 8..31 (width 24):
// robust: required 6 available 12 satisfiable 1 chosen 19
// weak: chosen 14
// -- both inside the window; different points, both usable.
// narrow part, window 14..21 (width 8):
// robust: required 2 available 4 satisfiable 1 chosen 17
// weak: chosen = 14 + 6 = 20 chosen_valid 1
// -- inside, but 1 tap from the failing edge instead of 4.
// very narrow, window 16..19 (width 4):
// robust: required 1 available 2 satisfiable 1 chosen 17
// weak: chosen = 16 + 6 = 22 chosen_valid 1
// -- OUTSIDE the window. The part is configured to fail, and
// `chosen_valid` says it is fine.
//
// gap: on the narrowest part, the chosen tap is 3 taps outside the
// measured window and the design reports success. margin_refusals:
// robust 0 (all three satisfiable), weak has no such counter.The third row is the escape, and note what it is not: it is not a margin that was too small. DERIVED: the weak build fails by asking for MORE margin than exists, and the arithmetic places the operating point past the far edge. A constant margin is safe on parts at least as good as the characterisation part and unsafe on every part worse than it — which is the half of the distribution nobody characterises.
And chosen_in_region from item 7 would have caught this. DERIVED: item 7's traceability output — the check that the choice lands inside the region it came from — fires on this build. The two items are independent questions with one shared cheap detector, and that is a finding about review economics: a single assertion that a selection lies inside its own measured region covers two of the nine items in this chapter.
14. Review Item 9 — Which Direction and Which Side Does This Value Belong To?
| Facet | Content |
|---|---|
| Under review | every reference level, delay and termination value |
| Invariant at risk | each value is written to the side of the link that uses it, for the direction it governs |
| Where it lives | the register-write target, and the direction the search ran in |
| Evidence to demand | a four-cell table: {read, write} × {host, device}, every value placed in exactly one cell |
| What escapes | a trained value applied to the wrong side, correct whenever the two sides are similar |
| How DV proves it | asymmetric sides — a host and device whose correct values differ measurably |
| Telemetry | per-side read-back, not a single merged value |
| Misleading evidence | the value is right, the search was right, and the part works |
CURRICULUM-DERIVED from 28.4 §2, which names this as one of its owned classes — the side-asymmetry error class — and from 21.4 §2, which owns the mechanism: the direction asymmetry, “which side of the link each reference level belongs to.” 21.4 explains why the sides differ. This item demands that the design's register map says which is which.
// ROBUST BUILD: direction and side are part of each value's identity,
// and the write path cannot lose them.
module direction_side_map #(
parameter int VAL_W = 8
)(
input logic clk,
input logic rst_n,
input logic wr_en,
input logic [VAL_W-1:0] wr_value,
input logic wr_is_write_dir, // 1 = write direction
input logic wr_is_device, // 1 = device side
// the four cells, held separately because they are four quantities
output logic [VAL_W-1:0] vref_rd_host, // read dir, host samples
output logic [VAL_W-1:0] vref_wr_device, // write dir, device samples
output logic [VAL_W-1:0] dly_rd_host,
output logic [VAL_W-1:0] dly_wr_device,
// the two cells that must stay EMPTY: 21.4's direction asymmetry
output logic [1:0] illegal_cell_writes,
output logic [3:0] cells_populated
);
// The legality rule, stated once. A reference level belongs to the
// side that SAMPLES, and the sampling side is decided by direction:
// reads are sampled by the host, writes by the device.
function automatic bit legal_cell(bit is_write_dir, bit is_device);
return (is_write_dir == is_device);
endfunction
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
vref_rd_host <= '0; vref_wr_device <= '0;
dly_rd_host <= '0; dly_wr_device <= '0;
illegal_cell_writes <= '0; cells_populated <= '0;
end else if (wr_en) begin
if (!legal_cell(wr_is_write_dir, wr_is_device)) begin
// A write naming an impossible {direction, side} pair is REFUSED.
// This is the whole item: the pair is checked, not assumed.
illegal_cell_writes <= illegal_cell_writes + 1'b1;
end else if (!wr_is_write_dir) begin
vref_rd_host <= wr_value;
dly_rd_host <= wr_value;
cells_populated[0] <= 1'b1;
cells_populated[2] <= 1'b1;
end else begin
vref_wr_device <= wr_value;
dly_wr_device <= wr_value;
cells_populated[1] <= 1'b1;
cells_populated[3] <= 1'b1;
end
end
end
endmodule// INTENTIONALLY DEFECTIVE. WEAK BUILD: one reference level, both sides.
//
// input logic [VAL_W-1:0] wr_value;
// ...
// always_ff ... if (wr_en) vref_all <= wr_value; // <-- one register
//
// with `wr_is_device` declared and never read -- 33.2 section 8's tell,
// appearing for the ninth time in this track's documented defects.
//
// CONTRACT VIOLATED: 21.4 section 2's direction asymmetry, and 28.4's
// side-asymmetry error class.
//
// WHY IT SURVIVES: on a symmetric link the two sides' correct values are
// close, so one register holding either is within tolerance. The training
// search finds a passing point. The part works. The defect appears when
// the sides stop being similar -- a different device vendor, a different
// rank population, a different termination setting.
//
// TRACE (ILLUSTRATIVE, correct host Vref 0x48, correct device Vref 0x5C):
// symmetric part (host 0x4A, device 0x4C):
// robust: vref_rd_host=0x4A vref_wr_device=0x4C illegal=0
// weak: vref_all=0x4C (last write wins)
// -- read direction runs 2 codes off centre; inside the eye. WORKS.
// asymmetric part (host 0x48, device 0x5C):
// robust: vref_rd_host=0x48 vref_wr_device=0x5C illegal=0
// weak: vref_all=0x5C
// -- read direction runs 20 codes off centre. Reads fail; writes pass.
//
// gap: 20 codes, and the failure is DIRECTIONAL -- one direction
// corrupts and the other does not, which is 28.4's side-asymmetry
// signature exactly.
//
// And the illegal-pair check: the robust build counts a write naming
// {read direction, device side} as illegal; the weak build cannot,
// because it does not read `wr_is_device` at all.The signature is what makes this item worth its place: a part where one direction works and the other does not. DERIVED: a directional failure is strong evidence of a side-asymmetry error and weak evidence of anything else, because almost every other defect in this chapter is direction-agnostic. CURRICULUM-DERIVED from 28.1 §4's failure-domain split: direction is one of the few discriminators available before a single probe is placed.
And the tell repeats. wr_is_device declared and never read. CURRICULUM-DERIVED from 33.2 §8: eight of the track's eighteen defects present this way, and this chapter adds a ninth and a tenth — item 1's trained_flight and item 9's wr_is_device. DERIVED: the unread-input tell now accounts for ten of twenty documented defects across this curriculum, and it remains the cheapest check either gate has.
15. The Review Assembled
Cost-ordered. DERIVED from §19's table, and the ordering is almost the reverse of 33.2 §15's — at the RTL gate five of nine items were free; here one is.
| # | Item | Question, in one line | Cost | What it needs |
|---|---|---|---|---|
| 1 | §8 Untrained refusal | What is the reset value of this gate? | minutes | one line of RTL |
| 2 | §14 Direction and side | Is is_device read anywhere? | minutes | grep, then a four-cell table |
| 3 | §9 Reset domains | Which reset clears this, and does that reset retrain? | hours | a two-column map |
| 4 | §6 Provenance census | Which side of the line is each quantity on? | hours | a census, judged on completeness |
| 5 | §12 Selection rule | Is this region one-sided or two-sided? | hours | per-search region shape |
| 6 | §7 Flag semantics | What would this flag report in each of five cases? | hours | five stimuli |
| 7 | §11 Range adequacy | What is the hardware's range, and what did we sweep? | days | a number that is not in the RTL |
| 8 | §10 Recal preconditions | What conditions must hold, and which line enforces each? | days | the measurement's validity conditions |
| 9 | §13 Margin provenance | What measurement is this margin a fraction of? | days | characterisation data |
The ordering carries this chapter's central finding about review economics. At 33.2's gate, the cheap items dominated: five of nine were decided from source text and they caught twelve of eighteen defects. Here, seven of nine require an artifact the codebase does not contain.
DERIVED: that is not a difference in review quality. It is a difference in where the information lives. A controller's obligations are in its specification; a PHY's obligations are partly in a datasheet, partly in a characterisation report, and partly in a board design owned by another discipline — 22.3 §3's callout. So a PHY review that produces only findings is an incomplete review. A PHY review must also produce a list of artifacts it could not obtain, and items 7, 8 and 9 are where that list comes from.
And three items compose into one cheap detector, which is the practical takeaway. Items 6, 7 and 12 all fire on a selection or a value that lies outside the region it claims to come from — item 12's chosen_in_region, item 6's census residue, item 7's trusted conjunction. DERIVED: one assertion that every committed value lies inside its own measured region covers three of the nine items, and it costs one line.
16. Quantitative Reasoning
| Item | Quantity | Robust | Weak | Gap | Provenance |
|---|---|---|---|---|---|
| §6 | boards the design is correct on | all | 1 | the characterisation board | DERIVED, ILLUSTRATIVE trace lengths |
| §7 | distinguishable failure cases of five | 5 | 1 | 4 cases read as success | DERIVED from 28.4's five |
| §8 | operations granted untrained | 0 | 1 | 1, and the counter under-reports by 1 | DERIVED |
| §9 | warm resets to failure | none | 1 | 0 cold boots fail | DERIVED |
| §10 | commits measured under traffic | 0 of 8 | 3 of 8 | 37.5% | DERIVED, ILLUSTRATIVE interval |
| §11 | distinguishable failure causes | 4 of 4 | 1 of 4 | 3, plus a false success | DERIVED from 28.1's five |
| §12 | chosen tap on a one-sided region | 9 | 18 | 9 taps, and reported margin 3 vs 12 | DERIVED |
| §13 | chosen tap on the narrowest part | 17 | 22 | 3 taps outside the window | DERIVED, ILLUSTRATIVE widths |
| §14 | reference-level error, asymmetric part | 0 | 20 codes | directional | DERIVED, ILLUSTRATIVE codes |
Three of the nine gaps are inversions — the weak build reports a better number than the robust one — and that is this chapter's signature finding.
| Item | Robust reports | Weak reports | Which looks better |
|---|---|---|---|
| §10 | 5 starts, 3 deferrals | 8 starts, 0 deferrals | the weak build |
| §12 | margin 3 | margin 12 | the weak build |
| §13 | 0 refusals, a narrower choice | valid on every part | the weak build |
DERIVED: at 33.2's gate, the weak builds mostly reported nothing — a silent wrong answer. Here, three of nine actively report success metrics that beat the correct design's.
The mechanism is the same in all three, and it is worth naming as a rule: each weak build removed a check, and the removed check was the thing that produced the unflattering number. Deferrals exist because preconditions are enforced. A small margin figure exists because the margin is measured against a real edge. A refusal count exists because satisfiability is tested. DERIVED: a design that reports no failures of a check it does not perform is indistinguishable, in its telemetry, from a design that passes that check — so a review must ask which counters a design is even capable of incrementing.
CURRICULUM-DERIVED from 28.4 §2's standing rule — a failure signature is evidence, not a verdict — and this chapter's corollary is the contrapositive: the absence of a failure signature is evidence only if the signature could have been produced.
17. What the Assertions Prove
// ---- Section 6: provenance. The measured term must be READ, which is
// the property a formula-based build cannot satisfy.
property p_offset_uses_trained_term;
@(posedge clk) disable iff (!rst_n)
launch_offset_valid |-> (launch_offset == (calc_part + trained_flight));
endproperty
assert property (p_offset_uses_trained_term)
else $error("the launch offset did not include the trained flight term");
property p_no_unclassified_quantity;
@(posedge clk) disable iff (!rst_n) unclassified_terms == '0;
endproperty
assert property (p_no_unclassified_quantity)
else $error("a quantity feeding a timing decision has no declared provenance");
property p_refuses_without_provenance;
@(posedge clk) disable iff (!rst_n)
(!trained_flight_valid) |-> !launch_offset_valid;
endproperty
assert property (p_refuses_without_provenance)
else $error("the launch offset was declared valid with no measured term");
// ---- Section 7: flag semantics. `trusted` is a CONJUNCTION, and the
// property names all three facts -- 28.4's five-into-one collapse.
property p_trusted_implies_all_facts;
@(posedge clk) disable iff (!rst_n)
trusted |-> (ran && converged && in_range);
endproperty
assert property (p_trusted_implies_all_facts)
else $error("trusted asserted without all of its constituent facts");
property p_trust_is_not_monotonic;
@(posedge clk) disable iff (!rst_n)
(trusted && env_changed) |=> !trusted;
endproperty
assert property (p_trust_is_not_monotonic)
else $error("trust survived an environment change: the flag cannot fall");
// ---- Section 8: untrained refusal. Reset value AND the reclose path.
property p_no_grant_while_gate_closed;
@(posedge clk) disable iff (!rst_n) op_grant |-> $past(gate_open);
endproperty
assert property (p_no_grant_while_gate_closed)
else $error("an operation was granted with the trained-value gate closed");
property p_gate_closed_without_validity;
@(posedge clk) disable iff (!rst_n) (!trained_valid) |=> !gate_open;
endproperty
assert property (p_gate_closed_without_validity)
else $error("the gate stayed open with no valid trained value");
// ---- Section 9: reset domains. The ONLY property in this chapter whose
// antecedent is a reset, and it must be written outside the
// `disable iff` that every other property uses.
property p_clear_implies_retrain;
@(posedge clk)
$fell(trained_valid) |-> ##[0:2] (retrain_req || trained_valid);
endproperty
assert property (p_clear_implies_retrain)
else $error("a trained value was cleared and no retraining was requested");
property p_no_ops_on_reset_value;
@(posedge clk) disable iff (!rst_por_n) ops_with_stale_offset == '0;
endproperty
assert property (p_no_ops_on_reset_value)
else $error("an operation used the reset-value offset");
// ---- Section 10: recalibration preconditions.
property p_start_only_when_permitted;
@(posedge clk) disable iff (!rst_n) recal_start |-> preconditions_met;
endproperty
assert property (p_start_only_when_permitted)
else $error("recalibration started with its preconditions unmet");
property p_commit_only_under_preconditions;
@(posedge clk) disable iff (!rst_n) commits_under_traffic == '0;
endproperty
assert property (p_commit_only_under_preconditions)
else $error("a measurement taken under traffic was committed");
// ---- Section 11: range adequacy. Two-sided, because "failed" being
// correct is exactly why one bit is not enough.
property p_failure_has_a_cause;
@(posedge clk) disable iff (!rst_n)
(done && !found) |-> (err_prereq || err_classifier || range_exhausted
|| (pass_count == '0));
endproperty
assert property (p_failure_has_a_cause)
else $error("a search failed and reported no distinguishing cause");
property p_prereq_checked_before_sweep;
@(posedge clk) disable iff (!rst_n)
(start && !prereq_done) |=> (err_prereq && done);
endproperty
assert property (p_prereq_checked_before_sweep)
else $error("the sweep ran with an unfinished prerequisite");
// ---- Section 12: selection rule. The shared cheap detector.
property p_choice_inside_its_region;
@(posedge clk) disable iff (!rst_n) chosen_valid |-> chosen_in_region;
endproperty
assert property (p_choice_inside_its_region)
else $error("the chosen tap lies outside the region it was selected from");
property p_one_sided_is_not_centred;
@(posedge clk) disable iff (!rst_n)
(chosen_valid && ONE_SIDED) |-> (chosen_tap == region_low + TAP_W'(LEAD_TAPS));
endproperty
assert property (p_one_sided_is_not_centred)
else $error("a one-sided region was centred");
// ---- Section 13: margin provenance. The satisfiability test, asserted.
property p_margin_fits_the_window;
@(posedge clk) disable iff (!rst_n)
chosen_valid |-> (margin_required <= margin_available);
endproperty
assert property (p_margin_fits_the_window)
else $error("a point was committed whose margin exceeds the measured window");
property p_unsatisfiable_margin_refuses;
@(posedge clk) disable iff (!rst_n)
(window_valid && !margin_satisfiable) |=> !chosen_valid;
endproperty
assert property (p_unsatisfiable_margin_refuses)
else $error("an unsatisfiable margin produced a valid choice");
// ---- Section 14: direction and side.
property p_illegal_pair_refused;
@(posedge clk) disable iff (!rst_n)
(wr_en && (wr_is_write_dir != wr_is_device)) |=> $stable(vref_rd_host)
&& $stable(vref_wr_device);
endproperty
assert property (p_illegal_pair_refused)
else $error("a write naming an impossible direction/side pair changed a cell");
property p_sides_are_separate;
@(posedge clk) disable iff (!rst_n)
(wr_en && !wr_is_write_dir && !wr_is_device) |=> $stable(vref_wr_device);
endproperty
assert property (p_sides_are_separate)
else $error("a read-direction host write disturbed the device-side value");
// ---- COVERS. Each on the dimension its defect scales with.
// Section 6: the trained term DIFFERING from any formula's answer.
cover property (@(posedge clk) disable iff (!rst_n)
trained_flight_valid && (trained_flight != '0));
// Section 7: each of the five cases reached. The antecedent of
// p_trusted_implies_all_facts is unreachable for four of them.
cover property (@(posedge clk) disable iff (!rst_n) fail_case == 3'd2);
cover property (@(posedge clk) disable iff (!rst_n) fail_case == 3'd3);
cover property (@(posedge clk) disable iff (!rst_n) fail_case == 3'd4);
cover property (@(posedge clk) disable iff (!rst_n) fail_case == 3'd5);
// Section 8: a request INSIDE the untrained window -- not a legal boot
// sequence, which is why no stimulus reaches it by accident.
cover property (@(posedge clk) disable iff (!rst_n) op_request && !gate_open);
// Section 9: a WARM reset after successful training. The dimension is
// the reset TYPE, and cold-booting more times never reaches it.
cover property (@(posedge clk) trained_valid ##1 $fell(rst_func_n));
// Section 10: the interval expiring while the bus is BUSY.
cover property (@(posedge clk) disable iff (!rst_n)
(ivl == INTERVAL[$clog2(INTERVAL+1)-1:0]) && bus_busy);
// Section 11: a pass region touching the sweep's end -- the false
// success of row 5, which is not a failure and must still be covered.
cover property (@(posedge clk) disable iff (!rst_n) done && found && edge_at_high);
// Section 12: a ONE-SIDED region, which a random pass map rarely gives.
cover property (@(posedge clk) disable iff (!rst_n)
map_valid && pass_map[TAPS-1] && !pass_map[0]);
// Section 13: a window NARROW enough that the constant margin fails.
cover property (@(posedge clk) disable iff (!rst_n)
window_valid && (width < (TAP_W+1)'(2*MIN_USABLE)));
// Section 14: an ASYMMETRIC pair. Symmetric sides hide the defect.
cover property (@(posedge clk) disable iff (!rst_n)
wr_en && (vref_rd_host != '0)
&& (wr_value > vref_rd_host + 8'd16));Reviewed against the eleven varieties, and three are worth stating because this chapter's property set had to be built around them.
Variety 9 — a property hidden by its own reset guard — governs §9, and it forced the only structural exception in this chapter. p_clear_implies_retrain has no disable iff, because the event it checks is a reset. CURRICULUM-DERIVED from 30.10 §12: a property whose antecedent is masked by the guard every other property needs is vacuous in exactly the window it was written for. Every other property here disables on reset. This one cannot, and a review that applies the house style uniformly deletes the item.
Variety 4 — the environment shares the design's wrong model — governs §6 and §8. §6's environment shares a constant (the board's trace length, in both design and testbench). §8's shares a schedule — a testbench modelling the boot ROM never drives the window the ROM never drove. DERIVED: variety 4's original form was a shared constant (30.7 §10) and 32.2 §16 broadened it to a shared model; §8 is a third form, a shared sequence, and it is the one no amount of randomisation reaches because the sequence is a legality constraint.
Variety 11 — the evidence-grade escape — governs §13. The margin constant is grade A evidence: documented for a named configuration — the characterisation part. It is used as though it were grade B: a documented mechanism. CURRICULUM-DERIVED from 18.4 §1's four grades and 32.1 §15's variety: the failure mode is category drift, and a frozen measurement drifting into a general rule is the cleanest instance of it in this curriculum.
18. Mutation Testing
Baseline first: all nineteen assertions pass and all twelve covers are non-zero.
| # | Mutation | Killed by | Survived? |
|---|---|---|---|
| M1 | §6: set ALLOW_CALC_FLIGHT = 1 | p_offset_uses_trained_term | killed |
| M2 | §6: drop the unclassified_terms residue | p_no_unclassified_quantity, by one cover | killed |
| M3 | §7: trusted <= engine_returned | p_trusted_implies_all_facts | killed |
| M4 | §7: delete the env_changed clause | p_trust_is_not_monotonic | killed |
| M5 | §8: gate_open <= 1'b1 at reset | p_no_grant_while_gate_closed, by one cover | killed |
| M6 | §9: move trained regs to rst_func_n | p_clear_implies_retrain, by one cover | killed |
| M7 | §10: recal_start on the timer | p_start_only_when_permitted | killed |
| M8 | §10: commit without rechecking preconditions | p_commit_only_under_preconditions | killed |
| M9 | §11: collapse the causes into one failed bit | p_failure_has_a_cause | killed |
| M10 | §11: check the prerequisite after the sweep | p_prereq_checked_before_sweep | killed |
| M11 | §12: always centre | p_one_sided_is_not_centred, by one cover | killed |
| M12 | §13: GUARD_TAPS constant, no satisfiability test | p_margin_fits_the_window | killed |
| M13 | §14: one merged vref_all register | p_sides_are_separate | killed |
| M14 | §11: widen TAPS to HW_TAPS | nothing | SURVIVES |
| M15 | §7: STALE_AT raised to its maximum | nothing | SURVIVES |
| M16 | §13: DRIFT_NUM/DRIFT_DEN set to 0 | nothing | SURVIVES |
DERIVED: thirteen of sixteen killed, three survived — and all three survivors are the same finding, which is the most useful result in this chapter.
M14, M15 and M16 each change a quantity rather than a mechanism, and every property here checks mechanism. Widening the sweep to the full hardware range is arguably an improvement and arguably a boot-time regression; raising the staleness threshold makes trusted almost permanent; zeroing the drift budget makes every margin satisfiable. All three keep the code structurally correct and make the design worse.
That is not a coverage gap. It is the boundary of what this gate can prove, and it is exactly where §23 draws the residual risk. CURRICULUM-DERIVED from 20.5, which owns the published figures this chapter deliberately does not derive: whether 32 taps, 512 ticks or a quarter of the window are the right numbers is a question about a datasheet and a characterisation report. DERIVED: a PHY review can prove that a design uses a measurement correctly. It cannot prove the measurement was the right one to take, and three mutations are the mechanical demonstration.
And four mutations are killed only by a cover — M2, M5, M6 and M11. Each property is correct and its antecedent is unreachable under a benign stimulus: no unclassified quantity ever appears, no request lands in the untrained window, no warm reset follows training, no one-sided pass map arises. M6's cover is the sharpest of the four, because its dimension is the reset type — CURRICULUM-DERIVED from 31.2 §14's coverage-dimension rule: cold-booting a thousand times never reaches a warm reset, and duration is not the dimension this defect scales with.
19. Baseline Defects Found Before Mutation
Each of the nine weak builds run against the robust build's property set, before any mutation.
| Item | Weak build caught by | At what cost |
|---|---|---|
| §6 | p_offset_uses_trained_term | nothing on a differing board — a grep finds the unread input |
| §7 | p_trusted_implies_all_facts | four stimuli, one per unreached case |
| §8 | p_no_grant_while_gate_closed | nothing — one reset branch |
| §9 | p_clear_implies_retrain | one warm reset, after training |
| §10 | p_start_only_when_permitted | traffic concurrent with the interval |
| §11 | p_failure_has_a_cause | a deliberately wrong sweep range |
| §12 | p_one_sided_is_not_centred | a one-sided pass map |
| §13 | p_margin_fits_the_window | a narrow part |
| §14 | p_sides_are_separate | an asymmetric device |
DERIVED: two of nine are visible in source text — §8's reset branch and §6's unread input. Seven require a stimulus, and five of those seven require a stimulus that is not a variation of the nominal one.
That is the inverse of 33.2 §19's result, where five of nine needed no simulation. The reason is structural: an RTL defect is a wrong line, and a line can be read. A PHY defect is a wrong belief about a measured value, and a belief is only visible when the value it is about behaves unexpectedly.
And the five stimuli that are not variations of the nominal one deserve naming, because each is a category of test a project either has or does not have.
| Item | The stimulus | Why more of the nominal test never reaches it |
|---|---|---|
| §9 | a warm reset after training | the dimension is reset type, not count |
| §12 | a one-sided pass map | random maps are two-sided; one-sidedness is a property of the search |
| §13 | a narrow part | the nominal part is the characterisation part |
| §14 | an asymmetric device | one vendor, one population, one termination setting |
| §11 | a wrong sweep range | nobody deliberately mis-programmes a register that works |
DERIVED: all five require the testbench to model a different system, not different traffic. CURRICULUM-DERIVED from 31.2 §14's refinement and this track's coverage-dimension rule: when the dimension a defect scales with belongs to the environment's model rather than its stimulus, running longer never reaches it — and at this gate, five of nine defects live on exactly that kind of dimension. That is the strongest argument this curriculum has for a configurable, multi-part PHY verification environment, and it is an argument 33.4 inherits.
20. Silicon Observability
| Item | What silicon can show | What it cannot |
|---|---|---|
| §6 | the same part working on one board and failing on another | which term carried the board's value |
| §7 | a done flag that has never been observed low since boot | which of five things it meant |
| §8 | an early-operation counter reading lower than the request count | whether the gap is refusal or a miscount |
| §9 | a part that passes cold boot and fails after warm reset, repeatably | which register lost its value |
| §10 | performance degrading under sustained load and recovering at idle | that the mechanism is recalibration, not temperature |
| §11 | an all-zeros bitmap | any of 28.1's five causes |
| §12 | a chosen tap at the midpoint of a region touching the sweep end | that the far edge was artificial |
| §13 | a yield cliff on parts with narrow windows | that the margin constant is the cause |
| §14 | one direction corrupting while the other is clean | which side's register is wrong |
Three signatures in this table are diagnostic — they point at one item and few others — and they are the three worth memorising.
§9: cold boot passes, warm reset fails, repeatably. DERIVED: almost nothing else in this curriculum produces that pattern, because almost nothing else distinguishes reset types. CURRICULUM-DERIVED from 19.5 §2's ordering hazards — this is what they look like from a bench.
§14: one direction corrupts, the other does not. CURRICULUM-DERIVED from 28.4's side-asymmetry class. DERIVED: direction is a free discriminator — it costs one read test and one write test — and it eliminates every direction-agnostic item in this chapter at once.
§6: the part moves and the failure moves with the board, not with the part. CURRICULUM-DERIVED from 22.3 §3: board design is a separate discipline, so a failure that swaps with the board and not with the die is a failure whose cause was transcribed across that boundary.
And one row is a warning rather than an observation: §11's. An all-zeros bitmap is silicon telling you nothing, and 28.1 §2 is the chapter that says so. DERIVED: the only fix is at design time, which is what makes item 6 a review item rather than a debug technique. A design that cannot distinguish its failure causes cannot be made to, after tapeout, by any amount of bench work.
21. Common Wrong Answers
-
“Training passed, so the PHY is configured correctly.” Seven of the nine weak builds train successfully. Training reports that a search found a point; it does not report that the point was selected by the right rule (§12), fits the window (§13), was written to the right side (§14), or will survive the next reset (§9).
-
“The formula gives the same answer as training, so training is redundant.” It gives the same answer on the board the formula's inputs describe. §6's trace: identical on board A, one cycle off on board B — and nothing in the RTL domain can revalidate a trace length.
-
“
calib_doneis high, so calibration succeeded.” It means one of five things (28.4 §2), and the worst of the five “boots, passes its own self-checks, and corrupts data hours later.” A flag that has never been low is not evidence; it is an untested output. -
“The gate opens at reset, but it is corrected on the first clock.” One cycle is enough for one operation, and §8's weak build grants exactly one — with a reset-value delay and no error. And the escape widens the moment anything changes the boot sequence.
-
“Cold boot works, so the reset logic is fine.” Cold boot works in every build in §9. Cold boot asserts both resets, so the mismatch between them is invisible. The defect is visible only on the reset that asserts one and not the other.
-
“The recalibration runs on schedule every time, so it is healthy.” Or it never checks whether running was valid. §10's weak build has a perfect schedule and commits three of eight measurements taken under traffic. A schedule with no deferrals is consistent with a design that cannot defer.
-
“We defer recalibrations sometimes, which is a problem to fix.” Inverted. The deferral count is what enforcement looks like. A design reporting zero deferrals has either a very quiet bus or no preconditions.
-
“The bitmap is all zeros, so we have a margin problem.” Five causes, one signature (28.1 §2). Three of the five are digitally decidable by the search itself, and a search reporting one bit threw that away.
-
“The search passed, so we measured the margin.” Not if the pass region touched the end of the sweep. §11 row 5: a reported success whose margin is a lower bound of unknown looseness — and the weak build cannot tell you it happened.
-
“We centre the window. That is best practice.” It is, for a two-sided region. 21.3 §2 states that the gate's pass region is one-sided and therefore not centred — and §12's weak build lands nine taps away while reporting a margin four times larger.
-
“The larger reported margin means the better configuration.” §12: robust 3, weak 12, and the weak number measures the distance to the end of a sweep. A margin is only a margin if the thing it is measured to is a failing point.
-
“A generous constant guard band is conservative engineering.” §13's weak build places the operating point three taps outside the measured window on a narrow part. A constant margin is conservative on parts better than the characterisation part and reckless on parts worse than it.
-
“
GUARD_TAPSwas measured, so it has provenance.” It has provenance for one configuration — evidence grade A — and it is being used as a general rule, grade B. CURRICULUM-DERIVED from 18.4 §1: the failure mode is category drift, not fabrication. -
“One Vref register is simpler, and the sides are nearly the same.” Nearly the same on the part you have. §14: 2 codes apart on a symmetric part, 20 on an asymmetric one, and the failure is directional.
-
“The unread
is_deviceinput is a lint warning.” It is the tenth instance of this tell in this curriculum (33.2 §8 counted eight; this chapter adds two). In every instance the unread port carried the value the design should have used. -
“The provenance census is clean, so provenance is fine.” The census counts the declaration. §6's weak build reports three classified terms and zero unclassified because the declaration was edited to match the code. A self-reported census is evidence about the declaration.
-
“A mutation survived, so we need more properties.” Not for M14, M15 or M16. All three change a quantity — a sweep width, a staleness threshold, a drift fraction — and no property about mechanism can adjudicate a quantity. The answer is a datasheet, not an assertion.
-
“The PHY is the analogue part, so a digital review has little to say.” Nine items, and not one of them needs a volt. Every defect here is a digital belief about a physical quantity — a reset domain, a flag's meaning, a selection rule, a range bound. CURRICULUM-DERIVED from 20.3: sample quality is not digitally observable, and none of these nine items asks about sample quality.
-
“Our PHY is vendor IP, so this checklist is not ours to run.” Items 1, 3, 4, 5 and 9 are about the integration, not the IP — which quantities you supply, what your reset tree does to its registers, when you trigger its recalibration, and which side's register your register map writes. 19.1 §3: six rows of the ownership table are implementation-dependent, so the boundary is yours to establish and therefore yours to review.
-
“We will find these in bring-up.” Five of the nine require the bench to model a different system — a warm reset, a narrow part, an asymmetric device, a one-sided region, a wrong sweep range (§19). Bring-up uses the boards and parts it has.
22. Self-Check
- A launch offset is
cfg_cwl + cfg_ser_depth + (TRACE_MM * PROP_NS_PER_MM) / CLK_PERIOD_NS. The expression is dimensionally correct. State the finding in one sentence, and name the input that is now unread. calib_donehas been high since boot on every part you have tested. Give the five things it could mean, and the one observation that would narrow it.- A trained-value gate's reset branch assigns
1'b1. How wide is the escape window, and what makes it wider? - A design passes cold boot on every part and fails after a warm reset. Name the mechanism, and say which two columns of a table you would ask for.
- A recalibration block reports 64 starts and 0 deferrals over a busy hour. Is that good? Give the one question that decides it.
- A training search reports
failed. The report is correct. Say what is wrong with it, and how many of 28.1's five causes the design could have distinguished. - A pass region runs from tap 6 to tap 31 in a 32-tap sweep, and the design chooses tap 18. Two things are wrong. Name both.
- Two builds report margins of 3 and 12 taps for the same search. Which is more likely to be correct, and why is the question not about the numbers?
GUARD_TAPS = 6was measured on real silicon. Under what conditions is it correct, and what is the evidence grade it is being used at versus the grade it holds?- One direction of the interface corrupts data; the other is clean. Which item does this point at, and what makes the signature diagnostic rather than merely suggestive?
- Three mutations survived this chapter's property set. What do all three have in common, and what artifact — not assertion — would kill them?
- Of the nine items, how many are decided by source text, how many by a stimulus, and how many by a stimulus that requires modelling a different system? Give the counts and name the five.
23. The Residual Risk
What this gate cannot catch, stated plainly — and here the boundary is unusually sharp, because three mutations already demonstrated it.
It cannot adjudicate a quantity. M14, M15 and M16 changed a sweep width, a staleness threshold and a drift fraction, and no property fired. Every item here checks that a design uses a measurement correctly; none can check that the measurement was the right one to take. CURRICULUM-DERIVED from 20.5, which owns the published figures: the answer to “is 32 taps enough” is in a datasheet and a characterisation report, and this chapter derives neither.
It cannot tell you that a converged value is wrong. CURRICULUM-DERIVED from 28.4 §2's sixth and worst case, which it inherits from 7.7: a calibration “whose outcome the interface does not report” — for that mechanism, “did it work?” is not a question digital logic can answer at all. Item 2 makes a done flag honest about what it observed. It cannot make the interface report something it does not report.
It cannot see a defect in the electrical domain that produced the measurement. Every item takes a pass/fail probe as a given. If the probe's classifier is miscalibrated, every search in this chapter converges confidently on the wrong point, and item 6's classifier_alive catches only a stuck classifier, not a biased one. Module 22 owns the channel and 28.6 owns its failures.
It cannot review the sequencer's order. 21.5 §2 owns the dependency structure and states the rule — the order is determined by which observable each step requires — and item 6 checks only that this search's prerequisite was done. A globally wrong order in which every step's immediate prerequisite is satisfied passes all nine items. DERIVED: checking an order requires a declared relation and a topological validation rather than a per-step precondition, and that is 33.6's subject — so this limit is not a gap in the module, it is a boundary between two of its gates.
And it cannot prove that the environment which exercised these nine items is capable of failing. §19 named five stimuli that require modelling a different system. A project can hold every artifact this chapter demands, pass every property, and have a testbench that cannot produce a narrow part or an asymmetric device. That is the next gate's subject, and it is why 33.4 reviews the environment rather than the design.
The honest summary: this gate's nine items decide whether a design's beliefs about measured values are warranted. It cannot decide whether the measurements were right, whether the probe that produced them was honest, or whether anything ever tested the beliefs it just checked.
24. Where This Goes
This gate reviewed the design's beliefs. The next gate reviews the thing that was supposed to test them.
Chapter 33.4 takes the environment as its subject. The question changes from is this belief warranted to could the thing that checked this belief ever have found it false — and this chapter hands it an unusually concrete brief, because §19 measured the answer for nine items and found it wanting in five.
| From here | What 33.4 must settle |
|---|---|
| §19's five model-dimension stimuli | Can this environment produce a different system, or only different traffic? |
| §17's variety-9 exception | Which properties in this project are disabled in the window they were written for? |
| §18's three quantity mutations | What kills a mutation that no property can express? |
The last row is the one to carry forward, and it is a genuinely open question rather than a rhetorical one. DERIVED: three mutations in this chapter changed a number, kept every mechanism correct, made the design worse, and were killed by nothing. A coverage report would show no gap. A property review would find no missing property. CURRICULUM-DERIVED from the eleven-variety taxonomy this track has built across 30.9 §6, 31.1 §14 and 32.1 §15: every variety so far describes a property that is wrong, vacuous, or unreachable. None describes a property that is right, reachable, non-vacuous — and silent about the thing that matters.
That gap is where 33.4 starts. The eleven varieties explain why a green assertion proves nothing. The next gate has to explain why a complete, green, fully covered assertion set can still leave a design worse than it should be — and the candidate mechanism is the one this chapter's survivors point at: a property set that is a perfect description of the mechanism and says nothing about the quantities the mechanism operates on.
Continue learning
Related tutorials
- Related topic
Architecture Review Checklist
The first of eight pre-tapeout gates. Eight review items asked of a controller architecture before RTL exists, each one a question whose pass criterion is a named mechanism rather than an intention — and whose weak build produces a document that reads as thorough.
- Related topic
RTL Review Checklist
Nine questions drawn from the eighteen documented defects this track already carries. Every finding is code that is legal, lint-clean, passes a nominal test and is wrong — and five of the nine are decided by reading a port list.
- Related topic
Verification Review Checklist
The gate that reviews the environment rather than the design. Nine items on configuration reach, antecedent publication, scale-vacuity, model independence and checker liveness — and it opens a twelfth variety the eleven-item taxonomy does not cover.
- Related topic
Performance Review Checklist
The first gate whose pass criterion is a comparison against a computed expectation rather than against a contract. Nine items on which efficiency, which denominator, which of five bandwidths, and which operating point — where every weak instrument computes correctly.
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.
