Skip to content
VLSI Mentor

DDR · Module 7

Refresh (REF)

Refresh is the first command whose legality depends on more than one bank, and the first that occupies the device rather than requesting a transfer. DDR5's same-bank variant exists precisely to weaken that precondition.

Every command so far has been an accessactivate, read, write, precharge — requested on behalf of something that wanted data. Refresh is the first that is not.

Nobody asks for a refresh. Chapter 2.3 established why it is mandatory: a 1T1C cell loses its charge continuously, so its contents must be periodically restored or they are gone. The command exists to serve the memory, not the requester.

That changes two things about it, and both are new to this module:

Its legality depends on more than one bank. Every previous command's precondition concerned the single bank it named. Refresh's concerns several, which makes it the first command a controller cannot evaluate from one table entry.

And it occupies the device. A read requests a transfer; a refresh takes the device away for a while. So the chapter's question is:

What does a maintenance command require, what does it occupy, and why did DDR5 add a second version of it?

The last part has a precise answer, and it is a good one: the new version exists to weaken the precondition.

1. REF Requests Maintenance, Not a Transfer

Semantically, an all-bank refresh says: refresh the next set of rows that need it.

Three things are unusual about that sentence, and each one matters.

It carries no row address. The controller does not say which rows. The device tracks that itself — it maintains an internal counter of where it is in the refresh cycle and advances it each time. Chapter 2.3 established the obligation is over all rows within a retention window, and the device manages the sequencing.

It carries no column and moves no data. Nothing appears on DQ. This is the first command in the module with no data path involvement at all.

And it is not addressed to a bank in the all-bank form. Like an all-bank precharge, it applies device-wide.

2. The Precondition Spans Banks

Here is what makes refresh structurally new.

A refresh needs the array free to work on. Chapter 3.5 established that refreshing a row means sensing and restoring it — the same machinery an open row is occupying. So a bank with an open row cannot be refreshed while that row is open.

For an all-bank refresh, that means every bank must be idle. The precondition is not about the bank the command names, because it names none. It is about all of them.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   REF (all-bank)  requires  EVERY bank closed

Which is why all-bank precharge exists in the form it does. Chapter 7.4 §3 noted it exists so a device-wide event does not cost a command per bank — and refresh is the device-wide event it was built for. The two commands are a pair: PREA then REF.

And this is the first command in the module a controller cannot evaluate from one table entry. Every previous precondition was "is this bank open?". This one is "are all banks closed?" — a reduction over Chapter 5.2's whole table.

3. DDR5's Same-Bank Refresh Weakens the Precondition

Verified: DDR5 provides an all-bank refresh, available in DDR5 and earlier generations, and adds a same-bank refresh.

The same-bank refresh targets the same bank in all bank groups, designated by bank bits carried on the command/address interface.

And the stated benefit is precisely a weaker precondition: only one bank in each bank group needs to be idle before it can be issued. The remaining banks in each group do not have to be idle.

An all-bank refresh requires every bank in the device to be idle, which is a strong precondition and means all traffic must stop. A same-bank refresh, added in DDR5, targets the same bank across all bank groups and requires only that one bank in each bank group be idle, so the remaining banks can continue serving accesses. The value of the new command is the weaker precondition rather than a faster refresh operation.All-bank refreshevery generationEvery bank idlestrong preconditionAll traffic stopsdevice-wideSame-bank refreshDDR5 additionOne bank per group idleweaker preconditionOthers keep servingthe actual benefitneedsneedssoDDR5 adds12
Figure 1 — the new command's value is a weaker precondition, not a faster refresh.

Read the figure's right-hand column as the point. The refresh work itself is not obviously cheaper; what changes is how much of the device has to stop to allow it. Chapter 4.6 §3 made the same argument from the architecture side: as capacity grows, refresh occupies more time, so converting a device-wide stall into a partial one is where the benefit is.

And notice this is a command-legality improvement. The new command is not faster — it is legal in more situations, which is a genuinely different kind of optimisation and one worth recognising. Weakening a precondition is a performance technique.

4. RTL — Precondition and Occupancy

Engineering problem

Decide whether a refresh command is acceptable — which requires reducing over bank state rather than looking up one entry — and model the device being occupied afterwards, during which accesses cannot proceed.

Handle both scope shapes, so the difference in their preconditions is expressed rather than described.

Classification

SYNTHESIZABLE RTL — an educational precondition and occupancy model.

