Skip to content
VLSI Mentor

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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
                       ┌──── tRCD ────▶  column command to B
   ACTIVATE bank B ────┼──── tRAS ────▶  PRECHARGE of B
                       └──── tRC  ────▶  next ACTIVATE to B

The 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  ACT ──── at least tRAS ────▶ PRE ──── at least tRP ────▶ ACT

  so the next ACT cannot be earlier than   tRAS + tRP   after the first

That 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  45.75 / 0.625  =  73.2        →  ceil  =  74 cycles

And a consistency check on the resolved values, which is a genuinely useful habit:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  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   ✓ agree

They 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  tRAS  →  ceil(38.40)  =  39
  tRP   →  ceil(16.50)  =  17
  sum                   =  56 cycles

  tRC   →  ceil(54.90)  =  55 cycles      ← one LESS than the sum

One 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  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 them

Same 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  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                  candidate

Establishing obligations from the issued commands

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  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  4

Candidate 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 >= 3LEGAL, 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 >= 3LEGAL, 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  tWR(b0) : PRE b0 not before  8 + 5  =  cycle 13

Note 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  tRAS(b0)  →  deadline  8      satisfied at 9
  tWR (b0)  →  deadline 13      NOT satisfied at 9

Maximum: max(8, 13) = 13, controlling tWR. Verdict: 9 < 13ILLEGAL, 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 < 13ILLEGAL, 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  tRP (b0)  →  13 + 4  =  deadline 17
  tRC (b0)  →   0 + 12 =  deadline 12      satisfied
  tRRD      →   2 + 2  =  deadline  4      satisfied

Maximum: max(17, 12, 4) = 17, controlling tRP. Verdict: 14 < 17ILLEGAL, 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 >= 17LEGAL.

The whole trace in one table

CycleCandidateStatetRCDtRAStWRtRPtRCtRRDmaxControllingVerdict
3READ b033tRCD✓ issue
5WRITE b033tRCD✓ issue
9PRE b081313tWR✗ short 4
12PRE b081313tWR✗ short 1
14ACT b01712417tRP✗ short 3
17ACT b01712417tRP✓ 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  Candidate 5', ACT b0:  tRP → 20 + 4 = 24
                         tRC → 12,  tRRD → 4
                         max(24, 12, 4) = 24  →  controlling tRP, deadline 24

tRC 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.

One activate arming three obligations, and the composition that follows. An accepted activate on a bank arms three bank-local obligations at once: the shorter column-command obligation, the longer precharge obligation, and the longest next-activate obligation. Separately, a write to the same bank creates a further precharge obligation measured from the last write data rather than from the command, and that obligation can expire later than the activate-derived one. When a precharge candidate is evaluated, the decision takes the maximum over every applicable obligation and reports which one controlled. When a subsequent activate candidate is evaluated, the obligation derived from the actual precharge can be later than the next-activate obligation, so the composed parameter is not the controlling one.ACTIVATE b0WRITE dataApplicable setMaximumVerdictarms tRCD, tRAS, tRCarms tWR from lastdataPRE: tRAS=8, tWR=13max = 13 — tWRcontrolsACT: tRP=17, tRC=12,tRRD=4max = 17 — tRPcontrolsissue arms the nextset

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 cycles
Ten cycles covering cycles five through fourteen of the worked trace. The tRAS obligation on bank zero was armed by the activate at cycle zero with a deadline of cycle eight, so its remaining count falls to zero by cycle eight. The write recovery obligation was created by the write at cycle five but its trigger is the last write data at cycle eight, so it is pending for three cycles and then counts down from five, giving a deadline of cycle thirteen. The composed deadline is the maximum of the two and is therefore thirteen throughout, so write recovery is the controlling obligation from the moment the write is issued even before its obligation is armed. A precharge candidate is refused at cycle nine and at cycle twelve, and becomes legal at cycle thirteen when the later of the two deadlines is finally met.tRAS counting, tWR pendingtRAS counting, tWR pendingtRAS met; tWR controlstRAS met; tWR controlsboth metboth mettRAS satisfied — still illegaltRAS satisfied — stillillegalcycle 9 refused, short by 4cycle 9 refused, short by 4cycle 12 refused, short by 1cycle 12 refused, short by1cycle 13 — max met, legalcycle 13 — max met, legalCKcycle567891011121314tRAS rem3210000000tWR phasePENDPENDPENDOBLOBLOBLOBLOBLOBLOBLtWR rem0005432100max deadline13131313131313131313controllingtWRtWRtWRtWRtWRtWRtWRtWRnonenonePRE offeredPRE legalt0t1t2t3t4t5t6t7t8t9

Note 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────
//  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

endmodule

Interface 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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  ACT bank 0 @ 0,  tRC = 12
     → next ACT bank 0 not before cycle 12
     → ACT bank 1 at cycle 1 is completely unconstrained by tRC

That 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  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 13

That 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 mechanismEvidenceDiscriminator
Only tRC enforced; tRP from the actual precharge ignoredShortfall 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 maximisedNo 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 designOff 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 unconstrainedViolations on activates to other banks§8. tRRD and tFAW were not consulted.
tWR omitted, so the precharge was earlyPrecharge violations before the activate violations§5's candidate 3. Fix the precharge side first; the activate symptom may be secondary.
Tie misreportedA 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

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.