DDR · Module 33
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.
Chapter 33.1 asked what mechanism enforces each invariant. This gate asks whether the code that was written is that mechanism — and it is the first review where the answer is a line rather than a paragraph.
An architecture review can be conducted on intent. An RTL review cannot.
What does this line actually do, in every cycle, under every combination of its inputs — including the ones the testbench never drove?
Every finding in this chapter is a construct that is individually legal, compiles without a warning, lints clean, passes a nominal test, and is wrong. That is not a stylistic preference — it is the definition of what an RTL review is for. A defect that fails a nominal test is found by running the test, and needs no reviewer.
And this chapter has an unusual advantage: it is not inventing its items. Measured across the live track excluding this module: 187 chapters, 574 systemverilog blocks, and eighteen chapters carrying an INTENTIONALLY DEFECTIVE block — each with a stated contract, a trace, a correction, and a paragraph explaining why it survived review. The nine items below are those eighteen defects sorted into the questions that would have caught them.
1. How To Use This Chapter
Each of the nine review items answers the same eight questions.
| Facet | What it settles |
|---|---|
| Under review | the construct being examined |
| Invariant at risk | the property that breaks if it is wrong |
| Where it lives | the specific line, not the module |
| Evidence to demand | what the reviewer should ask to see |
| 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 code look correct |
The last facet is harder here than at the architecture gate, and for a specific reason. A broken architecture produces a reassuring document, which a reviewer knows to distrust. Broken RTL produces a reassuring waveform, a clean lint run and a passing regression — three artifacts engineers are trained to trust, and two of the three are produced by tools.
Which is why five of the nine items below are findable by LINT OR BY READING A PORT LIST, and §19's table says which. The cheapest reviewer in this chapter is the one who reads the module header before opening the body.
2. The One-Sentence Model
An RTL review is sound when every counter has been checked against whether it holds a count or an index, when every classification is made against registered state, when every declared input is read, when no denominator is derived from its own numerator, when every validity gate's value is consumed as well as its gate, when every guard is reachable, when no parameter carries a platform quantity, when every accumulator starts at its operation's identity, and when every convention appears in the code rather than only in a specification — and “the RTL was reviewed” is bit 0.
3. What This Chapter Owns
| Ground | Owner |
|---|---|
| Reviewing the architecture before RTL exists | 33.1 |
| Reviewing whether a PHY function can be performed at all | 33.3 |
| Reviewing the environment that judges this RTL | 33.4 |
| The eleven ways a green assertion proves nothing | 30.9 §6, 31.1 §14, 32.1 §15 |
| Each individual defect's mechanism, trace and correction | its own chapter — 30.2 through 32.5 |
| The blocks these defects live in | Module 17, Module 23, Module 27 |
| Reviewing the RTL against the architecture | this chapter |
The boundary with 33.4 is worth stating precisely, because the two gates share a vocabulary. This chapter reviews the design. 33.4 reviews the environment that judges the design. A finding this chapter calls “the counter counts the wrong event” becomes, at that gate, “no property named the counter” — the same escape, seen from the other side of the testbench. Where an item below cites a variety from the assertion taxonomy, it is naming the shape 33.4 will audit, not auditing it here.
4. Teaching-Model Boundary And Source Discipline
Every RTL block in this chapter is a teaching model. Each isolates one review dimension so it can be examined, mutated and broken on purpose. None is a production DDR controller, an implementation of any JEDEC flow, or a complete design.
Nothing here states a normative DDR detail. No timing value, command encoding, mode-register field or capacity appears as fact. The review dimensions — count versus index, classification reference, unread inputs, denominator independence, gate-versus-value, guard reachability, parameter provenance, accumulator identity, convention locality — are general synchronous-design properties, examined in general form deliberately so the technique transfers to any block a reviewer is handed.
| Claim class | How it is marked |
|---|---|
| General RTL reasoning | stated plainly |
| Teaching abstraction | declared in the RTL header |
| Illustrative parameter | ILLUSTRATIVE at every concrete figure |
| Derived arithmetic | DERIVED, shown with its inputs and recomputed |
| Taken from a verified chapter | CURRICULUM-DERIVED, with chapter and section named |
| Holds across generations | STRUCTURAL |
Every model is built twice. A parameter selects the robust build — what the review should require — and the weak build, containing exactly the defect under discussion, written as it appears in the chapter that documented it rather than as a caricature. Each model computes its own truth in both builds, so it detects its own weak build, and every item's headline number is the gap.
5. The Eighteen Defects, Sorted
The items are the questions; this table is where they came from. Measured: eighteen chapters in the live track carry an INTENTIONALLY DEFECTIVE block.
| Chapter | The defect, in one line | Caught by item |
|---|---|---|
| 30.2 | classifies against state including the same cycle's own command | §7 |
| 30.3 | > where the stated convention requires >= | §14 |
| 30.4 | reserves the arrival cycle, not the occupancy window | §14 |
| 30.5 | the aged set is not intersected with the legal set | §11 |
| 30.6 | commits the first eligible run rather than the widest | §11 |
| 30.7 | compiled-in flight time; trained input declared and unread | §8, §12 |
| 30.8 | demand counted on arrival rather than on outstanding work | §6, §9 |
| 30.9 | the checker samples one cycle late, then gains a tolerance | §7, §14 |
| 30.10 | the evidence register is cleared by the functional reset | §10 |
| 31.1 | fixed-latency completion under a history-dependent contract | §8, §12 |
| 31.2 | the ledger accrues while the device owns the obligation | §10 |
| 31.3 | a straddling burst is never split | §11 |
| 31.4 | returns associated by position where the contract says tag | §8, §12 |
| 32.1 | a compiled field position; configuration gate honoured, value ignored | §10, §12 |
| 32.2 | the throttle has no authority over the loop term | §8 |
| 32.3 | pop_id declared and never read in the credit accounting | §8 |
| 32.4 | the utilisation denominator is derived from its numerator | §9 |
| 32.5 | two components blended into one published figure | §9 |
DERIVED from the table, and it is the chapter's organising finding: eight of the eighteen are caught by §8 alone — is every declared input read — and six of those eight are the same tell: a signal on the port list that the body never mentions. CURRICULUM-DERIVED from 30.9 §3: lint finds it before simulation starts, which makes §8 the highest-yield item in this chapter and the cheapest.
Two of the eighteen appear under three items each — 30.7 and 32.1 — and both are the same composite: a platform quantity compiled in, a configuration input unread, and a validity gate honoured while its value is ignored. That composite is worth naming as a single review reflex, and §12 does.
6. Review Item 1 — Does This Counter Hold a Count or an Index?
Under review. Every counter's width expression.
Invariant at risk. The counter's own arithmetic. A counter one bit too narrow saturates or wraps at exactly the value the comparison against it needs.
Where it lives. The $clog2 call in the parameter list.
The distinction, stated once. A value that indexes N things ranges over 0 … N−1 and needs $clog2(N) bits. A value that counts N things ranges over 0 … N inclusive and needs $clog2(N+1). The two differ by one bit exactly when N is a power of two, which is when the mistake is invisible in every other configuration.
// ---------------------------------------------------------------------
// count_vs_index -- TEACHING MODEL. Review item 1.
//
// CLASSIFICATION: synthesisable teaching model. Built TWICE.
// ILLUSTRATIVE depth.
//
// WHAT IT IS: a queue occupancy counter, sized as a count or as an
// index. The robust build uses $clog2(DEPTH+1); the weak build uses
// $clog2(DEPTH), which is the expression a designer writes when the
// same parameter also indexes the storage array -- and it does.
//
// WHY IT EXISTS HERE: this hazard appears in every module from 27
// onward, and its failure DIRECTION is the dangerous part: a wrapped
// occupancy reads EMPTY when the queue is full, so admission
// continues without limit.
//
// HOW TO RUN IT: fill the queue to DEPTH.
// EXPECTED RESULT: robust reports DEPTH and refuses further
// admission; weak wraps to 0 and admits without limit.
// EXPECTED TRACE: `occ_err` is low in the robust build and HIGH in
// the weak build on the cycle occupancy reaches DEPTH.
//
// SYNTHESIS: one counter. The weak build is one bit smaller, which
// is why it is sometimes chosen deliberately.
// LIMITATIONS: one counter, one comparison. 17.2 owns the entry pool.
// ---------------------------------------------------------------------
module count_vs_index #(
parameter bit ROBUST = 1'b1,
parameter int DEPTH = 16, // ILLUSTRATIVE
// The INDEX width -- correct for addressing the array.
parameter int IDX_W = $clog2(DEPTH),
// The COUNT width -- correct for occupancy, which reaches DEPTH.
parameter int CNT_W = $clog2(DEPTH + 1),
parameter int OCC_W = ROBUST ? CNT_W : IDX_W,
parameter int ERR_W = 16
)(
input logic clk,
input logic rst_n,
input logic push,
input logic pop,
output logic [OCC_W-1:0] occ,
output logic full,
output logic admit,
output logic [ERR_W-1:0] overadmit_count,
output logic occ_err
);
// The TRUTH: a wide-enough shadow counter, present in BOTH builds
// so the model detects its own weak build.
logic [CNT_W-1:0] truth;
assign full = (occ >= DEPTH[OCC_W-1:0]);
assign admit = push && !full;
always_ff @(posedge clk) begin
if (!rst_n) begin
occ <= '0; truth <= '0; overadmit_count <= '0;
end else begin
unique case ({admit, pop})
2'b10 : occ <= occ + 1'b1;
2'b01 : if (occ != '0) occ <= occ - 1'b1;
default : ;
endcase
// The truth advances on the same events, at full width.
unique case ({push && (truth < DEPTH[CNT_W-1:0]), pop})
2'b10 : truth <= truth + 1'b1;
2'b01 : if (truth != '0) truth <= truth - 1'b1;
default : ;
endcase
// Over-admission: the design admitted while the truth was full.
if (admit && (truth >= DEPTH[CNT_W-1:0]))
if (overadmit_count != {ERR_W{1'b1}})
overadmit_count <= overadmit_count + 1'b1;
end
end
assign occ_err = (occ != truth[OCC_W-1:0]) || (truth > DEPTH[CNT_W-1:0]);
endmoduleThe measurement.
ILLUSTRATIVE DEPTH = 16, so IDX_W = 4 and CNT_W = 5.
Push 20 times with no pops.
DERIVED, recomputed:
ROBUST = 1 (OCC_W = 5) : occ rises 0..16, then full=1 and
admit=0. overadmit_count = 0.
ROBUST = 0 (OCC_W = 4) : occ rises 0..15, then the 17th push
wraps it to 0, full drops, and
admission RESUMES.
overadmit_count = 4
gap = 4 over-admissions in 20 pushes, and the count grows without
bound as the offered load continues.DERIVED, and the direction is what makes it dangerous: the wrapped counter reads empty when the queue is full. A saturating counter would read full forever and stall — visible immediately. A wrapping one resumes admission, so the queue overruns and the failure appears as data corruption somewhere downstream.
Evidence to demand. Ask for the width expression of every counter, and ask which of them can reach their maximum. The reviewer's question is not is this $clog2 right but what is the largest value this holds, and is that value an index or a count? — and the answer is in the comparison, not the declaration.
What escapes. Queue overrun under load; a credit pool that issues more credits than it has; an occupancy figure that never reads full. CURRICULUM-DERIVED from 32.3 §10 and 32.5 §11, both of which state this hazard in their own headers because it reaches inside the case the counter exists to handle.
How DV proves it. Fill to exactly DEPTH and check full. Three lines, and it is absent from a testbench whose stimulus never saturates — which is 31.2 §14's scale-vacuity: the antecedent occurs only at full occupancy.
Telemetry. Publish the occupancy and a saturation flag. CURRICULUM-DERIVED from 30.8 §11: an instrument that can report an impossible value has never been checked, and a wrapped occupancy is exactly an impossible value reported as a plausible one.
Misleading evidence. A waveform in which occupancy rises and falls smoothly and never reaches DEPTH, because the stimulus was a steady-state flow. And a lint run that is clean — the width expression is legal, the arithmetic is legal, and no tool objects to a counter that is one bit narrow for its own comparison.
7. Review Item 2 — Is This Classified Against Registered State, or State Including This Cycle?
Under review. Every combinational block that reads a register and also reads an event that will change that register on this edge.
Invariant at risk. The meaning of the classification. An outcome computed against state that includes this cycle's own command describes a state that did not exist when the requester asked.
Where it lives. The else if that mentions a *_valid signal inside a block otherwise reading *_q.
CURRICULUM-DERIVED from 30.2 §10, whose defective tracker folds a same-cycle activate into the classification, and from 30.9 §10, whose checker registers the command and compares it against the next cycle's state. The two are the same error in opposite directions, and both produce a classification that is right about a moment nobody asked about.
// ---------------------------------------------------------------------
// classify_reference -- TEACHING MODEL. Review item 2.
//
// CLASSIFICATION: synthesisable teaching model. Built TWICE.
//
// WHAT IT IS: row-outcome classification against registered state or
// against state folded with this cycle's own command. The robust
// build reads only `*_q`. The weak build adds the same-cycle command,
// which is how it is written when a designer notices that the
// command "is about to" change the state and helpfully accounts for
// it.
//
// WHY IT EXISTS HERE: 30.2 §10 owns this defect and 30.9 §10 owns its
// mirror. The gap is the number of accesses classified against a
// state the requester never saw.
//
// HOW TO RUN IT: present a column request to a bank in the same cycle
// as an activate to that bank.
// EXPECTED RESULT: robust classifies against the pre-activate state;
// weak classifies against the post-activate state.
// EXPECTED TRACE: `misclassified` accumulates only in the weak build.
//
// SYNTHESIS: combinational only.
// LIMITATIONS: one bank, three outcomes. 30.2 §9 owns the full
// tracker; this model isolates the REFERENCE question.
// ---------------------------------------------------------------------
module classify_reference #(
parameter bit ROBUST = 1'b1,
parameter int ROW_W = 12,
parameter int CNT_W = 16
)(
input logic clk,
input logic rst_n,
input logic q_valid,
input logic [ROW_W-1:0] q_row,
input logic act_valid, // a same-cycle ACT to this bank
input logic [ROW_W-1:0] act_row,
output logic [2:0] outcome,
output logic [CNT_W-1:0] misclassified,
output logic classify_err
);
localparam logic [2:0] OUT_IDLE = 3'd0;
localparam logic [2:0] OUT_MISS = 3'd1;
localparam logic [2:0] OUT_HIT = 3'd2;
localparam logic [2:0] OUT_CONFLICT = 3'd3;
logic open_q, known_q;
logic [ROW_W-1:0] row_q;
logic [2:0] truth;
always_ff @(posedge clk) begin
if (!rst_n) begin
open_q <= 1'b0; known_q <= 1'b0; row_q <= '0; misclassified <= '0;
end else begin
if (act_valid) begin
open_q <= 1'b1;
known_q <= 1'b1;
row_q <= act_row;
end
// Computed IDENTICALLY in both builds.
if (q_valid && (outcome != truth))
if (misclassified != {CNT_W{1'b1}}) misclassified <= misclassified + 1'b1;
end
end
// The TRUTH: classification against REGISTERED state only.
always_comb begin
truth = OUT_IDLE;
if (q_valid) begin
if (!known_q) truth = 3'd4; // UNKNOWN, 7.4
else if (!open_q) truth = OUT_MISS;
else if (row_q == q_row) truth = OUT_HIT;
else truth = OUT_CONFLICT;
end
end
always_comb begin
outcome = OUT_IDLE;
if (q_valid) begin
if (ROBUST) begin
outcome = truth;
end else begin
// <-- THE DEFECT: the same-cycle command is folded in first,
// so the outcome describes the state AFTER this cycle.
if (act_valid) outcome = (act_row == q_row) ? OUT_HIT : OUT_CONFLICT;
else if (!known_q) outcome = 3'd4;
else if (!open_q) outcome = OUT_MISS;
else if (row_q == q_row) outcome = OUT_HIT;
else outcome = OUT_CONFLICT;
end
end
end
assign classify_err = q_valid && (outcome != truth);
endmoduleThe measurement.
ILLUSTRATIVE: 64 cycles, an ACT coincides with a column request
to the same bank on 9 of them.
DERIVED, recomputed:
ROBUST = 1 : misclassified = 0
ROBUST = 0 : misclassified = 9
gap = 9 of 64, which is exactly the coincidence rate. The two
builds agree on all 55 cycles with no coincidence.DERIVED: the error rate equals the same-cycle coincidence rate, so the defect is invisible in any stimulus where commands and requests do not coincide — and a testbench that presents one request at a time never produces the case at all. CURRICULUM-DERIVED from 30.2 §11: the catching artifact is a cover on the coincidence, not on either event.
Evidence to demand. Ask which signals the classification reads, and for each one, whether it is a register or a same-cycle input. A combinational block that mixes both has a reference question to answer, and the answer belongs in a comment.
What escapes. Hit/miss/conflict statistics that are wrong by the coincidence rate, and — worse in a controller — a legality decision made against a state that has not happened yet. CURRICULUM-DERIVED from 30.2 §10: the classification is used to choose the next command, so it can select a column command for a row that is only about to open.
How DV proves it. Drive a command and a request to the same bank on the same cycle. Chapter 30.2 §11 owns the property and the cover, and notes that the property constraining storage passes on the defective build because it never reads the classification output.
Telemetry. A same-cycle coincidence counter. Without it, a wrong hit rate and a correct one are indistinguishable, because the error's size is a property of the traffic rather than of the design.
Misleading evidence. A waveform in which the outcome is correct on every cycle the reviewer inspects — because a reviewer scrolling a waveform looks at cycles where something happens, and the coincidence cycles look like the cleanest examples.
8. Review Item 3 — Is Every Declared Input Read?
Under review. The module header, against the module body.
Invariant at risk. Whatever the unread input was for. The signal's presence on the interface is a claim that the block consumes it, and the claim is false.
Where it lives. The port list. Not the body — the review is a comparison between the header and a grep.
This is the highest-yield item in the chapter and the cheapest. DERIVED from §5's table: eight of the track's eighteen documented defects are caught here, and six of the eight are the identical tell. CURRICULUM-DERIVED, with the full roll: 30.7 §9 cfg_ch_bit; 30.8 §10 any_outstanding; 31.1 §12 access_outcome; 31.4 §12 ret_tag; 32.1 §14 cfg_ch_bit; 32.3 §13 pop_id.
// ---------------------------------------------------------------------
// unread_input_probe -- TEACHING MODEL. Review item 3.
//
// CLASSIFICATION: synthesisable teaching model. Built TWICE.
//
// WHAT IT IS: a completion model whose contract says the completion
// time depends on the access outcome. The robust build reads the
// outcome. The weak build declares it and uses a fixed latency --
// which is 31.1 §12's defect verbatim, and the same shape as five
// others in the track.
//
// WHY IT EXISTS HERE: six of the track's eighteen defects are one
// grep. 30.9 §3: lint finds a declared-and-unread input before
// simulation starts, and naming the cheapest tool that would have
// found a bug is a legitimate review answer.
//
// HOW TO RUN IT: present a CONFLICT outcome, whose true completion
// is longer than the fixed latency.
// EXPECTED RESULT: robust waits for the conflict path; weak signals
// completion at the fixed latency.
// EXPECTED TRACE: `early_done` accumulates only in the weak build.
//
// SYNTHESIS: a small shift register either way.
// LIMITATIONS: one outcome-dependent latency. 31.1 §12 owns the full
// defect and its correction; this model isolates the TELL.
// ---------------------------------------------------------------------
module unread_input_probe #(
parameter bit ROBUST = 1'b1,
parameter int FIXED_LAT = 4, // ILLUSTRATIVE
parameter int HIT_LAT = 4, // ILLUSTRATIVE
parameter int MISS_LAT = 18, // ILLUSTRATIVE
parameter int CONF_LAT = 32, // ILLUSTRATIVE
parameter int MAX_LAT = 32,
parameter int TW = $clog2(MAX_LAT + 1),
parameter int CNT_W = 16
)(
input logic clk,
input logic rst_n,
input logic req_accept,
// DECLARED in both builds. READ only by the robust one.
input logic [1:0] access_outcome, // 0=hit 1=miss 2=conflict
output logic done,
output logic [CNT_W-1:0] early_done,
output logic done_err
);
logic [TW-1:0] timer, truth_timer;
logic busy, truth_busy;
// The TRUTH, computed in BOTH builds from the outcome.
function automatic logic [TW-1:0] true_lat(input logic [1:0] o);
return (o == 2'd0) ? TW'(HIT_LAT) : (o == 2'd1) ? TW'(MISS_LAT) : TW'(CONF_LAT);
endfunction
always_ff @(posedge clk) begin
if (!rst_n) begin
timer <= '0; busy <= 1'b0; truth_timer <= '0; truth_busy <= 1'b0;
early_done <= '0;
end else begin
if (req_accept) begin
busy <= 1'b1;
truth_busy <= 1'b1;
truth_timer <= true_lat(access_outcome);
// <-- THE DEFECT is here: the weak build ignores the outcome.
timer <= ROBUST ? true_lat(access_outcome) : TW'(FIXED_LAT);
end else begin
if (busy && timer != '0) timer <= timer - 1'b1;
if (truth_busy && truth_timer != '0) truth_timer <= truth_timer - 1'b1;
if (busy && timer == '0) busy <= 1'b0;
if (truth_busy && truth_timer == '0) truth_busy <= 1'b0;
end
// Completion signalled while the truth is still outstanding.
if (done && truth_busy && truth_timer != '0)
if (early_done != {CNT_W{1'b1}}) early_done <= early_done + 1'b1;
end
end
assign done = busy && (timer == '0);
assign done_err = done && truth_busy && (truth_timer != '0);
endmoduleThe measurement.
ILLUSTRATIVE latencies: hit 4, miss 18, conflict 32; FIXED = 4.
DERIVED, recomputed, one access of each outcome:
ROBUST = 1 : done at 4 / 18 / 32. early_done = 0
ROBUST = 0 : done at 4 / 4 / 4. early_done = 2
gap: on a HIT the two builds are IDENTICAL. On a miss the weak
build is 14 cycles early; on a conflict, 28.DERIVED, and the first line of the gap is the item's whole point: on the outcome the stimulus produces most often, the two builds agree exactly. CURRICULUM-DERIVED from 30.2 §1: a row-hit-dominant stream is the normal case, so a testbench with good locality exercises the one path on which the defect is invisible.
Evidence to demand. Ask for the lint report's unused-input warnings, or run one grep per port. CURRICULUM-DERIVED from 30.9 §3: lint and elaboration prove structural facts, and this is a structural fact. A reviewer who asks does the body mention every port has done this item in a minute.
And ask the harder follow-up: why is it on the interface? A signal declared and unread was put there by somebody who expected it to be used — so its presence is evidence of an intention the code abandoned, and the commit that added the port is where the intention is recorded.
What escapes. Everything the unread signal was for. DERIVED from the six instances: a wrong field position, a wrong denominator, an early completion, a misattributed return, a starved population, and a channel concentration — six distinct escapes from one review question.
How DV proves it. Sweep the unread input. CURRICULUM-DERIVED from 30.7 §10: a testbench holding a shared input at one value cannot distinguish a design that uses it from one that ignores it, so the cover must be on the input varying.
Telemetry. Publish the value the block believes it is using. CURRICULUM-DERIVED from 32.1 §14: a block reporting the compiled value instead confirms the wrong answer, which is worse than reporting nothing.
Misleading evidence. A complete, well-documented port list. Every signal has a comment explaining its purpose, and the comments are accurate about the intent. The header is the artifact that makes the body look reviewed.
9. Review Item 4 — Is This Denominator Independent of Its Numerator?
Under review. Every ratio a block computes or publishes.
Invariant at risk. The ratio's ability to fall. If both terms derive from the same observation, the quotient cannot report a problem.
Where it lives. The accumulator that feeds the denominator.
CURRICULUM-DERIVED from 30.8 §10, whose efficiency monitor counts demand on arrival rather than on outstanding work and therefore reports the opposite of the truth; from 32.4 §14, whose utilisation denominator sums the streams that were busy rather than the streams provisioned, making the ratio self-confirming; and from 32.5 §14, which blends a load-dependent term into a figure a control loop consumes.
// ---------------------------------------------------------------------
// denominator_independence -- TEACHING MODEL. Review item 4.
//
// CLASSIFICATION: synthesisable telemetry. Built TWICE.
//
// WHAT IT IS: a utilisation report whose denominator is the
// PROVISIONED capacity or the OBSERVED occupancy. The robust build
// accumulates a constant; the weak build accumulates what was busy,
// which is the expression a designer writes when the busy count is
// already available in the same always_ff block.
//
// WHY IT EXISTS HERE: 30.8 §10 and 32.4 §14 are the same defect in
// two domains, and 32.4 §14 shows the second is SELF-CONFIRMING --
// the action it recommends does not change the number it produces.
//
// HOW TO RUN IT: run a workload that uses a quarter of the capacity.
// EXPECTED RESULT: robust reports 25%; weak reports 100%.
// EXPECTED TRACE: `ratio_cannot_fall` is 0 in the robust build and 1
// in the weak build.
//
// SYNTHESIS: two accumulators; no divider -- both terms are
// published so the consumer owns the rounding (27.5).
// LIMITATIONS: reports a ratio's TERMS. 32.4 §11 owns the
// attribution partition that says WHICH half was binding.
// ---------------------------------------------------------------------
module denominator_independence #(
parameter bit ROBUST = 1'b1,
parameter int CAPACITY = 128, // ILLUSTRATIVE
parameter int WIN = 4096,
parameter int CNT_W = $clog2(WIN + 1),
parameter int ACC_W = CNT_W + $clog2(CAPACITY + 1)
)(
input logic clk,
input logic rst_n,
input logic [7:0] busy_now, // units busy this cycle
input logic win_tick,
output logic [ACC_W-1:0] r_used,
output logic [ACC_W-1:0] r_capacity,
output logic [ACC_W-1:0] r_truth_capacity,
output logic result_valid,
output logic ratio_cannot_fall
);
logic [ACC_W-1:0] used, cap, truth_cap;
always_ff @(posedge clk) begin
if (!rst_n) begin
used <= '0; cap <= '0; truth_cap <= '0;
r_used <= '0; r_capacity <= '0; r_truth_capacity <= '0;
result_valid <= 1'b0;
end else if (win_tick) begin
r_used <= used;
r_capacity <= cap;
r_truth_capacity <= truth_cap;
result_valid <= 1'b1;
used <= '0; cap <= '0; truth_cap <= '0;
end else begin
result_valid <= 1'b0;
used <= used + {{(ACC_W-8){1'b0}}, busy_now};
// The TRUTH, in both builds: capacity is a constant per cycle.
truth_cap <= truth_cap + CAPACITY[ACC_W-1:0];
// <-- THE DEFECT in the weak build: the denominator is the
// numerator's own driver.
cap <= cap + (ROBUST ? CAPACITY[ACC_W-1:0]
: {{(ACC_W-8){1'b0}}, busy_now});
end
end
// The truth test: a denominator equal to the numerator means the
// ratio is pinned at unity and cannot report a shortfall.
assign ratio_cannot_fall = result_valid && (r_capacity == r_used)
&& (r_truth_capacity != r_used);
endmoduleThe measurement.
ILLUSTRATIVE CAPACITY = 128, WIN = 4096, and a workload using 32
units every cycle.
DERIVED, recomputed:
used = 32 x 4096 = 131,072
truth_capacity = 128 x 4096 = 524,288
robust ratio = 131,072 / 524,288 = 25.0% <- the truth
weak capacity = 32 x 4096 = 131,072
weak ratio = 131,072 / 131,072 = 100.0% <- what it reports
gap = 75 percentage points, and the reported value is pinned at
100% for EVERY workload, so the instrument has one output.DERIVED, and the self-confirming property is what makes this worse than a wrong number. CURRICULUM-DERIVED from 32.4 §14: a 100% reading recommends buy more capacity; the new capacity is also fully "busy" by the weak definition; the number is unchanged and the next decision reaches the same conclusion. A defect whose recommended action does not change its own measurement is confirmed by every generation that acts on it.
Evidence to demand. For every ratio, ask what would have to happen for it to read low. CURRICULUM-DERIVED from 32.4 §15's general rule: if nothing would, the denominator is wrong — and the question takes one sentence per ratio.
What escapes. A capacity decision taken on a number that cannot fall, and a controller reported as perfectly efficient on the workload where it is least relevant. CURRICULUM-DERIVED from 30.8 §10: the inverse case is worse, because a plausible-but-wrong ratio is neither obviously broken nor correct.
How DV proves it. Present a concentrated load — one that uses a fraction of capacity — and check the ratio falls. CURRICULUM-DERIVED from 32.4 §16: under a uniform load the two denominators are numerically identical, so the defect is absent rather than hidden, and a uniform-stimulus regression cannot reach it however long it runs.
Telemetry. Publish both terms, never the quotient. CURRICULUM-DERIVED from 27.5, whose closure model refuses to produce a single percentage, and from 30.8 §11's requirement that a derived statistic's range be asserted.
Misleading evidence. A high utilisation figure. It is the number everybody wants to see, it is reported by a block whose counters are conserved and whose results are latched cleanly, and a bounding assertion on it cannot fail — 32.4 §16 notes the bound is guaranteed by the defect.
10. Review Item 5 — Is This Gate's Value Consumed, or Only Its Gate?
Under review. Every signal that is both a permission and a quantity.
Invariant at risk. The obligation attached to the signal's second duty.
Where it lives. Two places at once: the if that reads the flag, and the expression that should read the value and does not.
CURRICULUM-DERIVED from 31.2 §12, whose ownership flag correctly forbids the owner's action and fails to suspend the non-owner's accounting; from 32.1 §14, whose block waits for a configuration it then ignores; and from 30.10 §11, whose snapshot honours its software clear and is also cleared by the functional reset. All three have one signal with two duties and one served.
// ---------------------------------------------------------------------
// gate_and_value -- TEACHING MODEL. Review item 5.
//
// CLASSIFICATION: synthesisable teaching model. Built TWICE.
//
// WHAT IT IS: an ownership flag with two duties -- it forbids the
// controller's action while the device owns an obligation, and it
// must suspend the controller's accrual for the same interval. The
// robust build serves both. The weak build serves the first, which
// is 31.2 §12's defect verbatim.
//
// WHY IT EXISTS HERE: three of the track's eighteen defects are this
// shape, and in all three the SERVED duty is what conceals the
// unserved one -- a reviewer checking "does it respect ownership?"
// finds that it does.
//
// HOW TO RUN IT: assert ownership for many interval ticks.
// EXPECTED RESULT: robust holds `debt` stable; weak accrues.
// EXPECTED TRACE: `debt_drift` is the accrual the weak build owes
// and the device already performed.
//
// SYNTHESIS: one flag, one counter.
// LIMITATIONS: one obligation. 15.3 owns the credit ledger and
// 17.3 §7 the one-writer rule; this isolates the TWO-DUTY question.
// ---------------------------------------------------------------------
module gate_and_value #(
parameter bit ROBUST = 1'b1,
parameter int DEBT_W = 8
)(
input logic clk,
input logic rst_n,
input logic dev_owns, // the flag, two duties
input logic interval_tick,
input logic ref_issued,
output logic ctrl_may_refresh,
output logic [DEBT_W-1:0] debt,
output logic [DEBT_W-1:0] truth_debt,
output logic [DEBT_W-1:0] debt_drift,
output logic gate_err
);
// DUTY ONE, served in BOTH builds: forbid the controller's action.
// This is the line that makes the block look correct.
assign ctrl_may_refresh = !dev_owns;
always_ff @(posedge clk) begin
if (!rst_n) begin
debt <= '0; truth_debt <= '0;
end else begin
// DUTY TWO: suspend accrual while the device owns it.
if (ROBUST) begin
if (interval_tick && !dev_owns && debt != {DEBT_W{1'b1}})
debt <= debt + 1'b1;
else if (ref_issued && debt != '0)
debt <= debt - 1'b1;
end else begin
// <-- THE DEFECT: the flag is not consulted here.
if (interval_tick && debt != {DEBT_W{1'b1}})
debt <= debt + 1'b1;
else if (ref_issued && debt != '0)
debt <= debt - 1'b1;
end
// The TRUTH, computed identically in both builds.
if (interval_tick && !dev_owns && truth_debt != {DEBT_W{1'b1}})
truth_debt <= truth_debt + 1'b1;
else if (ref_issued && truth_debt != '0)
truth_debt <= truth_debt - 1'b1;
end
end
assign debt_drift = debt - truth_debt;
assign gate_err = (debt != truth_debt);
endmoduleThe measurement.
ILLUSTRATIVE: the device owns the obligation for 300 interval
ticks. DEBT_W = 8, so the counter saturates at 255.
DERIVED, recomputed:
ROBUST = 1 : debt stays at its entry value. drift = 0
ROBUST = 0 : debt rises to 255 and saturates. drift = 255
gap = 255, and it is CAPPED by the counter width -- which 30.10
§11 notes is worse than uncapped, because a capped error looks
like a fixed cost rather than one proportional to residency.DERIVED, and the direction matters: the weak build owes work the device already did. CURRICULUM-DERIVED from 31.2 §12: on exit the manager drains every bank and issues 255 unnecessary refresh commands, spending exactly the energy the low-power state was entered to save — and adding that cost to the exit makes the state's break-even disappear entirely.
Evidence to demand. For every flag, list its duties and point at the line that serves each. A flag with two duties and one if is the finding, and the review question is mechanical: what does this signal permit, and what does it suspend?
What escapes. Work performed twice, or accounting that diverges silently from reality. DERIVED from the three instances: unnecessary refreshes, a decode that ignores its configuration, and evidence destroyed by the action that was supposed to preserve it.
How DV proves it. Name the second duty's signal in a property. CURRICULUM-DERIVED from 31.2 §14: the properties written for that block covered ownership transfer and the refresh prohibition, and neither mentioned debt — variety 2, and the property set had the same blind spot as the design.
And the cover must be on the DURATION. CURRICULUM-DERIVED from 31.2 §12's scale-vacuity refinement: an event cover on dev_owns is non-zero in any bring-up test that enters the state at all, so it proves the antecedent occurred and says nothing about whether the defect was observable. The threshold is derived from the failure mechanism — just past the counter's saturation — not from the use case.
Telemetry. Publish the drift, or publish enough to compute it. A single debt figure cannot be checked against anything, and 30.10 §11 notes that a capped drift looks like a constant.
Misleading evidence. The served duty. ctrl_may_refresh = !dev_owns is correct, prominent, and exactly what a reviewer looks for. The flag is tracked and the flag is used, and a review that confirms both has confirmed half the contract.
11. Review Item 6 — Is This Guard Reachable, and Does It Pick the Best Available?
Under review. Every guard, and every selection that claims to choose the best of several candidates.
Invariant at risk. Two at once. A guard another rule always satisfies is untested, and a selection that stops at the first acceptable candidate is not choosing the best one.
Where it lives. The !done term in a search loop, and the set a guard is intersected with.
Three of the track's defects live here and they are three faces of one error. CURRICULUM-DERIVED from 30.5 §10, where the aged set is built from valid alone rather than valid & legal_mask, so a fairness override sits above legality rather than above the preference; from 30.6 §10, where a window selector commits the first run clearing a minimum width rather than the widest; and from 31.3 §12, where a guard was deliberately removed because the block was supposed to handle the case, and the handling was never implemented.
// ---------------------------------------------------------------------
// guard_and_best -- TEACHING MODEL. Review item 6.
//
// CLASSIFICATION: synthesisable teaching model. Built TWICE.
//
// WHAT IT IS: a selector that must choose the WIDEST eligible run
// from a bitmap and must never select outside a permitted set. The
// robust build scans the whole bitmap and intersects with the
// permitted set; the weak build stops at the first eligible run and
// omits the intersection -- 30.6 §10 and 30.5 §10 in one block.
//
// WHY IT EXISTS HERE: both defects pass a selection property that
// checks MEMBERSHIP, because the chosen item IS a member. 30.6 §11
// owns the result: a property can check a local invariant and cannot
// check that a choice was the best available.
//
// HOW TO RUN IT: a bitmap with a narrow eligible run at a low index
// and a wider one above it, and a permitted mask excluding the low
// one.
// EXPECTED RESULT: robust selects the wide, permitted run; weak
// selects the narrow, unpermitted one.
// EXPECTED TRACE: `not_best` and `outside_permitted` are zero in the
// robust build.
//
// SYNTHESIS: one combinational scan over NTAPS.
// LIMITATIONS: one dimension. 30.6 §9 owns the full selector.
// ---------------------------------------------------------------------
module guard_and_best #(
parameter bit ROBUST = 1'b1,
parameter int NTAPS = 24,
parameter int MIN_WIDTH = 3, // ILLUSTRATIVE
parameter int W_W = $clog2(NTAPS + 1),
parameter int T_W = $clog2(NTAPS)
)(
input logic [NTAPS-1:0] bitmap,
input logic [NTAPS-1:0] permitted, // the set a guard must respect
output logic [T_W-1:0] sel_tap,
output logic [W_W-1:0] sel_width,
output logic [W_W-1:0] best_width,
output logic not_best,
output logic outside_permitted
);
// The TRUTH, computed in BOTH builds: the widest run that clears
// MIN_WIDTH and lies entirely inside `permitted`.
always_comb begin
logic [W_W-1:0] run, bw;
logic ok;
bw = '0; run = '0; ok = 1'b1;
for (int t = 0; t < NTAPS; t++) begin
if (bitmap[t]) begin
if (run == 0) ok = 1'b1;
if (!permitted[t]) ok = 1'b0;
run = run + 1'b1;
end
if (!bitmap[t] || (t == NTAPS - 1)) begin
if (ok && run >= MIN_WIDTH[W_W-1:0] && run > bw) bw = run;
run = '0;
end
end
best_width = bw;
end
always_comb begin
logic [W_W-1:0] run;
logic [T_W-1:0] start;
logic done, ok;
sel_tap = '0; sel_width = '0; run = '0; start = '0;
done = 1'b0; ok = 1'b1;
for (int t = 0; t < NTAPS; t++) begin
if (bitmap[t]) begin
if (run == 0) begin start = t[T_W-1:0]; ok = 1'b1; end
if (!permitted[t]) ok = 1'b0;
run = run + 1'b1;
end
if (!bitmap[t] || (t == NTAPS - 1)) begin
if (ROBUST) begin
// Widest, AND intersected with the permitted set.
if (ok && run >= MIN_WIDTH[W_W-1:0] && run > sel_width) begin
sel_width = run;
sel_tap = start + T_W'((run - 1) / 2);
end
end else begin
// <-- THE DEFECT, two faces: `!done` stops at the FIRST
// eligible run, and `ok` is never consulted so the
// permitted set is ignored.
if (!done && run >= MIN_WIDTH[W_W-1:0]) begin
sel_width = run;
sel_tap = start + T_W'((run - 1) / 2);
done = 1'b1;
end
end
run = '0;
end
end
end
assign not_best = (sel_width != best_width);
assign outside_permitted = (sel_width != 0) && !permitted[sel_tap];
endmoduleThe measurement.
ILLUSTRATIVE NTAPS = 24, MIN_WIDTH = 3.
bitmap : 000111000000111111111000
permitted : 111000111111111111111111
^^^ the low run is NOT permitted
runs: A = taps 3-5 width 3, not permitted
B = taps 12-20 width 9, permitted
DERIVED, recomputed:
ROBUST = 1 : sel_width = 9, sel_tap = 16.
not_best = 0, outside_permitted = 0
ROBUST = 0 : sel_width = 3, sel_tap = 4.
not_best = 1, outside_permitted = 1
gap = a 3-tap selection where a 9-tap one existed, and it is
outside the permitted set -- so BOTH faces of the defect fire on
one input.And the reason both faces appear together is structural rather than coincidental. A search that stops early has no reason to have computed the permitted-set intersection, because it never compares candidates. DERIVED: the !done term and the unconsulted ok are the same simplification, which is why 30.5 and 30.6 read as one defect in two domains.
Evidence to demand. For every selection, ask what the alternatives were and why this one won. CURRICULUM-DERIVED from 30.6 §11: best available is a comparative obligation, so it needs an independently computed expectation — and a reviewer asking for the comparison is asking for the reference model, which is the right thing to ask for.
And for every guard, ask whether anything else already prevents the case. CURRICULUM-DERIVED from 30.4 §7: tCCD shadows a bus-reservation guard so completely that the guard has never been exercised, and 31.3 §12 is the sequel — the guard was removed on the grounds that the block handled the case, and the handling was absent. So the review question is: what obligation did removing this guard create, and where is it discharged?
What escapes. A conservative selection that costs margin every time, a fairness mechanism that issues illegal commands, and a configuration in which a removed guard leaves a reachable case unprotected. CURRICULUM-DERIVED from 30.5 §10, the third is the worst: the override is biased toward producing the violation, because an entry blocked for a long time accrues age while it cannot be served.
How DV proves it. A directed bitmap, not a random one. CURRICULUM-DERIVED from 30.6 §11: random stimulus rarely produces a narrow low-index island beside a wide high-index one, so the cover on more than one eligible run stays near zero and the property passes without distinguishing the designs.
Telemetry. Publish the selected width alongside the selection. CURRICULUM-DERIVED from 30.6 §10: it is not a margin, but it is a repeatable number that changes when the input changes — and a committed value alone cannot tell a 3-tap selection from a 9-tap one.
Misleading evidence. A MIN_WIDTH guard. It is real, correctly applied, and rejects genuine noise — so a reviewer who sees a width floor tends to stop checking the selection. Diligence in one clause is what buys the other clause a pass.
12. Review Item 7 — Does This Parameter Carry a Platform or Physical Quantity?
Under review. The parameter list, read for provenance rather than for correctness.
Invariant at risk. The design's portability, and the validity of every number derived from the parameter.
Where it lives. A parameter int whose name denotes something outside the design.
CURRICULUM-DERIVED from 30.7 §9, which states the rule: a parameter holding a physical quantity is a design smell wherever it appears, because trace length, package delay and process corner do not exist when the parameter is set. CURRICULUM-DERIVED from 32.1 §14, which generalises it: a parameter holding a platform quantity is grade C or D at best, however it was obtained, and consuming it as documented is category drift with a localparam.
This item and §8 compose, and the composite is worth naming as one reflex. DERIVED from §5's table: 30.7 and 32.1 each appear under §8, §10 and §12 — a platform quantity compiled in, the configuration input declared and unread, and the validity gate honoured while its value is ignored. Three items, one commit. So the reflex is: when a parameter names something outside the design, immediately check whether a configuration input for it exists, and whether the body reads it.
// ---------------------------------------------------------------------
// parameter_provenance -- TEACHING MODEL. Review item 7.
//
// CLASSIFICATION: synthesisable teaching model. Built TWICE.
//
// WHAT IT IS: a read-gate position taken from a trained input or
// from a compiled constant. The robust build reads the input and
// refuses when untrained; the weak build uses the constant -- and
// the constant was an honest measurement on the first board, which
// is why it is defensible and wrong.
//
// WHY IT EXISTS HERE: 30.7 §9 is this defect and 32.1 §14 is its
// twin one module later. The gap is the number of boards on which
// the design works.
//
// HOW TO RUN IT: run with a trained offset different from the
// compiled default.
// EXPECTED RESULT: robust tracks the trained value; weak does not.
// EXPECTED TRACE: `ignores_training` is 1 in the weak build whenever
// the trained value differs.
//
// SYNTHESIS: a variable comparison versus a constant one.
// LIMITATIONS: one position. 30.7 §9 owns the full gate; 19.3 owns
// the launch-offset decomposition this rests on.
// ---------------------------------------------------------------------
module parameter_provenance #(
parameter bit ROBUST = 1'b1,
parameter int CL = 22, // ILLUSTRATIVE
parameter int OCC = 4, // ILLUSTRATIVE
parameter int GUARD = 2, // ILLUSTRATIVE
// GRADE C at best: an honest measurement on one board, compiled in.
parameter int FLIGHT_DEFAULT = 3,
parameter int CNT_W = 8
)(
input logic clk,
input logic rst_n,
input logic rd_commit,
input logic [5:0] trained_offset, // from the search (21.3)
input logic trained_valid,
output logic gate_open,
output logic ignores_training,
output logic [5:0] offset_in_use
);
logic [CNT_W-1:0] phase;
logic armed;
logic [5:0] effective, truth_offset;
// The TRUTH, in both builds: the trained value is the only valid
// source for a post-assembly quantity.
assign truth_offset = trained_offset;
assign effective = ROBUST ? trained_offset : FLIGHT_DEFAULT[5:0];
always_ff @(posedge clk) begin
if (!rst_n) begin
phase <= '0; armed <= 1'b0;
end else if (rd_commit) begin
phase <= '0; armed <= 1'b1;
end else if (armed) begin
if (phase >= CNT_W'(CL + 32 + OCC + GUARD)) armed <= 1'b0;
else phase <= phase + 1'b1;
end
end
// The robust build also REFUSES when untrained -- 30.7 §9: unknown
// is not a default. The weak build has nothing to wait for.
assign gate_open = armed
&& (ROBUST ? trained_valid : 1'b1)
&& (phase >= CNT_W'(CL + effective - GUARD))
&& (phase <= CNT_W'(CL + effective + OCC + GUARD));
assign offset_in_use = effective;
assign ignores_training = trained_valid && (effective != truth_offset);
endmoduleThe measurement.
ILLUSTRATIVE CL = 22, OCC = 4, GUARD = 2, FLIGHT_DEFAULT = 3.
DERIVED, recomputed:
board A, trained_offset = 3 :
robust window 23..31 weak window 23..31 IDENTICAL
board B, trained_offset = 7 :
robust window 27..35 weak window 23..31
overlap on board B = cycles 27..31, i.e. 5 of the 9 cycles the
strobe occupies. The tail is outside the window, so the beat
count is short.
gap: the design works on exactly one board, and the number of
boards it works on is 1 regardless of how many exist.DERIVED, and the one board figure is the item's transferable content. CURRICULUM-DERIVED from 30.7 §9: FLIGHT_DEFAULT was set from measurements on the first board, so the block passes bring-up, passes regression on that board, and passes every assertion written against it. It is not a bug that escaped testing; it is a bug that testing on one board cannot express.
Evidence to demand. Grep the parameter list for names that denote something outside the design — field positions, flight times, interleave granularities, channel counts, thresholds, latencies, capacities. For each, ask which register should have supplied it and whether the body reads that register. CURRICULUM-DERIVED from 32.1 §14: “is this number trained or compiled in?” is the cheapest question in that chapter and it finds the whole class in one sentence.
What escapes. A design that works on one board, one platform, one population or one speed bin. CURRICULUM-DERIVED from 32.1 §14, the escape is often a performance failure with no error signal — a wrong field position is still a bijection, so no data is wrong and the interleave granularity is 32× off.
How DV proves it. Sweep the configuration or trained input across its range. CURRICULUM-DERIVED from 30.7 §10: an environment that drives the input to one value can never distinguish a block that uses it from one that ignores it, so the requirement is on the stimulus and not on a property.
Telemetry. Publish offset_in_use. CURRICULUM-DERIVED from 32.1 §14: a block reporting the compiled value confirms the wrong answer, so the traceability output must report the value actually used — and then the review is a single register read.
Misleading evidence. The constant's own history. It came from a real measurement, taken carefully, on real hardware. CURRICULUM-DERIVED from 18.4 §1's category drift: nothing was fabricated at any step; each copy was faithful, and only the qualification was lost — and the qualification here is the board it was measured on.
13. Review Item 8 — Does This Accumulator Start at Its Operation's Identity?
Under review. Every reset value of every accumulator, min tracker, max tracker and AND-reduction.
Invariant at risk. The accumulator's first sample. An identity element chosen for the wrong operation makes the first sample either always win or never win.
Where it lives. The reset branch.
The rule is arithmetic and it is short. A max tracker starts at the minimum representable value; a min tracker starts at the maximum — all ones. An AND-accumulator starts at all ones; an OR-accumulator at zero. DERIVED: a min tracker reset to zero never accepts a sample, because no sample is below zero — so it reports zero forever, which reads as a legitimate minimum.
// ---------------------------------------------------------------------
// accumulator_identity -- TEACHING MODEL. Review item 8.
//
// CLASSIFICATION: synthesisable telemetry. Built TWICE.
//
// WHAT IT IS: a min/max tracker whose min starts at all-ones or at
// zero. The robust build uses the identity for `min`; the weak build
// resets both trackers to zero, which is what a single
// `for (...) x[i] <= '0;` reset loop produces.
//
// WHY IT EXISTS HERE: this reset appears correctly in 31.3 §13,
// 32.1 §14, 32.3 §11 and 32.5 §12, each with a comment explaining
// why -- because the wrong version is the natural one and produces
// a plausible number.
//
// HOW TO RUN IT: feed any stream of positive samples.
// EXPECTED RESULT: robust reports the true minimum; weak reports 0.
// EXPECTED TRACE: `min_never_accepted` is 1 in the weak build.
//
// SYNTHESIS: two registers.
// LIMITATIONS: the identity question only. Saturation and windowing
// are elsewhere; this isolates the RESET VALUE.
// ---------------------------------------------------------------------
module accumulator_identity #(
parameter bit ROBUST = 1'b1,
parameter int W = 12
)(
input logic clk,
input logic rst_n,
input logic sample_valid,
input logic [W-1:0] sample,
output logic [W-1:0] obs_min,
output logic [W-1:0] obs_max,
output logic [W-1:0] truth_min,
output logic min_never_accepted,
output logic spread_err
);
logic accepted_any;
always_ff @(posedge clk) begin
if (!rst_n) begin
obs_max <= '0; // correct identity for max
obs_min <= ROBUST ? '1 : '0; // <-- THE DEFECT when 0
truth_min <= '1; // the truth, always all-ones
accepted_any <= 1'b0;
end else if (sample_valid) begin
if (sample > obs_max) obs_max <= sample;
if (sample < obs_min) begin
obs_min <= sample;
accepted_any <= 1'b1;
end
if (sample < truth_min) truth_min <= sample;
end
end
// The weak build's min never accepts a sample, so the flag is a
// direct test of the identity choice.
assign min_never_accepted = !accepted_any;
// And the spread it reports is wrong by the true minimum.
assign spread_err = (obs_min != truth_min);
endmoduleThe measurement.
ILLUSTRATIVE samples: 120, 95, 140, 88, 210.
DERIVED, recomputed:
ROBUST = 1 : obs_min = 88, obs_max = 210, spread = 122
ROBUST = 0 : obs_min = 0, obs_max = 210, spread = 210
gap = 88 on the minimum and 88 on the spread, and the reported
spread is 72% larger than the truth.And the consequence depends entirely on what consumes the spread. CURRICULUM-DERIVED from 31.3 §13, an imbalance report built on a min tracker reset to zero flags maximum imbalance on every window regardless of the traffic — so the instrument has one output. CURRICULUM-DERIVED from 32.5 §12, a latency-span report built the same way makes every span look enormous and every slope look flat, which is worse: it does not scream, it quietly reports that nothing is happening.
Evidence to demand. Read every reset branch and ask, for each register, what operation accumulates into it. A reset loop that assigns '0 to everything is the finding — it is correct for counters, correct for max trackers, and wrong for exactly one class.
What escapes. An instrument with one output. DERIVED: the escape is never a functional failure, so it survives every functional test and is discovered when somebody tries to use the number.
How DV proves it. Feed samples whose minimum is above zero and check the reported minimum. One line, and it is absent from a testbench that checks the counters and not the trackers — because a tracker is not an obligation, so it does not appear in the obligation list 33.1 §5 produced.
Telemetry. Publish a sample count alongside every min and max. CURRICULUM-DERIVED from 32.5 §11's staleness flag: a minimum over zero samples is not a minimum, and the count is what lets a consumer tell an identity bug from an empty window.
Misleading evidence. A reported minimum of zero is plausible. Queue occupancy reaches zero; idle latency approaches zero; a stream count of zero is normal. The wrong answer is inside the range of right answers, which is why no range assertion catches it.
14. Review Item 9 — Is the Convention in the Code, or Only in a Specification?
Under review. Every comparison against a threshold, and every window's endpoints.
Invariant at risk. One cycle, in a direction nobody stated.
Where it lives. A > or a >=, and the absence of a comment naming which reading it encodes.
CURRICULUM-DERIVED from 30.3 §5, which owns the result: the sentence at least N cycles between two commands has three defensible readings, two of which agree and one of which differs by one — so a comparison operator encodes a choice, and the choice must be written where a reader can see it. That chapter's defective block uses > where the stated convention requires >= and is therefore one cycle too strict; 30.4 §8's reserves one cycle where the contract requires a window; 30.9 §10's checker samples one cycle late and then acquires a tolerance parameter to silence the resulting false failures.
// ---------------------------------------------------------------------
// convention_locality -- TEACHING MODEL. Review item 9.
//
// CLASSIFICATION: synthesisable teaching model. Built TWICE.
//
// WHAT IT IS: an eligibility comparison with the convention stated
// in the code or left to the reader. The robust build names the
// reading it encodes and uses >=; the weak build uses > with no
// comment, which is the form that produces a one-cycle
// disagreement nobody can attribute.
//
// WHY IT EXISTS HERE: 30.3 §5 owns the three readings and 30.9 §10
// owns the worst consequence -- a checker TUNED until it stopped
// complaining, which destroyed the evidence that the design and the
// checker disagreed about a boundary.
//
// HOW TO RUN IT: present a command at exactly the boundary.
// EXPECTED RESULT: robust admits it; weak defers one cycle.
// EXPECTED TRACE: `boundary_disagreement` fires on exactly the
// boundary cycle in the weak build.
//
// SYNTHESIS: one comparator.
// LIMITATIONS: one rule. 13.3 owns the taxonomy; 30.3 §5 the three
// readings. The SLACK parameter is included because 30.9 §10 shows
// it is the natural next commit, and §15 asserts against it.
// ---------------------------------------------------------------------
module convention_locality #(
parameter bit ROBUST = 1'b1,
parameter int TRCD = 14, // ILLUSTRATIVE
// 30.9 §10's tolerance. Present so §15 can forbid it, and set to
// zero here so it does not mask the primary defect.
parameter int SLACK = 0,
parameter int CNT_W = $clog2(TRCD + 1),
parameter int ERR_W = 16
)(
input logic clk,
input logic rst_n,
input logic act_commit,
input logic col_request,
output logic eligible,
output logic [CNT_W-1:0] since_act,
output logic [ERR_W-1:0] boundary_disagreement,
output logic convention_err
);
logic seen_act;
logic truth_eligible;
always_ff @(posedge clk) begin
if (!rst_n) begin
since_act <= '0; seen_act <= 1'b0; boundary_disagreement <= '0;
end else begin
if (seen_act && since_act != {CNT_W{1'b1}}) since_act <= since_act + 1'b1;
if (act_commit) begin since_act <= '0; seen_act <= 1'b1; end
if (col_request && (eligible != truth_eligible))
if (boundary_disagreement != {ERR_W{1'b1}})
boundary_disagreement <= boundary_disagreement + 1'b1;
end
end
// The TRUTH, in both builds: the STATED convention is that the
// column command may issue ON act_cycle + TRCD. 30.3 §5's reading
// two and three, which agree.
assign truth_eligible = seen_act && (since_act >= TRCD[CNT_W-1:0]);
// The robust build encodes that reading AND says so here. The
// comment is part of the mechanism, not decoration: 30.3 §5's
// whole point is that the operator is a choice.
assign eligible = ROBUST
? (seen_act && (since_act >= TRCD[CNT_W-1:0]))
// <-- THE DEFECT: `>` encodes reading one, with
// SLACK available to hide the consequence.
: (seen_act && ((since_act + SLACK[CNT_W-1:0]) > TRCD[CNT_W-1:0]));
assign convention_err = col_request && (eligible != truth_eligible);
endmoduleThe measurement.
ILLUSTRATIVE TRCD = 14, SLACK = 0. An ACT commits at cycle 100
and a column request is presented on every cycle after it.
DERIVED, recomputed:
ROBUST = 1 : eligible from cycle 114 onward.
ROBUST = 0 : eligible from cycle 115 onward.
boundary_disagreement = 1 (cycle 114 only)
gap = ONE cycle, on ONE cycle per activate. And the direction is
TOO STRICT, so nothing is violated -- it costs bandwidth.DERIVED, and too strict is why this survives indefinitely. CURRICULUM-DERIVED from 30.3 §8: a too-strict eligibility check delays a legal command, which violates nothing and produces no assertion failure, no data error and no protocol violation — it shows up as bandwidth a few per cent below expectation, attributed to the workload.
And the SLACK parameter is the item's sharpest content. CURRICULUM-DERIVED from 30.9 §10: the same one-cycle error is strict at one boundary and lenient at the other, so a checker built this way produces false failures during bring-up — and a tolerance was added to stop the noise. It worked, and it made the checker two cycles lenient.
A checker adjusted until it stopped complaining has been calibrated against the design rather than against the specification. The false failures were the evidence, and tuning them away destroyed it.
Evidence to demand. For every comparison against a timing threshold, ask which of 30.3 §5's three readings it encodes, and where that is written down. If the answer is the specification says at least N, the review has found the defect — because that sentence has three readings and the code chose one silently.
And grep for tolerance parameters. CURRICULUM-DERIVED from 30.9 §13: every SLACK, TOLERANCE, FUDGE or _ADJ parameter in a checker is a place where somebody's disagreement was parameterised instead of resolved, and it is a one-line grep with a real yield.
What escapes. A few per cent of bandwidth, permanently, attributed elsewhere. And in a checker, a silenced disagreement: the tolerance makes the checker lenient in the direction that matters while the false failures it suppressed were the only signal that the boundary was contested.
How DV proves it. Cover the boundary, do not only assert the rule. CURRICULUM-DERIVED from 30.3 §8: a property proves illegality did not happen and only a cover proves the boundary was exercised at all — cover (elapsed == required) with a request presented, and then a directed check against the stated convention.
Telemetry. The shortfall distribution of every violation. CURRICULUM-DERIVED from 30.3 §10: if every shortfall is exactly one cycle across every rule, stop looking for nine bugs — there is one, and it is a convention. That is the cheapest diagnostic in the chapter and it requires one histogram.
Misleading evidence. A clean regression. The direction is too strict, so no assertion fires, no data is wrong, and the design is correct in the only sense the testbench measures. And if a checker was involved, the evidence was not merely absent — it was actively removed, by a commit that made the noise go away.
15. The Review Assembled
Cost-ordered. DERIVED from §19's table: five of the nine items are findable without simulation, and three of those five take one grep each.
| # | Item | Question, in one line | Cost | Tool |
|---|---|---|---|---|
| 1 | §8 Unread inputs | Does the body mention every port? | minutes | lint or grep |
| 2 | §12 Parameter provenance | Does any parameter name something outside the design? | minutes | grep |
| 3 | §14 Tolerance parameters | Is there a SLACK, TOLERANCE, FUDGE or _ADJ? | minutes | grep |
| 4 | §6 Count versus index | For each counter, is its maximum a count or an index? | minutes | reading the comparison |
| 5 | §13 Accumulator identity | Does any reset loop assign '0 to a min tracker? | minutes | reading the reset branch |
| 6 | §10 Gate and value | For each flag, list its duties and point at each line | hours | reading |
| 7 | §7 Classification reference | Does this block mix registers and same-cycle inputs? | hours | reading |
| 8 | §9 Denominator independence | What would have to happen for this ratio to read low? | hours | reading, one sentence per ratio |
| 9 | §11 Guard and best | What were the alternatives, and what already prevents this case? | days | needs a reference model |
Items 1 to 5 cost minutes, need no testbench, and between them catch twelve of the track's eighteen documented defects. DERIVED from §5's table: §8 alone catches eight, §12 four (three shared with §8), §14 three, §6 and §13 the width and identity classes. An RTL review that begins by reading module headers and parameter lists has done most of the work before opening a body.
Item 9 is last because it is the only one that needs an independently computed expectation. CURRICULUM-DERIVED from 30.6 §11: best available cannot be checked by a property, so the item's cost is the cost of the reference model — and that is 33.4's gate, not this one.
16. Quantitative Reasoning
| Item | Quantity | Robust | Weak | Gap | Provenance |
|---|---|---|---|---|---|
| §6 | over-admissions in 20 pushes | 0 | 4 | 4, unbounded | DERIVED, ILLUSTRATIVE depth 16 |
| §7 | misclassified of 64 | 0 | 9 | 9 = the coincidence rate | DERIVED |
| §8 | early completions of 3 outcomes | 0 | 2 | 14 and 28 cycles | DERIVED, ILLUSTRATIVE latencies |
| §9 | reported utilisation | 25.0% | 100.0% | 75 pp, and pinned | DERIVED |
| §10 | refresh debt drift | 0 | 255 | 255, capped | DERIVED, DEBT_W = 8 |
| §11 | selected window width | 9 | 3 | 6 taps, and outside the permitted set | DERIVED |
| §12 | boards the design works on | all | 1 | — | DERIVED from the window overlap |
| §13 | reported minimum | 88 | 0 | 88, spread 72% high | DERIVED |
| §14 | eligibility boundary | 114 | 115 | 1 cycle, too strict | DERIVED, ILLUSTRATIVE TRCD |
Sort the gaps by what hides them and a pattern appears that is worth more than any individual number.
| What hides the defect | Items | The benign condition |
|---|---|---|
| A steady-state or unloaded stimulus | §6, §10 | occupancy never reaches the maximum; the state is never held long |
| A single value of an input | §8, §12 | the outcome is always a hit; the offset always equals the default |
| A uniform distribution | §9 | busy equals provisioned, so the two denominators are identical |
| No coincidence | §7 | commands and requests never land on the same cycle |
| Random rather than directed input | §11 | a narrow island beside a wide window does not arise by chance |
| Samples that reach zero | §13 | a minimum of zero is inside the range of correct answers |
| A direction that violates nothing | §14 | too strict costs bandwidth and fires no assertion |
DERIVED: every one of the nine has a benign condition under which the robust and weak builds are indistinguishable. CURRICULUM-DERIVED from 32.5 §15's module-level finding — three of Module 32's five defects were correct at the operating point where they were validated — and this table generalises it: it is not three of five, it is nine of nine. An RTL defect that had no benign condition would have been caught by the nominal test and would never have reached a reviewer.
17. What the Assertions Prove
// ---- §6: count versus index.
property p_occupancy_within_depth;
@(posedge clk) disable iff (!rst_n) truth <= DEPTH;
endproperty
assert property (p_occupancy_within_depth)
else $error("occupancy exceeded the queue depth");
// Names `truth`, so a design whose visible counter wrapped cannot
// satisfy it -- variety 2's repair.
property p_no_admit_when_truly_full;
@(posedge clk) disable iff (!rst_n)
(truth >= DEPTH) |-> !admit;
endproperty
assert property (p_no_admit_when_truly_full)
else $error("admitted while the queue was truly full");
// ---- §7: classification reference.
property p_classify_against_registered_state;
@(posedge clk) disable iff (!rst_n)
q_valid |-> (outcome == truth);
endproperty
assert property (p_classify_against_registered_state)
else $error("classification did not use registered state");
// ---- §8: unread inputs. The property that a fixed-latency model
// cannot satisfy, because it names the outcome.
property p_completion_follows_outcome;
@(posedge clk) disable iff (!rst_n)
done |-> !(truth_busy && truth_timer != '0);
endproperty
assert property (p_completion_follows_outcome)
else $error("completion signalled before the outcome's latency elapsed");
// ---- §9: denominator independence. The general form, and the one
// 32.4 §16 says catches the whole class.
property p_denominator_not_driven_by_numerator;
@(posedge clk) disable iff (!rst_n)
(!win_tick) |=> (cap - $past(cap)) == CAPACITY;
endproperty
assert property (p_denominator_not_driven_by_numerator)
else $error("the capacity denominator moved with the busy count");
property p_ratio_can_fall;
@(posedge clk) disable iff (!rst_n)
result_valid |-> (r_truth_capacity >= r_used);
endproperty
assert property (p_ratio_can_fall)
else $error("used exceeded the provisioned capacity");
// ---- §10: gate and value. Names `debt`, which the properties
// written for 31.2 §14 did not.
property p_no_accrual_while_device_owns;
@(posedge clk) disable iff (!rst_n)
dev_owns |-> $stable(debt) || ref_issued;
endproperty
assert property (p_no_accrual_while_device_owns)
else $error("debt accrued while the device owned the obligation");
property p_debt_matches_truth;
@(posedge clk) disable iff (!rst_n) debt == truth_debt;
endproperty
assert property (p_debt_matches_truth)
else $error("the controller's debt diverged from the obligation");
// ---- §11: guard and best. The COMPARATIVE obligation, checked
// against an independently computed expectation -- 30.6 §11 says a
// property alone cannot express "best", so `best_width` is the
// model and this is the comparison.
property p_selects_the_widest_eligible;
@(posedge clk) disable iff (!rst_n)
(sel_width != 0) |-> (sel_width == best_width);
endproperty
assert property (p_selects_the_widest_eligible)
else $error("the selection was not the widest eligible run");
property p_selection_inside_permitted;
@(posedge clk) disable iff (!rst_n)
(sel_width != 0) |-> permitted[sel_tap];
endproperty
assert property (p_selection_inside_permitted)
else $error("the selection fell outside the permitted set");
// ---- §12: parameter provenance. Names the trained input.
property p_uses_trained_offset;
@(posedge clk) disable iff (!rst_n)
trained_valid |-> (offset_in_use == trained_offset);
endproperty
assert property (p_uses_trained_offset)
else $error("the block used a compiled offset instead of the trained one");
property p_untrained_refuses;
@(posedge clk) disable iff (!rst_n)
(!trained_valid) |-> !gate_open;
endproperty
assert property (p_untrained_refuses)
else $error("the gate opened with no trained position");
// ---- §13: accumulator identity.
property p_min_accepts_a_sample;
@(posedge clk) disable iff (!rst_n)
(sample_valid && sample < truth_min) |=> (obs_min == $past(sample));
endproperty
assert property (p_min_accepts_a_sample)
else $error("the minimum tracker never accepted a sample");
property p_min_matches_truth;
@(posedge clk) disable iff (!rst_n) obs_min == truth_min;
endproperty
assert property (p_min_matches_truth)
else $error("the reported minimum disagrees with the observed minimum");
// ---- §14: convention locality. Both directions, because the
// defect is TOO STRICT and 30.3 §9 owns the result that safety
// cannot detect conservatism -- variety 8.
property p_eligible_at_the_stated_boundary;
@(posedge clk) disable iff (!rst_n)
(seen_act && since_act == TRCD) |-> eligible;
endproperty
assert property (p_eligible_at_the_stated_boundary)
else $error("not eligible at the stated boundary act + TRCD");
property p_not_eligible_before_the_boundary;
@(posedge clk) disable iff (!rst_n)
(seen_act && since_act < TRCD) |-> !eligible;
endproperty
assert property (p_not_eligible_before_the_boundary)
else $error("eligible before the stated boundary");
// And the forbidden parameter, asserted at elaboration's runtime
// shadow -- 30.9 §13's greppable finding, as a property.
property p_no_tolerance_parameter;
@(posedge clk) disable iff (!rst_n) SLACK == 0;
endproperty
assert property (p_no_tolerance_parameter)
else $error("a tolerance parameter is non-zero: a disagreement was parameterised");
// ---- COVERS. Each on the dimension its defect scales with, named.
// §6: OCCUPANCY. A steady-state flow never reaches DEPTH.
cover property (@(posedge clk) disable iff (!rst_n) truth == DEPTH);
// §7: COINCIDENCE, not either event -- 30.2 §11.
cover property (@(posedge clk) disable iff (!rst_n) q_valid && act_valid);
// §8: the INPUT VARYING. A hit-dominant stream never reaches the
// outcomes on which the builds differ.
cover property (@(posedge clk) disable iff (!rst_n)
req_accept && access_outcome == 2'd2);
// §9: a CONCENTRATED load. Under a uniform one the defect is
// absent rather than hidden -- 32.4 §16.
cover property (@(posedge clk) disable iff (!rst_n)
result_valid && (r_used * 4 < r_truth_capacity));
// §10: the DURATION, thresholded from the failure mechanism --
// 31.2 §14's scale-vacuity refinement. Past 2**DEBT_W ticks the
// two designs are distinguishable; below it they may not be.
cover property (@(posedge clk) disable iff (!rst_n) dev_owns [*256]);
// §11: MORE THAN ONE eligible run, which random input rarely gives.
cover property (@(posedge clk) disable iff (!rst_n)
(best_width != 0) && (sel_width != best_width));
// §12: the trained value DIFFERING from the compiled default --
// 30.7 §10 and 32.1 §15.
cover property (@(posedge clk) disable iff (!rst_n)
trained_valid && (trained_offset != FLIGHT_DEFAULT));
// §13: a sample stream whose minimum is ABOVE zero.
cover property (@(posedge clk) disable iff (!rst_n)
sample_valid && (sample > 0) && (truth_min > 0));
// §14: the BOUNDARY itself -- 30.3 §8: a property proves illegality
// did not happen; only a cover proves the boundary was exercised.
cover property (@(posedge clk) disable iff (!rst_n)
col_request && (since_act == TRCD));
// And the disagreement, so a one-cycle error is attributable.
cover property (@(posedge clk) disable iff (!rst_n) convention_err);Reviewed against the eleven varieties, and two are worth stating because they are the ones this chapter's own property set had to work at.
Variety 8 — safety cannot detect conservatism — governs §14 and §13. Both defects are in the harmless direction: too strict, and a minimum too low. CURRICULUM-DERIVED from 30.3 §9: a one-sided safety property is satisfied by a design that does nothing, so both items carry a two-sided pair and §14's pair is the reason p_eligible_at_the_stated_boundary exists at all.
Variety 3 — the obligation is inexpressible without a model — governs §11. Widest eligible is a comparison against alternatives, so best_width is a reference model computed independently in the same block, and the property is an equality against it. CURRICULUM-DERIVED from 30.6 §11, and from 27.3's independence requirement: a model that walked the bitmap the same way the selector does would inherit the same bug, so the truth loop is written separately even though it looks redundant.
18. Mutation Testing
Mutations are applied to the robust build. Baseline first: all seventeen assertions pass and all ten covers are non-zero.
| # | Mutation | Killed by | Survived? |
|---|---|---|---|
| M1 | §6: $clog2(DEPTH+1) → $clog2(DEPTH) | p_no_admit_when_truly_full | killed |
| M2 | §6: >= → > in full | p_occupancy_within_depth | killed |
| M3 | §7: add act_valid to the outcome chain | p_classify_against_registered_state, by one cover | killed |
| M4 | §8: true_lat(access_outcome) → TW'(FIXED_LAT) | p_completion_follows_outcome | killed |
| M5 | §9: CAPACITY → busy_now in the accumulator | p_denominator_not_driven_by_numerator | killed |
| M6 | §9: swap r_capacity for r_truth_capacity in the report | nothing | SURVIVES |
| M7 | §10: drop !dev_owns from the accrual | p_no_accrual_while_device_owns | killed |
| M8 | §11: add !done to the selection | p_selects_the_widest_eligible | killed |
| M9 | §11: drop the permitted intersection | p_selection_inside_permitted | killed |
| M10 | §12: trained_offset → FLIGHT_DEFAULT | p_uses_trained_offset, by one cover | killed |
| M11 | §13: min reset '1 → '0 | p_min_accepts_a_sample | killed |
| M12 | §14: >= → > | p_eligible_at_the_stated_boundary, by one cover | killed |
| M13 | §14: set SLACK to 1 | p_no_tolerance_parameter | killed |
| M14 | §8: delete the truth_timer update | nothing | SURVIVES |
DERIVED: twelve of fourteen killed, two survived, and both survivors are findings about the models rather than about the designs.
M6 survives because the report is telemetry. Substituting the truth for the observed denominator makes the block report the correct ratio while the internal accumulator remains wrong — so the mutation improves the output and corrupts the state. That is a finding about the property set's placement: the properties check the accumulator and the report separately, and nothing asserts that the reported value came from the accumulator. CURRICULUM-DERIVED from 32.1 §14: a block whose traceability output disagrees with what it uses confirms the wrong answer, and here the mutation exposes the mirror case.
M14 survives because deleting the truth disables the detector, not the design. The model's self-check is a scaffold, and a mutation that removes the scaffold removes the evidence. CURRICULUM-DERIVED from this track's recorded practice: a mutation that kills the checker rather than the design is a mutation-harness finding, and the discipline is to assert that the truth is being maintained — which is what p_debt_matches_truth and p_min_matches_truth do for §10 and §13 and what §8 lacks.
And three mutations are killed only by a cover — M3, M10 and M12. In each case the property is correct and the antecedent is unreachable under benign stimulus: no coincidence, no differing offset, no boundary cycle. CURRICULUM-DERIVED from 27.2 §6: the fix for a vacuous property is not a better property but a cover on its antecedent, and these three mutations are the mechanical proof that the cover is load-bearing rather than decorative.
19. Baseline Defects Found Before Mutation
Mutation testing measures a property set against deliberate damage. This section measures the review against damage nobody introduced on purpose — which is the only kind that reaches silicon.
Each of the nine weak builds was run against the robust build's property set before any mutation. The result is the honest measure of what this gate buys.
| Item | Weak build caught by | At what cost |
|---|---|---|
| §6 | p_no_admit_when_truly_full | a simulation that fills the queue |
| §7 | p_classify_against_registered_state | a simulation with coincident events |
| §8 | p_completion_follows_outcome | nothing — the port list is enough |
| §9 | p_denominator_not_driven_by_numerator | a simulation with a concentrated load |
| §10 | p_no_accrual_while_device_owns | 256 ticks of held ownership |
| §11 | p_selects_the_widest_eligible | a reference model |
| §12 | p_uses_trained_offset | nothing — the parameter list is enough |
| §13 | p_min_matches_truth | a sample stream bounded away from zero |
| §14 | p_no_tolerance_parameter | nothing — one grep is enough |
DERIVED: three of the nine weak builds are visible in the source text alone, and a fourth and fifth (§6's width expression, §13's reset literal) are visible by reading two lines. Five of nine need no simulation at all.
That ratio is this chapter's most useful number, and it inverts the usual intuition. CURRICULUM-DERIVED from 27.2's framing — assertions are how a design is proved — and the qualification this gate adds is that assertions are how a design is proved, not how a defect is first noticed. A property fires when a stimulus reaches it. A port list is available before the testbench exists.
Two of the four that do need simulation need a specific stimulus rather than more of the same. §9 needs a concentrated load; §10 needs 256 ticks of one condition held. CURRICULUM-DERIVED from 31.2 §14's scale-vacuity refinement: running the existing test longer reaches §10 and never reaches §9, because §9's dimension is the load's shape and no amount of duration changes a distribution.
And §11 is the one item whose cost is a second design. CURRICULUM-DERIVED from 30.6 §11: best available has no property form. A review can flag §11's shape — a guard where a comparison was specified — in minutes; proving which candidate the design should have chosen takes a model, and that is the next gate's expense.
20. Silicon Observability
Each item's question, asked of a part that is already packaged. CURRICULUM-DERIVED from 28.7 and 30.10 §12.
| Item | What silicon can show | What it cannot |
|---|---|---|
| §6 | a stall counter that never saturates while throughput drops | whether the counter wrapped or the queue drained |
| §7 | a misclassification rate that tracks the coincidence rate | which of the two inputs was the stale one |
| §8 | a latency histogram with one bin where the model has three | that a port is unread — silicon has no port list |
| §9 | a utilisation figure pinned at 100% under every load | whether the pin is real saturation or a shared denominator |
| §10 | a debt counter that only ever rises | that the rise is accrual rather than demand |
| §11 | a chosen window position, if the register is readable | what the alternatives were |
| §12 | the same design passing on one board and failing on another | which parameter carried the board's value |
| §13 | a reported minimum of exactly zero, every run | that zero is the reset value rather than a sample |
| §14 | a bandwidth shortfall with no error anywhere | that the shortfall is a checker's convention |
Three entries deserve their own line because they are the shape of a real silicon investigation rather than a counter reading.
§12's signature is board-dependence, and it is the most diagnostic signature in this table — DERIVED: a design whose behaviour depends on which board it is plugged into has a compiled-in platform quantity, and the parameter list names it in minutes. CURRICULUM-DERIVED from 30.7 §10 and 22.3 §3, whose callout names board and package design as a discipline with its own measurement methods — which is exactly why its quantities cannot be constants in RTL.
§13's signature is a value that is exactly the reset value, every run, across every part. A statistic that never varies is not a measurement. CURRICULUM-DERIVED from 30.10 §12's variety 9: evidence cleared by its own reset. Here the register is not cleared — it is never written, which reads identically from outside.
§14's signature is the absence of a signature. No error, no assertion, no timeout — only less bandwidth than the datasheet implies. CURRICULUM-DERIVED from 30.3 §9 (variety 8) and 23.2: a conservative controller is indistinguishable from a correct one except by measurement against a computed expectation, so §14 is the one item whose silicon detection requires knowing what the number should have been.
21. Common Wrong Answers
-
“Lint is clean, so the RTL is clean.” Lint found the unread input in §8 and the width in §6. It cannot find §9's shared denominator, §11's guard-for-comparison, or §14's convention — all three are well-formed code with no suspicious construct. DERIVED from §19: lint's reach is exactly the three text-visible items.
-
“The regression passes, so this code is correct.” Nine of nine weak builds pass a regression that does not reach their benign condition — §16's second table is that claim's refutation, item by item.
-
“An unread input is a warning, not a bug.” Eight of the track's eighteen documented defects present as an unread input (§5), and in six of them the unread port is the correct value being ignored in favour of a compiled one. The warning is not about tidiness; it is about which value the design used.
-
“The waveform looked right.” A waveform shows what happened on the cycles displayed for the stimulus applied. §10's defect needs 256 ticks; nobody scrolls 256 ticks of an unchanging signal.
-
“It is a minimum tracker, so resetting it to zero is the safe default.” Inverted. For a minimum, zero is the absorbing value: it can never be replaced. Safe for an accumulator is unsafe for an extremum, and the direction of safety is a property of the operator, not of the register. (§13)
-
“The counter is the right width — it holds the depth.” A counter that must represent how many, including all of them, needs
$clog2(N+1)bits. ‘It holds the depth’ is the index requirement, not the count requirement. (§6) -
“100% utilisation means we are saturated.” Or it means the denominator moved. The review question is not is this number high but what would have to happen for it to read low — and if no answer exists, the number is not a measurement. (§9)
-
“We added a tolerance because the checker was too sensitive.” A tolerance parameterises a disagreement between a design and a checker instead of resolving it. One of them is wrong, and the parameter's value is a measurement of how wrong nobody determined. (§14)
-
“The design is conservative, so at worst we lose a little performance.” Conservative by one cycle on every column access is not a little. And no property fires, which is why conservatism reaches production more often than illegality does. CURRICULUM-DERIVED from 30.3 §9.
-
“It works on the board we have.” §12's weak build works on exactly one board, and the board it works on is the one the parameter was measured against. ‘Works here’ and ‘works’ are the same sentence only if here is everywhere.
-
“The guard prevents the illegal case, so the selection is correct.” Legality and optimality are different obligations (30.6 §11). A guard proves the design will not do something forbidden. It says nothing about whether the thing it did was the best thing available.
-
“The flag is checked, so the value is used.” §10 and 32.1 §14 are the same finding twice: a gate honoured while its payload is ignored. The check and the use are separate lines, and the review must point at both.
-
“A same-cycle input is fresher, so using it is better.” Fresher is not the question; the question is what the specification names as the reference. If the classification is defined against the state before this cycle's command, then including that command is a one-cycle error that appears only on coincidence. (§7)
-
“The mutation survived, so the property set is weak.” Sometimes. M6 and M14 survived and both are findings about the harness and the placement, not about coverage. A mutation that improves the output or deletes the checker is a mutation-harness result. (§18)
-
“We will catch it in the next testbench run.” For §9 there is no next run that catches it — the defect is absent under the distribution the testbench uses, and more cycles of that distribution is more of the same. Duration reaches §6 and §10. It never reaches §9, §11 or §12.
-
“The fixed latency matches the datasheet, so it is fine.” It matches one entry of the datasheet. §8's contract has three outcomes with three latencies, and a constant is right on one of them. CURRICULUM-DERIVED from 31.1 §14's variety 10.
-
“The block reports the right number, so its internals must be right.” M6 is the counterexample: a mutation that made the report correct while leaving the state wrong. A telemetry output is evidence about the output.
-
“The reference model is redundant — it computes the same thing.” If it computes it the same way, it is worse than redundant: it inherits the bug and reports agreement. §11's truth loop is written differently on purpose. CURRICULUM-DERIVED from 27.3's independence requirement.
-
“This is a code-style review, so behaviour is out of scope.” Five of nine items here are decided by reading the source text, and all nine are about behaviour. The distinction between style and behaviour does not survive contact with a width expression.
-
“The defect would show up in the performance numbers.” §9's defect is a performance number, and it reads 100%. A corrupted measurement does not announce itself by looking wrong.
22. Self-Check
Answer before reading on. Each answer is a line of reasoning, not a recall.
- A queue of depth 16 has
logic [3:0] count. Under what stimulus is the design correct, and what is the first cycle at which it is not? - A block classifies a request against bank state. Name the two candidate references and the observable that distinguishes them. How often does the wrong choice show?
- You are handed a module you did not write. Which single artifact do you read first, and how many of this track's eighteen documented defects does it decide?
- A utilisation register reads 100% under every load you have tried. Give the one-sentence test that tells you whether the design is saturated or the metric is broken.
- A flag named
dev_ownsappears in three places. What is the review question, and why is “it is checked” not an answer to it? - A selector picks the first eligible candidate. The specification says widest. Why can no assertion, written alone, catch this — and what must be built?
- A parameter is named
FLIGHT_DEFAULT. State the finding in one sentence without reading the body. - A minimum tracker reports 0 on every run across every part. Give two hypotheses, and the one line of source that separates them.
- A design has
since_act > TRCDwhere the convention is eligible atact + TRCD. Which of the eleven varieties explains why the regression is green, and what direction is the error? - Of the nine items in this chapter, which need simulation, which need only source text, and which needs a second design? Give the counts.
- A mutation survived. Give the three distinct reasons a survivor is not automatically a coverage gap, with one example each.
- A reviewer says “the waveform looked right.” Name two of this chapter's items that a waveform cannot decide, and say why in each case.
23. The Residual Risk
What this gate does not catch, stated plainly.
It cannot catch a defect whose contract is wrong. Every item here compares code against a stated obligation. If the obligation is wrong, all nine items pass and the design is wrong. That is 33.1's gate, and it runs first for exactly this reason — CURRICULUM-DERIVED from 33.1 §12's commitment ledger: an undocumented commitment cannot be reviewed at this gate because there is nothing to compare against.
It cannot catch a defect that lives between two correct modules. Each item reads one block. §8's unread access_outcome is visible inside one module; a value that is correct in the producer, correct in the consumer, and misaligned in the wiring is invisible to both readings. That is the integration question, and 29.2 §7 records the result in its own closing: both layers satisfy their own contracts, and only a per-source measurement reveals the gap.
It cannot catch a defect in the environment. §11 needs a reference model, and a reference model is itself RTL-adjacent code that no gate here reviews. 33.4 exists because the model that judges the design is a design.
It cannot decide whether the number a correct block computes is the right number to compute. §9's robust build measures utilisation against provisioned capacity, independently and correctly. Whether utilisation-against-provisioned is the figure that should drive a decision is 33.5's question, and the two gates give different answers about the same passing code.
And it cannot catch what is correct in RTL and wrong in the analogue domain it drives. §12 flags a compiled flight time. It does not tell you what the flight time is, whether the training that measured it converged, or whether the value it converged to is trustworthy — that is 33.3, the next gate.
The honest summary: this gate's nine items decide, in minutes to hours, twelve of the eighteen defects this track documents — and every one of the six it misses is missed because the defect is not in the line.
24. Where This Goes
This gate produced a list of lines and a verdict on each. The next gate asks about a value that no line contains.
Chapter 33.3 reviews the PHY. The question changes from what does this line do to where did this number come from, and is it still true — because the PHY's correctness is carried in registers whose values were measured, not written. §12 flagged a compiled FLIGHT_DEFAULT and stopped there, by design: this gate can prove a parameter names something outside the design, and it cannot prove that the trained alternative is right.
Three of this chapter's items hand the next gate an open question rather than a finding.
| From here | The open question 33.3 answers |
|---|---|
| §12's compiled parameter | Which values must be trained, which may be compiled, and who decides? |
| §10's gate-and-value split | Which registers survive which reset — and is the trained value among them? |
| §14's tolerance parameter | When a margin is a real physical quantity, what makes it legitimate rather than a fudge? |
The last row is the one to carry forward, because it is where this chapter's sharpest rule meets its limit. Here, SLACK was a defect: a disagreement between a design and a checker, parameterised. In the PHY, a margin is the point — a trained delay with no margin is a part that works at one temperature. CURRICULUM-DERIVED from 21.4 §2, whose one-sentence model is that training does not create margin — it discovers an operating point with usable margin, and a grid with no passing cell must fail. The eye has a width; using its centre with no allowance is not confidence but an unstated assumption about drift.
So the next gate's first task is to distinguish the two, and the distinction is not in the code. A margin backed by a measured eye and a documented drift budget is engineering. A margin chosen because a test stopped failing is the defect this chapter named. They are the same line of Verilog. The reviewer's question — the one 33.3 opens with — is not is there a margin but what measurement is this margin's provenance, and what happens to the part when the assumption behind it is false.
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
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.
- 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.
