Skip to content
VLSI Mentor

DDR · Module 28

Refresh Issues

Three investigations wearing one name: a missed obligation, late service, or something else that merely correlates with refresh. Separating them is most of the work.

Chapter 28.2 debugged a rule between two commands. A violation there was local: one command, one predecessor, one shared resource, one named parameter.

Refresh has no such shape. A refresh obligation is a rate, not an event. The device requires a certain amount of refresh service over a window, and 15.3 owns the postponement and pull-in allowance that makes when negotiable within bounds.

So there is no single violating command. There is a ledger, a deadline, and a question that splits three ways before any investigation can start:

The questionWhat it is really asking
Obligation failuredid the device actually go unserved past its deadline?
Service failurewas the obligation met, but late, expensively, or unfairly?
Correlationis something else failing, and refresh merely happens to be nearby?

These are three different investigations, and the third is the one that consumes weeks. A failure that clusters near refresh is not a refresh failure — and the most common mistake in this chapter's subject is to read correlated with refresh as caused by retention.

1. An Obligation Is a Rate, Not an Event

The structural fact everything here depends on.

CURRICULUM-DERIVED from 15.3: the controller accrues an obligation at a specified average interval, and it may postpone service up to a bounded number of intervals or pull in service ahead of time, also bounded. Chapter 15.3 owns the credit ledger that tracks the resulting position and its bounds.

So three different things can be true at once, and conflating them is where refresh debugging goes wrong:

StatementMeaningIs it a failure?
A refresh is duean obligation has accruedno — this is normal
Service is urgentthe postponement allowance is nearly spentno — this is the ledger working
An obligation is overduethe allowance is spent and it is still unservedyes
The oldest obligation exceeded its deadlinethe device went unserved past its requirementyes, and differently

Rows three and four are not the same failure. Overdue is a statement about the controller's own ledger — it knows it is behind. A deadline miss is a statement about the device, and a controller can be overdue on its ledger while the device remains within specification if the ledger's bound is conservative.

That distinction is the first fork in the investigation, and it is measurable rather than a matter of judgement: compare the ledger's own state against the deadline arithmetic. §4 makes it a procedure.

2. Reading the Ledger as Evidence

Chapter 15.3 built the credit ledger. This chapter reads it, and the reading is not obvious because the ledger's healthy behaviour is to oscillate.

A ledger that never leaves zero is not a healthy ledger. It is a controller servicing eagerly at every opportunity, which 17.3's policy dial permits and which costs bandwidth. A ledger that swings across its full range and recovers is healthy. A ledger that swings to one bound and stays is the finding.

Ledger behaviourSupportsDoes not establish
Oscillates, returns to centrenormal operationthat deadlines were met — §4
Pinned at the postponement boundservice is starved — §10which resource is starving it
Pinned at the pull-in boundservice is over-eagerthat bandwidth is the problem
Monotonically driftingobligations accrue faster than they are retiredthe cause of the imbalance
Flat at zero with traffic runningthe ledger may not be accruinganything about the device

Row five is the dangerous one and it deserves the same suspicion 28.1 §4 gave an all-passing bitmap. A ledger reading zero with traffic running looks perfect. It is also exactly what a ledger that stopped accruing looks like — and a controller that never believes a refresh is due will never issue one.

The discriminating experiment is to stop issuing refresh and watch the ledger. If the ledger does not climb, it is not accruing, and every refresh decision downstream of it is being made on a constant. That is a control-path defect and no amount of retention analysis will find it.

3. Deadline Miss Versus Late Service

The first fork, as a procedure.

Four quantities localise it, and all four are available from telemetry:

QuantityWhat it tells you
Pending obligationshow many are outstanding right now
Oldest pending agehow long the oldest has waited
Maximum service delaythe worst age ever observed
Deadline misseshow many times the oldest exceeded its requirement

DERIVED under a stated ILLUSTRATIVE model — average interval T, postponement allowance 8 intervals:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   oldest_age (in intervals)   ledger state   device requirement
   -------------------------   ------------   ------------------
        0 .. 3                 due            met
        4 .. 7                 urgent         met
        8                      overdue        AT the bound
        9 and beyond           overdue        MISSED

   so: overdue begins at 8 and a MISS begins at 9.

   a controller reporting "overdue events: 412, deadline misses: 0"
   is a controller using its allowance -- which is what the allowance
   is for. That is a PERFORMANCE observation, not a correctness one.

   a controller reporting "overdue: 412, misses: 3" has three
   correctness events and 409 performance events, and the two need
   completely different investigations.

So the single most useful refresh telemetry field is the count of deadline misses, kept separately from the count of overdue events. A design that reports only overdue forces every performance excursion to be investigated as a correctness bug, and a design that reports only a combined refresh error count loses the distinction entirely.

And a miss count of zero does not clear refresh. It establishes that the obligation was met. §11 is about all the ways a refresh-adjacent failure survives that result.

4. A Deadline Miss, Cycle by Cycle

The ledger crossing its bound. ILLUSTRATIVE ages in interval units; 15.3 owns the verified allowance.

Overdue is a ledger state; a miss is a device statement

10 cycles
A ten-interval refresh trace. The clock runs throughout. A refresh obligation accrues at intervals one, three and six, shown as pulses on the accrue lane. The pending count rises from zero to one, then to two, then to three, and falls back to two at interval eight when a refresh command is finally issued. The oldest pending age lane tracks the age of the oldest unserved obligation, rising from zero at interval one through to five by interval nine. The ledger state lane shows the controller's own classification: due while the age is small, urgent once the postponement allowance is half spent at interval four, and overdue at interval seven when the allowance is exhausted. The deadline missed output rises at interval seven and stays high for the rest of the trace, because a miss is a latched historical fact rather than a current condition. A refresh command is issued at interval eight, which reduces the pending count from three to two, but the missed output does not fall, because issuing a refresh after a deadline has passed does not undo the fact that it passed. The first marker at interval four notes that the allowance is half spent and the state has become urgent, which is the ledger working as designed rather than a failure. The second marker at interval seven notes the allowance exhausted and the deadline missed. The third marker at interval eight notes the refresh being issued and explicitly that it does not undo the miss. The first phase spans intervals one to six and covers the period during which postponement was legal and nothing was wrong. The second phase spans intervals seven to nine and covers the period after the deadline was missed.postponement legalpostponement legalafter the missafter the missurgent: allowance half spent, not a faulturgent: allowance halfspent, not a faultallowance exhausted: deadline missedallowance exhausted:deadline missedREF issued — does not undo the missREF issued — does not undothe missCKaccruepending0112223322oldest_age0012345655stateOKDUEDUEDUEURGURGURGOVROVROVRref_cmdmisseddraint0t1t2t3t4t5t6t7t8t9