What it represents: the multi-bank precondition for both refresh scopes, acceptance or refusal with a reason, and device occupancy blocking subsequent accesses.

What it does not represent:

It does not schedule refreshes. Chapter 2.3's refresh_deadline_tracker already owns deadline tracking, and Module 15 owns the scheduling policy — how often, in what order, when to postpone. This block answers "may this refresh be issued now", not "should it be".

It does not store bank state, for Chapter 7.3 §4's reason: Chapter 5.2's table owns it and two models diverge silently.

And it does not model refresh timing. REF_OCCUPY_EVENTS is an educational cycle count. Real refresh durations are per-device and per-density and belong to Modules 13 to 15 and the device specification. Nothing here should be used to size anything.

Also absent: the internal refresh counter, the array, retention physics (Chapter 2.2), and temperature dependence (Chapter 2.3 §4).

Interface contract

ref_req with ref_same_bank and ref_bank_sel requests a refresh of one scope or the other. bank_open_in comes from Chapter 5.2's table. Acceptance, refusal reason, occupancy and access blocking come out.

State

The occupancy countdown. No bank state, no refresh counter.

Combinational behaviour

The precondition reduction — different for each scope.

Sequential behaviour

The occupancy countdown.

How to simulate

vlog ref_precondition_check.sv tb_ref_precondition_check.sv then vsim -c tb_ref_precondition_check -do "run -all".

Expected output

