Skip to content
VLSI Mentor

DDR · Module 34

“The DDR Controller Is Simple Arbitration”

The first belief in this module about a component's nature rather than a quantity. Arbitration is the last of four stages and one of thirteen obligations the destructive-read property creates — and a design built on the belief issues illegal commands on its second bank.

“The memory controller is a queue and an arbiter. Several requests want the bus, one gets it. The rest is bookkeeping.”

34.3 §24 closed by naming the change of object: the three preceding beliefs were each about a number, and this one is about a component's nature. DERIVED: that changes what refutes it. A quantity belief falls to arithmetic. A nature belief falls to a census — you count what the thing actually has to do, and the count is not a matter of opinion.

And the count is already established in this curriculum, which is unusual and useful. CURRICULUM-DERIVED from 31.1 §5 by way of 33.1 §5: the destructive-read property creates thirteen controller obligations, and 33.1's first review action is “arithmetic: count the blocks against thirteen.”

DERIVED: arbitration is one of the thirteen. The belief is not wrong about arbitration — it is wrong about the denominator.

1. The One-Sentence Correction

Arbitration is the last of four stages a request passes through and one of thirteen obligations a DDR controller carriesand the three stages before it exist because DRAM is stateful and destructively read, which is a property no arbiter in any other domain has to accommodate.

CURRICULUM-DERIVED from 30.5 §3, which owns the four-way separation — request, candidate, grant, committed command — and from 17.4 §3, which owns why legality must be masked before policy rather than checked by it.

2. What This Chapter Owns

GroundOwner
The destructive-read property and the thirteen obligations it creates31.1 §5
Counting instantiated blocks against thirteen, as a review action33.1 §5
The command scheduler; the queue; the refresh manager; request handling17.1, 17.2, 17.3, 17.5
The layered mask, the rotating pointer, starvation, the bounded bypass17.4
The four-way separation and the wrong answer each collapse produces30.5 §3
The build order for a whiteboard scheduler, and the three wrong first moves30.5 §2
FR-FCFS with its three published rules, and the starvation bound23.4
Open, close and adaptive page policy; the crossover; hysteresis23.5
Reviewing whether the code is the specified mechanism33.2
Why the belief is plausible, the system it is true of, and what it coststhis chapter

Two boundaries, and the first is unusually close.

With 30.5: that chapter owns the four-way separation and the specific wrong answer each collapse produces, addressed to a candidate at a whiteboard. DERIVED: 30.5 teaches the separation to somebody building a scheduler; this chapter is about somebody who has decided not to build one — a system architect allocating effort, a reviewer sizing a block, an engineer estimating a schedule. §14 measures what that allocation costs, which 30.5 has no reason to compute.

With 33.1 §5: that item counts instantiated blocks against thirteen as a pre-RTL review gate. DERIVED: this chapter asks which of the thirteen an arbiter-shaped design even contains a place for — a different question, because a census finds a missing block and this finds a missing category.

3. Teaching-Model Boundary And Source Discipline

Claim classWhat it means hereExample below
Structurala documented mechanism or a fixed countthe thirteen obligations; the four stages; the layered mask
Curriculum-derivedfollows from a cited chapter of this trackevery obligation name, and the collapse consequences
Derivedcomputed in this chapter from the models belowevery count and rate in §19
Illustrativea chosen number that makes a mechanism visiblebank counts, queue depths, timing values, request mixes

The thirteen is STRUCTURAL and it is not this chapter's number. CURRICULUM-DERIVED from 31.1 §5: the destructive-read property is what creates them, so the count is a consequence of DRAM physics rather than of any controller's design, and a controller cannot have fewer.

Every model below is an educational model of a controller shape, not a controller. 17.1 owns the scheduler and 17.4 owns the arbiter. DERIVED: the models here are deliberately small and structurally wrong in the weak build — the point is the shape of the omission, not the quality of the implementation.

No external source was consulted and no network tool was used.

4. Why a Competent Engineer Believes It

#The true statementWhat the belief does with it
1The controller does arbitratetreats one obligation as the set
2Arbitration is the visible function — it is what a block diagram showstreats visibility as significance
3An arbiter is a well-understood block with known solutionstreats a familiar analogy as a complete one
4On one bank with one requester, a queue plus an arbiter workstreats the degenerate case as the case

Reason 3 is the one that does the damage, and it does it by being productive. DERIVED: the arbiter analogy imports round-robin, priority, weighted fairness and starvation bounds — all of which are real and useful, and 17.4 uses every one. So the analogy pays off immediately, and an analogy that pays off is not re-examined.

What it does not import is the reason DDR is different, and that reason is a single physical fact. CURRICULUM-DERIVED from 2.5 and 31.1 §5: reading a DRAM cell destroys its contents, so a read is not an access but a sequence — activate, column, precharge — over a resource with state. DERIVED: an arbiter selects among requests that are ready. A DDR controller's central difficulty is that almost nothing is ever ready, and making things ready is the other twelve obligations.

Reason 2 deserves a line because it is a fact about documents rather than about designs. DERIVED: a controller appears in a block diagram as one box because a block diagram's grain is a clock domain, not a responsibilityCURRICULUM-DERIVED from 29.2, which records that the packaging varies: a controller and PHY may ship as one IP, address decode may sit in the interconnect, so the box boundary is a product decision and carries no information about the count inside.

5. The Region Where the Claim Is True

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   "THE CONTROLLER IS SIMPLE ARBITRATION" HOLDS WHEN ALL FIVE HOLD:

     1  ONE bank                     -> no bank state to track
     2  ONE requester class          -> no policy, only fairness
     3  the row is always OPEN       -> no activate, no precharge
     4  no refresh                   -> no deadline-bearing obligation
     5  no timing rule ever binds    -> every queued request is a
                                        CANDIDATE, so legality is trivial

   and then:  the controller IS a queue and an arbiter, and the
              belief is exactly right

DERIVED: the region is non-empty, and it is precisely what a first RTL exercise builds. A single-bank model with an always-open row and no refresh is the natural first step, and it works. CURRICULUM-DERIVED from 30.5 §2, which owns the build order and names three wrong first moves: the belief is what you get if the first move is the arbiter, and the arbiter is the most natural first move because it is the only stage whose inputs all exist at the start.

Condition 5 is the load-bearing one and it is worth restating in the vocabulary 30.5 §3 supplies. DERIVED: when no timing rule binds, request and candidate are the same set — so the four stages collapse to two, and two stages really are a queue and an arbiter. The belief is the four-way separation with two of its stages made vacuous by a one-bank assumption.

6. The Boundary, Computed — In Obligations, Not Cycles

This is the first chapter in this module whose boundary is not measured in cycles or bytes, because the belief is not about a quantity.

ExitConditionWhat it addsOwner
1a second bankbank state tracking, and per-bank timing16.1, 17.1
2a closed rowactivate, precharge, and a page policy9.1, 23.5
3any timing rule bindinga legality mask, separate from policy17.4 §3
4refresha deadline-bearing obligation with a credit ledger15.3, 17.3
5a second requester classpolicy, and a starvation bound23.4, 17.4
6reordering for performancethe legality/policy layering, and its proof17.4, 23.4

Exit 1 is the cheapest to reach and it is the one that breaks the belief hardest. DERIVED: a second bank means the same command is legal for one bank and illegal for another in the same cycle, so legal stops being a property of the queue and becomes a property of the (command, bank, cycle) triple. An arbiter has no input for that.

And exit 3 is where the belief produces an actual bug rather than an omission. CURRICULUM-DERIVED from 17.4 §3, whose finding this chapter's §9 measures: checking timing inside the arbiter is the worse error, because an empty result from a combined legality-and-policy block “is just a loop that found nothing” — and it cannot say whether nothing was legal or nothing was preferred.

7. The Four Stages, and Where the Arbiter Sits

The diagram earns its place because the belief's error is positional: it names the last stage and omits the three that make it possible.

A request passing through four stages: queued as a request, masked into a candidate set by legality, granted by policy and arbitration, then committed to the bus where it updates timing state. The arbiter operates only between candidate and grant.Legality maskPolicyRequestCandidateGrantCommittedqueuedmaskarbitratepreferissuedstate12

