DDR · Module 14
tRC — Row Cycle Time
tRC equals tRAS plus tRP, verified against published figures. Deriving it is the easy part — the chapter is about what happens when one candidate command is constrained by several obligations at once.
Three chapters have each ended with the same admission: the parameter just taught is frequently not the one that decides when a command may issue.
This chapter stops deferring that. tRC is the natural place to do it, because tRC is the first parameter in the module that is not an independent fact:
tRC is the ACTIVATE-to-ACTIVATE interval for one bank, and it equals tRAS plus tRP.
Deriving that is ten minutes' work. What takes a chapter is the consequence: if tRC is a composition of other obligations, then a controller that tracks tRAS and tRP already has the information tRC contains — so what is tRC for, and which of the three actually controls?
1. The Obligation, in Five Parts
PARAMETER tRC
1 triggering event ACTIVATE accepted on bank B
2 constrained event the NEXT ACTIVATE addressed to bank B
3 resource bank B BANK-LOCAL
4 magnitude published in NANOSECONDS, resolved to cycles
5 sense MINIMUM SEPARATION
─────────────────────────────────────────────────────────────────
consequence next ACTIVATE to bank B not before
act_cycle + ceil(tRC / tCK)So tRCD, tRAS and tRC all share one triggering event: an accepted ACTIVATE on bank B. One command, three obligations, constraining three different future command classes with three different magnitudes.
That is the structural picture worth carrying out of this chapter:
┌──── tRCD ────▶ column command to B
ACTIVATE bank B ────┼──── tRAS ────▶ PRECHARGE of B
└──── tRC ────▶ next ACTIVATE to BThe follow-up questions:
What re-arms it? The next accepted ACTIVATE to the same bank — which is also the event it constrains. tRC is the only obligation so far whose constrained event is the same class as its trigger, which makes it a rate limit on one bank's row cycling.
What can dominate it? For the next ACTIVATE: tRP, measured from the intervening PRECHARGE, and tRRD and tFAW, which are not bank-local at all. §6 shows tRP and tRC trading control depending on when the precharge was issued.
Cycle or absolute-time component? Absolute time — verified below.
Does generation matter? Not for the structure.
2. A Derived Parameter — and Why That Is Interesting
Here is the question that makes tRC worth a chapter rather than a line.
A bank's row cycle is: activate it, use it, close it, activate it again. Two obligations already govern that path — tRAS from the ACTIVATE to the PRECHARGE, and tRP from the PRECHARGE to the next ACTIVATE. Chain them:
ACT ──── at least tRAS ────▶ PRE ──── at least tRP ────▶ ACT
so the next ACT cannot be earlier than tRAS + tRP after the firstThat derivation is not a proof that tRC equals tRAS + tRP. It shows that tRAS + tRP is a lower bound on the activate-to-activate interval along that path. Whether the device's published tRC equals that bound is an empirical question about the specification, and §3 checks it.
The distinction matters because the algebra is tempting and algebra is not semantics. Chapter 13.3 §5 warned against treating a derivation as evidence about a standard; here is a concrete instance.
3. Verifying the Relationship
That is a satisfying check, and it is worth being precise about what it establishes and what it does not.
It establishes that for this speed grade, tRC equals tRAS plus tRP, and that the residual is a clean round number rather than an awkward one — which would be an unlikely coincidence if the relationship were false.
It does not establish that the relationship is definitional for every DDR generation, nor that tRAS is the only path that can determine tRC. On that second point there is a real subtlety worth stating.
Resolving tRC at DDR4-3200, where tCK is 0.625 ns:
45.75 / 0.625 = 73.2 → ceil = 74 cyclesAnd a consistency check on the resolved values, which is a genuinely useful habit:
tRAS → ceil(32.00 / 0.625) = ceil(51.2) = 52 cycles
tRP → ceil(13.75 / 0.625) = ceil(22.0) = 22 cycles
sum = 74 cycles
tRC → ceil(45.75 / 0.625) = ceil(73.2) = 74 cycles ✓ agreeThey agree here, and they will not always. Each parameter is rounded up independently, so the sum of two ceilings can exceed the ceiling of the sum by up to one cycle. At DDR4-2400, tCK is 0.8333 ns:
tRAS → ceil(38.40) = 39
tRP → ceil(16.50) = 17
sum = 56 cycles
tRC → ceil(54.90) = 55 cycles ← one LESS than the sumOne cycle of disagreement, purely from independent rounding. Neither number is wrong. A controller that enforces both obligations separately waits 56 cycles where the published tRC permits 55 — a cycle of quantisation waste that is invisible unless you look for it, and which is the correct conservative behaviour. §13's last misconception is about mistaking this for a bug.
4. The Exact Legal Boundary
ACTIVATE accepted (sampled) on cycle N
resolved requirement D cycles
next ACTIVATE to bank B at M is LEGAL ⟺ M - N >= D
earliest legal M = N + D
forbidden cycles = N … N+D-1 exactly D of themSame convention as every other parameter in the module.
5. The Worked Trace
This is the module's substantial mixed-command analysis. It uses Chapter 13.4 §2's eight-step procedure and exercises ACTIVATE, READ, WRITE, PRECHARGE, and a second bank.
The command stream. Issued commands are given; candidates are analysed.
cycle 0 ACTIVATE bank 0 (issued)
cycle 2 ACTIVATE bank 1 (issued)
cycle 3 READ bank 0 candidate
cycle 5 WRITE bank 0 candidate
cycle 9 PRECHARGE bank 0 candidate
cycle 12 PRECHARGE bank 0 candidate
cycle 14 ACTIVATE bank 0 candidate
cycle 16 ACTIVATE bank 0 candidateEstablishing obligations from the issued commands
ACT bank 0 @ 0 arms tRCD(b0) : column cmd b0 from cycle 3
arms tRAS(b0) : PRE b0 from cycle 8
arms tRC (b0) : next ACT b0 from cycle 12
arms tRRD : next ACT any from cycle 2
ACT bank 1 @ 2 arms tRCD(b1) : column cmd b1 from cycle 5
arms tRAS(b1) : PRE b1 from cycle 10
arms tRC (b1) : next ACT b1 from cycle 14
arms tRRD : next ACT any from cycle 4Candidate 1 — READ bank 0 at cycle 3
Resources: bank 0, shared data path.
Semantic and state: bank 0 open on the right row — legal.
Applicable: tRCD(b0) → deadline 3. No prior column command, so no column-spacing obligation yet.
Maximum: 3, controlling tRCD.
Verdict: 3 >= 3 — LEGAL, issue.
Update: the read's data occupies the bus; a subsequent column command acquires a spacing obligation, which Chapter 14.6 owns and this trace keeps out of the way by spacing the commands.
Candidate 2 — WRITE bank 0 at cycle 5
Applicable: tRCD(b0) → 3, already satisfied.
Maximum: 3. Verdict: 5 >= 3 — LEGAL, issue.
Update — and this is the important one: CWL is 2, so the write's data begins at cycle 7 and the burst occupies 2 cycles, so the last write data is at cycle 8. tWR is measured from that data event, not from the command:
tWR(b0) : PRE b0 not before 8 + 5 = cycle 13Note what just happened. The write command was at cycle 5, and it created a precharge obligation with a deadline of 13 — later than tRAS's deadline of 8. A designer tracking only tRAS would think bank 0 became closeable at cycle 8.
Candidate 3 — PRECHARGE bank 0 at cycle 9
Resources: bank 0. State: bank 0 open — legal. Applicable: two obligations now.
tRAS(b0) → deadline 8 satisfied at 9
tWR (b0) → deadline 13 NOT satisfied at 9Maximum: max(8, 13) = 13, controlling tWR.
Verdict: 9 < 13 — ILLEGAL, short by 4.
This is §30's composition case. tRAS is satisfied and the command is still illegal, because a second obligation on the same candidate expires later. A controller consulting only tRAS — which is exactly what Chapter 14.3 §6's block on its own would do — issues here and violates tWR.
Candidate 4 — PRECHARGE bank 0 at cycle 12
Applicable: tRAS(b0) → 8; tWR(b0) → 13.
Maximum: 13, controlling tWR.
Verdict: 12 < 13 — ILLEGAL, short by 1.
Worth including precisely because it is short by one. A design with an off-by-one and a missing tWR would issue here and the violation would look like a convention bug rather than a missing obligation.
Candidate 5 — ACTIVATE bank 0 at cycle 14
Assume the precharge was issued at cycle 13, the first legal cycle.
Resources: bank 0, plus whatever governs activate rate. State: bank 0 closed — legal. Applicable: three obligations.
tRP (b0) → 13 + 4 = deadline 17
tRC (b0) → 0 + 12 = deadline 12 satisfied
tRRD → 2 + 2 = deadline 4 satisfiedMaximum: max(17, 12, 4) = 17, controlling tRP.
Verdict: 14 < 17 — ILLEGAL, short by 3.
And here is the pay-off of the whole chapter. tRC's deadline is 12 and it is satisfied — yet the activate is illegal, because tRP from the actual precharge is later. §2 predicted this: the chain bound tRAS + tRP = tRC assumes the precharge happened as early as legally possible, and here it did not, because tWR delayed it.
Candidate 6 — ACTIVATE bank 0 at cycle 17
Maximum: max(17, 12, 4) = 17. Verdict: 17 >= 17 — LEGAL.
The whole trace in one table
| Cycle | Candidate | State | tRCD | tRAS | tWR | tRP | tRC | tRRD | max | Controlling | Verdict |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 3 | READ b0 | ✓ | 3 | — | — | — | — | — | 3 | tRCD | ✓ issue |
| 5 | WRITE b0 | ✓ | 3 | — | — | — | — | — | 3 | tRCD | ✓ issue |
| 9 | PRE b0 | ✓ | — | 8 | 13 | — | — | — | 13 | tWR | ✗ short 4 |
| 12 | PRE b0 | ✓ | — | 8 | 13 | — | — | — | 13 | tWR | ✗ short 1 |
| 14 | ACT b0 | ✓ | — | — | — | 17 | 12 | 4 | 17 | tRP | ✗ short 3 |
| 17 | ACT b0 | ✓ | — | — | — | 17 | 12 | 4 | 17 | tRP | ✓ issue |
Now change one prior event
Make the write a read. Candidate 2 becomes a READ at cycle 5 instead of a WRITE. tWR is never armed. Recompute:
Candidate 3, PRE b0 @ 9: applicable = tRAS(b0) → 8
max = 8, 9 >= 8 → LEGAL (was illegal)
Candidate 5, ACT b0 @ 14: PRE issued at 9, so tRP → 9 + 4 = 13
tRC → 12, tRRD → 4
max(13, 12, 4) = 13, 14 >= 13 → LEGAL (was illegal)One changed command moved two verdicts from illegal to legal, four cycles earlier, and the controlling constraint on the final activate changed from tRP-at-17 to tRP-at-13 — same parameter, different deadline, because its triggering event moved.
Make the precharge late instead. Keep the write, but suppose the scheduler had something better to do and issued the precharge at cycle 20 rather than 13:
Candidate 5', ACT b0: tRP → 20 + 4 = 24
tRC → 12, tRRD → 4
max(24, 12, 4) = 24 → controlling tRP, deadline 24tRC is now irrelevant by a margin of twelve cycles. This is §2's point in numbers: tRC bounds how fast a bank can cycle, and once the precharge is late, tRP from the actual precharge is what decides. A controller that enforced only tRC here would permit an activate at cycle 12 and violate tRP by twelve cycles.
The same composition, as a trace. This window covers §5's candidates 3 through 6, so the cycle numbers match the analysis above.
Composing two deadlines on one candidate
10 cyclesNote the controlling row: it reads tWR from cycle 5 onward, including the three cycles when the tWR obligation has not yet been armed. That is Chapter 14.5 §7's pending-window point arriving in the composition — the obligation's deadline is known as soon as the write is issued, even though its countdown has not started.
6. RTL — A Composite Deadline Calculator
Collision check. Chapter 13.3's deadline_scoreboard reduces abstract obligations to a maximum with timestamps — scope-agnostic and parameter-agnostic. Chapter 14.1's and 14.3's guards each own per-bank countdowns for specific obligations. Chapter 11.4's write_recovery_guard owns tWR.
What none of them does is compare a composed bound against a directly published one and report which governs. That is this block's job, and it is the concrete hardware form of §2's two-purposes argument.
The engineering problem. A controller can bound the next ACTIVATE two ways: from the published tRC against the last activate, or from tRP against the actual precharge. Both are valid obligations and both must be honoured — so the answer is the maximum, and a designer needs to know which one is binding because that determines what to change if the answer is unsatisfactory.
Classification: controller-side combinational reduction over sequential timing state.
// ─────────────────────────────────────────────────────────────────────
// row_cycle_composite
//
// CLASSIFICATION
// Controller-side timing bookkeeping plus a combinational maximum
// reduction. Counts cycles and compares numbers.
//
// WHAT IT MODELS
// The next-ACTIVATE decision for one bank, composed from the two
// obligations that bound it:
// tRC : trigger = ACTIVATE accepted on b, constrains next
// ACTIVATE to b. BANK-LOCAL, minimum
// tRP : trigger = PRECHARGE accepted on b, constrains next
// ACTIVATE to b. BANK-LOCAL, minimum
// and reports WHICH governs, because §5 showed control changes with
// history rather than with configuration.
//
// WHAT IT DOES NOT MODEL
// Any physical process. Also, deliberately, NOT the whole applicable
// set: tRRD and tFAW also constrain an ACTIVATE and are NOT
// bank-local (Chapters 14.7, 14.8). A consumer must take the maximum
// of THIS block's answer and those, which is why the output is named
// bank_local_deadline rather than activate_legal.
//
// WHY A MAXIMUM AND NOT A SUM
// §3 verified tRC = tRAS + tRP, so a sum is tempting. But the two
// obligations here have DIFFERENT TRIGGERS at different times: tRC
// from the last ACTIVATE, tRP from the actual PRECHARGE. They run
// CONCURRENTLY, so composing them is a max. Summing would serialise
// obligations that overlap -- Chapter 13.3 §3's third wrong reading.
//
// CONVENTION (Chapter 13.2 §6)
// Trigger accepted on cycle N with magnitude D forbids the
// constrained command on N .. N+D-1, permits from N+D. One command
// per cycle, so countdowns load D-1.
//
// SIMULTANEITY
// activate and expiry same cycle -> ACTIVATE WINS, tRC re-arms.
// precharge and expiry same cycle -> PRECHARGE WINS, tRP re-arms.
// activate and precharge same cycle -> impossible on one command
// bus; the encoding cannot express it.
// reset -> both abandoned; a reset controller has no history.
// ─────────────────────────────────────────────────────────────────────
module row_cycle_composite #(
parameter int BANKS = 16,
parameter int MAX_CYCLES = 128,
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, independently supplied. §3 showed that
// ceil(tRAS)+ceil(tRP) can exceed ceil(tRC) by a cycle, so these
// must NOT be derived from one another in hardware.
input logic [CNT_W-1:0] trc_cycles,
input logic [CNT_W-1:0] trp_cycles,
// ── Triggering events, on acceptance.
input logic act_accepted,
input logic [BK_W-1:0] act_bank,
input logic pre_accepted,
input logic [BK_W-1:0] pre_bank,
// ── Candidate ACTIVATE under evaluation.
input logic [BK_W-1:0] cand_bank,
// ── The composed verdict for the BANK-LOCAL obligations only.
output logic bank_local_ok,
// Cycles remaining on the CONTROLLING bank-local obligation -- the
// maximum of the two, which is §5's arithmetic in hardware.
output logic [CNT_W-1:0] bank_local_remaining,
// ── WHICH obligation governs. This is the output the chapter exists
// for: it answers "what would I have to change to issue sooner?"
// 0 = neither outstanding, 1 = tRC governs, 2 = tRP governs,
// 3 = both outstanding and equal.
output logic [1:0] governing,
// ── Observability for §5's lesson: tRC satisfied while tRP is not.
// Published so coverage can prove the case was reached.
output logic trc_met_trp_not
);
if (BANKS < 1) begin : g_banks
initial $fatal(1, "row_cycle_composite: BANKS must be >= 1");
end
if (MAX_CYCLES < 0) begin : g_max
initial $fatal(1, "row_cycle_composite: MAX_CYCLES must be >= 0");
end
logic [CNT_W-1:0] trc_cnt [BANKS];
logic [CNT_W-1:0] trp_cnt [BANKS];
logic [CNT_W-1:0] rc_rem, rp_rem;
always_comb begin
rc_rem = trc_cnt[cand_bank];
rp_rem = trp_cnt[cand_bank];
// ── The composition. A MAXIMUM, per the header: the two
// obligations were armed by different events at different times
// and run concurrently.
bank_local_remaining = (rc_rem > rp_rem) ? rc_rem : rp_rem;
bank_local_ok = (bank_local_remaining == '0);
// Which governs, with an explicit tie code so a diagnostic never
// has to guess. Reporting a tie as "tRC" would be true and
// misleading -- reducing tRC alone would not help.
if (bank_local_remaining == '0) governing = 2'd0;
else if (rc_rem > rp_rem) governing = 2'd1;
else if (rp_rem > rc_rem) governing = 2'd2;
else governing = 2'd3;
// §5's candidate-5 situation: the composed parameter is satisfied
// and the command is still illegal.
trc_met_trp_not = (rc_rem == '0) && (rp_rem != '0);
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (int unsigned b = 0; b < BANKS; b++) begin
trc_cnt[b] <= '0;
trp_cnt[b] <= '0;
end
end else begin
for (int unsigned b = 0; b < BANKS; b++) begin
if (trc_cnt[b] != '0) trc_cnt[b] <= trc_cnt[b] - 1'b1;
if (trp_cnt[b] != '0) trp_cnt[b] <= trp_cnt[b] - 1'b1;
end
if (act_accepted) begin
trc_cnt[act_bank] <= (trc_cycles >= CNT_W'(1))
? (trc_cycles - CNT_W'(1)) : '0;
end
if (pre_accepted) begin
trp_cnt[pre_bank] <= (trp_cycles >= CNT_W'(1))
? (trp_cycles - CNT_W'(1)) : '0;
end
end
end
endmoduleInterface contract. bank_local_ok is not an activate-legal signal and is deliberately not named as one. tRRD and tFAW also constrain an ACTIVATE and are not bank-local, so a consumer must conjoin this block's answer with theirs. Naming the output for its scope rather than its apparent purpose is the defence against a consumer assuming completeness — the same discipline Chapter 14.3 §6 applied to precharge_blocked_by_tras.
Parameter contract. trc_cycles and trp_cycles are independent inputs. §3 showed that ceil(tRAS) + ceil(tRP) can exceed ceil(tRC) by a cycle from independent rounding, so a design that computed one magnitude from the others would disagree with the published table by a cycle in one direction or the other.
Why the reduction is a maximum. This is the block's central design claim and it is worth restating: the two obligations have different triggers at different times and are therefore concurrent, so Chapter 13.3 §3's rule applies — the maximum. The fact that the magnitudes satisfy tRC = tRAS + tRP is a statement about the numbers, not about how the obligations compose at runtime. Summing them would serialise obligations that overlap.
Corner cases. Both counters zero: governing reports 0 and bank_local_remaining is 0. Both equal and nonzero: governing reports the tie code 3, which matters because Chapter 13.4 §12's exercise 2 showed a tie means every tied obligation must improve together. trc_cycles == 0: tRC never binds, which is not a realistic configuration and is legal. BANKS == 1: degenerates correctly, and note this is the configuration in which tRRD and tFAW have nothing to constrain, so the block's incompleteness is invisible.
Synthesis implications. 2 × BANKS counters, two read muxes, one comparator and a small encoder. The comparator is the only part that is not a copy of earlier chapters' logic.
Failure modes. Summing instead of maximising — the headline error, and it produces a design that waits tRC + tRP after an activate, which is safe, slow and completely wrong about why. Reporting a tie as one of the two obligations: a debugger reduces that parameter and sees no improvement. Deriving trc_cycles from trp_cycles plus a stored tRAS: disagrees with the published tRC by a cycle at some speed grades, in whichever direction the independent rounding happens to fall.
7. Four Assertions Worth Writing
// ── P1. THE property of this block: the composed answer is the
// MAXIMUM of the two obligations, not their sum and not either one
// alone. Written as the two halves of "is the maximum": it equals
// one of them, and it is at least as large as both.
// Catches the §6 headline failure -- summing concurrent
// obligations, which is safe, slow and silent.
property p_composition_is_a_maximum;
@(posedge clk) disable iff (!rst_n)
( ( (bank_local_remaining == trc_cnt[cand_bank])
|| (bank_local_remaining == trp_cnt[cand_bank]) )
&& (bank_local_remaining >= trc_cnt[cand_bank])
&& (bank_local_remaining >= trp_cnt[cand_bank]) );
endproperty
a_composition_is_a_maximum: assert property (p_composition_is_a_maximum);
// ── P2. The governing report agrees with the arithmetic, INCLUDING
// the tie. Reporting a tie as one parameter would be true and
// misleading: Chapter 13.4 §12's exercise 2 showed a tie means
// every tied obligation must improve together, so a debugger told
// "tRC governs" would reduce tRC and see no change.
property p_governing_is_accurate;
@(posedge clk) disable iff (!rst_n)
( (governing == 2'd0) |-> (bank_local_remaining == '0) )
and ( (governing == 2'd1) |-> (trc_cnt[cand_bank] > trp_cnt[cand_bank]) )
and ( (governing == 2'd2) |-> (trp_cnt[cand_bank] > trc_cnt[cand_bank]) )
and ( (governing == 2'd3) |-> ( (trc_cnt[cand_bank] == trp_cnt[cand_bank])
&& (bank_local_remaining != '0) ) );
endproperty
a_governing_is_accurate: assert property (p_governing_is_accurate);
// ── P3. Each obligation is armed by its OWN trigger to §5's
// convention, independently. Catches a block that arms tRC from a
// precharge or tRP from an activate -- a wiring error that produces
// plausible-looking counts and completely wrong deadlines.
// genvar-wrapped: a procedural for-loop is illegal in a property.
generate
for (genvar gb = 0; gb < BANKS; gb++) begin : g_arming
property p_trc_armed_by_activate;
@(posedge clk) disable iff (!rst_n)
( $past(act_accepted, 1) && ($past(act_bank, 1) == BK_W'(gb))
&& ($past(trc_cycles, 1) >= CNT_W'(2)) )
|-> (trc_cnt[gb] == ($past(trc_cycles, 1) - CNT_W'(1)));
endproperty
a_trc_armed_by_activate: assert property (p_trc_armed_by_activate);
property p_trp_armed_by_precharge;
@(posedge clk) disable iff (!rst_n)
( $past(pre_accepted, 1) && ($past(pre_bank, 1) == BK_W'(gb))
&& ($past(trp_cycles, 1) >= CNT_W'(2)) )
|-> (trp_cnt[gb] == ($past(trp_cycles, 1) - CNT_W'(1)));
endproperty
a_trp_armed_by_precharge: assert property (p_trp_armed_by_precharge);
end
endgenerate
// ── C1. The §5 candidate-5 situation is REACHED: tRC satisfied while
// tRP is not. A cover, not an assert, because the claim is that the
// two CAN differ -- and a run that never reaches it has tested the
// composition only in the case where it does not matter.
c_trc_met_trp_not: cover property (@(posedge clk) disable iff (!rst_n)
trc_met_trp_not);What these prove. That the composition is genuinely a maximum in both directions; that the governing report is accurate including the tie; that each obligation is armed by its own trigger; and that the case the chapter is about actually occurs in the stimulus.
What these do not prove. That trc_cycles and trp_cycles are the right values — inputs, and §3's independent-rounding arithmetic means a design deriving one from the others would satisfy every property here while disagreeing with the published table by a cycle. That the full applicable set was consulted: this block knows nothing of tRRD or tFAW, so a design relying on it alone violates both. And nothing about tWR, which §5's trace showed controlling the precharge that these obligations are measured around.
Vacuity. P3's two properties require a magnitude of at least 2 and an event to the specific bank; C1 requires the divergent case. Cover all three, or P1 is checking a block where one counter is permanently zero.
8. What tRC Does Not Cover
A short section, because the omission is load-bearing.
tRC is bank-local. It constrains the next ACTIVATE to the same bank. It says nothing whatever about activating a different bank.
ACT bank 0 @ 0, tRC = 12
→ next ACT bank 0 not before cycle 12
→ ACT bank 1 at cycle 1 is completely unconstrained by tRCThat is correct and it is also the seed of a serious misconception, because activating different banks is not unconstrained — it is constrained by obligations with a different scope. Chapter 14.7 owns the activate-to-activate spacing that applies across banks, and Chapter 14.8 owns the rolling-window limit on how many activates may occur in a window regardless of bank.
A controller that reasoned “tRC is bank-local, so different banks may be activated freely” would violate both. §12's fifth misconception is this, and §5's trace included tRRD specifically so its deadline would appear in an activate candidate's applicable set and not be forgotten.
9. DV — Independent Reconstruction
Invert the representation. The design holds two countdowns; the checker records act_cycle[bank] and pre_cycle[bank] and computes two deadlines by addition, then takes the maximum. Timestamps against countdowns, as Chapter 13.4 §8 requires.
Resolve each magnitude independently from its own table entry. And tRC adds a specific trap that §3 identified: a checker that computes tRC as tRAS + tRP will agree with a design that does the same, and both can then be a cycle away from the published value. The checker must read tRC from the table, precisely so that a design deriving it produces a disagreement.
Report the whole applicable set, with the controlling one marked. §5 showed that the interesting information is which obligation controlled, and that a satisfied obligation appearing in the list is informative rather than noise.
TIMING VIOLATION
command : ACTIVATE, bank 0
issued at : cycle 14
resource : bank 0
applicable : tRP -> legal from 17 CONTROLLING
tRC -> legal from 12 satisfied
tRRD -> legal from 4 satisfied
tFAW -> not full
violated : tRP
source event : PRECHARGE bank 0, cycle 13
short by : 3 cycles
note : tRC was satisfied; the composed bound assumed an
earliest-legal precharge, and this precharge was
delayed by tWR to cycle 13That closing note is the most valuable line in any report in this module. It pre-empts the exact objection a designer will raise — but tRC says twelve — and explains in one sentence why the composed bound did not apply. A checker that can say that converts a confusing violation into a closed question.
10. Debugging
Symptom. An ACTIVATE violates timing on a bank, and the controller's tRC accounting says the bank was available.
| Candidate mechanism | Evidence | Discriminator |
|---|---|---|
| Only tRC enforced; tRP from the actual precharge ignored | Shortfall grows with how late the precharge was | §5's candidate 5. Correlate the shortfall with pre_cycle − (act_cycle + tRAS). If it tracks, this is it. |
| Obligations summed rather than maximised | No violation; the bank cycles far slower than tRC permits | §6's headline failure. Compare measured activate-to-activate against the published tRC. |
tRC derived as tRAS + tRP in the design | Off by exactly one cycle at some speed grades and not others | §3's independent-rounding arithmetic. Compute both at the failing grade. |
| Different-bank activates unconstrained | Violations on activates to other banks | §8. tRRD and tFAW were not consulted. |
| tWR omitted, so the precharge was early | Precharge violations before the activate violations | §5's candidate 3. Fix the precharge side first; the activate symptom may be secondary. |
| Tie misreported | A parameter was reduced and nothing improved | §6's governing tie code. |
The discriminator that splits this table fastest is whether the shortfall is constant or varies with the precharge timing. A constant shortfall points at a magnitude or convention error. A shortfall that grows the later the precharge was issued is diagnostic of tRC-only enforcement, because that is precisely the quantity the composed bound assumed away.
Responsible layer. If the bank is cycling at exactly the published tRC and the workload wants more, nothing is wrong with the timing logic — the answer is more banks or a different access pattern, which is Module 18's address mapping and Module 23's performance work.
11. Common Misconceptions
“tRC is just another unrelated constant.”
Tempting because it sits in the table beside tRCD and tRP looking like a peer. Why it is wrong: it is a composition — verified as tRAS + tRP at a real speed grade — so it carries no information a controller tracking tRAS and tRP lacks. Consequence: engineers hunt for a separate mechanism behind it and find none, or treat it as independently tunable when it is not. Replacement model: a published bound on one bank's cycling rate, equal to the sum of the two obligations along the fastest path. Debugging clue: changing tRC without changing tRAS or tRP produces a configuration that contradicts itself.
“Since tRC = tRAS + tRP, a controller should add the obligations.”
Tempting because the magnitudes genuinely sum. Why it is wrong: the magnitudes sum; the obligations are concurrent, armed by different events at different times, so they compose by maximum (Chapter 13.3 §3). Consequence: a bank that cycles at roughly tRC + tRP — safe, substantially slower than the device permits, and with no assertion firing. Replacement model: sum the magnitudes on paper, maximise the deadlines in hardware. Debugging clue: measured activate-to-activate exceeds published tRC with no violations anywhere.
“If tRC is satisfied, the next ACTIVATE is legal.”
Tempting because tRC is the activate-to-activate parameter, so it looks authoritative. Why it is wrong: §5's candidate 5 — tRC satisfied at 12, tRP from the actual precharge not until 17. The composed bound assumed an earliest-legal precharge. Consequence: a violation whose size equals however late the precharge was, so it is workload-dependent and looks non-deterministic. Replacement model: tRC is one member of the applicable set. Debugging clue: the shortfall grows the later the precharge was issued.
“tRC is bank-local, so different banks can be activated freely.” Tempting because the first half is true and the inference feels immediate. Why it is wrong: §8 — different-bank activates are constrained by obligations at a different scope, tRRD and tFAW. Bank-locality of one obligation is not freedom from all obligations. Consequence: back-to-back activates across banks violating spacing and window limits, with a failure rate that rises with bank parallelism. Replacement model: per-candidate applicable sets, gathered by resource. Debugging clue: single-bank tests pass; multi-bank tests fail.
“The resolved cycle counts must satisfy tRC = tRAS + tRP too.”
Tempting because the nanosecond values do, exactly. Why it is wrong: each is rounded up independently, so the sum of two ceilings can exceed the ceiling of the sum — §3 showed a one-cycle disagreement at DDR4-2400. Consequence: an engineer finds the inconsistency and “fixes” it by deriving one magnitude from the others, which then disagrees with the published table. Replacement model: the relationship holds in absolute time; in cycles it holds to within one cycle of rounding. Debugging clue: a consistency check that fails at exactly one speed grade.
“A controller enforcing both tRAS+tRP and tRC is wasting a cycle, so one is redundant.” Tempting because §3's DDR4-2400 arithmetic shows the separate obligations costing 56 cycles where tRC permits 55. Why it is wrong: it is not waste, it is correct conservative behaviour — each obligation is individually binding and each was rounded up legitimately. Dropping either to recover the cycle means violating a published minimum. Consequence: removing a real obligation to reclaim a cycle of quantisation, which is Chapter 13.2 §11's warning exactly: a correct conservatism removed becomes intermittent corruption. Replacement model: one cycle is the price of integer cycles. Debugging clue: if the proposed saving equals one cycle, suspect rounding rather than a bug.
12. Interview Reasoning
“What is the relationship among tRAS, tRP and the next ACTIVATE?”
Chain them: tRAS bounds ACTIVATE to PRECHARGE, tRP bounds PRECHARGE to the next ACTIVATE, so tRAS + tRP lower-bounds activate-to-activate along that path — and that is what tRC publishes. It checks out against real figures: at one DDR4-3200 grade, tRC is 45.75 ns and tRP is 13.75 ns, leaving exactly 32.00 ns for tRAS. The caveat worth volunteering is that the chain assumes the precharge was issued as early as legally possible, which is the best case and often not what happened.
“If tRC = tRAS + tRP, should a controller add the two obligations?”
No — maximise. The magnitudes sum, but the obligations are armed by different events at different times and run concurrently, so composing them is a maximum. Adding them serialises delays that overlap and produces a bank cycling at roughly tRC + tRP: safe, noticeably slower than the part permits, and with nothing failing to indicate why.
“tRC is satisfied. Is the next ACTIVATE legal?”
Not necessarily, and the reason is the useful part. tRP measured from the actual precharge can be later, because tRC's bound assumed an earliest-legal precharge and the real one may have been delayed — by tWR after a write, or simply by a scheduler that had better things to do. And tRC is bank-local, so tRRD and tFAW are also in the applicable set. The answer is the maximum over all of them.
“Three constraints give deadlines 17, 12 and 4. When is the ACTIVATE legal, and what would you change to improve it?”
Cycle 17, since legality is a conjunction of lower bounds and the largest binds. What to change is the second half and the more important one: only the constraint holding 17 is worth touching. Reducing the one at 12 or 4 changes nothing at all until it would exceed 17 — which is why a timing block should report the controlling obligation and not merely that it is blocking.
“Why can ceil(tRAS) + ceil(tRP) differ from ceil(tRC)?”
Because each is rounded up independently and ceilings do not distribute over addition. At DDR4-2400 with 32.0 and 13.75 ns, the separate ceilings are 39 and 17 for 56 total, while tRC's 54.9 ns resolves to 55 — one cycle apart. Neither is wrong, a controller enforcing both waits the longer, and that extra cycle is quantisation rather than a bug. The trap is “fixing” it by deriving one magnitude from the others.
“tRC is bank-local. Can I activate other banks as fast as I like?” No. Bank-locality of tRC means tRC does not constrain them; it does not mean nothing does. Activate-to-activate spacing across banks and the rolling activate-window limit both apply, at coarser scopes. The failure signature if you get this wrong is memorable: single-bank tests pass and the violation rate rises as you use more banks — the design fails more the better it does its job.
13. Engineering Exercises
1. Verify the relationship at a second grade. A speed grade publishes tRP as 13.75 ns and tRC as 46.25 ns. What tRAS does the relationship imply, and how does that compare with the 32.00 ns implied by §3's grade? What would you conclude?
Worked: 46.25 − 13.75 = 32.50 ns. That is 0.5 ns more than the other grade's 32.00 — not a round number in the same way. Two readings are possible: this grade genuinely specifies a slightly longer tRAS, or the tRC figure carries margin for a different limiting path (§3's caveat). The honest conclusion is that the relationship is consistent to within a rounding step and that the exact tRAS should be read from the table rather than inferred — which is why §9 tells the checker not to derive it.
2. Find the rounding disagreement. At DDR4-2933, tCK is approximately 0.682 ns. Resolve tRAS of 32.0 ns, tRP of 13.75 ns and tRC of 45.75 ns. Do the separate ceilings sum to the tRC ceiling?
Worked: tRAS ceil(46.92) = 47; tRP ceil(20.16) = 21; sum 68. tRC ceil(67.08) = 68. They agree at this grade. Combined with §3's DDR4-3200 agreement and DDR4-2400 disagreement, the lesson is that the discrepancy appears at some grades and not others — so a consistency check that passes is not evidence the derivation is safe.
3. Work the trace cold. Using §5's educational magnitudes, analyse a PRECHARGE to bank 1 at cycle 14, given ACTIVATE bank 1 at cycle 2 and a READ to bank 1 at cycle 6 whose burst ends at cycle 10. Show the applicable set and the controlling obligation.
Worked: resources bank 1. Applicable: tRAS(b1) → 2 + 8 = 10. No write, so tWR is not armed. A read-to-precharge obligation exists in a real device; §5's educational set omits it, so the honest answer notes that the set as given is incomplete for a post-read precharge. With the given set: max is 10, 14 >= 10 — legal, controlling tRAS. Recognising the missing obligation is the point of the exercise.
4. Change one event twice. In §5's trace, first move the ACTIVATE to bank 1 from cycle 2 to cycle 10, then instead move the PRECHARGE of bank 0 from 13 to 30. For each, recompute candidate 5 and say which obligation controls.
Worked: moving the bank-1 activate changes tRRD's deadline to 12 and does not touch bank 0's obligations, so candidate 5's max is still max(17, 12, 12) = 17, controlling tRP — the change is invisible, which is Chapter 13.4 §4's lesson about non-controlling constraints. Moving the precharge to 30 makes tRP's deadline 34, so max is 34 and tRP controls by a margin of 22 over tRC — the composed parameter becomes irrelevant.
5. Price the wrong reduction. A design sums the two obligations instead of maximising. Using §5's magnitudes with the precharge at cycle 13, compute the activate cycle the design permits versus the correct one, and say why no assertion fires.
Worked: correct is max(12, 17) = 17. Summing the remaining counts at cycle 13 gives roughly (12−13 → 0) + 4 = 4 more cycles, but the instructive version is summing the deadlines' magnitudes: a design waiting tRC + tRP = 16 cycles after the precharge permits an activate at 29 instead of 17 — twelve cycles late, every row cycle. No assertion fires because every obligation is satisfied; the design is merely slow, which is Chapter 13.3 §4's safe-and-slow failure reached by arithmetic rather than by scope.
6. Name the missing scope. §6's block outputs bank_local_ok. List every obligation a real ACTIVATE candidate is subject to that this block does not know about, and say what a consumer must do.
7. Write the tie case. Construct magnitudes and a command history in which tRC and tRP have equal remaining counts on an activate candidate. What does §6's governing report, and why would reporting either parameter individually be misleading?
14. Summary
tRC constrains the next ACTIVATE to the same bank from the accepted ACTIVATE on that bank — bank-local, in nanoseconds, a minimum separation. It shares its trigger with tRCD and tRAS, so one accepted activate arms three obligations with three magnitudes.
Unlike its neighbours, tRC is derived. Chaining tRAS and tRP lower-bounds activate-to-activate at tRAS + tRP, and the relationship checks out against published figures: at one DDR4-3200 grade, tRC is 45.75 ns and tRP is 13.75 ns, leaving exactly 32.00 ns for tRAS. The chapter is explicit that other paths — through a read-to-precharge obligation, or through tWR after a write — can also limit the interval, and that their exact composition could not be verified from primary documentation.
In cycles the relationship holds only to within a rounding step, because each magnitude is ceiled independently: at DDR4-2400 the separate obligations total 56 cycles where the published tRC resolves to 55. A controller enforcing both waits the longer, and that cycle is quantisation rather than a bug.
The obligations compose by maximum, not by sum — the magnitudes add, but the obligations are armed by different events at different times and run concurrently, so Chapter 13.3 §3's rule applies. Summing produces a bank cycling at roughly tRC + tRP: safe, slower than the part permits, silent.
§5's trace is the module's proof that definitions are not analysis. Six candidates, and every verdict turned on which obligations applied and which deadline was largest. tRAS satisfied while the precharge was illegal because tWR expired later. tRC satisfied while the activate was illegal because tRP from the actual precharge expired later. And changing one WRITE to a READ moved two verdicts and four cycles.
row_cycle_composite maximises the two bank-local obligations and reports which governs, including an explicit tie code — because Chapter 13.4 §4 established that only the controlling obligation is worth changing. Its output is named bank_local_ok rather than activate_legal because tRRD and tFAW constrain an activate at coarser scopes and this block does not know about them.
15. What Comes Next
Every obligation in the module so far has been bank-local, and §5's trace quietly carried one that was not: tRRD appeared in both activate candidates' applicable sets with a deadline derived from the other bank's activate.
That was a placeholder. The next chapter takes the parameter this one has leaned on hardest without owning: Chapter 14.5 makes tWR precise — and in particular makes precise the thing §5 depended on entirely, that tWR is measured from a data event rather than from the WRITE command. Get that trigger wrong and every precharge after a write is early, which is the most consequential trigger error in DDR timing.
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.