The missed output stays high after the refresh is issued, and that is the design decision worth defending. A miss is a historical fact about the device, not a current condition. A flag that cleared when service resumed would make a system that missed a deadline every window look identical to one that never missed — and a boot log sampled at the wrong instant would read clean.

drain rising at interval 7 is 17.3's requirement, and it is also a debugging hazard. The manager must drain in-flight traffic before issuing refresh. So the interval during which drain is high is an interval in which normal traffic is deliberately stopped — and §9's latency spikes live exactly there. A latency excursion coincident with drain is the refresh manager working, not a fault.

And note what the trace does not contain: any statement about charge. The ages, the states and the miss are all controller bookkeeping. Whether the device actually lost data is a separate question that this trace cannot answer, and §11 is about why that gap matters.

5. The Three-Way Split as a Flow

A sequence diagram with five participants showing how a refresh symptom is routed to one of three investigations. A symptom arrives at the triage step, which first reads the telemetry rather than forming a hypothesis. The telemetry block reports four separate numbers: the count of deadline misses, the count of overdue events, the maximum service delay observed, and whether the credit ledger is accruing at all. Triage sends the first question to the obligation investigation only when the deadline miss count is greater than zero, because a miss is a statement about the device going unserved past its requirement. Triage sends the second question to the service investigation when misses are zero but overdue events or the maximum service delay are large, because that is a performance and scheduling matter rather than a correctness one, and chapter seventeen point three owns the policy dial that governs it. Triage sends the third question to the correlation investigation when misses are zero and service is healthy but a failure still clusters near refresh activity, and this is the branch that must not be short-circuited into a retention verdict. The correlation branch is explicitly told to check the drain interval first, because the refresh manager deliberately stops normal traffic while draining, so latency excursions coincident with drain are the manager working rather than a fault. Finally the correlation branch reports back that a refresh-adjacent failure has at least four competing explanations and that increasing the refresh rate changes several variables at once, so it is an experiment rather than a fix.Which of the three refresh questions is this?SymptomTriageTelemetryObligationCorrelationfailure near refreshread beforehypothesisingmisses / overdue /max delay / accruingmisses > 0: devicewent unservedmisses 0, overduehigh: servicemisses 0, servicehealthycheck drain intervalfirstfour explanations,not a verdict

The second message is the whole method: read the telemetry before forming a hypothesis. A refresh symptom invites an immediate retention story, and the four numbers that would refute it cost one register read.

And the third branch loops back to itself before reporting, because the drain interval is the cheapest thing to exclude and the most commonly mistaken for a fault.

6. Latency Spikes

The most common refresh complaint, and usually not a bug.

CURRICULUM-DERIVED from 17.3: the manager drains in-flight traffic, issues refresh, and re-enables normal traffic at an occupancy boundary. During the drain and the refresh, normal traffic is stopped. Chapter 15.5 owns the availability arithmetic that quantifies the cost.

So a periodic latency spike at the refresh interval is expected behaviour, and the debugging question is whether its shape is right.

ObservationSupportsDiscriminating experiment
Spike height ≈ drain + refresh durationnormalnone needed — this is the design
Spike much taller than thatthe drain is not completing, or traffic resumes latemeasure drain duration separately from refresh duration
Spikes at the wrong periodthe interval is misconfigured, or the ledger is accruing wronglycompare accrual rate against the configured interval
Spikes irregulardeferred policy bunching several refreshes17.3's policy dial — expected under deferral
Spike with no refresh issuedthe drain ran and the refresh did notdrain high with ref_cmd low — a manager bug

Row five is a real defect with a clean signature and it is worth naming. A manager that drains, then fails to issue, pays the entire cost of refresh and retires nothing. The ledger keeps climbing while the system pays for service it never received — and the symptom is latency spikes plus a drifting ledger, which §2 row four already flagged.

Row four is the one that gets misreported as a bug. Chapter 17.3 owns the eager-versus-deferred dial, and deferral deliberately bunches service to protect bursts. Irregular spikes under a deferred policy are the policy working, and the correct response is to check what the dial is set to before investigating anything.

7. Starvation and Non-Recovery

Two failure shapes that look similar and are not.

Starvation: refresh cannot get service. The ledger pins at the postponement bound, deadline misses begin, and the cause is upstream — some traffic class or arbitration policy is never yielding.

Non-recovery: traffic never resumes after refresh. The refresh completes, the ledger retires, and normal traffic does not restart.

StarvationNon-recovery
Ledgerpinned at the bound, climbingretires normally
Deadline missesrisingzero
Trafficrunning, refresh starvedstopped
Cause lives inarbitration / policy — 17.4the occupancy boundary — 17.3
First thing to readwhat is winning arbitrationwhether drain ever deasserts

They are distinguished by one observation: is traffic running? Starvation is refresh losing to traffic; non-recovery is traffic never being let back in. The two have opposite fixes and identical top-level symptoms — "the system stalls around refresh".

Non-recovery has a specific failure mode worth stating. Chapter 17.3 owns an occupancy boundary at which normal traffic is re-enabled. If that boundary is never reached — because the condition depends on a queue that only fills when traffic runs — the manager deadlocks. The signature is drain permanently high with an empty queue, and it is a liveness bug rather than a refresh bug.

8. Who Owns the Obligation Right Now

Two mechanisms produce the same uncomfortable result: the controller's ledger is clean and part of the device went unserved. Both are about who owns the obligation, and neither appears anywhere in §3's four quantities.

Scope mismatch

Chapter 15.2 owns the mapping from command scope to controller bookkeeping, and its central point is that the two must match. A device-wide refresh retires one device-level obligation; a narrower-scope refresh retires one obligation for only the banks that command names, which requires one counter per bank.

So a mismatch between what the controller counts and what it issues is a silent starvation machine.

Controller countsCommands actually issuedResult
One device-level obligationdevice-widecorrect
One device-level obligationper-bankone counter retired per command; most banks never served
Per-bank countersdevice-wideall counters retired per command — correct, if the device agrees
Per-bank countersper-bank, wrong bank fieldthe named bank is served, a different counter is retired

Row two is the dangerous one and its signature is exactly the signature of health. The controller accrues one obligation per interval and retires one per command, so pending oscillates correctly, oldest_age stays small, deadline_misses stays zero — and if the commands are per-bank, each one served a single bank. With 16 banks, 15 of them are being refreshed at one-sixteenth of the required rate.

No aggregate counter can detect this, which is the point. §3's four quantities are all device-level, and the failure is a distribution problem underneath them.

The discriminating evidence is per-resource, and 15.2 already built the instrument — its scope-aware service ledger tracks service per resource rather than in aggregate. The debugging instruction is to read that ledger's per-bank ages, not to build a new one, and the signature is unmistakable: one bank's age near zero and fifteen climbing.