The belief names the cand → grant edge and calls it the controller. DERIVED: three of the diagram's six edges are upstream of it, and the sixth — commit → req — is the feedback that makes DRAM stateful. CURRICULUM-DERIVED from 30.5 §3's collapse table: each omitted stage produces a named wrong answer, and the chapter's single diagnostic question — a read has waited 200 cycles; why has it not issued? — has four possible answers in this diagram and one in the belief's.

8. The Census, Against Thirteen

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ROBUST MODEL: every obligation is declared, and arbitration is ONE
// bit of the vector. 31.1 section 5's thirteen, consumed as a count.
package controller_functions_pkg;
  // The thirteen obligations the destructive-read property creates.
  // STRUCTURAL: the names are this track's, the count is 31.1 section 5's.
  typedef enum int {
    OB_ACTIVATE,      //  1 open a row before a column access
    OB_PRECHARGE,     //  2 close it before another row in that bank
    OB_RESTORE,       //  3 respect tRAS -- the restore obligation
    OB_TRCD,          //  4 activate-to-column spacing
    OB_TRP,           //  5 precharge-to-activate spacing
    OB_TRRD_FAW,      //  6 activate rate across banks
    OB_TURNAROUND,    //  7 read/write bus direction
    OB_TCCD,          //  8 column-to-column spacing
    OB_REFRESH,       //  9 retention, with a deadline
    OB_BANK_STATE,    // 10 which row is open, per bank
    OB_ADDR_MAP,      // 11 request address to bank/row/column
    OB_POLICY,        // 12 page policy and requester preference
    OB_ARBITRATION    // 13 one bus, several legal candidates
  } obligation_e;
endpackage

module function_census #(
    parameter int NOB = 13          // STRUCTURAL, 31.1 section 5
)(
    input  logic        clk,
    input  logic        rst_n,
    input  logic        audit,
    input  logic [12:0] blocks_present,   // what the design instantiates
    output logic [3:0]  present_count,
    output logic [3:0]  missing_count,
    output logic [12:0] missing_mask,
    output logic        census_complete,
    output logic        arbitration_only,
    output logic [7:0]  arbitration_share_pct
);
  import controller_functions_pkg::*;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      present_count <= '0; missing_count <= '0; missing_mask <= '0;
      census_complete <= 1'b0; arbitration_only <= 1'b0;
      arbitration_share_pct <= '0;
    end else if (audit) begin
      present_count   <= 4'($countones(blocks_present));
      missing_count   <= 4'(NOB - $countones(blocks_present));
      missing_mask    <= ~blocks_present & 13'h1FFF;
      census_complete <= ($countones(blocks_present) == NOB);
      // The belief, detectable in one expression: arbitration present
      // and almost nothing else.
      arbitration_only <= blocks_present[OB_ARBITRATION]
                          && ($countones(blocks_present) <= 4);
      // And the share, which is the number the belief implicitly claims
      // is 100%.
      arbitration_share_pct <= 8'd100 / 8'(NOB);
      // = 7 percent. The belief's denominator is 1; the truth is 13.
    end
  end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// INTENTIONALLY DEFECTIVE. WEAK MODEL: the census has one entry.
//
//   // "the controller" = a queue and an arbiter
//   localparam int NOB = 2;
//   blocks_present = 2'b11;        // queue + arbiter
//   census_complete <= 1'b1;       // <-- the defect
//
// CONTRACT VIOLATED: 31.1 section 5's count. And note that the weak census
// is INTERNALLY CONSISTENT -- it declares two obligations and
// instantiates two, so its own completeness check passes.
//
// WHY IT SURVIVES: a census is only as good as its denominator, and the
// denominator is the thing the belief gets wrong. 33.1 section 5's review
// action -- "count the blocks against thirteen" -- works precisely
// because thirteen comes from outside the design.
//
// TRACE (ILLUSTRATIVE, an arbiter-shaped design):
//   blocks_present = OB_ARBITRATION | OB_ADDR_MAP | OB_BANK_STATE
//                  = 3 of 13
//   robust: present 3, missing 10, census_complete 0,
//           arbitration_only 1, arbitration_share 7%
//           missing_mask names all ten: ACTIVATE, PRECHARGE, RESTORE,
//           TRCD, TRP, TRRD_FAW, TURNAROUND, TCCD, REFRESH, POLICY
//   weak:   present 2, missing 0, census_complete 1
//
//   gap: ten obligations absent, reported as a complete design. And the
//   ten are not obscure -- eight of them are timing rules with names
//   the belief's holder can recite.

The ten missing obligations are the finding, and eight of them are timing rules the belief's holder already knows. DERIVED: this is not a knowledge gap — an engineer holding this belief can name tRCD and tRP and explain both. What the belief gets wrong is that they are the controller's responsibilities rather than facts about the device, and that is a claim about ownership rather than about physics.

It is worth naming what kind of mistake the weak census makes, because it is not an arithmetic one. DERIVED: the weak build declares two obligations, instantiates two, and passes its own completeness check — it is internally consistent and externally wrong. CURRICULUM-DERIVED from 33.1 §5, whose review action is to count against thirteen precisely because thirteen comes from outside the design: a census validates a numerator against a denominator, and a design cannot supply its own denominator.

And arbitration_share_pct = 7% is the denominator the belief replaces with 1. CURRICULUM-DERIVED from 33.1 §5, whose weak build declared eleven of thirteen and whose two omissions were both not per-bank counters: DERIVED: 33.1's gate catches a design that misses two; this chapter's belief misses ten, and it misses them by category rather than by oversight.

9. Arbitrating Without a Legality Mask