An all-bank refresh is refused while any bank is open and accepted once all are closed. A same-bank refresh is accepted while other banks remain open, provided the selected bank index is closed in every group. During occupancy, accesses are blocked.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// REFRESH PRECONDITION AND OCCUPANCY CHECK.
// Classification: SYNTHESIZABLE RTL -- an educational model.
//
// REFRESH IS THE FIRST COMMAND IN THIS MODULE WHOSE LEGALITY IS A
// REDUCTION OVER BANK STATE rather than a lookup of one entry:
//
//   all-bank refresh   requires EVERY bank closed
//   same-bank refresh  requires the SELECTED BANK INDEX closed in EVERY
//                      bank group; other banks may stay open
//
// VERIFIED (JEDEC / DDR5 material): DDR5 provides an all-bank refresh,
// available in DDR5 and earlier, and adds a SAME-BANK refresh that targets
// the same bank in all bank groups, designated by bank bits on the CA
// interface. Its stated benefit is that only one bank in each bank group
// need be idle -- the remaining banks do not.
//
// THE NEW COMMAND'S VALUE IS A WEAKER PRECONDITION, not a faster refresh.
// Weakening a precondition is a performance technique.
//
// WHAT THIS DOES NOT REPRESENT:
//   * refresh SCHEDULING -- Chapter 2.3's refresh_deadline_tracker owns
//     deadline tracking and Module 15 owns policy. This answers "MAY this
//     be issued now", never "SHOULD it be".
//   * bank state storage -- Chapter 5.2's table owns it; taken as input.
//   * refresh TIMING. REF_OCCUPY_EVENTS is an EDUCATIONAL cycle count.
//     Real durations are per-device and per-density (Modules 13-15 and the
//     device specification). DO NOT SIZE ANYTHING FROM IT.
//   * the device's internal refresh counter, the array, retention physics
//     (Chapter 2.2) and temperature dependence (Chapter 2.3).
// ─────────────────────────────────────────────────────────────────────────
module ref_precondition_check #(
  parameter int BANK_GROUPS     = 4,
  parameter int BANKS_PER_GROUP = 4,
  // Events the device is occupied after accepting a refresh. EDUCATIONAL.
  parameter int REF_OCCUPY_EVENTS = 6,

  parameter int TOTAL_BANKS = BANK_GROUPS * BANKS_PER_GROUP,
  parameter int BA_W  = (BANKS_PER_GROUP  <= 1) ? 1 : $clog2(BANKS_PER_GROUP),
  parameter int OCC_W = (REF_OCCUPY_EVENTS <= 1) ? 1 : $clog2(REF_OCCUPY_EVENTS + 1)
) (
  input  logic                   clk,
  input  logic                   rst_n,

  input  logic                   ref_req,
  // 0 = all-bank refresh (every generation).
  // 1 = same-bank refresh (DDR5 addition).
  input  logic                   ref_same_bank,
  // Which bank index within each group, for the same-bank scope. Ignored
  // for the all-bank scope, which names no bank at all.
  input  logic [BA_W-1:0]        ref_bank_sel,

  // ── Bank state from Chapter 5.2's table. Flattened as
  //    {group, bank_in_group}, matching Chapter 5.7's convention.
  input  logic [TOTAL_BANKS-1:0] bank_open_in,

  // ── An access the controller would like to issue, so occupancy can be
  //    shown to block it.
  input  logic                   access_req,

  output logic                   ref_accepted,
  // 0 none, 1 a bank is open that must be idle, 2 invalid bank select,
  // 3 device already occupied by a refresh.
  output logic [1:0]             ref_reject_reason,
  output logic                   ref_rejected,

  output logic                   device_busy,
  output logic [OCC_W-1:0]       busy_countdown,
  // An access was requested while a refresh occupies the device. Reported
  // rather than queued -- queueing is the scheduler's job (Module 17).
  output logic                   access_blocked
);

  // ── COMPILE-TIME legality.
  if (BANK_GROUPS < 1 || BANKS_PER_GROUP < 1) begin : g_geom
    initial $fatal(1, "ref_precondition_check: bank geometry must be >= 1");
  end
  // A zero-event occupancy would make a refresh free, which is the one
  // thing refresh is definitely not -- and it would hide the blocking
  // behaviour this block exists to show.
  if (REF_OCCUPY_EVENTS < 1) begin : g_occ
    initial $fatal(1, "ref_precondition_check: REF_OCCUPY_EVENTS must be >= 1");
  end

  localparam logic [1:0] RJ_NONE     = 2'd0;
  localparam logic [1:0] RJ_NOT_IDLE = 2'd1;
  localparam logic [1:0] RJ_BAD_SEL  = 2'd2;
  localparam logic [1:0] RJ_BUSY     = 2'd3;

  // ── Bank-select range check, using Chapter 5.1's generate pattern
  //    rather than a width cast that truncates for power-of-two counts.
  logic sel_bad;
  if (BANKS_PER_GROUP >= (1 << BA_W)) begin : g_sel_full
    assign sel_bad = 1'b0;
  end else begin : g_sel_chk
    assign sel_bad = ({1'b0, ref_bank_sel} >= (BA_W+1)'(BANKS_PER_GROUP));
  end

  // ── THE PRECONDITION. A REDUCTION over bank state, and the two scopes
  //    reduce differently -- which is the whole architectural difference
  //    between them.
  logic all_idle;
  logic same_bank_idle;

  always_comb begin
    // All-bank: every bank in the device must be closed.
    all_idle = (bank_open_in == '0);

    // Same-bank: the SELECTED INDEX must be closed in EVERY group, and
    // every other bank may remain open. Note the loop is over GROUPS and
    // the index is fixed -- that shape IS the command's semantics.
    same_bank_idle = 1'b1;
    for (int g = 0; g < BANK_GROUPS; g++) begin
      if (bank_open_in[g*BANKS_PER_GROUP + int'(ref_bank_sel)]) begin
        same_bank_idle = 1'b0;
      end
    end
  end

  logic precondition_met;
  assign precondition_met = ref_same_bank ? same_bank_idle : all_idle;

  always_comb begin
    ref_accepted      = 1'b0;
    ref_reject_reason = RJ_NONE;

    if (ref_req) begin
      if (device_busy) begin
        // A refresh while a refresh is in progress. Reported rather than
        // absorbed, because the controller has lost track of occupancy.
        ref_reject_reason = RJ_BUSY;
      end else if (ref_same_bank && sel_bad) begin
        ref_reject_reason = RJ_BAD_SEL;
      end else if (!precondition_met) begin
        // Chapter 7.1's question three: the encoding is fine, the
        // semantics are fine, and the STATE forbids it.
        ref_reject_reason = RJ_NOT_IDLE;
      end else begin
        ref_accepted = 1'b1;
      end
    end
  end

  assign ref_rejected   = ref_req && !ref_accepted;
  assign device_busy    = (busy_countdown != '0);
  assign access_blocked = access_req && device_busy;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      busy_countdown <= '0;
    end else if (ref_accepted) begin
      busy_countdown <= OCC_W'(REF_OCCUPY_EVENTS);
    end else if (busy_countdown != '0) begin
      busy_countdown <= busy_countdown - OCC_W'(1);
    end
  end

endmodule

Cycle-by-cycle example

BANK_GROUPS = 2, BANKS_PER_GROUP = 2 (four banks), REF_OCCUPY_EVENTS = 4. Bank state shown as {g1b1, g1b0, g0b1, g0b0}:

Cyclebank_open_inRequestResult
00100all-bank REFrefused — not all idle
10100same-bank REF, sel=0accepted — index 0 closed in both groups
20100accessblocked — device busy
50100busy clears
60100same-bank REF, sel=1refused — g1b1 is open
70000all-bank REFaccepted

Cycles 0 and 1 are the chapter in two rows. The same bank state refuses an all-bank refresh and accepts a same-bank one. Bank 1 of group 1 is open, which blocks the all-bank scope entirely and is irrelevant to a same-bank refresh of index 0.

That is the weaker precondition made concrete — and it is why DDR5 added the command.

Cycle 6 shows the same-bank scope's own limit. Index 1 is open in group 1, so a same-bank refresh of index 1 is refused. The precondition is weaker, not absent.

Cycle 2 is occupancy. The device is working and an access is blocked — the first command in this module to take the device away rather than use it.

Waveform expectation

§5. Watch the same bank state produce opposite answers for the two scopes.

Synthesis implication

An OR-reduction over the bank vector for the all-bank case, and a BANK_GROUPS-wide indexed reduction for the same-bank case, plus a counter. The same-bank reduction is the one that grows — it indexes the bank vector with a runtime value inside a loop over groups, which synthesises to a multiplexer per group. At realistic geometries it is small, and in a real controller it would likely be computed a cycle early since the refresh decision is not latency-critical.

Corner cases

BANKS_PER_GROUP == 1 makes BA_W == 1 through the guard, and the range check materialises — Chapter 5.1 §5's degenerate case again. BANK_GROUPS == 1 makes the same-bank reduction a single lookup, so the two scopes differ only in how many banks they require idle. REF_OCCUPY_EVENTS == 0 does not elaborate: a free refresh is the one thing refresh is not. A refresh requested while the device is busy is refused with its own reason rather than queued. An invalid bank select is refused rather than masked.

Debugging clues

If an all-bank refresh is accepted while banks are open, the reduction is testing the selected bank rather than the whole vector — check that all_idle compares against '0 and not against a single bit. If a same-bank refresh is refused when it should be accepted, check the flattening convention: the index must be group*BANKS_PER_GROUP + sel, and getting the two factors the wrong way round produces a check that looks plausible and tests the wrong banks. If accesses are not blocked during occupancy, device_busy is being derived from ref_accepted rather than from the countdown — which makes occupancy one cycle long regardless of the parameter.

Limitations

No schedulingChapter 2.3 and Module 15 own it. No bank state storage. No real timing. No internal refresh counter, so this block cannot say which rows were refreshed — and neither can a controller, which is §1's point. No temperature dependence. And the occupancy model blocks accesses uniformly, where a real same-bank refresh should leave non-target banks accessible — a stated simplification, and the one place this block under-models the command it is explaining.

5. Precondition and Occupancy in Cycles

ref_precondition_check — a refusal, a weaker-precondition acceptance, and occupancy

10 cycles
Ten cycles with four banks. With one bank open, an all-bank refresh is refused because its precondition requires every bank idle. With the identical bank state, a same-bank refresh of index zero is accepted because that index is closed in every bank group. The device is then occupied for four events, during which a requested access is blocked. A later same-bank refresh of index one is refused because that index is open in one group. Once all banks are closed an all-bank refresh is accepted.same state, opposite answerssame state, oppositeanswersdevice occupieddevice occupiedall-bank refusedall-bank refusedsame-bank acceptedsame-bank acceptedaccess blockedaccess blockedCKbank_open4444444000ref_reqsame_bankbank_sel--0--------1------ref_accepteddevice_busyaccess_blockedt0t1t2t3t4t5t6t7t8t9
Figure 2 — identical bank state, opposite answers for the two refresh scopes.

bank_open reads 4 — binary 0100, meaning bank 1 of group 1 is open — for the first seven cycles. It does not change across cycles 0 and 1.

Cycle 0 refuses and cycle 1 accepts, on identical state. The all-bank scope needs every bank idle and one is not. The same-bank scope of index 0 needs index 0 closed in both groups, and it is. That single pair of cycles is why DDR5 added the command.

Cycle 6 refuses a same-bank refresh of index 1, because index 1 is open in group 1. The precondition is weaker, not absent — and a controller that treats the same-bank form as unconditionally issuable will have refreshes refused.

Cycles 2 to 5 are occupancy. The device is working on the refresh and the access at cycle 2 is blocked. A refresh is the first command in this module that takes the device away rather than asking it for something.

Representative educational cycles. The four-event occupancy is a property of this model; real refresh durations are per-device and per-density.

6. Four Assertions Worth Writing

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// VERIFICATION-ONLY, inside ref_precondition_check.

// P1 -- THE ALL-BANK PRECONDITION. An all-bank refresh is never accepted
// while any bank is open. This is a REDUCTION over state, and it is the
// first precondition in this module that a single table lookup cannot
// evaluate.
property p_allbank_needs_all_idle;
  @(posedge clk) disable iff (!rst_n)
    (ref_accepted && !ref_same_bank) |-> (bank_open_in == '0);
endproperty
assert property (p_allbank_needs_all_idle);

// P2 -- THE SAME-BANK PRECONDITION, and the chapter's architectural point.
// The selected index must be closed in EVERY group -- and crucially,
// other banks may be open, so this must NOT be the all-bank check in
// disguise. Written per group with a genvar because the claim is about a
// set of entries, not one.
generate
  for (genvar g = 0; g < BANK_GROUPS; g++) begin : g_sb
    property p_selected_index_idle_in_group;
      @(posedge clk) disable iff (!rst_n)
        (ref_accepted && ref_same_bank)
          |-> !bank_open_in[g*BANKS_PER_GROUP + int'(ref_bank_sel)];
    endproperty
    assert property (p_selected_index_idle_in_group);
  end
endgenerate

// P3 -- THE WEAKER-PRECONDITION PROPERTY. A same-bank refresh CAN be
// accepted while some bank is open. Without this, an implementation that
// simply required all banks idle for both scopes would satisfy P1 and P2
// completely -- and would have thrown away the entire benefit of the new
// command while looking correct.
property p_samebank_can_accept_with_others_open;
  @(posedge clk) disable iff (!rst_n)
    (ref_req && ref_same_bank && same_bank_idle && !sel_bad && !device_busy)
      |-> ref_accepted;
endproperty
assert property (p_samebank_can_accept_with_others_open);

// P4 -- OCCUPANCY BLOCKS ACCESS. A refresh takes the device away, which is
// what distinguishes it from every other command in this module.
property p_busy_blocks_access;
  @(posedge clk) disable iff (!rst_n)
    (access_req && device_busy) |-> access_blocked;
endproperty
assert property (p_busy_blocks_access);

// And a refresh is not accepted while one is in progress.
property p_no_refresh_while_busy;
  @(posedge clk) disable iff (!rst_n)
    device_busy |-> !ref_accepted;
endproperty
assert property (p_no_refresh_while_busy);

P3 is the property that matters most here, and it is unusual in shape: it asserts that the design is permissive in a specific situation. P1 and P2 are both restrictive, and an implementation that required all banks idle for both scopes would pass both of them perfectly — while having discarded the entire reason DDR5 added the same-bank command.

That is a general pattern worth naming. When a feature's value is weakened restrictions, the restrictive properties cannot express it. You need a property that says "this must be allowed", or the feature can be silently optimised away by an implementation that is merely conservative. This is the same reasoning as the liveness companions elsewhere in the curriculum, applied to legality rather than to progress.

P2 uses a genvar because the claim is about a set of entries — the selected index across every group — and a scalar property cannot express a claim about a set.

What none of them prove. Nothing about refresh timing — the occupancy is an educational count, not a duration. Nothing about whether the refresh was needed, which is Chapter 2.3's deadline question and Module 15's scheduling one. Nothing about which rows were refreshed, since the device counts and this block does not model the counter. And nothing about retention — no assertion establishes that data survived, which is the only thing refresh actually exists to achieve.

7. DV — Refresh Is the Fourth Invisible State Change

Chapter 7.4 §3 listed three ways a bank's state changes without a command naming it. Refresh is the fourth, and it is the most awkward.

An all-bank refresh requires every bank closed and leaves them closed. So for a monitor, it is a confirmation rather than a change — it tells the monitor that every bank was idle, which is information it can use. Like all-bank precharge, it is a resynchronisation opportunity: after observing an accepted all-bank refresh, a monitor knows the state of every bank.

A same-bank refresh is harder. It requires a specific index idle across all groups and says nothing about the others. A monitor observing one learns something about some banks and nothing about the rest — a partial confirmation, which is a genuinely new shape.

And occupancy must be modelled. During a refresh the device will not accept accesses, so a monitor that does not track occupancy will report legal accesses as legal when the device is refusing them. Chapter 7.4 §4's monitor does not handle refresh — and its model_uncertain output exists partly for this: an unmodelled refresh is exactly the kind of event that should make a monitor admit it has lost track.

The practical consequence for a monitor's architecture is that refresh must be observed and modelled, not filtered out as uninteresting maintenance. It is the command most likely to be dismissed as noise and it carries real state information.

8. Debugging — Accesses Are Being Refused and the Bank State Looks Fine

Symptom. A controller issues a legal access — the bank is open, the row is right — and the device does not respond, or the access is refused. The controller's bank model shows nothing wrong.

"Bank state looks fine" is the clue, because it eliminates the whole Chapter 5.2 family of causes and points at something orthogonal to bank state.

Mechanism 1 — the device is occupied by a refresh. Inspect: whether a refresh was accepted recently and whether the controller models occupancy. Expected evidence: refusals clustered immediately after refresh commands. Discriminator: correlate refusals against refresh commands in the stream. This is first because it is one correlation and because occupancy is the most commonly unmodelled device state — bank state gets modelled carefully and occupancy often does not.

Mechanism 2 — a refresh was refused and the controller believed it succeeded. Inspect: whether the refresh's precondition was met when issued. Expected evidence: a refresh issued with banks open, so it was refused — and the controller then behaved as though the refresh obligation had been discharged. Discriminator: check bank state at the refresh, not at the failing access. Chapter 5.2 §10's lesson: find the first disagreement, not the first symptom. And the downstream consequence is worse than a refused access — an undischarged refresh obligation risks data loss.

Mechanism 3 — a same-bank refresh was assumed unconditional. Inspect: whether the selected bank index was idle in every group. Expected evidence: same-bank refreshes refused while the controller treated the weaker precondition as no precondition. Discriminator: check the selected index across all groups. §3's point that the precondition is weaker, not absent — and a controller ported to DDR5 that starts using the new command without checking its precondition produces exactly this.

Mechanism 4 — not refresh: the access was genuinely illegal. Inspect: whether an auto-precharge or all-bank precharge closed the bank invisibly. Expected evidence: the bank closed with no precharge command naming it. Discriminator: search for A10 on prior column commands, and for all-bank precharges. Chapter 7.4 §5's invisible state changes — and this mechanism makes the bank model wrong, so "bank state looks fine" was itself the mistaken premise.

Mechanism 5 — not the command path at all. Inspect: whether CKE was deasserted, or whether the interface had been reset without re-initialisation. Expected evidence: the command never reaching the device. Discriminator: was the device listening? Chapter 6.2 §4 established that a device not sampling does not see commands at all, and produces no error — so an access that was never received looks identical to one that was refused.

Discrimination, cheapest first. Correlate refusals against refresh commands — one comparison, and occupancy is the most likely and most commonly unmodelled cause. Then check bank state at each refresh command, which catches both mechanisms 2 and 3. Then search for invisible precharges. Then check whether the device was listening at all.

The reasoning lesson. Device state is more than bank state, and a controller that models banks meticulously often models nothing else. Occupancy, power state, initialisation state and configuration are all device state that gates command acceptance, and none of them appears in a bank table. So "the bank state is fine" narrows the search rather than ending it — and the productive move is to enumerate what else the device tracks, because that list is short and each entry has a distinctive correlation signature.

9. Common Misconceptions

"REFRESH is just a special READ." Wrong model: refresh reads rows to restore them, so it is a read with the data discarded. Why it is tempting: the mechanism genuinely involves sensing, which is what a read does, and Chapter 2.6 established that reading a row restores it. Consequence: expecting refresh to carry an address, to move data, to require an open row, or to be schedulable like an access. All four are wrong, and the last matters most: a refresh cannot be treated as another access in a queue because its precondition spans banks and it occupies the device. Correct model: refresh is maintenance requested by nobody. It carries no row — the device tracks which rows itself — no column, and moves no data. Its precondition is that banks be idle, which is the opposite of a read's requirement that a row be open. Prevention: ask who wanted it. If the answer is "nothing; the memory needs it", it is not an access.

"A refresh command says which rows to refresh." Wrong model: the controller specifies the target. Why it is tempting: every other command carries a target, and a controller that tracks bank and row state in detail seems like it would track this too. Consequence: looking for an address field that does not exist, and — more significantly — assuming the controller can know how far through the refresh cycle the device is. It cannot, which matters for postponement reasoning. Correct model: the controller owns when and the device owns which. The device maintains an internal counter and advances it per refresh. That division exists because the counter must not be losable — a controller-side counter that was lost would mean rows silently going unrefreshed. Prevention: ask what the command's operands are. An all-bank refresh has none, which is the answer.

"A same-bank refresh can be issued any time." Wrong model: the weaker precondition means no precondition. Why it is tempting: the whole point of the new command is that fewer banks need to be idle, which reads as "less restrictive" and slides into "unrestricted". Consequence: refreshes refused, and — if the controller assumes success — an undischarged refresh obligation, which risks data loss rather than merely performance. Correct model: verified — a same-bank refresh requires the selected bank index idle in every bank group. The remaining banks need not be. Weaker, not absent, and a controller must still check. Prevention: state the precondition explicitly for each scope. "Fewer banks" is not a precondition; "this index in every group" is.

"A refresh is free if the controller schedules it during idle time." Wrong model: a well-scheduled refresh costs nothing. Why it is tempting: it is nearly true for the scheduling problem, and Module 15 is largely about exploiting it. Consequence: omitting occupancy from a controller's model, which produces §8's first mechanism — accesses refused for reasons the controller cannot explain because it is not tracking that the device is busy. Correct model: a refresh occupies the device for a real interval during which accesses cannot proceed. Good scheduling reduces how often that interval collides with traffic; it does not make the interval disappear, and the controller must model it. Prevention: ask what the device is doing during the refresh. If the answer is "working", it is not available.

10. Interview Reasoning

"How is a refresh command different from an access?" Nobody asked for it. Every other command is issued on behalf of something that wants data; a refresh exists because the cells lose charge and must be periodically restored. Structurally that makes it different in three ways: it carries no row address, because the device tracks which rows itself with an internal counter; it carries no column and moves no data; and its precondition is that banks be idle, which is the opposite of a read's requirement that a row be open. It is also the first command that occupies the device rather than requesting a transfer, so during it accesses cannot proceed.

"Why does the device track which rows to refresh rather than the controller?" Because the counter must not be losable. A device has many thousands of rows per bank and the refresh obligation covers all of them within a retention window, so tracking it externally would mean the controller maintaining a counter per bank per device — and if that counter were ever lost or reset incorrectly, rows would silently go unrefreshed and data would be lost with no error. Putting it in the device also means the interface only has to carry "do the next one", so an all-bank refresh needs no operands at all. The cost is that the controller cannot know how far through the cycle the device is, which matters when reasoning about postponing refreshes.

"What is the point of DDR5's same-bank refresh?" A weaker precondition. An all-bank refresh requires every bank in the device to be idle, which means all traffic has to stop — and as capacity grows and the number of rows grows with it, that device-wide stall gets more expensive. A same-bank refresh targets the same bank index across all bank groups and requires only that one bank in each group be idle, so the remaining banks can keep serving accesses. The interesting thing architecturally is that this is a command-legality improvement rather than a speed improvement: the new command is not faster, it is legal in more situations. Weakening a precondition is a performance technique.

"Why is a refresh awkward for a verification monitor?" Because it changes or confirms bank state without naming banks, and because it introduces device occupancy which is not bank state at all. An all-bank refresh is actually helpful — it requires every bank idle and leaves them idle, so observing an accepted one tells a monitor the state of every bank, which makes it a resynchronisation opportunity like an all-bank precharge. A same-bank refresh is harder: it confirms something about the selected index across all groups and nothing about the others, which is a partial confirmation and a new shape. And occupancy has to be modelled separately, because a monitor that does not track it will report accesses as legal while the device is refusing them.

"A controller issues a legal access and the device does not respond, and the bank state looks correct. Where do you look?" The fact that bank state looks fine is useful, because it eliminates that whole family of causes and points at other device state. First correlate the refusals against refresh commands in the stream, because occupancy is the most commonly unmodelled device state — controllers model banks meticulously and often model nothing else. Then check bank state at each refresh command rather than at the failing access, because a refresh issued with banks open is refused, and if the controller believed it succeeded then the refresh obligation is undischarged, which risks data loss rather than just a refused access. Then look for invisible precharges — an auto-precharge operand or an all-bank precharge — because if one of those closed the bank then "bank state looks fine" was itself the mistaken premise. And it is worth confirming the device was listening at all, since a device with clock enable deasserted does not see commands and produces no error.

11. Engineering Exercise

Verified scope behaviour; educational cycle counts; no refresh timing values implied.

1. Four banks in two groups. Bank 1 of group 0 is open. Can an all-bank refresh be issued? No. The all-bank precondition is that every bank be idle, and one is not. A precharge must come first — and an all-bank precharge is the command built for exactly this, which is why the two form a pair.

2. Same state. Can a same-bank refresh of index 0 be issued? Yes. Index 0 is closed in both groups, and index 1 being open in group 0 is irrelevant to it. Identical bank state, opposite answer — which is the entire reason the command exists.

3. Same state. Can a same-bank refresh of index 1 be issued? No. Index 1 is open in group 0, and the precondition requires the selected index closed in every group. Weaker is not absent.

4. §6's P1 and P2 are both restrictive. Construct an implementation that passes both and has lost the feature. One that requires all banks idle for both scopes. It never accepts a refresh with a bank open, so P1 holds and P2 holds vacuously-strongly. It has discarded the whole benefit of the same-bank command while looking correct — which is why §6's P3 asserts that the design must be permissive in a specific case.

5. A controller issues a refresh, does not check whether it was accepted, and moves on. What is the worst consequence? Not a refused command — an undischarged refresh obligation. Chapter 2.3 established that cells lose charge continuously and that retention worsens with temperature, so a refresh that did not happen means rows approaching the end of their retention window with nothing restoring them. The consequence is data loss, and it appears later and elsewhere.

6. A monitor observes an accepted all-bank refresh. What does it learn, and how is a same-bank refresh different? From an all-bank refresh it learns that every bank was idle and remains so — a complete resynchronisation, like an all-bank precharge. From a same-bank refresh it learns only that the selected index was idle in every group, and nothing about the other banks. That partial confirmation is a shape a monitor must handle explicitly, and it is why Chapter 7.4's monitor declares uncertainty rather than modelling refresh at all.

12. Summary

Refresh is the first command in this module that nobody requested. Chapter 2.3 established the obligation: cells lose charge, so contents must be periodically restored. The command serves the memory, not the requester.

It carries no row, no column, and moves no data. The controller owns when and the device owns which — the device maintains an internal counter and advances it per refresh. That division exists because a controller-side counter would be losable, and a lost refresh counter means rows silently going unrefreshed.

Its precondition spans banks, which is new. Every earlier precondition concerned the single bank a command named. An all-bank refresh requires every bank closed — a reduction over Chapter 5.2's whole table, not a lookup — which is why all-bank precharge exists in the form it does. The two are a pair.

And it occupies the device. A read requests a transfer; a refresh takes the device away, and accesses cannot proceed during it. Occupancy is device state that is not bank state, and it is the one controllers most often fail to model.

Verified: DDR5 adds a same-bank refresh alongside the all-bank form. It targets the same bank index across all bank groups, and requires only one bank per group to be idle — the others may keep serving accesses. Its value is the weaker precondition, not a faster refresh: the new command is not quicker, it is legal in more situations. Weakening a precondition is a performance technique.

And weaker is not absent. A same-bank refresh still requires the selected index idle in every group, and a controller that treats it as unconditional will have refreshes refused — with the worst consequence being an undischarged refresh obligation, which is data loss rather than lost performance.

For verification, refresh is the fourth way bank state changes without a command naming a bank. An all-bank refresh is a resynchronisation opportunity; a same-bank refresh gives only a partial confirmation; and occupancy must be modelled separately or a monitor will report accesses as legal while the device refuses them.

13. What Comes Next

Chapter 7.6 takes a command with a property nothing in this module has had.

Every command so far changes state — a row opens, a bank closes, the device is occupied. A mode-register command changes how later commands are interpreted.

That makes it the only command whose effect is on the decoder rather than on the array, and it creates something the module has so far avoided: a decode-time dependency on command history. Chapter 7.2 §4's decoder was a pure function of the sampled values; after a mode-register write, it is not.

And Chapter 6.11 already showed a consequence — a mode register decides whether a pin carries the data mask or the bus-inversion flag. 7.6 explains the command that does that.

Return to The Refresh Requirement for why refresh is mandatory and how temperature affects it, Precharge for the command that satisfies refresh's precondition, Banks for the state the precondition reduces over, or DDR5 for the architectural argument the same-bank form serves. Module 15 owns refresh scheduling. The full path is on the DDR tutorials index.

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.