DDR · Module 14
tRAS — Row Active Time
tRAS produces the most counter-intuitive pairing in DDR timing: a row already read successfully that you are not yet permitted to close. A minimum duration is not a completion latency.
Chapter 14.1 said when a column command may start. Chapter 14.2 said when the next ACTIVATE may start. Neither said anything about when the PRECHARGE in between may be issued.
tRAS answers that, and the answer produces the most counter-intuitive situation in DDR timing:
A row you have already read, whose data is in your hands, that you are not yet permitted to close.
READ legal and PRECHARGE illegal, on the same bank, at the same instant. That is not a corner case — it is the normal condition for a short access to a freshly opened row, and it follows from tRAS being a minimum duration rather than a completion latency.
1. The Obligation, in Five Parts
PARAMETER tRAS
1 triggering event ACTIVATE accepted on bank B
2 constrained event PRECHARGE addressed to bank B
3 resource bank B BANK-LOCAL
4 magnitude published in NANOSECONDS, resolved to cycles
5 sense MINIMUM SEPARATION
— experienced as a MINIMUM DURATION, see §2
─────────────────────────────────────────────────────────────────
consequence PRECHARGE of bank B not before
act_cycle + ceil(tRAS / tCK)Note that the triggering event is the same as tRCD's — an accepted ACTIVATE on bank B. One event, two obligations, with different constrained events and different magnitudes.
That is worth pausing on, because it is the first time in this module that one trigger arms two obligations at once. Chapter 13.1 §5 said every command is a triggering event for some future obligation; here it is two, and Chapter 14.4 will make it three.
The follow-up questions:
What re-arms it? Another ACTIVATE to the same bank — which, on an open bank, is a row conflict the state layer refuses. So in a correct controller tRAS is armed once per row.
What can dominate it? For a precharge after a write, tWR — measured from a data event rather than a command, and frequently later. For a precharge after a read, a read-to-precharge obligation exists too. So PRECHARGE typically has two or three applicable obligations, and tRAS is often not the controlling one. Chapter 14.4 §5 works a case.
Cycle or absolute-time component? Absolute time, published in nanoseconds.
Does generation matter? Not for the structure.
2. Minimum Duration Is Not Completion Latency
Here is the distinction the chapter exists for, and it is subtler than tRCD's.
tRCD feels natural because it matches an intuition: the row is not ready yet, so wait. tRAS inverts the intuition. The row is ready — you may read it, and the read will return correct data — and you still may not close it.
ACT ──┬──── tRCD ────▶ column command legal
│
└──────────── tRAS ─────────────▶ PRECHARGE legal
tRCD < tRAS, so there is a window where
READ is legal and PRECHARGE is notTwo different classes of statement are in play, and Chapter 13.3 §2's taxonomy separates them:
A completion latency would say: the operation finishes at this offset, after which the resource is free. If tRAS were that, then once the read succeeded the row would be closeable, because the thing you were waiting for evidently happened.
A minimum duration says: this resource must remain in this condition for at least this long, whatever else happens. Your successful read is evidence about the read. It is not evidence that the minimum duration has elapsed, because the duration is not about the read at all.
This is also why §1's part 5 carries a qualifier. Formally tRAS is a Class A minimum separation, exactly like tRCD — same inequality, same arithmetic. Experientially it is a minimum duration, because the constrained event is the one that ends the state the trigger began, so the separation between them is the state's lifetime. Both descriptions are correct; the second is the one that makes the behaviour predictable.
3. Why the Obligation Exists
One paragraph, architectural.
An ACTIVATE begins sensing; a PRECHARGE ends the row's presence in the sensing circuitry. Between them, Chapter 2.5 and Chapter 2.6 establish that the cells whose charge was disturbed must be driven back to full levels from the resolved sense amplifiers. That restoration is work that takes time, and it must complete before the sensing circuitry is released — otherwise the row is left with partially restored cells, which is data loss rather than a timing violation you can see.
tRAS is the contractual expression of “long enough for that work.” The controller models none of it: a nanosecond figure and a counter.
4. Units, and an Honest Note on the Value
tRAS is published in nanoseconds, like tRCD and tRP.
Resolving 32.0 ns at DDR4-3200, where tCK is 0.625 ns:
32.0 / 0.625 = 51.2 → ceil = 52 cyclesAnd the pairing that makes §2 concrete. At that same grade, tRCD is 13.75 ns, which resolves to 22 cycles:
ACT at cycle 0
column command legal from cycle 22 (tRCD)
PRECHARGE legal from cycle 52 (tRAS)
→ a 30-cycle window in which READ is legal and PRECHARGE is notThirty cycles is not a corner case. A burst of 8 transfers occupies 4 clock cycles on the data bus (Chapter 12.1), so a controller that opens a row, reads one burst and wants to close it immediately will find itself waiting most of that window with nothing to do. §12 is about what to do with that observation; Module 9 already built the policy argument.
5. The Exact Legal Boundary
ACTIVATE accepted (sampled) on cycle N
resolved requirement D cycles
PRECHARGE sampled at M is LEGAL ⟺ M - N >= D
earliest legal M = N + D
forbidden cycles = N … N+D-1 exactly D of themIdentical convention to Chapter 14.1 §5 and 14.2 §5. The module keeps one boundary convention for every parameter, deliberately.
6. RTL — A Minimum-Active-Window Guard
Collision check. Chapter 14.1's act_to_column_guard counts from an ACTIVATE toward a column command. Chapter 14.2's bank_close_state_vs_timing counts from a PRECHARGE and tracks open state. Chapter 13.3's deadline_scoreboard holds abstract obligations with timestamps.
A third per-bank countdown with different port names would be the ten-timers architecture this module is meant to argue against. So this block's new responsibility is the one thing the earlier two do not do: hold two obligations from one trigger and publish which command classes each permits.
That is genuinely different. act_to_column_guard answers one question; this answers two questions that were armed by the same event and expire at different times — which is §2's structure made into hardware, and the smallest honest vehicle for the READ-legal-PRECHARGE-illegal window.
The engineering problem. One accepted ACTIVATE creates two obligations with different magnitudes on the same bank. A consumer needs both verdicts, separately, and needs to know which is outstanding when a command is refused.
Classification: controller-side sequential timing bookkeeping. Counts cycles.
// ─────────────────────────────────────────────────────────────────────
// row_active_window
//
// CLASSIFICATION
// Controller-side sequential timing bookkeeping. Counts cycles.
//
// WHAT IT MODELS
// TWO obligations armed by ONE event, per bank:
// trigger = ACTIVATE accepted on bank b
// constrained 1 = column command to bank b (tRCD, shorter)
// constrained 2 = PRECHARGE of bank b (tRAS, longer)
// scope = BANK-LOCAL for both
// sense = MINIMUM SEPARATION for both
// and publishes the two verdicts SEPARATELY, because §2's whole
// point is that they expire at different times.
//
// WHAT IT DOES NOT MODEL
// Any physical process. No restoration, no sensing, no charge. In
// particular there is NO "restore complete" input, because the
// device provides none -- the contract is a separation, and a port
// implying observable completion would teach a fiction.
//
// WHY BOTH LIVE IN ONE BLOCK
// They share a trigger. Splitting them into two instances would
// work and would hide the fact that one event arms both, which is
// the structural insight of this chapter. Keeping them together
// also makes the ORDERING assumption checkable -- see g_ordering.
//
// CONVENTION (Chapter 13.2 §6)
// ACTIVATE accepted on cycle N with magnitude D forbids the
// constrained command on cycles N .. N+D-1, permits it from N+D.
// One command per cycle on the command bus, so cycle N cannot host
// a competing command; countdowns load D-1.
//
// SIMULTANEITY
// activate and expiry same cycle -> ACTIVATE WINS, both re-arm.
// activate to an already-open bank -> a row conflict, which the
// STATE layer refuses (Chapter 9.5). If one is nonetheless
// accepted, both obligations re-arm from the new event.
// reset mid-obligation -> both abandoned.
// ─────────────────────────────────────────────────────────────────────
module row_active_window #(
parameter int BANKS = 16,
// Largest magnitude either obligation must represent, in cycles.
// tRAS dominates in every real configuration, so this is sized for
// it. Sized as a COUNT: $clog2(MAX+1).
parameter int MAX_CYCLES = 64,
parameter int BK_W = (BANKS <= 1) ? 1 : $clog2(BANKS),
parameter int CNT_W = (MAX_CYCLES <= 1) ? 1 : $clog2(MAX_CYCLES + 1)
) (
input logic clk,
input logic rst_n,
// ── Resolved magnitudes, in cycles, from Chapter 13.2's resolver.
// Two INDEPENDENT inputs. Chapter 14.2 §11 covered why sharing a
// register between two parameters that happen to coincide at one
// speed grade is a bug waiting for the next part.
input logic [CNT_W-1:0] trcd_cycles,
input logic [CNT_W-1:0] tras_cycles,
// ── Triggering event.
input logic act_accepted,
input logic [BK_W-1:0] act_bank,
// ── Candidate under evaluation.
input logic [BK_W-1:0] cand_bank,
// ── The two verdicts, separately. A consumer wanting a combined
// "bank ready" must say which command class it means, because
// there is no single answer -- that is the chapter.
output logic column_timing_ok,
output logic precharge_timing_ok,
output logic [CNT_W-1:0] column_wait_remaining,
output logic [CNT_W-1:0] precharge_wait_remaining,
// ── THE observable this chapter is about: a bank in the window
// where a column command is legal and a precharge is not.
// Published for coverage -- §7 argues a testbench that never
// reaches this state has not tested tRAS.
output logic readable_not_closeable,
// ── Which obligation would refuse a precharge, for the diagnostic.
// 0 = none outstanding, 1 = tRAS. Kept deliberately narrow:
// Chapter 14.5's tWR is the OTHER precharge obligation and lives
// in its own block, so this block must not claim to know the
// full applicable set. Chapter 14.4 composes them.
output logic precharge_blocked_by_tras
);
if (BANKS < 1) begin : g_banks
initial $fatal(1, "row_active_window: BANKS must be >= 1");
end
if (MAX_CYCLES < 0) begin : g_max
initial $fatal(1, "row_active_window: MAX_CYCLES must be >= 0");
end
// ── Two countdowns per bank, from one trigger.
logic [CNT_W-1:0] trcd_cnt [BANKS];
logic [CNT_W-1:0] tras_cnt [BANKS];
always_comb begin
column_wait_remaining = trcd_cnt[cand_bank];
precharge_wait_remaining = tras_cnt[cand_bank];
column_timing_ok = (trcd_cnt[cand_bank] == '0);
precharge_timing_ok = (tras_cnt[cand_bank] == '0);
// The window. Note this is a CONJUNCTION of one expired and one
// outstanding obligation -- it cannot be derived from either
// countdown alone, which is why a single-obligation block could
// not express it.
readable_not_closeable = column_timing_ok && !precharge_timing_ok;
precharge_blocked_by_tras = !precharge_timing_ok;
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (int unsigned b = 0; b < BANKS; b++) begin
trcd_cnt[b] <= '0;
tras_cnt[b] <= '0;
end
end else begin
// Age both, then arm both. Ageing first keeps a fresh ACTIVATE
// from being decremented on its own arming cycle.
for (int unsigned b = 0; b < BANKS; b++) begin
if (trcd_cnt[b] != '0) trcd_cnt[b] <= trcd_cnt[b] - 1'b1;
if (tras_cnt[b] != '0) tras_cnt[b] <= tras_cnt[b] - 1'b1;
end
if (act_accepted) begin
trcd_cnt[act_bank] <= (trcd_cycles >= CNT_W'(1))
? (trcd_cycles - CNT_W'(1)) : '0;
tras_cnt[act_bank] <= (tras_cycles >= CNT_W'(1))
? (tras_cycles - CNT_W'(1)) : '0;
end
end
end
endmoduleInterface contract. Two magnitude inputs, two verdicts, one trigger. readable_not_closeable is published as an observable rather than consumed internally — it exists so that coverage can prove the window was reached.
Parameter contract. MAX_CYCLES is sized for tRAS because tRAS dominates: §4's figures give 52 cycles against tRCD's 22. CNT_W is $clog2(MAX_CYCLES + 1) — a count, so a magnitude equal to a power-of-two MAX_CYCLES does not truncate to zero.
Why the two magnitudes are separate ports. Chapter 14.2 §11 covered the shared-register bug for tRP and tRCD. The same argument applies with more force here: tRAS and tRCD are never equal in a real configuration, so a design that shared a register would be obviously broken — but a design that computed one from the other by a fixed offset would work on one part and fail on the next. They are independent published values.
What this block deliberately does not know. precharge_blocked_by_tras reports only tRAS. A precharge after a write is also constrained by tWR, and a precharge after a read by a read-to-precharge obligation. This block does not model those and must not claim to, which is why the output is named for the specific obligation rather than called precharge_blocked. Chapter 14.4 builds the composition that takes the maximum over the full applicable set.
Corner cases. tras_cycles <= trcd_cycles: physically unrealistic but electrically legal, and it makes readable_not_closeable unreachable — the window closes. g_ordering below is the property that flags it. tras_cycles == 0: precharge never blocked by this obligation. BANKS == 1: degenerates correctly. Activate on the expiry cycle of either countdown: arming wins for both, together.
Synthesis implications. 2 × BANKS counters. For 16 banks and 7-bit counts, 224 flops. Two read muxes on cand_bank.
Failure modes. Deriving precharge_timing_ok from column_timing_ok — the conflation this block exists to prevent, producing a precharge as soon as the row is readable and violating tRAS by the difference between the two magnitudes, which §4 sizes at 30 cycles. Sharing one countdown for both obligations: whichever magnitude is loaded, one of the two verdicts is wrong. Arming before ageing: both obligations one cycle short.
7. Four Assertions and a Cover Worth Writing
// ── C1. The window is REACHED. Like Chapter 14.2 §7's cover, this is
// existential and cannot be an assertion: the claim is that the two
// verdicts DIFFER somewhere. A run where this never hits has not
// tested this chapter, and P1 below is then vacuous.
generate
for (genvar gb = 0; gb < BANKS; gb++) begin : g_window_cover
c_readable_not_closeable:
cover property (@(posedge clk) disable iff (!rst_n)
((trcd_cnt[gb] == '0) && (tras_cnt[gb] != '0)) );
end
endgenerate
// ── P1. No precharge permitted while tRAS is outstanding. The safety
// property. Catches precharge permission derived from column
// permission -- the §6 headline failure.
property p_no_precharge_during_tras;
@(posedge clk) disable iff (!rst_n)
precharge_timing_ok |-> (tras_cnt[cand_bank] == '0);
endproperty
a_no_precharge_during_tras: assert property (p_no_precharge_during_tras);
// ── P2. Both obligations are armed by the SAME event to the SAME
// magnitudes stated in §5, checked in both directions. Catches a
// block that arms only one of the two -- which passes P1 whenever
// the unarmed one happens to be tRCD.
generate
for (genvar gc = 0; gc < BANKS; gc++) begin : g_both_armed
property p_one_trigger_arms_both;
@(posedge clk) disable iff (!rst_n)
( $past(act_accepted, 1) && ($past(act_bank, 1) == BK_W'(gc))
&& ($past(trcd_cycles, 1) >= CNT_W'(2))
&& ($past(tras_cycles, 1) >= CNT_W'(2)) )
|-> ( (trcd_cnt[gc] == ($past(trcd_cycles, 1) - CNT_W'(1)))
&& (tras_cnt[gc] == ($past(tras_cycles, 1) - CNT_W'(1))) );
endproperty
a_one_trigger_arms_both: assert property (p_one_trigger_arms_both);
end
endgenerate
// ── P3. tRAS outlives tRCD. This is a CONFIGURATION property rather
// than a logic property: it holds because tras_cycles >= trcd_cycles
// in every real part, and it is asserted so that a mis-programmed
// configuration is caught at the point of programming rather than
// as a mysterious violation later. Guarded on the antecedent so it
// does not fire during the reset-value window.
property p_tras_dominates_trcd;
@(posedge clk) disable iff (!rst_n)
(tras_cycles >= trcd_cycles);
endproperty
a_tras_dominates_trcd: assert property (p_tras_dominates_trcd);
// ── P4. SCOPE. An ACTIVATE to one bank disturbs no other bank's
// obligations -- either of them. Chapter 13.3 §4's Bug 1.
generate
for (genvar gd = 0; gd < BANKS; gd++) begin : g_scope
property p_activate_is_bank_local;
@(posedge clk) disable iff (!rst_n)
( $past(act_accepted, 1) && ($past(act_bank, 1) != BK_W'(gd))
&& ($past(trcd_cnt[gd], 1) == '0)
&& ($past(tras_cnt[gd], 1) == '0) )
|-> ((trcd_cnt[gd] == '0) && (tras_cnt[gd] == '0));
endproperty
a_activate_is_bank_local: assert property (p_activate_is_bank_local);
end
endgenerateWhat these prove. That the readable-not-closeable window is actually visited (C1); that no precharge enters it (P1); that one trigger arms both obligations to their stated magnitudes (P2); that the configuration has tRAS at least tRCD (P3); and that both obligations are bank-local (P4).
Why P3 is worth having even though it looks trivial. It is not a property of the logic — the logic works for any magnitudes. It is a property of the configuration, and it catches a class of bug that is otherwise very hard to localise: a controller programmed with a tRAS smaller than its tRCD behaves oddly in ways that look like a scheduler problem. Asserting configuration invariants at the point where the configuration arrives is much cheaper than diagnosing their consequences.
What these do not prove. That the magnitudes are the right values — inputs, as always, and Chapter 14.1 §9 covered why no block-level property can see it. That the full applicable set for a precharge was consulted: this block knows only tRAS, and a design that consulted only this block would violate tWR after every write. That gap is deliberate and is the reason Chapter 14.4 exists — no single-obligation block can discharge a composition obligation.
8. The Window, in Cycles
row_active_window — readable but not closeable
10 cyclesCycles 4 through 7 are the chapter. column_ok is high and precharge_ok is low, from one triggering event, because the two obligations it armed have different magnitudes.
Cycle 6 is the moment that defeats intuition. The read at cycle 5 succeeded — correct data, no violation. A designer reasoning “the read worked, so the row is done” issues the precharge at cycle 6 and violates tRAS by two cycles. §2 explained why the inference fails: the read is evidence about sensing, and tRAS is about restoration.
Note that precharge_ok is high at cycles 0 and 1, before the activate. That is correct and worth not misreading: a bank with no outstanding tRAS obligation is not tRAS-blocked. Whether a precharge is sensible on a closed bank is a different question — Chapter 14.2 §6's redundant_precharge — and a different layer.
9. DV — Independent Reconstruction
Invert the representation. The design counts down two counters; the checker records act_cycle[bank] once and computes two differences from it. One stored timestamp, two comparisons — which is structurally better than the design's two counters and is exactly the kind of asymmetry Chapter 13.4 §8 wants, because the checker cannot reproduce a bug in which the design armed one counter and not the other.
Resolve both magnitudes independently. And here tRAS needs a specific caution that no other parameter in this module needs: §4 obtained its tRAS figure by subtracting tRP from tRC. If the checker does the same subtraction using the same two table entries, and the design does too, then a wrong tRC entry produces an agreed wrong answer. Where a directly published tRAS figure is available, the checker should use it, precisely so that it disagrees with anything derived.
Report the full applicable set, not just tRAS. A precharge violation report that names only tRAS when tWR was also outstanding sends debugging to the wrong obligation.
TIMING VIOLATION
command : PRECHARGE, bank 1
issued at : cycle 46
resource : bank 1 (bank-local)
applicable : tRAS -> legal from 52 CONTROLLING
tWR -> not armed (no write to this bank)
violated : tRAS
source event : ACTIVATE bank 1, cycle 0
requirement : 32.0 ns / 0.625 ns = 52 cycles (ceil)
short by : 6 cycles
note : last READ to this bank completed at cycle 30The applicable block and the closing note are what make this report good. The applicable list shows every obligation considered and which controlled, so a reader can see that nothing was overlooked — Chapter 13.4 §2's step 4 made visible. The note pre-empts the objection a designer will actually raise: but the read finished. Stating when the read completed, next to a violation that occurred after it, is the fastest way to communicate that read completion is not the criterion.
10. Debugging
Symptom. Precharge commands violate tRAS, and every read to that bank returned correct data.
| Candidate mechanism | Evidence | Discriminator |
|---|---|---|
| Precharge permission derived from column permission | Shortfall ≈ tRAS − tRCD; violation always right after a successful read | The most decisive check here: does the shortfall match the difference of the two magnitudes? That number implicates exactly one bug. |
| Precharge issued on read completion | Violation offset tracks burst length and CL, not the magnitudes | Correlate the violating cycle with the last data beat. If it is a fixed offset from the burst end, the trigger is the data, not the obligation. |
| Only tRAS consulted, after a write | Violations only on precharge following a write | tWR. This is the opposite error — the applicable set was too small. |
| Off-by-one | Shortfall exactly 1 | §5's convention. Count forbidden cycles. |
| tRAS derived by subtraction from a wrong tRC | Shortfall constant and equal to the tRC error | §9's caution. Compare against a directly published tRAS if one exists. |
| Only one of the two obligations armed | Column commands correct, precharge unconstrained (or vice versa) | §7's P2. |
| Scope error | Precharge to bank 9 blocked by an activate to bank 3 | §7's P4; throughput against bank count. |
The discriminator is the size of the shortfall, and tRAS gives it a uniquely sharp form. A shortfall equal to tRAS − tRCD means precharge permission came from the column verdict. A shortfall of 1 means the convention. A shortfall that moves with burst length means the trigger was a data event. Three distinguishable numbers, three different files, and reading one violation report tells you which.
Responsible layer. A tRAS refusal with a plausible elapsed time is the contract working. The question then becomes whether the controller should have kept the row open and served more requests from it rather than trying to close it — which is Module 9's row policy and Module 17's scheduler, not a timing bug.
11. Common Misconceptions
“tRAS is how long an ACTIVATE takes.”
Tempting because the name pairs “row” with “active” and it is the delay that follows an activate. Why it is wrong: an ACTIVATE occupies one cycle on the command bus. tRAS constrains a different, later command — the precharge. Consequence: latency models that treat tRAS as serial activate cost, inflating read latency by tens of cycles. Replacement model: a minimum separation between two commands. Debugging clue: modelled read latency far exceeds measured.
“tRAS is how long the row stays open.” Tempting because it is the phrase everyone uses. Why it is wrong: it has no upper bound. tRAS says the row may not be closed before a boundary; how long it actually stays open is policy. Consequence: engineers look for a violation when a row is held open a long time, and find none, and lose confidence in their model. Replacement model: a floor on the active interval, not its length. Debugging clue: a row held open for thousands of cycles with no violation is correct, not lucky.
“If the READ succeeded, the row can be closed.”
Tempting because the read is the thing you wanted and it demonstrably worked. Why it is wrong: §2 — the read is evidence about sensing; tRAS is about restoration, which is a different process with its own requirement. Consequence: a precharge issued immediately after the last data beat, violating tRAS by tRAS − tRCD minus the burst, on every short access to a fresh row. Replacement model: read success and closeability are unrelated conditions. Debugging clue: violations always follow a successful read, with a shortfall matching the magnitude difference.
“If a READ is legal, a PRECHARGE must be too.”
Tempting because both concern the same open bank, and precharge feels like the lesser operation. Why it is wrong: different constrained events with different obligations and different magnitudes — 22 cycles against 52 at §4's grade. Consequence: a controller with one per-bank “ready” bit violates tRAS constantly. Replacement model: one verdict per command class. Debugging clue: a design with a single bank_ready output almost certainly has this bug.
“tRAS and tRP are two halves of one thing, so one register does both.”
Tempting because Chapter 14.4 shows they sum to tRC, which makes them look like partners. Why it is wrong: they constrain different events from different triggers — tRAS from an ACTIVATE toward a PRECHARGE, tRP from a PRECHARGE toward an ACTIVATE — and at §4's grade they are 32.0 ns and 13.75 ns, nothing like each other. Consequence: whichever is loaded, the other edge is wrong. Replacement model: two obligations that happen to be adjacent in the row cycle. Debugging clue: violations on one edge and unexplained slowness on the other.
“A longer tRAS is always safe.” Tempting because tRAS is a Class A minimum, so exceeding it is legal. Why it is wrong: it is functionally safe and it lengthens the whole row cycle, since Chapter 14.4 shows tRC depends on it — so over-programming tRAS reduces how often a bank can be recycled. Consequence: silent throughput loss on miss-heavy workloads, attributed to the access pattern. Replacement model: margin on tRAS is paid at every row cycle. Debugging clue: row-miss-heavy workloads underperform the model while row-hit-heavy ones match it.
12. Interview Reasoning
“What does tRAS constrain?”
Five parts: an accepted ACTIVATE on a bank triggers it; a PRECHARGE of that bank is constrained; the resource is the bank, so bank-local; the magnitude is in nanoseconds, resolved by ceiling; and it is a minimum separation. The detail worth adding unprompted is that this is the same trigger as tRCD — one accepted activate arms two obligations with different magnitudes, and that is where the interesting behaviour comes from.
“Why can a READ be legal while a PRECHARGE is not?”
Because both obligations were armed by the same ACTIVATE and tRAS is much the longer — 52 cycles against 22 at a real DDR4-3200 grade, so a 30-cycle window. The deeper reason is that the two constrain different things: the column obligation is about the row being resolved enough to select from, and tRAS is about the row having been present long enough for restoration to complete. A successful read is evidence for the first and says nothing about the second.
“Is tRAS a latency or a minimum?” Formally a minimum separation, with a lower bound and no upper bound — so holding a row open longer is always legal. What makes it feel different from tRCD is that its constrained event ends the state its trigger began, so the separation is experienced as a minimum duration of the active state. Both descriptions are right; treating it as a completion latency is what goes wrong, because then a successful read looks like proof the operation finished.
“Your controller precharges immediately after the last data beat and violates tRAS. Diagnose it.”
The trigger is wrong: the design is treating data completion as the criterion when the obligation is measured from the ACTIVATE. The fingerprint is that the violating cycle sits at a fixed offset from the burst end rather than at a fixed shortfall from the tRAS boundary — so as the burst length or CL changes, the shortfall moves. Contrast that with permission derived from the column verdict, where the shortfall is constant and equals tRAS − tRCD.
“tRC is 45.75 ns and tRP is 13.75 ns. What is tRAS, and how confident are you?”
32.0 ns by subtraction, given the relationship tRC = tRAS + tRP. The confidence qualifier is the real answer: that is an inference from two published figures, not a published figure, and the round result is corroborating rather than confirming. For anything that matters I would read tRAS from the table directly — and in verification I would deliberately not derive it the same way the design does, so that a wrong tRC entry produces a disagreement instead of agreement.
“Why is over-programming tRAS not free?” Because tRC depends on it, so every row cycle lengthens — a bank can be recycled less often. It is functionally safe and it costs throughput on exactly the workloads that already hurt, the miss-heavy ones. The general point is that margin on a Class A minimum is safe in correctness and never free in rate.
13. Engineering Exercises
1. Compute the window. At DDR4-2400, tCK is 0.8333 ns. Using tRCD of 13.75 ns and tRAS of 32.0 ns, resolve both and give the width in cycles of the readable-not-closeable window.
Worked: tRCD is ceil(16.50) = 17 cycles; tRAS is ceil(38.40) = 39 cycles. The window is cycles N+17 through N+38 — 22 cycles wide. Compare with 30 cycles at DDR4-3200 from §4: the window is narrower in cycles at the slower part, because both magnitudes shrink in cycles while their ratio is roughly preserved. Worth noting that in absolute time both windows are about 18 ns.
2. Size the conflation bug. Using exercise 1's figures, a design derives precharge permission from the column verdict. By how many cycles does it violate, and what is the shortfall if the precharge is issued at the first cycle the design thinks is legal?
Worked: the design permits a precharge at N+17 where N+39 is legal — short by 22 cycles, which is exactly tRAS_cycles − tRCD_cycles. Recognising that the shortfall equals the magnitude difference is the diagnosis.
3. Distinguish two wrong triggers. Design A derives precharge permission from the column verdict. Design B issues the precharge on the last data beat of the read. Both violate tRAS. Give one measurement that tells them apart.
Worked: change the burst length, or CL, without changing the timing magnitudes. Design A's shortfall is unchanged, because it depends only on the two magnitudes. Design B's shortfall moves, because its trigger is the data event. One re-run with a different burst length separates them.
4. Decide whether a precharge is legal. A bank is activated at cycle 100. tRCD resolves to 22 and tRAS to 52. A read is issued at cycle 130 and its burst completes at cycle 145. Is a precharge legal at cycle 146? At 152? What if a write had been issued at 130 instead?
Worked: at 146, 146 − 100 = 46 < 52 — illegal, short by 6. At 152, 52 >= 52 — legal as far as tRAS is concerned. With a write at 130, tRAS is satisfied at 152 but tWR is also armed, from a data event rather than the command, and may well be the controlling obligation — so the honest answer is that the question is unanswerable without tWR and the write's data-completion cycle. Recognising which information is missing is the skill being tested.
5. Write the cover and say what it means. Explain why “a column command can be legal while a precharge is not” must be a cover rather than an assertion, and what it means if that cover never hits in a long random run.
Worked: it asserts that two verdicts differ somewhere, which is existential; assertions are universal. If the cover never hits, either the stimulus never activated a bank and then waited, or tras_cycles <= trcd_cycles was programmed — and in the second case §7's P3 should have fired. Either way P1 has been passing vacuously and the tRAS logic is untested.
6. Justify the configuration assertion. §7's P3 asserts tras_cycles >= trcd_cycles, which is a property of configuration rather than logic. Argue that it does not belong in the design, then rebut.
7. Price the margin. A team adds 4 cycles of margin to tRAS “for safety”. Using Chapter 14.4's relationship, state the effect on the row cycle and on the maximum rate at which one bank can be recycled. On which workload is the cost invisible, and on which is it worst?
14. Summary
tRAS constrains the PRECHARGE of a bank from the accepted ACTIVATE on that bank — bank-local, published in nanoseconds, a minimum separation. Its trigger is the same event that arms tRCD, so one accepted activate arms two obligations with different magnitudes, and the gap between them is where this chapter lives.
Formally it is the same class of constraint as tRCD. Experientially it is a minimum duration, because its constrained event ends the state its trigger began. That framing is what makes the behaviour predictable, and the alternative — reading it as a completion latency — produces the module's most seductive wrong inference: the read worked, so the row is finished. It does not follow. A successful read is evidence that sensing resolved; tRAS is about restoration, which Chapter 2.6 established is separate work with its own requirement.
The resulting window is large and ordinary. At one verified DDR4-3200 speed grade, tRCD resolves to 22 cycles and tRAS to 52, so there are 30 cycles in which a READ is legal and a PRECHARGE is not — longer than the 4 clock cycles a burst of 8 transfers occupies. §4 is explicit that the 32.0 ns tRAS figure is an arithmetic inference from published tRP and tRC values rather than a directly published number, and §9 turns that caveat into a verification rule: the checker should not derive tRAS the same way the design does.
row_active_window holds both obligations from one trigger and publishes both verdicts separately, plus readable_not_closeable so that coverage can prove the window was visited. It deliberately reports only tRAS as a precharge blocker, because a precharge after a write is also constrained by tWR — a gap that no single-obligation block can close.
The diagnostic that makes tRAS failures quick to place is the size of the shortfall: equal to tRAS − tRCD means permission came from the column verdict; exactly 1 means the boundary convention; moving with burst length means the trigger was a data event.
15. What Comes Next
Three chapters have now populated three edges around one bank, and each has ended by noting that its parameter is often not the controlling one.
Chapter 14.4 stops deferring that. It composes tRAS and tRP into the row cycle, verifies the relationship tRC = tRAS + tRP against published figures, and then does the thing this module has been building toward: takes an ACTIVATE candidate constrained by several obligations at once, computes the maximum, identifies the controlling one, and changes a single prior event to show the controlling constraint hand over to a different parameter.
That is where memorising definitions stops being enough and Module 13's analysis procedure starts earning its keep.
Continue learning
Related tutorials
- Related topic
Why Timing Parameters Exist
A DDR command can be perfectly meaningful and target a bank in exactly the right state and still be illegal right now. That third refusal is what timing parameters are, and it is a different question from the first two.
- Related topic
Device Physics Behind Timing
The array's processes take absolute time and know nothing about any clock. A controller counts cycles. Converting between them is where a physical duration becomes a digital obligation — and where the same device needs more cycles the faster you run it.
- Related topic
Timing Constraints Catalogue
Four classes of timing constraint, the resource that owns each one, and the equation the whole subject reduces to: a command becomes legal at the maximum of every applicable deadline — never the minimum, and never the most recent.
- Related topic
Command Scheduling
An eight-step procedure for deciding when a command becomes temporally legal, worked through a full trace by hand — and a timing checker built in the opposite representation to the design, so the two cannot share a bug.
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.