The self-refresh handover

The second mechanism is a transfer rather than a mismatch. While the device is in self-refresh, the device refreshes itself and the controller's obligation is suspended. Entry and exit are therefore handover points, and a handover has two failure modes.

FailureMechanismSignature
Ledger not suspended on entrythe controller keeps ageing obligations it no longer ownsa burst of misses recorded during self-refresh, when nothing was wrong
Ledger not resumed on exitaccrual stays off after the device hands backaccruing_seen is true from before, so §3's dead-ledger check passes — and nothing accrues
Exit without re-establishingsettings that self-refresh invalidated are used as-iscorruption at the first accesses after exit — 28.4

Row two is the cruel one. Chapter 15.5's accounting and §3's accruing_seen bit both report healthy, because the ledger did accrue — before the sleep. A system that enters self-refresh once and never resumes accrual looks identical to a healthy one on every device-level field.

So the check is a rate, not a flag: obligations accrued per unit of non-self-refresh time. A ledger that has accrued nothing since the last exit, while traffic runs, is the finding — and it is the same shape as §2 row five, one level down.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SIMULATION-ONLY. Shows why aggregate refresh telemetry cannot see a
// scope mismatch, by modelling per-bank service against a device-level
// ledger. It does NOT rebuild 15.2's scope-aware ledger -- it consumes
// the idea and demonstrates the blind spot.
//
// WHAT IT DOES NOT MODEL: the device, charge, or the command encoding.
// WHAT IT WOULD MISS: any starvation that is uniform across banks,
//   which an aggregate counter WOULD see -- this model is only
//   sensitive to the distribution.
class scope_mismatch_demo #(parameter int BANKS = 16);
  int  bank_age[BANKS];      // per-bank intervals since service
  int  device_pending;       // the aggregate ledger §11 publishes
  int  device_misses;

  function void new_interval();
    device_pending++;
    foreach (bank_age[b]) bank_age[b]++;
  endfunction

  // A device-wide command serves every bank and retires one aggregate
  // obligation. Both views agree.
  function void refresh_all_banks();
    if (device_pending > 0) device_pending--;
    foreach (bank_age[b]) bank_age[b] = 0;
  endfunction

  // A per-bank command serves ONE bank -- and a controller keeping a
  // single device-level counter retires the same one obligation it
  // would have retired for a device-wide command. That is the bug.
  function void refresh_one_bank(int b);
    if (device_pending > 0) device_pending--;
    if (b >= 0 && b < BANKS) bank_age[b] = 0;
  endfunction

  function int worst_bank_age();
    int w = 0;
    foreach (bank_age[b]) if (bank_age[b] > w) w = bank_age[b];
    return w;
  endfunction

  // The whole point: the aggregate view is clean and the distribution
  // is not. A test asserts these DISAGREE under a scope mismatch.
  function bit aggregate_looks_healthy(int allowance);
    return device_pending <= allowance;
  endfunction

  function bit distribution_is_starved(int deadline);
    return worst_bank_age() >= deadline;
  endfunction
endclass

The class exists to make one assertion possible: aggregate_looks_healthy() && distribution_is_starved(). That conjunction is impossible under matched scope and routine under a mismatch — and it is the only way to express a failure that every device-level number reports as health.

9. The Trap — Corruption That Correlates With Refresh

The third question, and the one this chapter exists to slow down.

Observation: data errors cluster near refresh activity. The instinct is retention: charge decayed, refresh came too late, data was lost.

That is one hypothesis out of at least four, and it is not the cheapest to test.

HypothesisMechanismEvidence that would support itEvidence that would refute it
A. Retentionthe cell genuinely lost charge before serviceerrors grow with oldest-age; worse at higher temperature; deadline misses non-zerodeadline misses are zero and errors do not track age
B. Drain / turnaroundthe traffic stopped and restarted around refresh, and the restart is wrongerrors are at the first access after drain falls, regardless of ageerrors are uniformly spread through the window
C. Timing interactiona rule involving refresh is violated — 28.2a timing violation snapshot with a refresh-adjacent rulethe snapshot is empty
D. Thermal coincidencerefresh bursts raise local temperature, and something else is temperature-sensitiveerrors track temperature, not refresh counttemperature held constant and errors persist

Hypothesis B is the one most often missed and it is usually cheapest to test. The refresh window is a traffic stop and restart. A bug in the restart — a stale pointer, a queue resumed in the wrong state, a turnaround timing case that only occurs after an idle gap — produces errors at the first access after every refresh, with no retention involvement at all.

And the discriminating observation is beautifully simple: where in the window are the errors?

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   bucket the failing accesses by their position in the refresh window

   (B) drain / restart          (A) retention
   ------------------           --------------
   |#                 |         |                #|
   |#                 |         |               ##|
   |##                |         |             ####|
   +------------------+         +-----------------+
    ^ first access               age ->     ^ oldest
      after drain falls                       cells

   B clusters at the START of the window, immediately after traffic
   resumes, and does not care how old the obligation was.
   A clusters at the END, and gets worse as oldest_age grows.

   the same top-level symptom -- "corruption correlated with refresh"
   -- produces opposite distributions, and one histogram separates
   them.

10. What Raising the Refresh Rate Actually Proves

The standard experiment, and the standard over-reading.

Raising the refresh rate changes at least four things simultaneously:

ChangedConsequence
Retention margincells are served sooner — the intended variable
Bus occupancyless bandwidth for traffic — 15.5
Drain frequencymore traffic stops and restarts per unit time — hypothesis B's exposure rises
Thermal profilemore refresh activity, different local heating

So "the failure went away when I doubled the refresh rate" supports hypothesis A — and it is also consistent with the others in ways that are easy to miss.

The counter-intuitive case is hypothesis B. More refresh means more drain-and-restart events, so a restart bug should get worse, not better. If the failure improves, that argues against B — which makes the experiment genuinely informative, just not in the direction people usually claim.

And there is a confound that makes the result weaker than it looks. Raising the refresh rate reduces available bandwidth, which reduces the traffic rate — and a failure whose probability scales with traffic will improve for that reason alone, with no refresh involvement at all.

To make the experiment discriminatingHold this constant
Separate retention from traffic ratethrottle traffic to the same delivered bandwidth in both runs
Separate retention from thermalhold case temperature and allow soak time
Separate retention from restart countcompare errors per drain event, not per unit time

The third row is the one that turns the experiment from suggestive into decisive. Errors per refresh window rather than errors per second removes the exposure-rate confound entirely: if doubling the refresh rate halves errors per second but leaves errors per window unchanged, the refresh rate changed the exposure and not the mechanism.

11. Temperature Band Changes

Chapter 15.4 owns the band-change problem that invalidates prior accounting, and it produces a debugging signature worth recognising on sight.