This is the belief compiled, and it produces the specific defect 17.4 §3 documents.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ROBUST MODEL: legality is a MASK applied before policy, so an empty
// grant is attributable. 17.4's layering, consumed and not rebuilt.
module masked_then_arbitrated #(
    parameter int NBANK = 4,
    parameter int TRCD  = 6          // ILLUSTRATIVE
)(
    input  logic             clk,
    input  logic             rst_n,
    input  logic [NBANK-1:0] col_pending,
    input  logic [NBANK-1:0] row_open,
    input  logic [3:0]       since_act [0:NBANK-1],
    output logic [NBANK-1:0] legal_mask,
    output logic             grant_valid,
    output logic [1:0]       grant_bank,
    output logic             illegal_grant,
    output logic [15:0]      illegal_grants,
    output logic             nothing_legal,
    output logic             nothing_preferred
);
  logic [1:0] ptr;

  // STAGE: candidate. Legality is computed for EVERY bank, independently
  // of who wants the bus -- which is what makes an empty result
  // attributable.
  always_comb begin
    for (int b = 0; b < NBANK; b++)
      legal_mask[b] = row_open[b] && (since_act[b] >= 4'(TRCD));
  end

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      ptr <= '0; grant_valid <= 1'b0; grant_bank <= '0;
      illegal_grant <= 1'b0; illegal_grants <= '0;
      nothing_legal <= 1'b0; nothing_preferred <= 1'b0;
    end else begin
      automatic logic [NBANK-1:0] eligible = col_pending & legal_mask;
      grant_valid   <= 1'b0;
      illegal_grant <= 1'b0;
      // The two distinguishable empty results -- 17.4 section 3's point.
      nothing_legal     <= (legal_mask == '0) && (col_pending != '0);
      nothing_preferred <= (legal_mask != '0) && (eligible == '0);

      // STAGE: grant. Rotating pointer over the ELIGIBLE set only.
      for (int i = 0; i < NBANK; i++) begin
        automatic int b = (int'(ptr) + i) % NBANK;
        if (eligible[b] && !grant_valid) begin
          grant_valid <= 1'b1;
          grant_bank  <= 2'(b);
          ptr         <= 2'((b + 1) % NBANK);
          // Cannot fire: the grant came from the masked set.
          if (!legal_mask[b]) begin
            illegal_grant  <= 1'b1;
            illegal_grants <= illegal_grants + 1'b1;
          end
        end
      end
    end
  end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// INTENTIONALLY DEFECTIVE. WEAK MODEL: arbitrate, then check.
//
//   // the belief: the arbiter picks, and timing is checked after
//   for (int i = 0; i < NBANK; i++) begin
//     automatic int b = (int'(ptr) + i) % NBANK;
//     if (col_pending[b] && !grant_valid) begin      // <-- no mask
//       grant_valid <= 1'b1; grant_bank <= 2'(b);
//       ptr <= 2'((b + 1) % NBANK);
//     end
//   end
//   // ... and downstream, a separate check drops the command if it
//   // turns out to be illegal. The bus slot is already gone.
//
// CONTRACT VIOLATED: 17.4 section 3 -- legality must be masked before
// policy, and checking inside the arbiter is "the worse error".
//
// WHY IT SURVIVES: on ONE bank it is identical to the robust build --
// with a single bank, `col_pending` and `col_pending & legal_mask`
// differ only when the one bank is illegal, and then both produce no
// grant. The defect requires a SECOND bank to appear at all.
//
// TRACE (ILLUSTRATIVE, 4 banks, TRCD 6, 64 cycles of mixed demand):
//   one bank enabled:
//     robust: grants 28, illegal_grants 0
//     weak:   grants 28, illegal_grants 0      IDENTICAL
//   four banks enabled, staggered activates:
//     robust: grants 41, illegal_grants 0,
//             nothing_legal 7 cycles, nothing_preferred 16 cycles
//     weak:   grants 41, of which 12 named a bank whose tRCD had not
//             elapsed -> 12 illegal commands, each dropped downstream
//             -> 29 commands actually issued, and 12 bus slots wasted
//
//   gap: 12 of 41 grants illegal, 29% of the bus wasted, and a
//   throughput loss that reads as "the memory is slow".
//
//   And the attribution difference is the expensive half: the robust
//   build distinguishes 7 cycles of NOTHING LEGAL from 16 of NOTHING
//   PREFERRED. The weak build reports neither -- it just found nothing,
//   which is 17.4 section 3's "a loop that found nothing".

The one-bank case being identical is the whole reason the belief survives contact with RTL. DERIVED: a single-bank testbench cannot distinguish the two builds at all, and a single-bank testbench is what a first bring-up of a scheduler uses. The defect requires exit 1 from §6 — a second bank — which is the cheapest exit and the one a real device always takes.

And the attribution loss is worse than the wasted slots. DERIVED: 7 cycles of nothing legal and 16 of nothing preferred are different problems with different fixes — the first is a timing or row-state issue, the second a policy or demand issue. CURRICULUM-DERIVED from 30.5 §3: the diagnostic question why has this not issued has four answers in a layered design and one in a collapsed one, and a design that cannot answer it cannot be tuned.

10. What Each Collapse Costs

CURRICULUM-DERIVED from 30.5 §3, which owns the four-way separation and the wrong answer each collapse produces. This chapter consumes the table and measures the cost, which is what an effort-allocation decision needs.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ROBUST MODEL: the four stages are distinct, and a collapse is
// detectable because each stage publishes its own count.
module stage_separation #(
    parameter int NREQ = 16
)(
    input  logic        clk,
    input  logic        rst_n,
    input  logic        tick,
    input  logic [15:0] requests_queued,
    input  logic [15:0] candidates_legal,
    input  logic [15:0] grants_made,
    input  logic [15:0] commands_committed,
    output logic [3:0]  stages_distinct,
    output logic        can_attribute_a_stall,
    output logic [15:0] blocked_by_timing,
    output logic [15:0] blocked_by_policy,
    output logic [15:0] lost_after_grant,
    output logic [15:0] expansion_commands
);
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      stages_distinct <= '0; can_attribute_a_stall <= 1'b0;
      blocked_by_timing <= '0; blocked_by_policy <= '0;
      lost_after_grant <= '0; expansion_commands <= '0;
    end else if (tick) begin
      // Each difference is one of 30.5 section 3's four answers, and it is
      // only computable because the four counts are separate.
      blocked_by_timing  <= (requests_queued > candidates_legal)
                          ? (requests_queued - candidates_legal) : '0;
      blocked_by_policy  <= (candidates_legal > grants_made)
                          ? (candidates_legal - grants_made) : '0;
      lost_after_grant   <= (grants_made > commands_committed)
                          ? (grants_made - commands_committed) : '0;
      // Committed exceeding requests is EXPANSION -- one request became
      // several commands, which is 30.5's fourth answer and the one the
      // belief's "one request is one command" collapse erases.
      expansion_commands <= (commands_committed > requests_queued)
                          ? (commands_committed - requests_queued) : '0;
      stages_distinct       <= 4'd4;
      can_attribute_a_stall <= 1'b1;
    end
  end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   THE SAME 64 CYCLES, ATTRIBUTED FOUR WAYS AND ONE WAY
                                      (ILLUSTRATIVE, 4 banks)

     requests_queued     52
     candidates_legal    31      -> blocked_by_timing   21
     grants_made         24      -> blocked_by_policy    7
     commands_committed  38      -> expansion           -- 38 > 24
                                    because activates and precharges
                                    are commands nobody requested

     ROBUST answers "why has this read waited 200 cycles?" with:
       21 requests were never legal      -> which timing rule?
        7 were legal and not preferred   -> which policy?
        0 were granted and lost
       14 extra commands were expansion  -> it was never one command

     WEAK answers: "it's busy."

   DERIVED: four numbers from four counters, and the belief's design
   has one counter because it has one stage.

The four differences are four subtractions, and that is the entire mechanism — which is worth saying because it makes the instrument cheap. DERIVED: four counters and three subtractions produce the four answers 30.5 §3 says a layered design can give, and the cost is four registers. CURRICULUM-DERIVED from 33.5 §7's exhaustive-attribution requirement: the same structure, applied to a pipeline's stages rather than to a window's cycles — and with the same property, that the parts must reconcile with the whole.

The expansion row is the one the belief erases most completely, and it is the one that surprises people. DERIVED: commands_committed exceeds requests_queued — 38 against 52 queued but 14 more commands than grants — because activates and precharges are commands no requester asked for. CURRICULUM-DERIVED from 30.5 §3's first collapse: one request is one command produces a design with no precharge, no activate, no expansionand an arbiter over requests has no place to put a command that has no request.

11. The Obligation With No Requester

§6's exit 4 is the one this chapter called qualitatively different, and the difference is a type rather than a priority.

CURRICULUM-DERIVED from 17.3, which owns the refresh manager and the illegal-versus-deprioritised distinction. DERIVED: an arbiter's inputs are requests and its output is a selection, so the only way to express this must happen in an arbiter is this has the highest priority — and a priority can always be outbid by a higher one, while a deadline cannot be outbid at all.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ROBUST MODEL: a deadline-typed obligation, which is a different TYPE
// from a prioritised request. 17.3's illegal-versus-deprioritised line,
// made structural.
module obligation_vs_priority #(
    parameter int MAX_DEFER = 8,     // STRUCTURAL: 15.3 section 1, 1X mode
    parameter int NREQ      = 4
)(
    input  logic             clk,
    input  logic             rst_n,
    input  logic             interval_tick,
    input  logic             obligation_served,
    input  logic [NREQ-1:0]  req_pending,
    input  logic [2:0]       req_priority [0:NREQ-1],
    output logic             serve_obligation,
    output logic             serve_request,
    output logic [1:0]       served_req,
    output logic signed [7:0] debt,
    output logic             obligation_is_deferrable,
    output logic [15:0]      obligation_outbid_cycles,
    output logic             deadline_violated
);
  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      debt <= '0; serve_obligation <= 1'b0; serve_request <= 1'b0;
      served_req <= '0; obligation_is_deferrable <= 1'b1;
      obligation_outbid_cycles <= '0; deadline_violated <= 1'b0;
    end else begin
      if (interval_tick && !obligation_served) debt <= debt + 8'sd1;
      else if (obligation_served && !interval_tick) debt <= debt - 8'sd1;

      // The TYPE distinction: deferrability is a function of the debt,
      // not of anyone's priority. Below the bound the obligation is
      // deprioritisable; at the bound it is not.
      obligation_is_deferrable <= (debt < 8'sd(MAX_DEFER));
      deadline_violated        <= (debt > 8'sd(MAX_DEFER));

      serve_obligation <= 1'b0;
      serve_request    <= 1'b0;

      if (debt >= 8'sd(MAX_DEFER)) begin
        // No priority can outbid this. There is no comparison to make.
        serve_obligation <= 1'b1;
      end else if (req_pending != '0) begin
        automatic int best = 0;
        for (int i = 1; i < NREQ; i++)
          if (req_pending[i] && (req_priority[i] > req_priority[best]))
            best = i;
        serve_request <= 1'b1;
        served_req    <= 2'(best);
        // Counted, so the deferral is visible as a quantity rather
        // than inferred -- 34.2 section 7's longest-block lesson.
        if (debt > 8'sd0)
          obligation_outbid_cycles <= obligation_outbid_cycles + 1'b1;
      end else begin
        serve_obligation <= (debt > 8'sd0);
      end
    end
  end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// INTENTIONALLY DEFECTIVE. WEAK MODEL: refresh as priority 7.
//
//   // "model refresh as the highest-priority requester"
//   localparam int OB_PRIORITY = 7;                  // the maximum
//   ...
//   if (debt > 0 && OB_PRIORITY >= req_priority[best]) serve_obligation <= 1;
//   else                                              serve_request    <= 1;
//   // no MAX_DEFER, no deadline, no violation flag
//
// CONTRACT VIOLATED: 17.3's illegal-versus-deprioritised distinction, and
// 15.3 section 1's bounded allowance.
//
// WHY IT SURVIVES: with priority 7 as the maximum, refresh wins every
// comparison it enters, so the weak build serves refresh promptly and
// looks correct. It is correct -- until somebody adds a requester at
// priority 7 for a real-time stream, which is a normal thing to do.
//
// TRACE (ILLUSTRATIVE, 24 intervals; a priority-7 realtime stream
// arrives at interval 4 and runs continuously):
//   intervals 1-3   no competition
//     robust: debt 0..1, obligation served     weak: same   IDENTICAL
//   intervals 4-24  a priority-7 requester is always pending
//     robust: debt climbs to 8; at 8 `serve_obligation` asserts and
//             the requester waits. obligation_outbid_cycles 7,
//             deadline_violated 0.
//     weak:   OB_PRIORITY >= req_priority[best] is 7 >= 7, TRUE, so
//             refresh still wins -- by a tie-break, not by type.
//             Change the requester to priority 7 and the arbiter's
//             comparison to strict `>` and refresh never wins again:
//             debt climbs past 8 and keeps going. deadline: violated.
//
//   gap: the weak build's correctness depends on a TIE-BREAK in a
//   comparison. The robust build has no comparison to depend on.
//   DERIVED: 21 intervals of unserved refresh is a retention failure,
//   and the change that caused it was adding a requester -- an action
//   nobody would review as touching refresh.

The weak build's correctness depending on a tie-break is the finding. DERIVED: 7 >= 7 is true and 7 > 7 is false, so whether refresh is served at all turns on a comparison operator in an unrelated blockand the action that breaks it is adding a requester, which no reviewer would flag as touching retention.

CURRICULUM-DERIVED from 17.3's distinction: deprioritised means someone else went first; illegal means this cannot be deferred. DERIVED: a priority encodes the first and cannot encode the second, so representing a deadline as a priority is a type error and not a tuning choice — which is why §21's eleventh entry rejects it rather than bounding it.

12. One Request, Several Commands

§6's exit 2 and 30.5 §3's first collapse are the same thing: one request is one command is the belief's most load-bearing consequence, and it is false by a factor that depends on row state.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ROBUST MODEL: a request EXPANDS into a command sequence whose length
// depends on the bank's state. An arbiter over requests has no place to
// put the commands nobody requested.
module request_expansion #(
    parameter int NBANK = 4,
    parameter int ROW_W = 8
)(
    input  logic             clk,
    input  logic             rst_n,
    input  logic             req_valid,
    input  logic [1:0]       req_bank,
    input  logic [ROW_W-1:0] req_row,
    output logic [1:0]       commands_needed,   // 1, 2 or 3
    output logic             emit_pre, emit_act, emit_col,
    output logic [15:0]      requests_seen,
    output logic [15:0]      commands_emitted,
    output logic [7:0]       expansion_x100,
    output logic             one_to_one
);
  logic             row_open [0:NBANK-1];
  logic [ROW_W-1:0] open_row [0:NBANK-1];

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      commands_needed <= '0; emit_pre <= 1'b0; emit_act <= 1'b0;
      emit_col <= 1'b0; requests_seen <= '0; commands_emitted <= '0;
      expansion_x100 <= 8'd100; one_to_one <= 1'b1;
      for (int b = 0; b < NBANK; b++) begin
        row_open[b] <= 1'b0; open_row[b] <= '0;
      end
    end else begin
      emit_pre <= 1'b0; emit_act <= 1'b0; emit_col <= 1'b0;

      if (req_valid) begin
        requests_seen <= requests_seen + 1'b1;
        // The three cases -- 9.x's hit, miss and conflict, as command
        // counts rather than as latency terms.
        if (row_open[req_bank] && (open_row[req_bank] == req_row)) begin
          commands_needed  <= 2'd1;              // hit: column only
          emit_col         <= 1'b1;
          commands_emitted <= commands_emitted + 16'd1;
        end else if (!row_open[req_bank]) begin
          commands_needed  <= 2'd2;              // miss: activate, column
          emit_act <= 1'b1; emit_col <= 1'b1;
          commands_emitted <= commands_emitted + 16'd2;
          row_open[req_bank] <= 1'b1;
          open_row[req_bank] <= req_row;
        end else begin
          commands_needed  <= 2'd3;              // conflict: pre, act, col
          emit_pre <= 1'b1; emit_act <= 1'b1; emit_col <= 1'b1;
          commands_emitted <= commands_emitted + 16'd3;
          open_row[req_bank] <= req_row;
        end

        expansion_x100 <= (requests_seen == '0) ? 8'd100
                        : 8'((32'(commands_emitted) * 32'd100)
                             / 32'(requests_seen + 16'd1));
        one_to_one     <= (commands_emitted + 16'd1 == requests_seen + 16'd1);
      end
    end
  end
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// INTENTIONALLY DEFECTIVE. WEAK MODEL: one request, one command.
//
//   // the arbiter selects a request and issues "it"
//   if (req_valid) begin
//     emit_col         <= 1'b1;            // <-- the only command
//     commands_needed  <= 2'd1;
//     commands_emitted <= commands_emitted + 16'd1;
//   end
//   // no row state, no activate, no precharge
//
// CONTRACT VIOLATED: 30.5 section 3's first collapse -- "one request is one
// command" produces a design with no precharge, no activate, no
// expansion.
//
// WHY IT SURVIVES: with the row always open -- section 5's condition 3 --
// every request IS one command, and the weak build is exactly right.
// A model initialised with all rows open and a single row per bank
// never leaves that state.
//
// TRACE (ILLUSTRATIVE, 100 requests, 40% hit / 25% miss / 35% conflict):
//   robust: commands_emitted = 40x1 + 25x2 + 35x3 = 40 + 50 + 105 = 195
//           expansion_x100 195, one_to_one 0
//   weak:   commands_emitted = 100, expansion_x100 100, one_to_one 1
//
//   gap: 95 commands the belief has no place for -- 25 activates and
//   35 precharge/activate pairs, none of which any requester asked for.
//   That is 49% of the command bus, and a design sized for 100
//   commands has provisioned for half the traffic.
//
//   and at 100% hits: robust 100, weak 100. IDENTICAL -- section 5's
//   condition 3 exactly.

195 commands from 100 requests is the number, and the 95 extra are the ones the belief has no field for. DERIVED: a command queue sized from a request count is undersized by 95%, a command-bus budget by 49%, and neither error is a wrong value — both are a missing category.

CURRICULUM-DERIVED from 23.3, which owns what produces the hit, miss and conflict mix: DERIVED: the expansion factor is a function of the access pattern against the address map, so it ranges from 1.00 at perfect locality to 3.00 at total conflictand the belief is the 1.00 endpoint, which 23.3 establishes is reachable and rare.

13. Where the Belief Breaks First

RankThe exitWhy it comes firstWhat it produces
1a second bankevery real device has at least eight§9's 12 illegal grants of 41
2a closed rowany address stride beyond one rowexpansion commands with no request
3any timing rule bindingimmediately, at any real data ratean unattributable stall
4refreshevery tREFI, unavoidablyan obligation an arbiter cannot express
5a second requester classevery SoCstarvation with no bound

DERIVED: rank 1 arrives before the design runs, because a single-bank DRAM does not exist. CURRICULUM-DERIVED from 5.2 and 16.1: banks are the mechanism DRAM uses to hide row-cycle latency, so the belief's region requires deliberately disabling the feature the device exists to provide.

And rank 4 is qualitatively different from the other four, which is worth one line. DERIVED: exits 1, 2, 3 and 5 add constraints on selection — things an arbiter could in principle be taught. Refresh adds a self-originating obligation with a deadline, which no arbiter has: nothing requested it, and it cannot be deferred indefinitely. CURRICULUM-DERIVED from 17.3 and 34.2 §8's bounded ledger: an arbiter arbitrates among requests, and refresh is not one.

14. The Decision Built on the Belief

This is what the belief costs when it is used to allocate effort, which is the decision it actually drives.

Where it is appliedWhat it producesMeasured consequence
A schedule estimatea queue and an arbiter — two weeksten of thirteen obligations unplanned
A block diagramone box labelled controllerthe census has no rows to check (§8)
An RTL structurearbitrate, then check timing12 illegal grants of 41; 29% of the bus
A debug capabilityone counterit's busy instead of four attributions (§10)
A verification planarbiter properties — fairness, one-hot30.5 §11's shape-versus-content error
A hiring or review rubriccan they build an arbitertests stage four of four

Row 5 is the one that closes a loop with 33.8 §14, and the connection is exact. DERIVED: a verification plan derived from the arbiter analogy writes properties about the grant's shape — one-hot, fair, eventually-served — and none about legal_mask. CURRICULUM-DERIVED from 30.5 §11 and 30.9 §6's variety 2: “every property was about the grant's shape; none mentioned legal_mask, so none saw the grant leave the legal set.” The belief and the recorded assertion defect are the same error at two levels.

Row 6 deserves one line because it perpetuates the belief rather than merely acting on it. DERIVED: a rubric that asks can they build an arbiter tests stage four of four, so it selects for people who will build stage four — and the belief reproduces itself through hiring. CURRICULUM-DERIVED from 30.5 §1, which owns what the scheduler question is really grading: the four-way separation is the grading criterion precisely because the arbiter is the part everybody can do.

And row 1 is the most expensive in practice. DERIVED: an estimate of two weeks for a queue and an arbiter is correct for a queue and an arbiter — the error is not in the estimate but in the scope it prices. CURRICULUM-DERIVED from 33.1 §5, whose review action is to count blocks against thirteen: the same arithmetic applied to a schedule prices ten missing blocks, and it takes a minute.

15. What the Assertions Prove

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // ---- Section 8: the census. The denominator is the obligation, and it
  // comes from outside the design -- 31.1 section 5.
  property p_census_counts_against_thirteen;
    @(posedge clk) disable iff (!rst_n)
      (audit && census_complete) |=> (present_count == 4'd13);
  endproperty
  assert property (p_census_counts_against_thirteen)
    else $error("a census was called complete with fewer than thirteen obligations");

  property p_missing_mask_matches_the_count;
    @(posedge clk) disable iff (!rst_n)
      audit |=> (missing_count == 4'($countones(missing_mask)));
  endproperty
  assert property (p_missing_mask_matches_the_count)
    else $error("the missing count disagrees with the missing mask");

  property p_arbitration_only_is_flagged;
    @(posedge clk) disable iff (!rst_n)
      (audit && blocks_present[OB_ARBITRATION]
       && ($countones(blocks_present) <= 4)) |=> arbitration_only;
  endproperty
  assert property (p_arbitration_only_is_flagged)
    else $error("an arbiter-shaped design was not flagged");

  // Two-sided -- 30.3 section 9's variety 8: a model that flags every design
  // satisfies the property above and distinguishes nothing.
  property p_complete_design_is_not_flagged;
    @(posedge clk) disable iff (!rst_n)
      (audit && (blocks_present == 13'h1FFF)) |=> !arbitration_only;
  endproperty
  assert property (p_complete_design_is_not_flagged)
    else $error("a complete thirteen-obligation design was flagged as arbiter-only");

  // ---- Section 9: the mask must precede the arbiter -- 17.4 section 3.
  property p_grant_is_always_legal;
    @(posedge clk) disable iff (!rst_n)
      grant_valid |-> legal_mask[grant_bank];
  endproperty
  assert property (p_grant_is_always_legal)
    else $error("a grant named a bank that was not legal this cycle");

  property p_no_illegal_grants_ever;
    @(posedge clk) disable iff (!rst_n) illegal_grants == '0;
  endproperty
  assert property (p_no_illegal_grants_ever)
    else $error("the design issued a command the timing rules forbade");

  // The attribution property: the two empty results must be
  // DISTINGUISHABLE, which is what a collapsed block cannot do.
  property p_empty_result_is_attributable;
    @(posedge clk) disable iff (!rst_n)
      ((col_pending != '0) && !grant_valid)
        |-> (nothing_legal ^ nothing_preferred);
  endproperty
  assert property (p_empty_result_is_attributable)
    else $error("a stall was neither attributed to legality nor to policy");

  // Legality must not depend on demand -- the independence that makes
  // the mask a mask rather than a search.
  property p_legality_independent_of_demand;
    @(posedge clk) disable iff (!rst_n)
      legal_mask == {NBANK{1'b1}} & legal_mask;
  endproperty
  assert property (p_legality_independent_of_demand)
    else $error("the legal mask was narrowed by the pending set");

  // ---- Section 10: the four stages.
  property p_four_stages_are_distinct;
    @(posedge clk) disable iff (!rst_n)
      tick |=> (stages_distinct == 4'd4);
  endproperty
  assert property (p_four_stages_are_distinct)
    else $error("fewer than four stages are separately counted");

  property p_stall_is_attributable;
    @(posedge clk) disable iff (!rst_n)
      tick |=> can_attribute_a_stall;
  endproperty
  assert property (p_stall_is_attributable)
    else $error("a stall could not be attributed to a stage");

  property p_candidates_never_exceed_requests;
    @(posedge clk) disable iff (!rst_n)
      tick |-> (candidates_legal <= requests_queued);
  endproperty
  assert property (p_candidates_never_exceed_requests)
    else $error("more candidates than requests: the stages are conflated");

  property p_grants_never_exceed_candidates;
    @(posedge clk) disable iff (!rst_n)
      tick |-> (grants_made <= candidates_legal);
  endproperty
  assert property (p_grants_never_exceed_candidates)
    else $error("more grants than candidates: policy selected an illegal command");

  // But COMMITTED may exceed requests -- that is expansion, and a
  // property forbidding it would encode the belief's first collapse.
  property p_expansion_is_permitted_and_counted;
    @(posedge clk) disable iff (!rst_n)
      (tick && (commands_committed > requests_queued))
        |=> (expansion_commands != '0);
  endproperty
  assert property (p_expansion_is_permitted_and_counted)
    else $error("expansion occurred and was not counted");

  // ---- Section 11: the obligation's TYPE. A deadline must not be
  // expressible as a priority -- 17.3's illegal-versus-deprioritised.
  property p_obligation_wins_at_the_bound;
    @(posedge clk) disable iff (!rst_n)
      (debt >= 8'sd(MAX_DEFER)) |=> serve_obligation;
  endproperty
  assert property (p_obligation_wins_at_the_bound)
    else $error("a deadline-bound obligation lost to a request");

  property p_no_priority_outbids_a_deadline;
    @(posedge clk) disable iff (!rst_n)
      (debt >= 8'sd(MAX_DEFER)) |=> !serve_request;
  endproperty
  assert property (p_no_priority_outbids_a_deadline)
    else $error("a request was served while an obligation was at its bound");

  property p_deadline_never_violated;
    @(posedge clk) disable iff (!rst_n) !deadline_violated;
  endproperty
  assert property (p_deadline_never_violated)
    else $error("the obligation's debt exceeded its bound");

  // Two-sided: below the bound the obligation IS deprioritisable, and a
  // design that always serves it is 15.3 section 13's first misconception.
  property p_deferrable_below_the_bound;
    @(posedge clk) disable iff (!rst_n)
      ((debt < 8'sd(MAX_DEFER)) && (req_pending != '0)) |=> serve_request;
  endproperty
  assert property (p_deferrable_below_the_bound)
    else $error("the obligation pre-empted a request with headroom remaining");

  // ---- Section 12: expansion. The command count is a FUNCTION of row
  // state, which is the field the belief does not have.
  property p_expansion_matches_row_state;
    @(posedge clk) disable iff (!rst_n)
      (req_valid && row_open[req_bank] && (open_row[req_bank] == req_row))
        |=> (commands_needed == 2'd1);
  endproperty
  assert property (p_expansion_matches_row_state)
    else $error("a row hit required more than one command");

  property p_conflict_needs_three;
    @(posedge clk) disable iff (!rst_n)
      (req_valid && row_open[req_bank] && (open_row[req_bank] != req_row))
        |=> (commands_needed == 2'd3);
  endproperty
  assert property (p_conflict_needs_three)
    else $error("a row conflict did not expand to precharge, activate and column");

  property p_column_always_emitted;
    @(posedge clk) disable iff (!rst_n) req_valid |=> emit_col;
  endproperty
  assert property (p_column_always_emitted)
    else $error("a request expanded without a column command");

  // ---- The CLAIM, as a conditional pair -- 34.1 section 1's required form.
  property p_claim_holds_in_its_region;
    @(posedge clk) disable iff (!rst_n)
      ((NBANK == 1) || (legal_mask == {NBANK{1'b1}}))
        |-> (illegal_grants == '0);
  endproperty
  assert property (p_claim_holds_in_its_region)
    else $error("with one bank or everything legal, an illegal grant still occurred");

  property p_claim_fails_with_a_second_bank;
    @(posedge clk) disable iff (!rst_n)
      ((col_pending & ~legal_mask) != '0)
        |-> (nothing_legal || nothing_preferred || grant_valid);
  endproperty
  assert property (p_claim_fails_with_a_second_bank)
    else $error("demand for an illegal bank produced no attributable outcome");

  // ---- COVERS. Each on the dimension the belief's failure scales with.
  // The REGION: everything legal, so the claim pair is not vacuous.
  cover property (@(posedge clk) disable iff (!rst_n)
                  (col_pending != '0) && (legal_mask == {NBANK{1'b1}}));
  // DEMAND FOR AN ILLEGAL BANK -- the condition a one-bank testbench
  // cannot produce, and the dimension is the BANK COUNT.
  cover property (@(posedge clk) disable iff (!rst_n)
                  (col_pending & ~legal_mask) != '0);
  // Both empty results, separately, so the attribution is exercised.
  cover property (@(posedge clk) disable iff (!rst_n) nothing_legal);
  cover property (@(posedge clk) disable iff (!rst_n) nothing_preferred);
  // A census that is COMPLETE, so p_census_counts_against_thirteen is
  // not satisfied by never completing one.
  cover property (@(posedge clk) disable iff (!rst_n)
                  audit && census_complete);
  // And an arbiter-shaped one, so the flag has a witness.
  cover property (@(posedge clk) disable iff (!rst_n)
                  audit && arbitration_only);
  // A census missing MORE THAN HALF the obligations -- section 8's ten.
  cover property (@(posedge clk) disable iff (!rst_n)
                  audit && (missing_count >= 4'd7));
  // Section 10: EXPANSION -- committed exceeding requested, which the
  // belief's one-request-one-command collapse makes impossible.
  cover property (@(posedge clk) disable iff (!rst_n)
                  tick && (commands_committed > requests_queued));
  // A stall attributed to TIMING rather than policy, and vice versa.
  cover property (@(posedge clk) disable iff (!rst_n)
                  tick && (blocked_by_timing > blocked_by_policy));
  cover property (@(posedge clk) disable iff (!rst_n)
                  tick && (blocked_by_policy > blocked_by_timing));
  // And a grant LOST after being made -- 30.5's third answer, which
  // needs a dropped bus slot to occur at all.
  cover property (@(posedge clk) disable iff (!rst_n)
                  tick && (lost_after_grant != '0));
  // Section 11: the obligation AT its bound, winning without a
  // comparison. The dimension is sustained competition duration.
  cover property (@(posedge clk) disable iff (!rst_n)
                  (debt >= 8'sd(MAX_DEFER)) && (req_pending != '0));
  // And a request winning below the bound, so the two-sided pair holds.
  cover property (@(posedge clk) disable iff (!rst_n)
                  serve_request && (debt > 8'sd0));
  // Section 12: a CONFLICT expanding to three commands -- the case a
  // single-row testbench never produces.
  cover property (@(posedge clk) disable iff (!rst_n)
                  req_valid && (commands_needed == 2'd3));
  // And a hit expanding to one, which is the belief's region.
  cover property (@(posedge clk) disable iff (!rst_n)
                  req_valid && (commands_needed == 2'd1));

Two notes, and the first is the most interesting property in this chapter.

p_expansion_is_permitted_and_counted permits something a naive property set would forbid. DERIVED: a property asserting commands_committed <= requests_queued looks obviously right and encodes the belief's first collapse — one request is one command. CURRICULUM-DERIVED from 30.5 §3: that collapse produces a design with no activate and no precharge, so a property forbidding expansion would make the correct design fail and the belief's design pass. That is the sharpest example in this module of a misconception reaching into a property set.

And p_legality_independent_of_demand is the structural form of 17.4 §3's rule. DERIVED: the mask must be computable without knowing who is asking, which is what makes an empty eligible set attributable. A search that considers only pending requests cannot report whether nothing was legal or nothing was wanted.

16. Mutation Testing

Baseline first: all twenty-two assertions pass and all fifteen covers are non-zero.

#MutationKilled bySurvived?
M1§8: NOB reduced to 2p_census_counts_against_thirteen, by one coverkilled
M2§8: missing_mask tied to zerop_missing_mask_matches_the_countkilled
M3§8: drop the arbitration_only flagp_arbitration_only_is_flagged, by one coverkilled
M4§8: flag every designp_complete_design_is_not_flaggedkilled
M5§9: arbitrate over col_pendingp_grant_is_always_legal, by one coverkilled
M6§9: mask computed from col_pending & row_openp_legality_independent_of_demandkilled
M7§9: collapse both empty results into one flagp_empty_result_is_attributablekilled
M8§9: illegal_grants never incrementedp_no_illegal_grants_everkilled*
M9§10: stages_distinct set to 1p_four_stages_are_distinctkilled
M10§10: blocked_by_policy folded into timingp_stall_is_attributablekilled
M11§10: forbid expansion (committed <= requested)p_expansion_is_permitted_and_counted, by one coverkilled
M12§10: allow grants > candidatesp_grants_never_exceed_candidateskilled
M13NBANK set to 1nothingSURVIVES
M14TRCD set to 0nothingSURVIVES
M15the col_pending stimulus restricted to one banknothingSURVIVES
M16§8: blocks_present hard-wired to all-onesnothingSURVIVES
M17§11: refresh modelled as priority 7p_no_priority_outbids_a_deadline, by one coverkilled
M18§11: deadline_violated tied lowp_deadline_never_violatedkilled
M19§11: obligation served every intervalp_deferrable_below_the_boundkilled
M20§12: commands_needed fixed at 1p_conflict_needs_three, by one coverkilled
M21§12: conflict treated as a miss (2 commands)p_conflict_needs_threekilled
M22§12: emit_col dropped on a conflictp_column_always_emittedkilled
M23§11: MAX_DEFER raised to 64nothingSURVIVES

DERIVED: eighteen of twenty-three killed, five survived — and four of the five reconstruct §5's region, which is now this module's fourth consecutive chapter with that result.

M23 is the fifth and it is 34.2 §17's M14 arriving in a different chapter. Raising MAX_DEFER to 64 keeps every mechanism intact — the type distinction holds, the bound still forces, no priority outbids it — and the bound is now wrong by 8×. DERIVED: every property here is written against MAX_DEFER, so the property set re-derives a weaker obligation and confirms it. CURRICULUM-DERIVED from 15.3 §1: the correct value is 8, 16 or 32 by refresh mode, and it is a datasheet figure. The fix is the same elaboration check 34.2 §17 arrived at, and finding it twice in one module is the point rather than a repetition.

M13, M14 and M15 are the region itself: one bank, no timing constraint, one bank's worth of demand. DERIVED: each makes the belief true, and inside the belief's region the robust and weak builds are provably identical — §9's trace says so. CURRICULUM-DERIVED from 33.4 §15's variety 12: the mechanism is correct and the scope is unconstrained, and here the scope is a bank count.

M16 is the one that needs naming separately, because it is the belief's mirror image. Hard-wiring blocks_present to all-ones makes every census complete — so a design with three blocks reports thirteen. DERIVED: the census is only as good as its input, and §8's weak build got the denominator wrong while M16 gets the numerator wrong. CURRICULUM-DERIVED from 33.6 §18's M17, which emptied an assumes matrix: an instrument whose input is unvalidated reports clean on every design, and the fix is an elaboration-time tie between the vector and the instantiated module list — which is a build-system check, not a property.

Four mutations are killed only by a cover, and M11's is unique in this module. Forbidding expansion is killed by the cover on commands_committed > requests_queuedDERIVED: and that cover is unreachable in any single-row testbench, because expansion requires a row change. So the property that protects against the belief's first collapse is itself only exercised once the design leaves the belief's region.

17. Baseline Defects Found Before Mutation

Belief applied toCaught byAt what cost
a block censusp_census_counts_against_thirteennothing — count against thirteen
an RTL structurep_grant_is_always_legala second bank in the testbench
a stall attributionp_empty_result_is_attributablenothing — count the counters
a schedule estimatep_missing_mask_matches_the_countnothing — read the missing mask
a property setp_legality_independent_of_demandnothing — read the mask's inputs
a one-request-one-command modelp_expansion_is_permitted_and_counteda row change

DERIVED: four of six are found without running anything, and all four are a count. CURRICULUM-DERIVED from 33.1 §5: “the first review action is arithmetic”and this chapter's belief is the one case in the module where counting is the whole method.

Two need a stimulus, and both need the same one.

ApplicationThe stimulusWhy the usual test misses it
an RTL structurea second bank with a binding timing rulea first scheduler testbench is single-bank
a one-request-one-command modelan access that changes rowsa single-row test never expands

DERIVED: both reduce to use a realistic device, and that is the cheapest stimulus requirement in this module. CURRICULUM-DERIVED from 16.1: banks exist to hide row-cycle latency, so a single-bank testbench is not a simplification of a DDR device — it is a different device.

18. Silicon Observability

What silicon showsWhat it says about the belief
command-bus occupancy high with low data throughput§9 — grants issued and dropped
a stall counter that reports only busy§10 — one stage, one counter
bandwidth that collapses as banks are enabled§13 rank 1 — the second bank
a design that passes a single-bank test and fails on hardware§9's identical one-bank behaviour
latency that improves when a requester is removed, unpredictablyno starvation bound; §6 exit 5
refresh-correlated errors with no refresh manager§13 rank 4 — an obligation with no owner

Row 1 is the diagnostic signature and it is cheap. DERIVED: a controller that arbitrates before masking issues commands it then drops, so the command bus is busy and the data bus is not — and the ratio between the two is measurable with two counters. CURRICULUM-DERIVED from 33.5 §6's command-efficiency measure: measure A is column commands over all commands, and this defect depresses it in a way no timing parameter explains.

Row 4 is the most expensive in schedule terms. DERIVED: §9's trace establishes that the two builds are identical on one bank, so a design carrying this defect passes its own unit test completely and fails at integration. CURRICULUM-DERIVED from 33.4 §6's configuration-reach item: the dimension is the bank count, it belongs to the environment's model rather than its stimulus, and no amount of traffic at one bank reaches it.

19. Quantitative Reasoning

QuantityTruthUnder the beliefGapProvenance
controller obligations131–210 missingSTRUCTURAL, 31.1 §5
arbitration's share7%100%93 pointsDERIVED
stages a request passes413CURRICULUM-DERIVED, 30.5 §3
illegal grants, 4 banks0 of 4112 of 4129% of the busDERIVED, ILLUSTRATIVE
illegal grants, 1 bank00noneDERIVED — the region
stall attributions available413DERIVED
expansion commands of 3814inexpressibleDERIVED
answers to why has it not issued4it's busyCURRICULUM-DERIVED

Sort by what kind of thing the belief gets wrong, as the three preceding chapters did, and this one is the first whose errors are not numeric.

KindRowsWhat the belief does
Denominatorobligations, share, stagesreplaces 13 with 1 and 4 with 1
Behaviourillegal grantsissues commands the device forbids
Expressibilityexpansion, attributionhas no place for the quantity

DERIVED: the third row is new to this module. 34.1 was wrong about magnitudes, 34.2 silent about quantities it did not name, 34.3 wrong by identification. Here a design built on the belief has no field for expansion and no second counter for attributionso the error cannot be corrected by changing a value, which is what makes a nature belief different from a quantity belief.

20. The Beliefs This One Generates

Downstream beliefWhy it followsWhere it is refuted
“one request is one command”if the controller selects among requests§10; 30.5 §3
“the arbiter checks timing”if the arbiter is the controller§9; 17.4 §3
“chosen means issued”if there is one stage§10's lost_after_grant
“assert the grant is one-hot and fair”if arbitration is the contract§15; 30.9 §6's variety 2
“a faster arbiter is a faster controller”if arbitration is the bottleneck33.5 §9's ceiling

The fourth row is the one that has already been documented as a defect in this curriculum, and the coincidence is not accidental. CURRICULUM-DERIVED from 30.5 §11 and 30.9 §6: variety 2 is does not name the key signal, and its recorded instance is every property about the grant's shape and none mentioning legal_mask. DERIVED: that assertion defect is this belief's direct consequence — a verification plan inherits its subject from the designer's model of the component, and a component believed to be an arbiter gets arbiter properties.

And the fifth row is worth a line because it misallocates optimisation effort. DERIVED: arbitration is 7% of the obligations and its latency contribution is a single pipeline stage, so a faster arbiter has a ceiling near zero — CURRICULUM-DERIVED from 33.5 §9: compute the affected fraction before proposing, and here the affected fraction is one cycle of a tRCD-bounded path.

21. Common Wrong Answers

  1. “The controller arbitrates between requests.” It does — at stage four of four. CURRICULUM-DERIVED from 30.5 §3: request, candidate, grant, committed. The claim is true and its scope is one stage.

  2. “So arbitration does not matter.” Inverted, and this is the over-correction. 17.4 owns the rotating pointer, starvation and the bounded bypass, and §20's third downstream belief — chosen means issued — is an arbitration-stage bug. Arbitration is one of thirteen, not zero of thirteen.

  3. “It is a queue and an arbiter plus some timing checks.” The word plus is doing the work. DERIVED: §8's census finds ten missing obligations, eight of them timing rules — and some timing checks is a schedule estimate of a week for the largest part of the design.

  4. “The arbiter can check timing while it selects.” CURRICULUM-DERIVED from 17.4 §3: that is the worse error, because an empty result “is just a loop that found nothing”. §9: 12 illegal grants of 41 and both empty results conflated.

  5. “We check legality downstream and drop illegal commands.” The bus slot is already gone. DERIVED: §9's weak build makes 41 grants and issues 29 commands — 29% of the command bus spent on grants that were never legal.

  6. “Our single-bank testbench passes.” §9: the two builds are identical on one bank. DERIVED: a single-bank test cannot distinguish them at any traffic level, which is why this defect reaches integration intact.

  7. “Real devices have eight banks but the logic generalises.” The logic does; the structure does not. DERIVED: with one bank, legality is a scalar and an arbiter can carry it. With eight, legality is a vector indexed by bank and the mask is a separate stage.

  8. “A read has waited 200 cycles because the memory is busy.” CURRICULUM-DERIVED from 30.5 §3: there are four answers — never legal, never preferred, granted and lost, or expansion. It's busy is what a one-stage design can say.

  9. “Committed commands cannot exceed requests.” §10: 38 committed against 52 requested with 14 more commands than grants, because activates and precharges have no requester. DERIVED: asserting that inequality encodes the belief and makes the correct design fail — §15's p_expansion_is_permitted_and_counted exists for exactly this.

  10. “Refresh is just another requester.” It is a self-originating obligation with a deadline. CURRICULUM-DERIVED from 17.3 and 34.2 §8: nothing requests it, and its deferral is bounded at eight. An arbiter's inputs are requests; this is not one.

  11. “Then model refresh as a high-priority requester.” That works until the bound. CURRICULUM-DERIVED from 17.3's illegal-versus-deprioritised distinction: a priority can always be outbid and a deadline cannot, so priority is the wrong type for the obligation.

  12. “We wrote the arbiter properties — one-hot, fair, no starvation.” All correct, and none names legal_mask. CURRICULUM-DERIVED from 30.9 §6's variety 2, whose recorded instance is exactly this set.

  13. “A faster arbiter means a faster controller.” Its affected fraction is one pipeline stage of a tRCD-bounded path. CURRICULUM-DERIVED from 33.5 §9: compute the ceiling first, and here it is near zero.

  14. “The block diagram shows one box, so it is one function.” A block diagram's grain is a clock domain. CURRICULUM-DERIVED from 29.2: controller and PHY may ship as one IP and address decode may sit in the interconnect, so the box boundary is a product decision.

  15. “Thirteen sounds arbitrary.” CURRICULUM-DERIVED from 31.1 §5: the destructive-read property creates them, so the count is a consequence of DRAM physics and an SRAM controller has almost none of them. That contrast is what makes the number checkable rather than stipulated.

  16. “We have all thirteen, so we are fine.” §16's M16: hard-wiring the present vector to all-ones makes every census complete. DERIVED: a census is only as good as its input, and the input needs an elaboration-time tie to the instantiated module list.

  17. “This is an architecture point, not an RTL one.” §9 produces 12 illegal grants of 41 in RTL. DERIVED: the belief is a structural claim and its consequence is a command the device forbids33.2's gate, reached from a belief rather than from a typo.

  18. “An experienced engineer would not make this mistake.” The three wrong first moves are 30.5 §2's, and the arbiter is the most natural first move — it is the only stage whose inputs all exist at the start. DERIVED: the belief is what a correct build order produces if it stops after step one.

  19. “Our controller works, so our model of it is right.” A working controller can be described by a wrong model. DERIVED: §14's rows 4, 5 and 6 — debug capability, verification plan, review rubric — are all downstream of the model rather than the implementation, and all three degrade while the design runs.

  20. “Counting obligations is bureaucracy.” CURRICULUM-DERIVED from 33.1 §5: it is the first review action, it takes a minute, and §17 finds four of six applications of this belief by counting alone.

  21. “We will discover the missing pieces as we build.” You will, and the schedule was priced for two of thirteen (§14 row 1). DERIVED: the estimate is correct for what it priced, which is why the overrun is attributed to complexity rather than to scope.

  22. “I know there is more to it than arbitration.” The test is §8's census: can you name the thirteen? DERIVED: there is more to it and here are the ten you have not instantiated are different states, and the second is what a review produces.

22. Self-Check

  1. Name the four stages a request passes through, and say which one arbitration is.
  2. How many controller obligations does the destructive-read property create, which chapter establishes the count, and why is it checkable rather than stipulated?
  3. State the five conditions under which the controller is simple arbitration is true, and name the one a real DRAM device makes impossible.
  4. A design arbitrates over pending requests and checks timing downstream. Give the measured cost on four banks, and say why a one-bank test cannot find it.
  5. Why is an empty result from a combined legality-and-policy block worse than from a masked one? Name the chapter that owns the finding.
  6. A read has waited 200 cycles. Give the four possible answers, and say what a one-stage design answers.
  7. Committed commands exceed requests. Is that a bug? Explain, and say what a property forbidding it would encode.
  8. A colleague proposes modelling refresh as a high-priority requester. Give the refutation and name the distinction it turns on.
  9. Four mutations survived this chapter's property set. Group them, and say what the bank count has to do with variety 12.
  10. M16 hard-wired the census input to all-ones. What does that make the instrument report, and what kind of check fixes it?
  11. Which assertion defect recorded in this curriculum is this belief's direct consequence? Name the variety and the chapter.
  12. §19's table has a row the three preceding chapters do not. Name it, and say why it makes a nature belief different from a quantity belief.

23. The Residual Risk

What this chapter cannot settle.

It cannot tell you how to build the other twelve. §8 names the obligations and §9 models one stage boundary. CURRICULUM-DERIVED from Module 17, which owns the scheduler, the queue, the refresh manager, arbitration and request handling across five chapters, and 23.4, which owns the heuristics: DERIVED: this chapter establishes the count and the shape of the omission, and every obligation's mechanism belongs elsewhere.

It cannot validate its own census input. M16 made every design complete by hard-wiring the vector. DERIVED: the fix is an elaboration-time tie between blocks_present and the instantiated module list, which is a build-system check — the fifth consecutive chapter in this module whose survivor needs an artifact rather than a property.

It cannot price the missing ten. §14's first row says a schedule was priced for two of thirteen and does not say what thirteen costs. DERIVED: the obligations are not equal — a tCCD counter is hours and a page policy with hysteresis is weeks (23.5) — so the census gives a count and not an estimate, and converting one to the other needs a plan this chapter does not have.

It cannot tell you which obligations a given device relaxes. §8's thirteen come from the destructive-read property, and a specific generation may fold, rename or bound them differently. CURRICULUM-DERIVED from 25.4, which owns DDR5's directed refresh management and on-die ECC: DERIVED: a device with on-die ECC changes what the controller must guarantee about data integrity without changing the count, so the census is a list of obligations rather than of blocks — and 34.6 is where that distinction becomes the whole subject.

And it cannot tell you where the boundary of the controller is. CURRICULUM-DERIVED from 19.1 §3, whose ownership table has six implementation-dependent rows and one strongly so, and 29.2, which records that address decode may sit in the interconnect. DERIVED: so a design may legitimately not instantiate an obligation because a neighbouring block owns it — and §8's census cannot distinguish that from an omission without the ownership table 33.1 demands.

24. Where This Goes

Four beliefs down. Three were about numbers and this one was about a component's nature.

Chapter 34.5 changes the object again: it is a belief about a process's lifetime. DERIVED: training happens only once is not a claim about a quantity or a structure but about when something occurs, and it is the first belief in this module whose refutation is a state machine.

And it inherits one specific finding from this chapter. DERIVED: §13 ranked refresh fourth among the exits and called it qualitatively different — a self-originating obligation with a deadline, which an arbiter cannot express. Training is the second such obligation, and it is worse: it originates in the PHY, it has no requester, and unlike refresh it is not periodic.

CURRICULUM-DERIVED from 19.5 §4, which owns the one-time versus continuous split and states what this chapter's successor is built on: a PHY becomes usable after establishing values that cannot be calculated, and some of those values stop being correct while the system is running. DERIVED: that sentence contains both the belief and its refutation, and the next chapter is about why readers of it hold the belief anyway.

Continue learning

Standards & specifications

Governing standard
JEDEC JESD79 (DDR SDRAM)(opens JEDEC Solid State Technology Association in a new tab)

Defines the DDR SDRAM device itself — signals, command encoding, mode registers, timing parameters and the initialisation sequence — one document per generation. Memory-controller microarchitecture, address-mapping policy, PHY training algorithms and board-level design are not specified by it.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the DDR curriculum.