Skip to content
VLSI Mentor

DDR · Module 11

Write Recovery (tWR)

The bus is free, the controller owes nothing, and the bank still cannot be closed. A precharge issued too early does not delay the write — it interferes with data still being driven into cells.

Chapter 11.3 ended with every beat delivered, the bus released, and the controller owing nothing on any wire.

And the write is not finished.

This chapter is about the interval that remains, and it is the largest structural difference between a read transaction and a write one. A read ends when its last beat is received — the data is in the controller and nothing further is owed by anyone. A write's last beat discharges the controller's obligation and leaves the device's untouched.

So the question is:

Why can the data burst be over while the bank still cannot be precharged?

The answer is mechanical rather than conventional, and deriving it makes the parameter that follows obvious instead of arbitrary.

1. What the Last Beat Left Undone

Follow the data one step further than Chapter 11.3 did.

The final beat crosses the interface. The device has it — at its input. From there it must travel the column path into the bank's sense-amplifier structure, and it must change what that structure holds for the selected columns.

That structure is the open row. Chapter 9.2 §1 established what "open" means: the sense amplifiers are in a resolved state holding the row's values. A write does not put data somewhere else — it overwrites part of what those amplifiers are holding.

And the row is still connected to its cells. Chapter 9.1 §2 established that the wordline is asserted for the whole time a row is open, and that the amplifiers drive their resolved values back into the cells — that is restoration, and it does not stop when the activate finishes. A row that is open is a row whose cells are being held at the amplifiers' values.

So a write has to propagate twice:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   interface → column path → sense amplifiers      (the new value is held)

   sense amplifiers → cells, through the wordline  (the new value is stored)

The second arrow takes time, and it is not finished when the last beat arrives. It has barely started.

2. The Mechanism — Why a Precharge Interferes

Now the part that makes recovery a necessity rather than a courtesy.

Chapter 9.1 §4 established what a precharge does: the wordline is deasserted, disconnecting the row's cells from the bitlines, and the bitline pair is returned toward the balanced condition sensing requires.

Both halves are destructive to a write in progress.

Deasserting the wordline disconnects the cells — and the cells are precisely what the new value has not yet finished being driven into. Cut that connection early and the cells hold whatever charge they had reached at that moment: not the old value, not reliably the new one.

Equalising the bitlines removes the driving values themselves. The amplifiers' hold on the new data is what was doing the writing; returning the bitlines to balance ends it.

That is the mechanism, and it explains the shape of the requirement without a single number: the interval exists to let the new values finish being driven into cells before the connection carrying them is removed.

3. Five Events, Not Two

The module has been accumulating events. Here they are together, because the recovery interval is defined against one of them specifically and the wrong choice is common.

#EventWhoWhat it discharges
1Request admittedcontrollernothing — it incurs a debt (11.1)
2Command acceptedDRAMnothing — the data has not moved (7.3)
3Data launchedcontrollerthe deadline is met (11.2)
4Final beat deliveredcontrollerthe payload debt; the bus is free (11.3)
5Recovery satisfieddevicethe bank may be closed — this chapter

Five events, and only the last two are close together in the way people assume.

And note who owes what. Events 1 through 4 are the controller's obligations. Event 5 is the device's — the controller owes only patience, and it cannot observe the obligation being discharged. §5 is about representing something you do not own and cannot see.

4. Naming the Parameter

Only now, with the mechanism established, is the terminology useful.

The interval is conventionally called write recovery time, written tWR, and it constrains the gap between the end of a write's data and a precharge of that bank.

What it is not:

It is not CWL. Chapter 11.2 §4 separated them: write latency is a deadline the controller must meet at the start of the data; recovery is an obligation the device holds after the end of it. They sit at opposite ends of the transaction and are owed by different parties.

It is not a measure of how long the write "takes." It is the interval after the data during which closing the row would interfere.

It is not a delay the controller inserts for politeness. It is a correctness requirement, and violating it corrupts data rather than degrading performance.

5. RTL — Representing an Obligation You Do Not Own

The engineering problem