The refresh requirement is specified against an operating condition, and crossing into a different temperature band changes the requirement. So a system that was correct becomes incorrect without any configuration change — the configuration stopped matching the conditions.

SignatureSupports
Misses begin after sustained load, never at idlethe band changed under thermal load and the rate did not follow
Misses correlate with ambient temperaturesame
Misses begin immediately at a fixed ratea static misconfiguration, not a band change
The rate does change with temperature, and misses still occurthe band detection or its threshold is wrong

The distinguishing question is whether the refresh rate tracked the condition. Reading the configured interval alongside the temperature reading answers it, and it is a two-register check.

This is also the clearest example in the module of the second law. A miss reported at minute 40 of a soak test is not a minute-40 event: the causal error is the band crossing that happened earlier and was not acted on, and the telemetry that would show it is the interval-versus-temperature history, not the miss itself.

12. Refresh Telemetry

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// refresh_telemetry -- CLASSIFICATION: synthesisable, BINDABLE.
//
// WHAT IT DOES: keeps §3's four quantities, separates OVERDUE events
// from DEADLINE MISSES (§3 -- they route to different
// investigations), and buckets data errors by position in the refresh
// window so §9's two opposite distributions are distinguishable.
//
// WHY OVERDUE AND MISS ARE SEPARATE COUNTERS: 15.3 owns a postponement
// allowance, and USING it is not a fault. A design reporting one
// combined "refresh error" count forces every performance excursion to
// be investigated as a correctness bug.
//
// WHY THE MISS FLAG IS STICKY (§4): a miss is a historical fact about
// the device, not a current condition. A flag that cleared when
// service resumed would make a system missing every window look
// identical to one that never missed.
//
// WHY THE HISTOGRAM (§9): retention clusters errors at the END of the
// window and grows with age; a drain/restart bug clusters them at the
// START and does not care about age. One histogram separates the two
// hypotheses that a "correlated with refresh" symptom cannot.
//
// WHAT IT CANNOT TELL YOU: whether charge was lost. Every field is
// controller bookkeeping. 15.4 owns retention as a SPECIFICATION and
// nothing here measures a physical quantity.
//
// SYNTHESIS: counters, one small histogram array, no DUT handle.
// ---------------------------------------------------------------------
module refresh_telemetry #(
  parameter int MAX_PENDING  = 16,   // postponement allowance, ILLUSTRATIVE
  parameter int DEADLINE_AGE = 9,    // ages beyond this are MISSES -- §3
  parameter int BUCKETS      = 8,    // window-position histogram -- §9
  parameter int AGE_W        = 12
)(
  input  logic                        clk,
  input  logic                        rst_n,

  // ---- refresh manager interface (observed only)
  input  logic                        accrue,     // an obligation accrues
  input  logic                        ref_issued, // a refresh retires one
  input  logic                        drain,      // 17.3's drain window
  input  logic                        temp_band_changed, // §11

  // ---- data-path error strobe (§9's histogram input)
  input  logic                        err_valid,

  input  logic                        clear,

  // ---- published evidence
  output logic [$clog2(MAX_PENDING+1)-1:0] pending,
  output logic [AGE_W-1:0]                 oldest_age,
  output logic [AGE_W-1:0]                 max_delay,
  output logic [15:0]                      overdue_events,
  output logic [15:0]                      deadline_misses,
  output logic                             miss_sticky,
  output logic                             accruing_seen,
  output logic [15:0]                      drains,
  output logic [15:0]                      drains_without_issue,
  output logic [15:0]                      band_changes,
  output logic [15:0]                      err_bucket [BUCKETS]
);
  // $clog2(1) is 0, which makes the pending vector illegal as [-1:0].
  initial begin
    if (MAX_PENDING  < 2) $fatal(1, "refresh_telemetry: MAX_PENDING must be >= 2 (got %0d)", MAX_PENDING);
    if (BUCKETS      < 2) $fatal(1, "refresh_telemetry: BUCKETS must be >= 2");
    if (AGE_W        < 4) $fatal(1, "refresh_telemetry: AGE_W must be >= 4");
    // A deadline at or below the allowance would make every use of the
    // allowance a miss, collapsing §3's distinction the block exists
    // to preserve.
    if (DEADLINE_AGE < 2) $fatal(1, "refresh_telemetry: DEADLINE_AGE must be >= 2");
  end

  localparam logic [AGE_W-1:0] AGE_MAX = {AGE_W{1'b1}};

  logic                      drain_q;
  logic                      in_window;      // since drain fell
  logic [AGE_W-1:0]          since_drain;
  logic                      issued_this_drain;

  always_ff @(posedge clk) begin
    if (!rst_n || clear) begin
      pending              <= '0;
      oldest_age           <= '0;
      max_delay            <= '0;
      overdue_events       <= '0;
      deadline_misses      <= '0;
      miss_sticky          <= 1'b0;
      accruing_seen        <= 1'b0;
      drains               <= '0;
      drains_without_issue <= '0;
      band_changes         <= '0;
      drain_q              <= 1'b0;
      in_window            <= 1'b0;
      since_drain          <= '0;
      issued_this_drain    <= 1'b0;
      for (int b = 0; b < BUCKETS; b++) err_bucket[b] <= '0;
    end else begin
      drain_q <= drain;

      // §2 row five: a ledger that never accrues looks identical to a
      // healthy one at zero. Publishing "have we EVER seen an accrual"
      // makes a dead accrual path a one-bit check.
      if (accrue) accruing_seen <= 1'b1;

      // ---- pending, saturating at the allowance rather than wrapping
      if (accrue && !ref_issued && pending != MAX_PENDING[$clog2(MAX_PENDING+1)-1:0])
        pending <= pending + 1'b1;
      else if (ref_issued && !accrue && pending != '0)
        pending <= pending - 1'b1;

      // ---- oldest age. Ages while anything is pending; resets only
      // when the LAST pending obligation retires, because the age is
      // the oldest one's, not the most recent one's.
      if (ref_issued && pending <= 1) oldest_age <= '0;
      else if (pending != '0 && oldest_age != AGE_MAX)
        oldest_age <= oldest_age + 1'b1;

      if (oldest_age > max_delay) max_delay <= oldest_age;

      // ---- §3's two DIFFERENT counters. Overdue is a ledger state;
      // a miss is a device statement. Counting them together would
      // make 412 performance events and 3 correctness events into one
      // meaningless number.
      if (pending == MAX_PENDING[$clog2(MAX_PENDING+1)-1:0]
          && overdue_events != 16'hFFFF)
        overdue_events <= overdue_events + 1'b1;

      if (oldest_age >= DEADLINE_AGE[AGE_W-1:0]) begin
        if (deadline_misses != 16'hFFFF) deadline_misses <= deadline_misses + 1'b1;
        // §4: sticky. Issuing a refresh afterwards does not undo it.
        miss_sticky <= 1'b1;
      end

      // ---- §6 row five: a drain that runs without issuing pays the
      // whole cost and retires nothing.
      if (drain && !drain_q) begin
        if (drains != 16'hFFFF) drains <= drains + 1'b1;
        issued_this_drain <= 1'b0;
      end
      if (drain && ref_issued) issued_this_drain <= 1'b1;
      if (!drain && drain_q) begin
        if (!issued_this_drain && drains_without_issue != 16'hFFFF)
          drains_without_issue <= drains_without_issue + 1'b1;
        // window position restarts when traffic resumes
        in_window   <= 1'b1;
        since_drain <= '0;
      end

      if (in_window && since_drain != AGE_MAX) since_drain <= since_drain + 1'b1;

      // ---- §9's histogram: bucket errors by position since traffic
      // resumed. Bucket 0 is the first access after drain fell.
      if (err_valid && in_window) begin
        automatic int b = (since_drain >= BUCKETS) ? BUCKETS - 1 : int'(since_drain);
        if (err_bucket[b] != 16'hFFFF) err_bucket[b] <= err_bucket[b] + 1'b1;
      end

      if (temp_band_changed && band_changes != 16'hFFFF)
        band_changes <= band_changes + 1'b1;
    end
  end
endmodule

overdue_events and deadline_misses are two counters, and that is §3's argument in hardware. Using the postponement allowance is what the allowance is for. A single combined counter would make 412 performance events and 3 correctness events indistinguishable — and the 3 are the ones that matter.

accruing_seen is one bit that answers §2's most dangerous signature. A ledger reading zero is either healthy or dead, and a design with no accrual path reports a perfect refresh record forever. One sticky bit separates them.

drains_without_issue catches §6 row five. A manager that drains and fails to issue pays the full bandwidth cost of refresh and retires nothing, and the ledger climbs while the system appears to be doing refresh work. The counter makes it a query rather than a waveform hunt.

13. Ranking the Correlation Hypotheses

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// refresh_correlation_ranker -- SIMULATION-ONLY.
//
// Ranks §9's four hypotheses from §12's telemetry. It returns a LIST
// and an experiment, never a cause -- and it refuses to return a
// retention verdict at all, because nothing digital measures charge.
//
// WHAT IT DOES NOT MODEL: charge, temperature physics, the device.
//
// WHAT IT WOULD MISS: a mechanism whose window-position distribution
// matches another's. The histogram separates START-clustered from
// END-clustered; it does not separate all four hypotheses.
// ---------------------------------------------------------------------
typedef enum {
  HYP_RETENTION, HYP_DRAIN_RESTART, HYP_TIMING, HYP_THERMAL
} refresh_hyp_e;

class refresh_snapshot;
  int pending;
  int oldest_age;
  int max_delay;
  int overdue_events;
  int deadline_misses;
  bit miss_sticky;
  bit accruing_seen;
  int drains;
  int drains_without_issue;
  int band_changes;
  int err_bucket[];        // §9's histogram
  bit timing_snapshot_valid;   // from 28.2's snapshot
  bit temperature_controlled;  // was the run thermally held?
endclass


class refresh_correlation_ranker;

  // §9: START-clustered means the first buckets dominate; END-clustered
  // means the last do. Returns -1 when the histogram is empty, so "no
  // errors" is distinct from "uniformly spread".
  function int peak_bucket(refresh_snapshot s);
    int best = -1, bestv = 0;
    foreach (s.err_bucket[i])
      if (s.err_bucket[i] > bestv) begin bestv = s.err_bucket[i]; best = i; end
    return best;
  endfunction

  function int total_errors(refresh_snapshot s);
    int n = 0;
    foreach (s.err_bucket[i]) n += s.err_bucket[i];
    return n;
  endfunction

  // §2 row five, checked FIRST: a ledger that never accrued makes every
  // other refresh number meaningless, so no hypothesis is ranked until
  // it is excluded.
  function bit telemetry_trustworthy(refresh_snapshot s);
    return s.accruing_seen;
  endfunction

  function void rank(refresh_snapshot s, ref refresh_hyp_e out[$]);
    int pk = peak_bucket(s);
    int n  = s.err_bucket.size();
    out.delete();
    if (!telemetry_trustworthy(s)) return;   // §2: nothing is rankable

    // §6 row five and §7: a manager that drains without issuing is a
    // control defect, and its errors are restart errors.
    if (s.drains_without_issue > 0) out.push_back(HYP_DRAIN_RESTART);

    // §9: START-clustered -> drain/restart. Checked before retention
    // because it is cheaper to test and is the commonly missed one.
    if (pk >= 0 && pk < (n / 4)) out.push_back(HYP_DRAIN_RESTART);

    // 28.2's snapshot is non-empty: a refresh-adjacent timing rule
    // fired, and that is a digital fact rather than an inference.
    if (s.timing_snapshot_valid) out.push_back(HYP_TIMING);

    // §11: a band change with misses points at the rate not following
    // the condition.
    if (s.band_changes > 0 && !s.temperature_controlled)
      out.push_back(HYP_THERMAL);

    // §9 hypothesis A is offered ONLY when the obligation was actually
    // missed AND the errors cluster at the END of the window. Without
    // a miss, the device was served within its requirement and a
    // retention story has no supporting evidence.
    if (s.deadline_misses > 0 && pk >= (3 * n / 4))
      out.push_back(HYP_RETENTION);
  endfunction

  // §10's confound, made a method so a caller cannot forget it. Errors
  // per DRAIN EVENT removes the exposure-rate confound that errors per
  // second does not.
  function real errors_per_window(refresh_snapshot s);
    if (s.drains == 0) return -1.0;          // not "zero errors"
    return real'(total_errors(s)) / real'(s.drains);
  endfunction

  function string next_experiment(refresh_snapshot s);
    int pk = peak_bucket(s);
    int n  = s.err_bucket.size();
    if (!telemetry_trustworthy(s))
      return "the ledger never accrued: stop issuing refresh and confirm it climbs before anything else is investigated";
    if (s.drains_without_issue > 0)
      return "drains without an issue are non-zero: the manager pays for refresh and retires nothing -- read 17.3's issue path before any retention work";
    if (pk >= 0 && pk < (n / 4))
      return "errors cluster at the first accesses after drain: compare errors per drain event across two refresh rates; unchanged per-window => restart bug, not retention";
    if (s.deadline_misses == 0)
      return "no deadline was missed: the obligation was met, so a retention story has no supporting evidence -- bucket the errors and check the drain interval";
    if (pk >= (3 * n / 4))
      return "errors cluster at the end of the window with misses present: hold temperature and bandwidth constant, then compare errors per window across refresh rates";
    return "no distinguishing distribution: collect more windows before changing any configuration";
  endfunction

  // §9's boundary, enforced in code rather than in a comment.
  function string retention_verdict(refresh_snapshot s);
    return "unavailable: retention is a specified physical property (15.4). No controller telemetry measures charge, and a correlation with refresh position is consistent with at least four mechanisms (§9)";
  endfunction
endclass

rank() returns an empty list when the ledger never accrued, and that ordering is §2's argument made structural: if the telemetry is not trustworthy, no hypothesis about the device is rankable at all.

Retention is offered only when a deadline was actually missed and the errors cluster at the end of the window. Without a miss the obligation was met, and a retention story then has no supporting evidence — so the class declines to rank it rather than listing it last.

And errors_per_window() returns −1 when there were no drains. Zero errors over zero windows is not a rate, and returning 0.0 would let a run that never refreshed look like a clean one — the same three-valued discipline this curriculum has now needed in 27.3 §6, 27.4 §2, 28.1 §17 and 28.2 §16.

14. What the Assertions Prove

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Bound to §12's telemetry. Every property carries `disable iff`, and
// every antecedent is covered below -- 27.2 measured this curriculum
// at 78.53% implications, so a silent pass is otherwise
// indistinguishable from an unbound block.
module refresh_telemetry_sva #(
  parameter int MAX_PENDING = 16,
  parameter int DEADLINE_AGE = 9,
  parameter int AGE_W = 12
)(
  input logic clk, rst_n, clear,
  input logic accrue, ref_issued, drain, err_valid, temp_band_changed,
  input logic [$clog2(MAX_PENDING+1)-1:0] pending,
  input logic [AGE_W-1:0] oldest_age, max_delay,
  input logic [15:0] overdue_events, deadline_misses, drains, drains_without_issue,
  input logic miss_sticky, accruing_seen
);
  // ---- P1. FORBIDDEN. A miss flag never clears without an explicit
  // clear. §4: a miss is a historical fact, and a flag that cleared on
  // service would make a system missing every window look clean.
  property p_miss_sticky;
    @(posedge clk) disable iff (!rst_n)
      (miss_sticky && !clear) |=> miss_sticky;
  endproperty
  assert property (p_miss_sticky)
    else $error("telemetry: miss_sticky cleared without a clear");

  // ---- P2. FORBIDDEN. Issuing a refresh does not clear the miss.
  // This is P1's specific and most tempting violation.
  property p_issue_does_not_clear_miss;
    @(posedge clk) disable iff (!rst_n)
      (miss_sticky && ref_issued && !clear) |=> miss_sticky;
  endproperty
  assert property (p_issue_does_not_clear_miss)
    else $error("telemetry: a refresh issue cleared the miss flag");

  // ---- P3. A miss is recorded whenever the oldest age reaches the
  // deadline. Without this the flag could be sticky and never set.
  property p_miss_recorded;
    @(posedge clk) disable iff (!rst_n)
      (oldest_age >= DEADLINE_AGE) |=> miss_sticky;
  endproperty
  assert property (p_miss_recorded)
    else $error("telemetry: deadline age reached without recording a miss");

  // ---- P4. FORBIDDEN. Overdue and miss are DIFFERENT counters. An
  // overdue event must not increment the miss count. §3: 412
  // performance events and 3 correctness events must stay separable.
  property p_overdue_is_not_a_miss;
    @(posedge clk) disable iff (!rst_n)
      (pending == MAX_PENDING && oldest_age < DEADLINE_AGE)
        |=> (deadline_misses == $past(deadline_misses));
  endproperty
  assert property (p_overdue_is_not_a_miss)
    else $error("telemetry: an overdue event incremented the miss count");

  // ---- P5. INVARIANT. pending never exceeds the allowance. The
  // counter is sized for MAX_PENDING+1 values -- COUNT versus INDEX --
  // precisely so this cannot wrap silently.
  property p_pending_bounded;
    @(posedge clk) disable iff (!rst_n)
      (pending <= MAX_PENDING);
  endproperty
  assert property (p_pending_bounded)
    else $error("telemetry: pending %0d exceeded the allowance", pending);

  // ---- P6. max_delay is monotone within a run. A high-water mark
  // that fell would erase the worst case it exists to remember.
  property p_max_delay_monotone;
    @(posedge clk) disable iff (!rst_n)
      (!clear) |=> (max_delay >= $past(max_delay));
  endproperty
  assert property (p_max_delay_monotone)
    else $error("telemetry: max_delay decreased");

  // ---- P7. max_delay bounds the current age at all times.
  property p_max_delay_bounds_age;
    @(posedge clk) disable iff (!rst_n)
      (max_delay >= oldest_age) || (oldest_age == 0);
  endproperty
  assert property (p_max_delay_bounds_age)
    else $error("telemetry: oldest_age %0d exceeds max_delay %0d", oldest_age, max_delay);

  // ---- P8. FORBIDDEN. The age never advances with nothing pending.
  // An age that ticked on an empty ledger would manufacture misses.
  property p_no_age_without_pending;
    @(posedge clk) disable iff (!rst_n)
      (pending == '0) |=> (oldest_age <= $past(oldest_age));
  endproperty
  assert property (p_no_age_without_pending)
    else $error("telemetry: oldest_age advanced with nothing pending");

  // ---- P9. accruing_seen is sticky. §2 row five: it is the one bit
  // that separates a healthy ledger at zero from a dead one.
  property p_accruing_seen_sticky;
    @(posedge clk) disable iff (!rst_n)
      (accruing_seen && !clear) |=> accruing_seen;
  endproperty
  assert property (p_accruing_seen_sticky)
    else $error("telemetry: accruing_seen dropped");

  // ---- P10. An accrual sets it. Together with P9 this makes the bit
  // a faithful "has the ledger ever moved".
  property p_accrue_sets_seen;
    @(posedge clk) disable iff (!rst_n)
      accrue |=> accruing_seen;
  endproperty
  assert property (p_accrue_sets_seen)
    else $error("telemetry: an accrual did not set accruing_seen");

  // ---- P11. A drain that ends without an issue is counted. §6 row
  // five: the manager paid the whole cost and retired nothing.
  property p_drain_without_issue_counted;
    @(posedge clk) disable iff (!rst_n)
      ($fell(drain) && !$past(ref_issued) && drains_without_issue != 16'hFFFF)
        |-> (drains_without_issue >= $past(drains_without_issue));
  endproperty
  assert property (p_drain_without_issue_counted)
    else $error("telemetry: a drain without an issue was not counted");

  // ---- P12. INVARIANT. Counters saturate rather than wrap. A wrapped
  // miss count reading 2 after 65,538 misses would understate a
  // systemic failure catastrophically.
  property p_misses_saturate;
    @(posedge clk) disable iff (!rst_n)
      (deadline_misses == 16'hFFFF) |=> (deadline_misses == 16'hFFFF);
  endproperty
  assert property (p_misses_saturate)
    else $error("telemetry: deadline_misses wrapped past saturation");

  // ---- 27.2 §7: publish every antecedent.
  cover property (@(posedge clk) disable iff (!rst_n) accrue);
  cover property (@(posedge clk) disable iff (!rst_n) ref_issued);
  cover property (@(posedge clk) disable iff (!rst_n) drain);
  cover property (@(posedge clk) disable iff (!rst_n) $fell(drain));
  cover property (@(posedge clk) disable iff (!rst_n) err_valid);
  cover property (@(posedge clk) disable iff (!rst_n) clear);
  cover property (@(posedge clk) disable iff (!rst_n) temp_band_changed);
  cover property (@(posedge clk) disable iff (!rst_n) pending == MAX_PENDING);
  cover property (@(posedge clk) disable iff (!rst_n) miss_sticky);
  // P3's antecedent, published explicitly: without this cover, a run
  // that never reached the deadline age is indistinguishable from one
  // where P3 armed and held -- which is 27.2 §2's vacuity argument
  // applied to this property specifically.
  cover property (@(posedge clk) disable iff (!rst_n)
                  oldest_age >= DEADLINE_AGE);
  cover property (@(posedge clk) disable iff (!rst_n) !accruing_seen);
  cover property (@(posedge clk) disable iff (!rst_n) drains_without_issue != 16'd0);
  cover property (@(posedge clk) disable iff (!rst_n) overdue_events != 16'd0 && deadline_misses == 16'd0);
  cover property (@(posedge clk) disable iff (!rst_n) err_valid && drain);
endmodule

15. DV — Testing the Telemetry

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SIMULATION-ONLY. Independent reference: it replays the accrual and
// issue stream into a QUEUE of obligation timestamps and derives the
// age by inspecting the queue head, rather than maintaining an
// incremental age register. A different algorithm, so agreement is
// evidence -- 27.4 §15's argument.
class refresh_reference;
  int obligations[$];      // accrual cycle of each pending obligation
  int now;
  int max_delay_seen;
  int misses;
  int overdue;

  function void tick();
    now++;
    if (obligations.size() > 0) begin
      int age = now - obligations[0];
      if (age > max_delay_seen) max_delay_seen = age;
    end
  endfunction

  function void accrue();
    obligations.push_back(now);
  endfunction

  // A refresh retires the OLDEST obligation, which is what makes the
  // queue head the right place to read the age from.
  function void issue();
    if (obligations.size() > 0) void'(obligations.pop_front());
  endfunction

  function int oldest_age();
    if (obligations.size() == 0) return 0;
    return now - obligations[0];
  endfunction

  // §3: two different classifications from one age.
  function void classify(int allowance, int deadline);
    int a = oldest_age();
    if (obligations.size() >= allowance) overdue++;
    if (a >= deadline) misses++;
  endfunction

  function int pending(); return obligations.size(); endfunction
endclass
CheckWhat it establishes
Replay §4's trace; compare pending, oldest_age, max_delayQueue-based and register-based models agree
100,000 random accrue/issue streams through bothTwo algorithms agree on all four §3 quantities
Accrue 16 with no issue (MAX_PENDING = 16)pending saturates; P5
Hold oldest_age at DEADLINE_AGE − 1overdue_events rises, deadline_misses stays 0 — P4
Push oldest_age to DEADLINE_AGEmiss_sticky sets — P3
Issue a refresh after a missmiss_sticky stays set — P2
Assert clearFlag and counters release
Run with accrue tied lowaccruing_seen stays 0; ranker returns an empty hypothesis list
Drain with no issuedrains_without_issue rises — P11
Errors only at the first access after drain fallsHistogram peaks at bucket 0; ranker returns drain/restart first
Errors only at the end of the window, misses non-zeroRanker offers retention last
Errors at the end of the window, misses zeroRanker does not offer retention at all
errors_per_window() with zero drainsReturns −1.0, not 0.0
70,000 missesdeadline_misses saturates — P12
Run with the manager disconnectedAll 12 properties pass; all 13 covers empty

The two runs worth publishing are the ones that look like a clean refresh result and are not:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  TWO PASSING REFRESH REPORTS

  (A) the ledger never accrued
        accrue tied low; traffic running; no refresh ever issued
        pending                     0
        oldest_age                  0
        max_delay                   0
        overdue_events              0
        deadline_misses             0
        miss_sticky                 0
        accruing_seen               0     <-- the only non-clean field
        ------------------------------------------------
        all 12 properties           PASS
        cover accrue                0 hits
        cover ref_issued            0 hits
        cover pending == MAX        0 hits

        every refresh number is perfect and no refresh ever happened.
        A report reading "refresh: 0 misses, 0 overdue" is produced by
        this run AND by a flawless one, and the ONE field that
        separates them is a sticky bit nobody usually publishes.

        §13's ranker returns an EMPTY hypothesis list here, which is
        the correct behaviour: with untrustworthy telemetry, nothing
        about the device is rankable.

  (B) the obligation was met and data was still corrupted
        stimulus : full traffic, refresh healthy, errors observed
        deadline_misses             0
        overdue_events              91
        max_delay                   6      (deadline 9)
        drains                      4,118
        drains_without_issue        0
        err_bucket[0]               2,204  <-- first access after drain
        err_bucket[1..6]            0
        err_bucket[7]               3
        ------------------------------------------------
        all 12 properties           PASS
        all 13 covers               HIT

        the refresh obligation was met with three intervals of margin
        and 2,207 data errors occurred. 99.9% of them are at the FIRST
        access after traffic resumed.

        §9's histogram says drain/restart, not retention -- and the
        retention story, which is what "corruption correlated with
        refresh" usually becomes, is not merely unsupported here: the
        obligation was never missed, so it has no evidence at all.

    diagnosis : (A) is the vacuity case with a refresh face. (B) is
      §9's trap: the correlation is real, the mechanism is a traffic
      restart, and raising the refresh rate -- §10 -- would have made
      it WORSE while appearing to change something.

    the fix : (A) publish accruing_seen and read it first. (B) bucket
      the errors by window position before proposing any mechanism,
      and compare errors PER WINDOW across rates rather than per
      second.

16. Corner Cases

CaseBehaviourWhy
MAX_PENDING = 1Elaboration fails$clog2(1) is 0; the pending vector would be [-1:0]
DEADLINE_AGE < 2Elaboration failsevery use of the allowance would become a miss
Ledger at zero, traffic runningaccruing_seen distinguishes healthy from dead§2 row five
Overdue but not past the deadlineOverdue counts; misses do not§3, P4
Refresh issued after a missmiss_sticky stays set§4, P2
Age advancing with nothing pendingP8 firesit would manufacture misses
Drain with no issuedrains_without_issue rises§6 row five
Drain never deassertsNon-recovery, not starvation§7 — read whether traffic is running
Errors peaked at bucket 0Drain/restart ranked first§9
Errors at the end, zero missesRetention not offered§13 — no supporting evidence
Zero drainserrors_per_window() returns −1.0not a rate; 0.0 would read as clean
Temperature band change with missesThermal hypothesis offered§11
70,000 missesCount saturatesP12 — honest, imprecise
Refresh rate doubled, errors per second halvedNothing proved§10 — traffic rate fell too
Refresh rate doubled, errors per window unchangedExposure changed, mechanism did not§10's decisive form

Rows fourteen and fifteen are the pair this chapter exists for. The same experiment supports a retention conclusion or refutes it depending on which denominator you use, and per-second is the one people report.

17. Misconceptions

“A refresh is due, so something is wrong.” §1. Due is normal, urgent is the ledger working, overdue is a controller state, and only a deadline miss is a statement about the device.

“Overdue means a deadline was missed.” §3. Overdue is the postponement allowance being spent — which is what it is for. 412 overdue events with zero misses is a performance observation.

“Zero misses means refresh is fine.” §2, report (A). A ledger that never accrues reports zero forever. Publish whether it has ever moved.

“Corruption correlated with refresh is a retention problem.” §9. Four hypotheses, and a drain/restart bug is usually cheaper to test and clusters the errors at the opposite end of the window.

“Doubling the refresh rate fixed it.” §10. It changed retention margin, bandwidth, drain frequency and thermal profile at once — and reduced the traffic rate, which alone can improve a traffic-proportional failure.

“More refresh can only help.” §10. It increases the number of drain-and-restart events, so a restart bug gets more exposure, not less.

“Latency spikes at refresh are a bug.” §6. The manager stops traffic to drain. The question is whether the spike's height and period match the design, not whether it exists.

“Irregular refresh spacing is a scheduling bug.” §6 row four. Chapter 17.3's policy dial deliberately defers and bunches service. Read the dial before investigating.

“The system stalls around refresh, so refresh is starved.” §7. Starvation and non-recovery have identical top-level symptoms and opposite causes; whether traffic is running separates them.

“A miss flag should clear when service resumes.” §4, P2. Then a system missing every window looks identical to one that never missed.

“The telemetry proves the device retained its data.” §14's callout. Every field is controller bookkeeping. Nothing here measures charge.

18. Interview Reasoning

What is a refresh obligation? A rate, not an event. The device requires a certain amount of service over a window, and the controller may postpone or pull in within a bounded allowance.

Distinguish overdue from a deadline miss. Overdue is the controller's own ledger saying the allowance is spent. A miss is the device going unserved past its requirement. A design that counts them together makes correctness events invisible among performance events.

Refresh telemetry reads zero misses and zero overdue. Are you satisfied? Not until I know the ledger ever accrued. A dead accrual path produces exactly those numbers and never issues a refresh.

Data errors correlate with refresh. What do you do first? Bucket the failing accesses by their position in the refresh window. Retention clusters them at the end and grows with age; a drain/restart bug clusters them at the first access after traffic resumes.

Why is that histogram decisive? Because the two mechanisms produce opposite distributions from the same top-level symptom, and it costs one counter array rather than a thermal chamber.

You double the refresh rate and errors halve. What have you proved? Very little. You changed retention margin, bandwidth, drain count and thermal profile, and you reduced the traffic rate. Compare errors per refresh window instead.

What would make that experiment decisive? Holding delivered bandwidth and case temperature constant, and comparing errors per drain event. If per-window error count is unchanged, the rate changed exposure, not mechanism.

The system stalls around refresh. Starvation or non-recovery? Ask whether traffic is running. Starvation is refresh losing arbitration with traffic flowing; non-recovery is traffic never being re-enabled after the drain.

Misses begin 40 minutes into a soak test. Hypothesis? A temperature band change that the refresh rate did not follow. The causal event is the crossing, not the miss — read the configured interval alongside the temperature history.

Can you conclude retention from digital evidence? No. You can conclude that errors occurred, that they correlated with window position and with age, and that deadlines were missed. Retention is a specified physical property and nothing in a controller measures charge.

19. Exercises

  1. §3 places the miss boundary one interval past the allowance. Derive what changes if the deadline equals the allowance, and say which of §12's counters becomes useless.

  2. §9 gives two histogram shapes. Construct a third mechanism whose shape matches neither, and say what additional instrumentation would separate it.

  3. §10 lists four variables that change together. Design the smallest set of runs that isolates each, and state the total lab time at one hour per run.

  4. §12 makes the miss flag sticky and the overdue count free-running. Argue for making the overdue count sticky as well, then decide.

  5. §13's ranker refuses retention when misses are zero. Construct the case where that refusal is wrong, and say what evidence would have to be added for the ranker to handle it.

  6. §7 distinguishes starvation from non-recovery. Write the single telemetry field you would add to make the distinction automatic, and say why it is not already there.

  7. §11's band-change signature is "misses after sustained load". Design the test that distinguishes it from a static misconfiguration in one run rather than two.

  8. A colleague reports that disabling refresh entirely makes a corruption failure disappear. Identify every hypothesis that result is consistent with, and say what it proves about retention.

20. Where This Goes

A refresh problem is three investigations wearing one name. An obligation is a rate with a bounded allowance, so due and urgent and overdue are not failures and only a deadline miss is a statement about the device; a ledger reading zero is either healthy or dead and one sticky bit separates them; and corruption that correlates with refresh has at least four mechanisms whose window-position distributions differ.

Four results carry forward. Overdue and miss are different counters, and merging them buries three correctness events under four hundred performance ones. A miss flag must be sticky, because service resuming does not undo a deadline that passed. The window-position histogram separates a restart bug from retention at the cost of one counter array. And errors per window, not per second, is what makes a refresh-rate experiment decisive rather than suggestive.

Two things stay open. Nothing here measures charge, so the strongest available statement is a correlation plus a miss count — and §13 encodes that as a refusal rather than a caveat. And the deadline itself arrives as a parameter through 28.2 §11's provenance chain, so every property in §14 is satisfied by a faithfully recorded miss against a wrong deadline.

Chapter 28.4 takes the next family, and it shares this chapter's most uncomfortable feature. Refresh telemetry can report a clean obligation while data is corrupt; calibration can report success while producing a result that fails at runtime — and 28.1 already depended on exactly that, because its hypothesis C took a prerequisite step's committed flag on trust. The question splits five ways: whether calibration failed to start, failed to converge, failed to commit, became stale, or committed a result that was never robust.

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.