From the end of a write's data, hold a blocking condition for a configured interval, expose it to whatever would issue a precharge, and report a precharge attempted while it is held — because the device will not report one.

Why hardware needs it

The device does not signal that recovery is outstanding and does not refuse a precharge that violates it. The controller's representation is the only thing standing between a correct design and silent corruption, which makes this the highest-consequence small block in the module.

Classification

SYNTHESIZABLE EDUCATIONAL CONTROLLER RTL. A controller timing-state abstraction.

What it models

A recovery obligation started by a modelled data-completion event; its remaining duration; the blocking condition it implies; and detection of a precharge attempted during it.

What it does NOT model

The device or any physical process. Other timing constraints (Modules 13, 14) — !precharge_blocked means this obligation is discharged, never that a precharge is legal. Bank state (Chapter 9.1). Multiple banks — one bank's obligation; a real controller instantiates per bank. Scheduling. Auto-precharge's internal handling, which §6 discusses and which the device performs itself.

Interface and parameter contract

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// write_recovery_guard
//
// Classification: SYNTHESIZABLE EDUCATIONAL CONTROLLER RTL.
//
// RECOVERY_EVENTS IS AN EDUCATIONAL MODEL PARAMETER. IT IS NOT A JEDEC tWR
// VALUE. A real controller derives its count from the configured write
// recovery for the operating point (Chapter 11.4 Section 4).
//
// MODELS: a recovery obligation started by a MODELLED DATA-COMPLETION
// EVENT, its remaining duration, the blocking condition it implies, and
// detection of a precharge attempted during it.
//
// MODELS NO PHYSICAL OR ANALOG BEHAVIOUR. Nothing here represents a cell,
// a bitline, a sense amplifier or a charge. It models the CONTROLLER'S
// BELIEF about an obligation the DEVICE holds -- and the device neither
// signals that obligation nor refuses a precharge that violates it.
//
// IS NOT A TIMING ENGINE. It enforces ONE interval against ONE operation.
// !precharge_blocked means THIS obligation is discharged, NEVER that a
// precharge is legal -- Modules 13 and 14 own the rest.
//
// ONE BANK. A real controller instantiates this per bank.
//
// BLOCKS, DOES NOT SCHEDULE (Module 17).
// ─────────────────────────────────────────────────────────────────────────
module write_recovery_guard #(
  // EDUCATIONAL. See the header. Zero is legal and models a configuration
  // with no recovery requirement -- which no real DDR device has, and
  // which exists here so the degenerate case is structural rather than a
  // special case in a consumer.
  parameter int RECOVERY_EVENTS = 6,
  parameter int CNT_W = (RECOVERY_EVENTS <= 1) ? 1 : $clog2(RECOVERY_EVENTS + 1)
) (
  input  logic             clk,
  input  logic             rst_n,

  // ── From Chapter 11.3's sequencer: burst_done. THE END OF THE DATA,
  //    which Section 3 establishes is the event recovery is measured from
  //    -- not the command.
  input  logic             data_done,

  // ── A precharge somebody wants to issue to this bank.
  input  logic             precharge_req,

  output logic             recovery_active,
  output logic [CNT_W-1:0] recovery_remaining,
  // One-cycle pulse as the obligation is discharged.
  output logic             recovery_done,

  // ── The condition a consumer must respect.
  output logic             precharge_blocked,

  // ── A precharge was requested while the obligation was outstanding. The
  //    DEVICE WOULD NOT REPORT THIS -- it would accept the command and
  //    corrupt the row. Reported here because this is the only place the
  //    violation is still visible.
  output logic             precharge_violation
);

  if (RECOVERY_EVENTS < 0) begin : g_re
    initial $fatal(1, "write_recovery_guard: RECOVERY_EVENTS must be >= 0");
  end

  logic [CNT_W-1:0] cnt_q;
  logic             active_q;

  assign recovery_active    = active_q;
  assign recovery_remaining = cnt_q;
  assign precharge_blocked  = active_q;
  assign precharge_violation = precharge_req && active_q;

  // ── The degenerate configuration. With no recovery requirement the
  //    obligation is discharged in the cycle it is incurred, so nothing is
  //    ever blocked. Structural rather than a runtime branch, because a
  //    zero-length count has no meaningful sequential behaviour.
  if (RECOVERY_EVENTS == 0) begin : g_zero

    assign recovery_done = data_done;

    always_ff @(posedge clk) begin
      if (!rst_n) begin
        cnt_q    <= '0;
        active_q <= 1'b0;
      end else begin
        cnt_q    <= '0;
        active_q <= 1'b0;
      end
    end

  end else begin : g_count

    // Discharged when the last event of the interval elapses.
    assign recovery_done = active_q && (cnt_q == CNT_W'(1));

    always_ff @(posedge clk) begin
      if (!rst_n) begin
        cnt_q    <= '0;
        active_q <= 1'b0;
      end else if (data_done) begin
        // A new write's data completing RESTARTS the obligation, whatever
        // was outstanding. Deliberate: a second write to the same bank
        // creates a fresh requirement, and taking the later of the two is
        // the only safe interpretation.
        cnt_q    <= CNT_W'(RECOVERY_EVENTS);
        active_q <= 1'b1;
      end else if (active_q) begin
        if (cnt_q <= CNT_W'(1)) begin
          cnt_q    <= '0;
          active_q <= 1'b0;
        end else begin
          cnt_q <= cnt_q - CNT_W'(1);
        end
      end
    end

  end

endmodule

State representation and transitions

A countdown and an active flag.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   idle      --data_done-->              active(RECOVERY_EVENTS)
   active(n) --n > 1-->                  active(n-1)
   active(1) --recovery_done-->          idle

data_done while already active restarts the count, and that is a correctness decision rather than a convenience: a second write's data completing creates a fresh obligation, and the later deadline is the only safe one. Taking the earlier would discharge a requirement that is still outstanding.

Combinational behaviour

Four direct outputs and one comparison. precharge_blocked is active_q directly — deliberately not a computed condition, so there is no logic between the obligation and the block on it.

Sequential behaviour and reset

Nonblocking only. Reset clears the obligation, and this is the block's most dangerous behaviour: the device's obligation is not cleared by the controller's reset. A controller coming out of reset with a write recently completed will not block a precharge that the device still requires. §9's mechanism 5, and a real reason controllers do not issue precharges immediately after reset without first establishing a known state.

Cycle-by-cycle trace

RECOVERY_EVENTS = 4:

Cycledata_doneprecharge_reqrecovery_remainingblockedrecovery_doneviolation
0000000
1104100
2013101
3002100
4001110
5010000

Cycle 2 is the violation. A precharge was requested with the obligation outstanding. On hardware this command would be accepted — the device does not refuse it — and the row would be closed mid-write. This output is the only place that failure is visible.

Cycle 5 is the same request, permitted. Nothing about the request changed; the obligation elapsed.

How to simulate, and expected output

Drive the trace and check all six outputs. Then:

precharge_req on every cycle of the interval, including the first and the last. The last is the one worth directing: a block that clears active_q one cycle early permits a precharge one cycle too soon, and that is an off-by-one whose consequence is corruption.

RECOVERY_EVENTS = 0 — nothing ever blocks, recovery_done tracks data_done. Exercises the g_zero arm. Note in the report that no real DDR device has this configuration; it exists so a consumer need not special-case it.

RECOVERY_EVENTS = 1 — the obligation lasts one event. This is where the cnt_q <= CNT_W'(1) boundary is exercised and where an off-by-one in the countdown shows up immediately.

data_done during an active obligation must restart the count to the full value, not extend it by one or ignore it.

Reset mid-obligation must clear it — and the test should assert that this is observed, because the model's willingness to forget is the hazard, not a bug.

Expected waveform

§7, which shows the obligation blocking and then releasing the same request.

Synthesis implications

A CNT_W counter and a flag — under six flops at the defaults, per bank. At sixteen banks that is under a hundred flops to prevent a silent data-corruption mode, which is the cheapest correctness mechanism in the module.

Corner cases

RECOVERY_EVENTS == 0 is legal and structural, and does not describe any real device. RECOVERY_EVENTS == 1 exercises the countdown boundary. Negative does not elaborate. CNT_W is sized with +1 so the counter can hold RECOVERY_EVENTS itself. Simultaneous data_done and the count reaching one resolves to a restart, because data_done is tested first — the safe interpretation.

Failure modes and debugging clues

precharge_violation asserting at all is a design defect in the consumer, not in this block. recovery_active never clearing means data_done is pulsing continuously — check that Chapter 11.3's burst_done is a pulse and not a level. precharge_blocked low immediately after a write means the count was configured at zero or the obligation was started from the wrong event — §9's mechanism 1.

Extension ideas

Per-bank instantiation is the first real step, and it is what a controller needs. Starting the obligation from an auto-precharge's internal data completion — where the controller must model an event it never issued a command for — is the natural extension and is §6's subject.

Limitations

One bank, one interval, one blocked operation. It cannot see the device, so it enforces a belief. It does not know whether a precharge is otherwise legal!precharge_blocked is a necessary condition and not a sufficient one, and a consumer treating it as permission has imported Modules 13 and 14' entire subject into one signal.

6. Auto-Precharge Does Not Escape This

Chapter 7.2 §3 established auto-precharge as a flag on a column command, and Chapter 7.3 §5 established that it makes completion invisible — a bank closes with no precharge command on the interface.

The obvious question is whether a write with auto-precharge bypasses recovery. It does not.

The requirement is physical, not procedural. §2 derived it from what a precharge does to a row whose new values are still being driven into cells, and that is equally true whether the precharge was commanded explicitly or requested by a flag. A device that closed the row immediately on receiving the last beat would corrupt the write it had just been given.

So the device honours the obligation internally. The flag requests automatic closure; the closure happens when it is safe to happen, not when the command was issued.

What this chapter does not do is state when exactly the device begins its internal closure or how the interval is specified for the auto-precharge case. That is generation-specific, it is defined in the device's documentation, and Modules 13 and 14 own the timing system it belongs to. The architectural point stands without it: the flag changes who issues the closure, not whether the data must be safe first.

7. Recovery, in Cycles

write_recovery_guard — an obligation the controller does not own

10 cycles
Ten cycles with an educational recovery interval of four events. A write burst delivers its final beat at cycle one, asserting data-done, which starts the recovery obligation with four events remaining and asserts precharge-blocked. A precharge request at cycle two is made while the obligation is outstanding, so the precharge-violation output asserts — on real hardware the device would accept this command and close the row while the write was still being driven into cells. The counter decrements each cycle and recovery-done pulses as the last event elapses. A precharge request at cycle seven, after the obligation has cleared, is not a violation. Nothing about the request changed; only the obligation elapsed.bus free · bank NOT closeablebus free · bank NOT closeablelast beat — obligation startslast beat — obligationstartsprecharge now = corruptionprecharge now = corruptionobligation dischargedobligation dischargedsame request, permittedsame request, permittedCKdata_doneprecharge_reqrecovery_remain0043210000recovery_activeprecharge_blockedrecovery_doneprech_violationt0t1t2t3t4t5t6t7t8t9
Figure 1 — The same precharge request, refused and then permitted. EDUCATIONAL — NOT TO SCALE, NOT A JEDEC tWR VALUE.

The phase label is the chapter. From cycle 2 to cycle 6 the data bus is free, the controller owes nothing on any wire, Chapter 11.1's payload debt is discharged — and the bank cannot be closed.

Compare cycles 2 and 7. The precharge_req pulses are identical. At cycle 2 it is a violation; at cycle 7 it is fine. Nothing about the request changed — the difference is entirely in an obligation held by a device that never mentioned it.

And on hardware there is no precharge_violation signal. The device would accept the cycle-2 command, close the row, and corrupt a write it had just been given. That output exists because this is the last layer at which the failure is observable, and it is the practical argument for modelling recovery explicitly rather than folding it into a general timing check.

EDUCATIONAL — NOT TO SCALE, NOT A JEDEC tWR VALUE. The interval of four is chosen to fit a readable waveform.

8. Five Assertions Worth Writing

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────────
// Guarded because RECOVERY_EVENTS == 0 is a legal configuration in which
// nothing ever blocks, and P1-P3 have no content there.
// ─────────────────────────────────────────────────────────────────────────
if (RECOVERY_EVENTS == 0) begin : g_zero_asrt

  // P0 -- with no requirement, nothing is ever blocked and no request can
  // violate anything.
  property p_zero_never_blocks;
    @(posedge clk) disable iff (!rst_n)
      !precharge_blocked && !precharge_violation;
  endproperty
  assert property (p_zero_never_blocks);

end else begin : g_count_asrt

// P1 -- the obligation begins at the END OF THE DATA. Section 3's
// definition made checkable: a guard started from the command instead
// would under-wait by the write latency and the burst, and would satisfy
// every other property here.
property p_obligation_starts_at_data_done;
  @(posedge clk) disable iff (!rst_n)
    (!$past(recovery_active) && recovery_active) |-> $past(data_done);
endproperty
assert property (p_obligation_starts_at_data_done);

// P2 -- a precharge is blocked for the whole obligation. THE safety
// property: it is the one thing standing between a legal-looking command
// and a corrupted row, and the device will not enforce it.
property p_blocked_throughout;
  @(posedge clk) disable iff (!rst_n)
    recovery_active |-> precharge_blocked;
endproperty
assert property (p_blocked_throughout);

// P3 -- the obligation is not discharged early. Written against the
// counter so an off-by-one in the countdown fails here rather than
// silently permitting a precharge one event too soon.
property p_no_early_release;
  @(posedge clk) disable iff (!rst_n)
    recovery_done |-> recovery_active && (recovery_remaining == CNT_W'(1));
endproperty
assert property (p_no_early_release);

// P4 -- a new data completion restarts the full obligation. A second
// write to the same bank creates a fresh requirement, and extending or
// ignoring it would discharge a requirement still outstanding.
property p_restart_is_full;
  @(posedge clk) disable iff (!rst_n)
    data_done |=> (recovery_remaining == CNT_W'(RECOVERY_EVENTS))
                  && recovery_active;
endproperty
assert property (p_restart_is_full);

// P5 -- a violation is reported exactly when one occurs. Both directions:
// under-reporting hides the corruption, over-reporting trains a consumer
// to ignore the output.
property p_violation_reported_exactly;
  @(posedge clk) disable iff (!rst_n)
    precharge_violation == (precharge_req && recovery_active);
endproperty
assert property (p_violation_reported_exactly);

end

What these prove. P1 is the one that catches the most damaging configuration error — an obligation started from the command rather than the data, which under-waits by the entire write latency plus the burst and passes every other check. P2 is the safety property. P3 forbids early release, whose consequence is corruption rather than a stall. P4 handles back-to-back writes safely. P5 makes the violation output trustworthy.

What these do not prove. Nothing here proves RECOVERY_EVENTS is the right value — a guard configured too short satisfies all five and permits precharges that corrupt data. That is §9's central point and is only catchable against the device's configured setting. Nothing proves a precharge is legal when not blocked: this is one constraint among many, and Modules 13 and 14 own the rest — a consumer treating !precharge_blocked as permission has made a serious error. Nothing proves the device honoured its own obligation. And nothing proves anything physical — no property here concerns a cell, a bitline or a charge, and §2's mechanism is the reason for the design, not something this RTL models or could check.

9. DV — Checking an Interval With No Observable Endpoint

Recovery presents a verification problem the read side has no equivalent of: the obligation's start is observable, its end is not, and its violation produces no error.

The start is observable. The final write beat crosses the interface, and a monitor watching the write path sees it — Chapter 11.3's burst_done has an observable counterpart.

The end is not. Nothing marks recovery as satisfied. There is no signal, no status, and no command. A monitor can only compute it, from the observed data end plus the configured value.

And the violation is silent. A precharge issued during recovery is accepted. The device does not refuse it, does not flag it, and does not report it afterwards. The only evidence is data that is wrong later — possibly much later, possibly at addresses the failing test never reads.

Which produces four requirements:

Check the interval as a command-sequence property, not a state. The checkable statement is no precharge to bank B within the configured interval after the last write beat to bank B, and it is checkable entirely from observed events. A monitor does not need to model the device to enforce it, which is the good news in this section.

Derive the interval from the programmed mode registers. Same argument as Chapter 11.2 §8 and Chapter 10.2 §8: a checker configured from the design's own constant cannot detect a wrong constant. And here the undetected failure is corruption rather than a stall, which raises the stakes.

Include auto-precharge. §6: a write with the flag creates the same obligation with no precharge command to watch for. A checker keyed on observed precharge commands will miss the entire auto-precharge case, which is the case a close-page policy uses constantly.

Report the margin, not just the verdict. Record the observed interval between the last write beat and the subsequent precharge for every write, and report the distribution. A design that never violates but frequently comes within one event of violating is a design about to fail on a different device, and only the margin shows that. A pass/fail check cannot.

10. Debugging — The Write Succeeded and the Precharge Destroyed It

Symptom. A write completes with no reported error. Reading the location back returns wrong data — sometimes the old value, sometimes a mixture, sometimes values that correspond to neither. The corruption correlates with how soon the bank was closed after the write, and disappears when traffic is light.

That correlation is the diagnostic. A failure that vanishes under light load and appears when the controller is closing rows aggressively is a recovery problem until proven otherwise — and it is worth recognising early, because the symptom otherwise looks like a data-path fault and sends the investigation to the wrong layer entirely.

Candidate mechanisms.

  1. The recovery obligation is started from the write command rather than the end of the data, so the controller under-waits by the write latency plus the burst duration. §3.
  2. RECOVERY_EVENTS is configured shorter than the device requires — a wrong constant, or one taken from a different speed grade.
  3. Recovery is modelled but auto-precharge does not start it, so every close-page write violates while explicit precharges are fine. §6.
  4. The guard is per-controller rather than per bank, so a write to one bank releases the block for another.
  5. A reset cleared the obligation while the device still held it. §5's reset note.
  6. Recovery is correct and the corruption is elsewhere — an underrun (Chapter 11.3), a launch-timing error (Chapter 11.2), or a masking fault (Chapter 6.11).

Evidence to collect. The measured interval between the last write beat and the subsequent precharge, per failing write — this is the decisive number and it is computable entirely from observed events. RECOVERY_EVENTS against the programmed mode-register setting. Whether the failing writes used auto-precharge. Whether the failing precharge and the write targeted the same bank. And whether a reset occurred in the window.

Discriminator.

  • Compute the observed interval and compare it against the configured requirement. If it is short, the question is why — and the magnitude says which. Short by roughly the write latency plus the burst is mechanism 1, and the arithmetic identifies it in one step.
  • Is it short by a constant amount independent of the burst? Mechanism 2 — a wrong value.
  • Do only auto-precharge writes fail? Mechanism 3, and it is common because the explicit path is the one that gets tested.
  • Did the precharge target the same bank as the write? If not, mechanism 4 — a shared guard blocking and releasing across banks.
  • If the interval is always adequate, recovery is exonerated. Mechanism 6, and the search moves to underrun, launch timing or masking — all of which produce wrong data without any precharge involvement, and are distinguished by whether the corruption correlates with row closure at all.

Responsible layer. Mechanisms 1 through 5 are the controller's model. Mechanism 6 is elsewhere in this module. None is a device fault — the device did exactly what it was told, which is the uncomfortable part: a precharge during recovery is a legal command that destroys data.

Fix. Per mechanism. And in every case, add the margin measurement from §9, because a design that is correct today with one event of margin is a design that fails on the next part.

11. Common Misconceptions

"The final burst beat means the bank can be precharged."

Why it is tempting: the data is gone, the bus is free, and the controller owes nothing — every visible obligation is discharged.

Concrete failure: a precharge issued immediately closes the row while the new values are still being driven into cells. The cells end up holding neither the old value nor reliably the new one, and nothing reports it.

Correct model: the last beat discharges the controller's obligation and leaves the device's untouched. §1 and §3.

Prevention: the signal naming — burst_done, not write_done — and a guard between the two.

"tWR is measured from the WRITE command."

Why it is tempting: most timing parameters in DDR are anchored to a command, so anchoring this one to a command is the natural guess.

Concrete failure: the controller under-waits by the write latency plus the burst duration, so every write with an aggressive page policy violates recovery — and the failure appears only under load.

Correct model: measured from the end of the write data, because the interval protects data being driven into cells and the driving cannot start before the data arrives. §3.

Prevention: P1, which is written specifically to catch this.

"CWL and tWR are the same kind of number."

Why it is tempting: both are write-side intervals in cycles, both appear in the same part listing.

Concrete failure: a budget that counts one interval twice, or a controller that satisfies the launch deadline and believes it has therefore satisfied everything.

Correct model: opposite ends of the transaction with different owners. The controller owes the first; the device owes the second. Chapter 11.2 §4.

Prevention: name the two events every interval relates before using it.

"Auto-precharge closes the row immediately."

Why it is tempting: "auto" suggests immediacy, and there is no command to wait for.

Concrete failure: a controller model that marks the bank closed at the final beat classifies a subsequent access against a state that is in transition, and a monitor built the same way misclassifies every close-page write.

Correct model: the flag requests automatic closure; the closure happens when it is safe. The requirement is physical and does not care how the precharge was requested. §6.

Prevention: start the recovery obligation from the data completion regardless of how the close was requested.

"A symbolic recovery counter is a DRAM model."

Why it is tempting: it counts the right interval and blocks the right operation.

Concrete failure: an engineer expects it to reveal a wrong configured value, and it cannot — it is the belief, so it agrees with itself by construction.

Correct model: it models the controller's belief about an obligation the device holds. §5's header.

Prevention: §9 — derive the checker's interval from the programmed configuration, and report the margin.

"A recovery violation will show up as an error."

Why it is tempting: every other protocol violation an engineer has met is refused or flagged.

Concrete failure: a team waits for an error that never comes, and the violation is discovered months later as field data corruption.

Correct model: the device accepts the precharge. It does not refuse it, flag it, or report it. The only evidence is wrong data later.

Prevention: the precharge_violation output, and treating it as a first-class error in simulation.

12. Interview Reasoning

"Why can the write burst finish while the bank still cannot be precharged?"

Because the data has reached the device but has not finished being driven into the cells. A write overwrites part of what the open row's sense amplifiers are holding, and those amplifiers drive their values into the cells through the wordline that stays asserted while the row is open. That second propagation takes time and has barely started when the last beat arrives. A precharge deasserts the wordline — disconnecting exactly the cells being written — and equalises the bitlines, removing the driving values. So a precharge issued too early does not delay the write, it interferes with it, and the cells end up holding an indeterminate result.

"What does write recovery protect?"

A value in transit, which is what makes it different from most DDR timing parameters. Most of them protect a resource: issue too early and something is still busy, and the consequence is a stall or undefined behaviour on that command. Recovery protects data that is partway into storage, and violating it corrupts a location silently — the device accepts the precharge, closes the row, and reports nothing. There is no stall and no error. The only evidence is wrong data later, possibly much later.

"Why isn't write recovery the same thing as write latency?"

They sit at opposite ends of the transaction and are owed by different parties. Write latency is a deadline the controller must meet at the start of the data — present data at this offset or the device samples something else. Recovery is an obligation the device holds after the end of the data — the controller owes only patience. And they are measured from different events: the launch offset from the command, the recovery interval from the end of the data. A controller that satisfies the first and ignores the second delivers every write correctly and then destroys them by closing rows too soon.

"How would you verify recovery when its end is not observable?"

As a command-sequence property rather than a state. The checkable statement is that no precharge to a bank occurs within the configured interval after the last write beat to that bank — and every term in it is observable, so a monitor can enforce it without modelling the device. Two additions matter: the interval must be derived from the programmed mode registers rather than from the design's own constant, because a checker sharing the constant cannot detect a wrong one; and the check must include auto-precharge, where there is no precharge command to watch for. And I would report the margin distribution rather than a pass/fail — a design that never violates but routinely comes within one event of violating is about to fail on a different part, and only the margin shows it.

"A write reads back corrupted, and the problem disappears under light traffic. What does that suggest?"

Recovery, almost immediately. Light traffic means rows stay open longer, so the interval between a write's last beat and the bank being closed is naturally large and the requirement is met by accident. Under load, an aggressive page policy closes rows promptly and the violation appears. The check is to measure the observed interval from last beat to precharge for the failing writes — and the magnitude of the shortfall identifies the cause: short by roughly the write latency plus the burst duration means the obligation was started from the command rather than the data, which is the single most common version of this bug.

13. Engineering Exercise

RECOVERY_EVENTS = 6, BEATS_PER_WRITE = 8, LAUNCH_EVENTS = 5.

1. List the five events of §3 in order for one write, and say who discharges each.

2. A controller starts its recovery count at the write command instead of the last beat. By how many cycles does it under-wait? Show the reasoning.

3. data_done pulses at cycle 10 and again at cycle 13. Give recovery_remaining at cycles 11 through 20.

4. A precharge is requested at the cycle recovery_done pulses. Is it a violation? Justify from the RTL.

5. A controller uses one recovery guard for all banks. Construct the two-write sequence that corrupts data, and name the property that catches it.

6. Why does a checker keyed on observed precharge commands miss the auto-precharge case entirely, and what does it need instead?

14. Summary

A write's last beat discharges the controller's obligation and leaves the device's untouched. The bus is free, the payload debt is gone, and the bank still cannot be closed.

The mechanism is physical. The new values must be driven from the sense amplifiers into the cells through the wordline that stays asserted while the row is open, and a precharge deasserts that wordline and equalises the bitlines. A precharge issued too early does not delay the write — it interferes with it, leaving cells holding neither the old value nor reliably the new one.

Which makes recovery different from most timing constraints. Most protect a resource and cost a stall. Recovery protects a value in transit and costs data.

It is measured from the end of the write data, not from the command — because the driving cannot start before the data arrives. A controller anchoring it to the command under-waits by the launch interval plus the burst, and every write violates under an aggressive page policy.

Auto-precharge does not escape it. The requirement is physical and does not care how the closure was requested; the device honours it internally. A monitor keyed on precharge commands misses the entire close-page case.

And nothing reports a violation. The device accepts the precharge, closes the row, and says nothing. The only evidence is wrong data later — which is why the obligation must be modelled explicitly, why the margin is worth measuring, and why the violation output exists at the last layer where the failure is still visible.

15. What Comes Next

Every piece of a write now exists: a transaction created with a debt, a deadline scheduled, a burst sustained, and an obligation outlasting the data.

Chapter 11.5 — Write Timing Analysis puts them together and asks what none of them can answer alone:

Given a trace, are the command, the launch, the burst and the recovery mutually consistent?

The five events of §3 happen at five different times, owed by two different parties, and only two of them are directly observable. The launch deadline is computed, the recovery end is computed, and the whole analysis depends on knowing what the device was configured to expect — which is why that chapter's method, like Chapter 10.5's, begins before the waveform is opened.

It builds the integrated tracker that carries a write through all five, the verification-only checker that watches it, and the methodology for reading a real write trace — including how to tell a controller fault from a configuration fault when both produce the same silent corruption.


Return to Burst Writes for the event that starts this obligation, Row Opening for what a precharge does to a row, Restore Operations for why the whole row participates, The Row Buffer for what an open row is, Write Latency (CWL) for the obligation at the other end, Precharge for the command this interval blocks, and Write (WR / WRA) for auto-precharge as an invisible closure.

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.