Skip to content
VLSI Mentor

DDR · Module 13

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.

Chapter 13.1 established that every timing obligation has five parts and worked carefully through four of them. It left the fifth — the magnitude — as a number that simply arrived from somewhere.

This chapter is about where it arrives from, and the answer contains a genuine impedance mismatch:

The processes that create timing obligations take absolute time and know nothing about any clock. A controller counts cycles. Those are different quantities, and converting between them is neither free nor exact.

That conversion is the whole subject. It is also the origin of one of the most counter-intuitive facts in memory engineering: the same device needs more cycles to honour the same physical delay when you run it faster.

1. What Physics Actually Hands Over

Module 3 finished with a row resolved in the sensing circuitry. Chapter 3.5 §3 described that resolution as a process with phases, and Chapter 2.6 described restoration as a process that must complete before the row can be disturbed.

Notice what those chapters never did: give a number. That was not an omission. It was the only honest option, because the duration of those processes is not a property of DDR at all. It depends on the process node, the cell design, the array geometry, the supply voltage, the temperature and the particular part. Two devices that implement the identical DDR4 interface can have materially different internal timings.

So what the device manufacturer publishes is not a description of the processes. It is a contract about separations:

If you wait at least this long after that event before issuing this class of command, the device guarantees correct behaviour. How long the internals actually take is not your concern.

This is worth dwelling on, because it is the reason controllers are portable. A controller does not know, and must not need to know, how long sensing takes. It knows a number that came from a datasheet, and it counts.

2. The Array Does Not Know About the Clock

Here is the fact that makes the conversion necessary, and it is easy to say and easy to forget.

The physical processes inside a DRAM array are not clocked. A bitline settling toward a resolvable difference is not counting anything. A restoration completing is not waiting for an edge. These processes take the time they take, in absolute time — picoseconds and nanoseconds — and they would take the same time if you stopped the clock entirely.

The command interface, by contrast, is thoroughly clocked. Commands are sampled on edges. A controller's only way to express “wait” is to count cycles, because cycles are the only temporal unit it has.

So the obligation has to cross from one regime to the other, and the crossing has a direction:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  absolute time                         cycles
  (what the process needs)              (what the controller counts)

     3.3 ns  ────────────────────────▶   how many cycles is that?
                    depends on tCK

The arrow only goes one way in practice. The physical requirement is the fixed thing; the cycle count is derived from it. Get the direction backwards and you will think the physical requirement changes with frequency, which is the misconception §12 spends the most words on.

One more term, since the rest of the chapter depends on it. tCK is the clock period — the duration of one CK cycle, in absolute time. It is the exchange rate between the two regimes.

3. Converting a Duration Into Cycles

Given a requirement of D absolute time and a clock period of tCK, how many cycles must the controller count?

The tempting answer is D / tCK. It is nearly right and wrong in the way that matters.

Consider a requirement of 3.3 ns with a clock period of 1.25 ns. The quotient is 2.64. There is no such thing as 2.64 cycles. The controller must pick an integer, and the choice is forced:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  wait 2 cycles  =  2 × 1.25 ns  =  2.5 ns    <  3.3 ns   ✗ VIOLATES
  wait 3 cycles  =  3 × 1.25 ns  =  3.75 ns   >= 3.3 ns   ✓ legal

Two cycles is not “close enough.” It is a violation of the device's contract, and the device makes no promises about what happens. So the conversion is a ceiling, always:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  cycles_required  =  ceil( D / tCK )

Rounding to nearest would violate whenever the fraction fell below one half. Truncating would violate whenever there was any fraction at all. Only rounding up is safe, and the asymmetry is not a convention — it follows from the requirement being a minimum.

Two consequences follow, and both matter in practice.

The controller almost always waits longer than necessary. With D of 3.3 ns and tCK of 1.25 ns, the controller waits 3.75 ns for a 3.3 ns requirement. That 0.45 ns is quantisation waste, and it is unavoidable: you cannot wait a fractional cycle. It is largest when the quotient's fractional part is just above an integer — a requirement of 2.51 cycles costs three.

Integer arithmetic makes this exact, not approximate. In hardware or firmware you will not compute a real quotient and round it. The standard integer identity is:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  ceil(a / b)  =  (a + b - 1) / b        for integers a >= 0, b > 0

with truncating integer division. Working the example in picoseconds: (3300 + 1250 - 1) / 1250 = 4549 / 1250 = 3. Correct, with no floating-point anywhere. §7's RTL uses exactly this form, and §9's second property checks it against the definition rather than against itself.

4. Why the Same Device Needs More Cycles When It Runs Faster

Now the consequence that surprises people, including people who have worked with DDR for years.

Take one physical requirement — 3.3 ns — and resolve it at several clock periods. Nothing about the device changes. Only the clock changes.

CK frequencytCK3.3 ns / tCKceilActual wait
400 MHz2.5 ns1.322 cycles5.0 ns
800 MHz1.25 ns2.643 cycles3.75 ns
1200 MHz0.833 ns3.964 cycles3.33 ns
1600 MHz0.625 ns5.286 cycles3.75 ns
2000 MHz0.5 ns6.67 cycles3.5 ns

The cycle count triples across this range while the physical requirement does not move at all. The device is not getting slower. The cycle is getting shorter, so the same interval contains more of them.

This is why datasheets publish timing tables per speed grade, and why a parameter quoted in cycles is meaningless without the frequency attached. “tRRD is 4” is not a fact about a device. “tRRD is 4 cycles at this data rate” might be.

There is a second, subtler consequence. Look at the actual wait column: it does not decrease monotonically. At 1200 MHz the fit is nearly perfect (3.33 ns for a 3.3 ns need). At 1600 MHz it is worse (3.75 ns) despite the faster clock. Quantisation waste is not monotonic in frequency, because it depends on where the quotient falls relative to the next integer. Raising the clock can leave a particular obligation relatively more wasteful than it was before, which is a real and frequently surprising effect when a design moves to a higher speed bin.

5. Specifications That State the Requirement Twice

Everything so far assumed the requirement is a duration. Many are. But some obligations are not physical at all.

Chapter 13.1 §3 distinguished two origins: obligations arising because a physical process takes time, and obligations arising because a shared resource cannot carry two things at once. The second kind has no natural expression in nanoseconds. If the command path needs three cycles to move a command through its stages, that is three cycles — at any frequency. It does not become fewer cycles because the clock slowed down.

So the two kinds scale in opposite ways:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  physical requirement    fixed in ns      →  cycle count RISES with frequency
  structural requirement  fixed in cycles  →  absolute time FALLS with frequency

A single obligation can be subject to both. And when it is, the specification states it both ways and requires the greater:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  cycles_required  =  max( SPEC_CYCLES ,  ceil( SPEC_PS / tCK ) )

At low frequency the cycle floor governs, because the clock is slow enough that few cycles already cover the physical need. At high frequency the absolute term governs, because cycles have become too short. The max covers both ends with one specification.

How a timing obligation's magnitude is resolved into a cycle count. On the upper path, a physical process inside the array has a duration expressed in absolute time, which becomes the absolute term of the specification; that term is divided by the clock period and rounded up, always up, to give a cycle count. The clock period feeds this conversion, which is why the resulting count depends on frequency. On the lower path, a structural requirement such as command-path pipeline depth is already expressed in cycles and needs no conversion, giving the cycle term of the specification. The two candidate counts are compared and the greater is taken, producing a single integer cycle obligation that a counter can enforce.Physical processduration, in psAbsolute termfixed in nsDivide, round upalways upClock periodtCK — the rateShared resourcepipeline depthCycle termfixed in cyclesTake the greaterneither may be shortCycle obligationone integerin psper cyclecandidatecandidate12

We now have an integer. That is still not enough to build anything, because “wait four cycles” does not identify a cycle.

Chapter 13.1 §4 flagged three ambiguities in that phrase. Here they get closed, because everything in the rest of the module depends on it.

Convention 1 — events are dated by the cycle on which they are sampled. Not presented, not driven: sampled. A command driven during cycle 20 and captured on the edge that ends cycle 20 is an event at cycle 20. This is the only defensible choice, because the sampling edge is the moment both ends agree something happened.

Convention 2 — separation is measured as a difference of event cycles. If the triggering event is at cycle N and a candidate would be sampled at cycle M, the separation is M - N.

Convention 3 — a minimum separation D is satisfied when the difference is at least D.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  candidate at M is LEGAL  ⟺  M - N  >=  D

From which the earliest legal cycle follows by rearrangement, and it is worth doing the rearrangement rather than asserting the result:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  M - N >= D        ⟹        M >= N + D        ⟹        M_earliest = N + D

Now the example that settles the argument. Triggering event at cycle 10, minimum separation of 4:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  M = 13  :  13 - 10 = 3   >= 4 ?  no    ✗ illegal
  M = 14  :  14 - 10 = 4   >= 4 ?  yes   ✓ legal, and earliest

Cycle 14 is legal. There are four forbidden issue opportunities — cycles 10, 11, 12 and 13 — and the count of forbidden cycles equals D. Cycle 10 is forbidden because the event happened on it, and 10 - 10 = 0, which is not at least 4.

And define D = 0 explicitly, because it will occur. Under this convention D = 0 gives M >= N, so the candidate may be sampled on the same cycle as the trigger. That is a real configuration meaning no separation required — not a disabled constraint, and not an error. A design that treats zero as “disabled” and a specification that means “no delay” agree by accident here, and disagree the moment anything is layered on top.

7. RTL — Resolving a Specification Into a Cycle Count

Collision check first. Chapter 11.4's write_recovery_guard counts down an obligation. Chapter 4.5's bank_group_spacer holds two separations. Chapter 2.3's refresh_deadline_tracker tracks a recurring deadline. All three consume a magnitude; none of them computes one. That gap is this block's territory, and it is the only new thing here.

The engineering problem. A specification arrives as up to two terms in two different units. A counter needs one integer. Something must do the conversion, must round the right way, and must say which term governed — because that answer tells you whether the constraint is physics-limited or structure-limited at this frequency, which determines whether raising the clock will make it worse.

Classification: configuration-time arithmetic, expressed as combinational RTL.

That classification deserves a sentence of honesty, because it affects how you should read the code. In a real controller this resolution happens once, in firmware or at elaboration, and the result is written into a configuration register. It is presented here as combinational logic so that §8 can show the whole frequency sweep as a waveform — and because seeing the arithmetic is the point of the chapter. §7's synthesis note says plainly what it would cost if you were foolish enough to build it in gates.

What it models: integer ceiling division, the max of two candidate counts, and range checking.

What it does not model: anything physical. There is no representation of any array process here — only a number that came from a datasheet and some arithmetic. The block cannot tell whether SPEC_PS describes sensing, restoration or a board delay, and that indifference is exactly right.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─────────────────────────────────────────────────────────────────────
//  timing_spec_resolver
//
//  CLASSIFICATION
//    Configuration-time arithmetic, expressed as combinational RTL.
//    Pure function of tck_ps. No clock, no state, no timekeeping --
//    it computes how long to wait and never waits.
//
//  WHAT IT MODELS
//    §5's resolution rule:
//       cycles_required = max(SPEC_CYCLES, ceil(SPEC_PS / tCK))
//    plus which term governed, and whether the result fits.
//
//  WHAT IT DOES NOT MODEL
//    Any physical process. SPEC_PS is a number from a datasheet; this
//    block has no idea what it describes and does not need one. There
//    is deliberately nothing here that could be read as a model of
//    sense amplification, restoration or any analog behaviour.
//
//  WHERE THIS REALLY LIVES
//    In a production controller: firmware, once, at configuration or
//    on a frequency change, writing the result to a register. The
//    combinational form here makes the arithmetic visible and lets
//    §8 sweep frequency in a single waveform. See the synthesis note.
//
//  UNITS -- stated once, obeyed everywhere
//    SPEC_PS and tck_ps are PICOSECONDS.
//    SPEC_CYCLES and cycles_required are CK CYCLES.
//    tck_ps is the CK PERIOD, not a data-rate interval. Passing half
//    the period here -- the classic MT/s-for-MHz error of §12 --
//    halves every obligation in the design and is not detectable
//    from inside this block.
// ─────────────────────────────────────────────────────────────────────
module timing_spec_resolver #(
  // The cycle term. Fixed in cycles at any frequency. Zero means the
  // specification has no cycle floor, which is legal.
  parameter int SPEC_CYCLES = 4,
  // The absolute term, in picoseconds. Zero means the specification
  // has no absolute component, which is also legal.
  parameter int SPEC_PS     = 3300,
  // Width of the clock-period input, in ps. 16 bits reaches 65.5 ns,
  // comfortably more than any DDR clock period.
  parameter int TCK_PS_W    = 16,
  // Width of the resolved count. Must hold the largest count this
  // specification can produce at the lowest tCK you intend to support.
  parameter int CYC_W       = 8
) (
  // ── The exchange rate between the two regimes, in ps per cycle.
  input  logic [TCK_PS_W-1:0] tck_ps,

  // ── The resolved obligation, in cycles. This is the number a
  //    countdown or deadline block consumes.
  output logic [CYC_W-1:0]    cycles_required,

  // ── WHICH term governed. High means the absolute term won, so this
  //    obligation is physics-limited at this frequency and its cycle
  //    count will RISE if the clock rises. Low means the cycle floor
  //    won, so raising the clock costs nothing here -- yet. §10's
  //    sweep is built on this output.
  output logic                abs_term_governs,

  // ── tck_ps of zero. Division would be undefined, so it is refused
  //    rather than silently producing a number. A resolver that
  //    returned something plausible for an invalid clock would be
  //    worse than one that refuses.
  output logic                tck_invalid,

  // ── The true requirement exceeds CYC_W bits. REPORTED, and the
  //    output SATURATES rather than wrapping: a saturated count is
  //    too long, which is slow, while a wrapped count is too short,
  //    which is a contract violation. Slow is the safe failure.
  output logic                overflow
);

  // ── Elaboration guards.
  if (SPEC_CYCLES < 0) begin : g_neg_cycles
    initial $fatal(1, "timing_spec_resolver: SPEC_CYCLES must be >= 0");
  end
  if (SPEC_PS < 0) begin : g_neg_ps
    initial $fatal(1, "timing_spec_resolver: SPEC_PS must be >= 0");
  end
  // A specification with neither term constrains nothing. Almost
  // certainly a wiring mistake, so it is refused loudly rather than
  // resolving to zero and disappearing.
  if ((SPEC_CYCLES == 0) && (SPEC_PS == 0)) begin : g_empty_spec
    initial $fatal(1, "timing_spec_resolver: specification has no terms");
  end
  if ((CYC_W < 1) || (CYC_W > 30)) begin : g_bad_cyc_w
    initial $fatal(1, "timing_spec_resolver: CYC_W must be 1..30");
  end
  if (TCK_PS_W < 1) begin : g_bad_tck_w
    initial $fatal(1, "timing_spec_resolver: TCK_PS_W must be >= 1");
  end

  // Largest value CYC_W bits can hold. CYC_W <= 30 is guarded above so
  // this shift stays inside a 32-bit int.
  localparam int MAX_CYCLES = (1 << CYC_W) - 1;

  // 32-bit intermediates. Educational clarity over minimal width: the
  // arithmetic is the lesson, and this is configuration-time logic.
  int unsigned tck_u;
  int unsigned abs_cycles;
  int unsigned required;

  always_comb begin
    tck_u       = 32'(tck_ps);
    tck_invalid = (tck_u == 32'd0);

    // ── §3's integer ceiling: ceil(a/b) == (a + b - 1) / b.
    //    Guarded against the zero divisor. Note that SPEC_PS == 0
    //    yields (0 + b - 1)/b == 0 for any b >= 1, which is correct:
    //    a specification with no absolute term contributes no
    //    absolute-derived cycles.
    if (tck_invalid) begin
      abs_cycles = 32'd0;
    end else begin
      abs_cycles = (32'(SPEC_PS) + tck_u - 32'd1) / tck_u;
    end

    // ── §5's rule: neither term may be short-changed.
    required         = (abs_cycles > 32'(SPEC_CYCLES)) ? abs_cycles
                                                       : 32'(SPEC_CYCLES);
    abs_term_governs = (abs_cycles > 32'(SPEC_CYCLES));

    // ── Range check, then saturate. Reported either way.
    overflow        = (required > 32'(MAX_CYCLES));
    cycles_required = overflow ? CYC_W'(MAX_CYCLES) : CYC_W'(required);
  end

endmodule

Interface contract. One input, four outputs, no clock. cycles_required is meaningful only when tck_invalid is low; with an invalid clock the absolute term contributes nothing and the output degenerates to the cycle floor, which a consumer must not trust — hence the flag.

Parameter contract. Both terms may be zero individually; both zero together is refused at elaboration, because a specification that constrains nothing is a wiring error rather than a configuration. CYC_W is bounded at 30 so 1 << CYC_W cannot overflow a signed 32-bit int — a guard whose absence would make the overflow detector itself overflow, which is a pleasing class of bug to have ruled out structurally.

Why the casts are safe. CYC_W'(MAX_CYCLES) is exact by construction: MAX_CYCLES is 2**CYC_W - 1, the largest value the target width holds. CYC_W'(required) is reached only when overflow is false, which is precisely the test that required <= MAX_CYCLES. Both casts are guarded by the comparison immediately above them — which is the pattern to copy, because the alternative habit of casting first and checking later loses the information you needed.

Corner cases. SPEC_PS == 0: the ceiling yields 0 and the cycle floor always governs, so abs_term_governs stays low at every frequency. SPEC_CYCLES == 0: the absolute term always governs whenever it yields anything at all. tck_ps larger than SPEC_PS: the ceiling yields 1, not 0 — one cycle already exceeds the requirement, and 1 is correct because the requirement is nonzero. tck_ps == 0: refused. A requirement that resolves beyond CYC_W: saturates high and flags.

Synthesis implications — and why you should not build this. The divider is the problem. abs_cycles is a full integer division by a runtime value, which synthesises to a multi-cycle divider or a very deep combinational one. In a real controller this is firmware arithmetic, computed once per frequency change and written to a register. If you genuinely needed it in gates you would restrict tck_ps to a small set of supported periods and use a lookup table, or pipeline the division over the many cycles a frequency change already takes. The max, the comparisons and the saturation are all trivial; the division is the entire cost.

Failure modes. Using truncating division instead of the ceiling identity under-waits by one cycle whenever the division is inexact — a violation that appears only at frequencies where the quotient has a remainder, which is most of them but not all, so it survives a single-frequency test. Using >= instead of > for abs_term_governs misreports a tie as absolute-governed, which is harmless for the count and wrong in the diagnostic. Letting the result wrap instead of saturate converts a too-slow design into a contract violation — the one failure direction that is not safe.

8. The Same Obligation Across Five Frequencies

The clearest way to see §4's table is to sweep it. This trace holds the specification fixed and varies only tck_ps, so every change in cycles_required is the conversion at work.

timing_spec_resolver — one specification, five clock periods

10 cycles
Ten observation points showing the resolver's response as the clock period is swept from twenty-five hundred picoseconds down to five hundred picoseconds, with the specification held fixed at four cycles and thirty-three hundred picoseconds. At twenty-five hundred picoseconds the absolute term needs only two cycles so the four-cycle floor governs and the requirement is four cycles. At twelve hundred and fifty picoseconds the absolute term needs three cycles, still below the floor, so the requirement remains four. At eight hundred and thirty-three picoseconds the absolute term needs exactly four cycles, tying the floor, and the requirement is still four. At six hundred and twenty-five picoseconds the absolute term needs six cycles and now governs, so the requirement rises to six and the absolute-term-governs flag goes high. At five hundred picoseconds the absolute term needs seven cycles and the requirement rises to seven. A final point shows a clock period of zero being refused as invalid.cycle floor governscycle floor governsphysics governsphysics governsinvalidinvalidexact tie — floor still governsexact tie — floor stillgovernsabsolute term takes overabsolute term takes overzero period refusedzero period refusedCKtck_ps25002500125012508338336256255000CK MHz400400800800120012001600160020002000abs term cyc2233446677cycle floor4444444444cycles_reqabs_governstck_invalidt0t1t2t3t4t5t6t7t8t9

The crossover is the interesting feature. Below 1600 MHz this obligation is structure-limited — the four-cycle floor governs, and slowing the clock down would not reduce the cycle count. At and above 1600 MHz it becomes physics-limited, and every further increase in clock frequency costs more cycles. abs_governs is the flag that tells you which side of the crossover you are on, and that single bit answers a question architects ask constantly: will this constraint get worse if we move to a faster bin?

Note also the tie at 833 ps. The absolute term needs exactly 4 cycles and the floor is exactly 4, so the requirement is 4 and abs_governs stays low — because the block reports strict domination, not “at least as large.” §7 called the alternative a harmless-but-wrong diagnostic; here you can see that it changes one bit and no counts.

9. Four Assertions Worth Writing

First, a point about where these live, because the block has no clock.

timing_spec_resolver is combinational and has no clk or rst_n port at all. A concurrent property, however, needs a sampling event — SVA has no notion of “check this whenever anything changes.” So these properties cannot live inside the module; they belong in a testbench or a bind unit, sampled on the surrounding environment's clock and reset.

This is the normal situation for combinational blocks and it has a consequence worth internalising: the properties check the block's arithmetic once per sampling edge, not on every input change. If tck_ps glitched between two edges and settled, the properties would never see it. For configuration-time arithmetic that is entirely acceptable, because the input changes once per frequency change and is stable for millions of cycles either side. For a block whose inputs moved continuously it would be a real gap, and immediate assertions inside always_comb would be the right tool instead.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── Sampled on the ENVIRONMENT's clock: this block has none of its
//    own. Place these in a testbench or bind unit, not in the module.
//
// ── P1. The result is never short of either term. THE safety property
//    of this block: a resolver that under-reports produces a design
//    that violates the device contract everywhere, at once.
property p_result_covers_both_terms;
  @(posedge clk) disable iff (!rst_n)
    !tck_invalid |-> ( (cycles_required >= CYC_W'(SPEC_CYCLES))
                    or overflow );
endproperty
a_result_covers_both_terms: assert property (p_result_covers_both_terms);

// ── P2. The conversion is a genuine ceiling, checked against the
//    DEFINITION rather than against the implementation. The definition
//    of ceil(a/b) = c is: c*b >= a, and (c-1)*b < a. Re-deriving the
//    implementation's own formula here would prove only that the
//    formula equals itself, which is the most common way an assertion
//    suite fools its author.
//    Catches truncating division, rounding to nearest, and an
//    off-by-one in the +b-1 term.
property p_ceiling_is_a_ceiling;
  @(posedge clk) disable iff (!rst_n)
    (!tck_invalid && !overflow && (SPEC_PS > 0)) |->
      (  (32'(cycles_required) * 32'(tck_ps) >= 32'(SPEC_PS))
      && ( (32'(cycles_required) == 32'(SPEC_CYCLES))
        || ((32'(cycles_required) - 32'd1) * 32'(tck_ps) < 32'(SPEC_PS)) ) );
endproperty
a_ceiling_is_a_ceiling: assert property (p_ceiling_is_a_ceiling);

// ── P3. The governing-term report agrees with the result. If the
//    absolute term governs, the result strictly exceeds the floor; if
//    not, the result EQUALS the floor. Catches a >= / > confusion and,
//    more usefully, a max() that picked the wrong operand while
//    happening to report the right flag.
property p_governing_term_is_consistent;
  @(posedge clk) disable iff (!rst_n)
    (!tck_invalid && !overflow) |->
      ( abs_term_governs
          ? (cycles_required >  CYC_W'(SPEC_CYCLES))
          : (cycles_required == CYC_W'(SPEC_CYCLES)) );
endproperty
a_governing_term_is_consistent: assert property (p_governing_term_is_consistent);

// ── P4. Overflow saturates rather than wraps, and saturation is
//    reported. The failure direction matters: a saturated count is too
//    long and therefore safe, a wrapped count is too short and
//    therefore a violation. This property is the difference.
property p_overflow_saturates_high;
  @(posedge clk) disable iff (!rst_n)
    overflow |-> (cycles_required == CYC_W'((1 << CYC_W) - 1));
endproperty
a_overflow_saturates_high: assert property (p_overflow_saturates_high);

What these prove. That the resolved count covers both terms, that the rounding is genuinely upward as checked against the mathematical definition, that the diagnostic agrees with the arithmetic, and that the overflow failure direction is the safe one.

What these do not prove. Three things, and the third is the one that bites.

They do not prove the inputs are right. A resolver fed a data-rate interval where a clock period belongs satisfies all four properties perfectly and halves every obligation. No block-level property can catch a units error at its own boundary — the block cannot see what the number means. Only a system-level check comparing resolved counts against an independent computation from the datasheet will find it, which is Chapter 13.4's territory.

They do not prove SPEC_PS is the right physical requirement. That is a datasheet-reading question, not a verification one.

And they say nothing about whether the consumer counts the resolved number correctly. §6's convention lives in the counter, not here.

Vacuity. P2 requires SPEC_PS > 0 and all four require !tck_invalid, so a parameterisation with no absolute term or a testbench that never drives a valid clock passes them all trivially. Cover both antecedents.

10. Verification Perspective

Sweep, do not sample. The natural test drives one clock period and checks one number. The bug this block is most likely to have — a truncating division — is invisible at any frequency where the division happens to be exact. Sweep tck_ps across every value in a realistic range and check each against an independently computed expectation. This is cheap: the block is combinational, so a sweep is a loop with no time in it.

Compute the expectation differently from the implementation. A checker that computes (SPEC_PS + tck - 1) / tck proves that the RTL contains that expression. A checker that computes the smallest c such that c * tck >= SPEC_PS by searching upward proves the RTL computes a ceiling. The second is slow and correct and is the one worth writing, and its slowness does not matter in a combinational sweep. This is the representation-inversion principle Chapter 13.4 §9 builds a whole checker around, in miniature.

Hunt the crossover deliberately. For a given specification there is a clock period at which abs_term_governs flips. Find it by sweep, then test the two periods either side of it and the exact tie. §8 showed the tie is where a > / >= confusion hides, and it is a single point in a continuous sweep — easy to step over.

Test the degenerate parameterisations. SPEC_PS == 0 should leave abs_term_governs low at every frequency. SPEC_CYCLES == 0 should make it high whenever the absolute term yields anything. Both zero should fail elaboration — and a test suite that cannot demonstrate its own elaboration guards firing has not tested them. Compile that case separately and expect the $fatal.

Force the overflow. Set CYC_W to 2 and a specification that resolves past 3. Confirm saturation, confirm the flag, and confirm the value did not wrap — the distinction P4 exists to police.

11. Debugging — One Extra Idle Cycle Everywhere

Symptom. The controller works. Data is correct. Every obligation appears to be honoured. But measured bandwidth is a few percent below the model, and the gap does not track any particular access pattern — it is uniform.

This is the signature of a systematic off-by-one, and it is worth its own section because it does not present as a bug at all. Nothing fails. It looks like a modelling inaccuracy.

Candidate mechanismEvidenceDiscriminator
Convention mismatch between resolver and counterEvery obligation is one cycle longer than §6 predictsPick one obligation and trace it: trigger cycle, first blocked cycle, first legal cycle. Compare with N + D. This is a five-minute check and it settles the question.
The resolver rounds up when it need notGap tracks frequency, worst where quotients are just above an integerCompute the quantisation waste from §4's table. If the measured gap matches, this is not a bug — it is the cost of integer cycles.
Counter counts the trigger cycle twiceBlocked interval is D + 1 cycles wideCount forbidden issue opportunities. §6 says exactly D.
A > where >= belongs in the legality testEarliest legal is N + D + 1 consistentlyThe comparison itself. M - N >= D, not > D.
Both resolver and counter add a marginGap is two cycles, not oneTwo independent conservatisms compounding. Common when two engineers each defend their own boundary.

The discriminator that resolves this fastest is counting the forbidden cycles for a single obligation on a waveform. §6 fixes the answer at exactly D, and comparing one obligation against that number distinguishes all five candidates above in one measurement.

Responsible layer. If the gap matches §4's quantisation arithmetic, nothing is wrong — the design is paying the unavoidable cost of integer cycles, and the fix is a different clock frequency or a different specification, not different logic. Reaching that conclusion confidently requires the arithmetic, which is the practical reason §3 did it carefully.

And a warning about the inviting fix. Having found a one-cycle conservatism, the obvious response is to remove one cycle. Do not, until you know which of the five mechanisms is responsible: removing a cycle from a correct conservatism converts uniform small slowness into intermittent data corruption, which is a far worse bug and far harder to find. Chapter 13.1 §12 covered why timing margin is not a performance dial.

12. Common Misconceptions

“DDR-3200 means a 3200 MHz clock.” Tempting because the number is quoted in the part name and 3200 looks like a frequency. Why it is wrong: 3200 is the data rate in MT/s. The CK frequency is half of it, 1600 MHz, because two transfers occur per cycle. Consequence: the most expensive unit error available here. Use 3200 MHz to derive a clock period and you get 0.3125 ns instead of 0.625 ns, so every ns-specified obligation resolves to twice the cycles it needs — a design that is correct, safe and roughly half the speed it should be. Make the inverse error, using the period where the half-period belongs, and every obligation is half as long as required, which is a contract violation. Replacement model: transfer_rate = clock_frequency × 2, from Chapter 4.2 §2. Debugging clue: every obligation in the design is wrong by exactly a factor of two.

“Nanoseconds and cycles are interchangeable once you know the frequency.” Tempting because there is an exact conversion. Why it is wrong: the conversion is exact in one direction and lossy in the other — a duration becomes a cycle count only through a ceiling, and the ceiling discards information. Two requirements of 3.3 ns and 3.7 ns both resolve to 6 cycles at 0.625 ns and are no longer distinguishable. Consequence: engineers convert to cycles early, then reason about frequency changes using the cycle number, which is exactly the number that was only valid at the old frequency. Replacement model: keep the absolute requirement as the source of truth and re-resolve on frequency change. Debugging clue: obligations that were fine at one speed bin and violate at another, with nobody having changed the specification.

“A faster clock means shorter waits.” Tempting because faster is faster. Why it is wrong: the absolute wait shrinks slightly or not at all, and the cycle count rises — §4's table shows it tripling. The physical requirement is indifferent to your clock. Consequence: a latency budget in cycles, carried across a speed-grade change, under-counts every physics-limited obligation. Replacement model: cycle counts are frequency-relative; absolute requirements are not. Debugging clue: violations appear only in the fastest speed bin.

“Every timing parameter is specified in nanoseconds.” Tempting because physical processes take absolute time and most conversation about timing is in ns. Why it is wrong: §5 — some obligations are structural and are specified in cycles at any frequency, and some are specified both ways with the greater required. Consequence: a resolver that handles only the absolute term drops the cycle floor, and the obligation comes out too short at low frequency, which is the regime nobody tests. Replacement model: the max of both terms. Debugging clue: failures at the bottom of the frequency range, which is a counter-intuitive place to find a timing bug and therefore a strong signal.

“Rounding to the nearest cycle is fine — the error averages out.” Tempting because rounding to nearest is the statistically sensible choice for measurements. Why it is wrong: a minimum is not a measurement. Rounding down by even one cycle violates the contract on every single instance, not on average. There is nothing to average over. Consequence: intermittent corruption at frequencies where the quotient's fraction is below one half. Replacement model: minima round up, unconditionally. Debugging clue: corruption that appears and disappears as frequency changes, tracking the fractional part of a quotient.

“The controller's counter models how long the array takes.” Tempting because the counter's value is derived from the array's behaviour. Why it is wrong: the counter models the contract, and the number came from a datasheet. The array's actual duration varies with voltage, temperature and part; the contract does not. §1 is the whole argument. Consequence: engineers look for analog explanations of digital off-by-ones, and — worse — write RTL claiming to model sensing, which is a fiction that misleads every later reader. Replacement model: physics determines, specification publishes, controller counts. Debugging clue: if an explanation requires knowing what the array is doing right now, it is wrong, because the controller does not know either.

“A D of zero means the constraint is disabled.” Tempting because zero conventionally means off. Why it is wrong: under §6's convention D = 0 means “same cycle is legal” — a real, meaningful, no-separation requirement. Consequence: code that treats zero as disabled behaves identically for a genuinely absent constraint and a zero-separation one, until something is layered on top that distinguishes them, at which point the behaviour is arbitrary. Replacement model: zero is a magnitude; absence is a separate fact needing its own representation. Debugging clue: an obligation that vanishes entirely under one configuration.

13. Interview Reasoning

“Why is DDR-3200 not a 3200 MHz clock, and why does it matter?” The name is a data rate: two transfers per clock cycle, so CK is 1600 MHz and tCK is 0.625 ns. Why it matters is the better half of the answer — tCK is the exchange rate for every absolute-time obligation in the design, so using the wrong one scales every timing obligation by two. In one direction that is a uniformly slow but correct controller; in the other it is a contract violation on every command. Getting the factor of two backwards is worse than not knowing it.

“A device requires 3.3 ns between two events. How many cycles at 0.625 ns, and why not five?” Six. 3.3 / 0.625 is 5.28, and five cycles is 3.125 ns, which is less than 3.3 — a violation. The requirement is a minimum, so the conversion must round up regardless of how small the fraction is. The follow-up worth volunteering is that six cycles is 3.75 ns, so 0.45 ns is quantisation waste that no logic can recover.

“Why do specifications state a requirement as the greater of a cycle count and an absolute time?” Because two different kinds of requirement are being covered by one line. A physical process needs absolute time and its cycle cost rises with frequency; a structural requirement such as pipeline depth needs cycles and its absolute cost falls with frequency. Whichever dominates depends on where you are running, so the specification states both and requires the greater. DDR4's tRRD_S is a real instance — the greater of 4 nCK or 3.3 ns at DDR4-2400 for a 1KB page. The architecturally useful consequence is that you can ask, per obligation and per frequency, whether it is physics-limited or structure-limited, which predicts whether it gets worse in a faster bin.

“Why do some timing requirements need both cycle and absolute-time reasoning?” Same fact, asked from the other side, and the answer to give is the scaling argument rather than the example: the two terms scale oppositely with frequency, so neither alone is safe across a device's whole operating range. A specification with only the absolute term is too short at low frequency; with only the cycle floor it is too short at high frequency.

“An event is sampled at cycle 10 and the minimum separation is 4. Which cycle is first legal, and how do you know?” Cycle 14, from M - N >= D rearranged to M >= N + D. The reason to state the convention rather than the number is that “wait four cycles” admits two readings a cycle apart, and the cost of the other reading is one extra idle cycle on every obligation in the system. Four cycles are forbidden — 10 through 13 — and the trigger cycle itself is forbidden because its separation is zero.

“Your controller is a few percent below its bandwidth model, uniformly. Where do you look?” Count the forbidden issue opportunities for one obligation on a waveform and compare against the magnitude. If it is D, the convention is right and the gap is probably quantisation waste, which is arithmetic rather than a bug. If it is D + 1, there is a systematic off-by-one, and the two likely homes are the comparison's strictness and whether the trigger cycle is counted. The important discipline is not removing the cycle until you know which — a correct conservatism removed becomes intermittent corruption.

14. Engineering Exercises

1. Resolve one specification at four frequencies. A requirement of 15 ns with a cycle floor of 2. Compute cycles_required at tCK of 2.5, 1.25, 0.625 and 0.5 ns. At which frequency does the absolute term take over? What is the quantisation waste at each?

Worked: at 2.5 ns, ceil(15/2.5) is 6, so max(2,6) is 6, waste 0 — the requirement divides exactly. At 1.25 ns, ceil(15/1.25) is 12, waste 0 again. At 0.625 ns, ceil(15/0.625) is 24, waste 0. At 0.5 ns, 30, waste 0. The absolute term governs at every frequency here, because 15 ns is far above two cycles anywhere, and the waste is zero throughout because 15 is an exact multiple of all four periods. The lesson is the one the exercise is built to deliver: a specification can be physics-limited across its entire range, and clean arithmetic is a property of the numbers rather than something to expect.

2. Now break the exactness. Repeat with 15.1 ns. Which frequency has the worst waste, in absolute terms and as a fraction?

Worked: at 2.5 ns, ceil(6.04) is 7 — waste 2.4 ns, the largest absolute waste, because one wasted cycle is expensive when cycles are long. At 1.25 ns, ceil(12.08) is 13, waste 1.15 ns. At 0.625 ns, ceil(24.16) is 25, waste 0.525 ns. At 0.5 ns, ceil(30.2) is 31, waste 0.4 ns. Waste falls as the clock rises, because quantisation error is bounded by one cycle and cycles are getting shorter — the opposite of the cycle count, which rises. Both facts are true at once and they are frequently confused.

3. Find the off-by-one. A colleague writes cycles = SPEC_PS / tck_ps; and tests at tCK of 1.1 ns with a 3.3 ns requirement. The test passes. Explain why, and construct the frequency that exposes the bug.

Worked: 3300/1100 is exactly 3, so truncation and ceiling agree and the test cannot distinguish them. Any period that does not divide 3300 exposes it — at 1.25 ns, truncation gives 2 where the ceiling gives 3, and the design under-waits. The general lesson is that a division bug hides at every frequency where the division is exact, so a single-frequency test of a converter proves close to nothing.

4. Classify five obligations. For each, say whether you expect it specified in cycles, in absolute time, or both, and justify from the mechanism rather than from memory: (a) minimum separation between activates to different bank groups; (b) the depth of the controller's own command pipeline; (c) the interval before a row may be precharged after opening; (d) the latency from a read command to first data; (e) the maximum interval between refreshes.

Worked: (a) both — §5's verified example, since there is a physical component and a structural floor. (b) cycles only, and note it is not a device obligation at all but a property of your own logic. (c) absolute time primarily, since the mechanism is a physical process, though a real specification may add a cycle floor. (d) cycles, because it is a configured pipeline latency rather than a minimum — which is Chapter 10.2's subject and a different sense in Chapter 13.1 §4's five-part terms. (e) absolute time, because it derives from charge leakage, which knows nothing about clocks. The instructive pair is (c) against (d): both are “delays after a command” and they are different kinds of obligation, which Chapter 13.3 §2 separates.

5. Write the convention down for a real obligation. Take Chapter 11.4's write-recovery obligation. Using §6's convention, state the triggering event's cycle, the first forbidden cycle, the number of forbidden cycles, and the first legal cycle, symbolically. Then check your answer against that chapter's RTL.

6. Defend a design decision. timing_spec_resolver saturates on overflow rather than wrapping or asserting. Argue for each of the three behaviours and say which you would ship in a production controller, and why the answer might differ between a research prototype and a shipping product.

7. Reason about where the arithmetic belongs. §7 says this resolution really happens in firmware. Suppose your controller must support dynamic frequency scaling with a fast switch. What breaks if resolution takes a thousand cycles of firmware? Sketch two designs that fix it, and name the cost of each.

15. Summary

Timing obligations originate in processes that take absolute time and are indifferent to any clock. A controller can only count cycles. The conversion between them is the subject of this chapter, and it has four properties worth carrying forward.

It is a ceiling, always. A minimum requirement cannot be satisfied by rounding down or to nearest, so cycles_required = ceil(D / tCK), computed in integers as (D + tCK - 1) / tCK. The cost is quantisation waste — the controller waits longer than required, by up to one cycle.

It is frequency-dependent in a direction that surprises people. The same physical requirement costs more cycles at a higher clock. Cycle counts are therefore frequency-relative and absolute requirements are not, which is why timing tables are published per speed grade and why a parameter quoted in cycles without a frequency is not a fact.

Some obligations are structural rather than physical, fixed in cycles at any frequency. Because the two kinds scale oppositely, specifications state both terms and require the greater — verified in DDR4's tRRD_S, the greater of 4 nCK or 3.3 ns at DDR4-2400 for a 1KB page. Which term governs tells you whether an obligation will worsen in a faster bin.

An integer is still not a rule until the sampling convention is stated. Events are dated by their sampled cycle; separation is M - N; a minimum D is met when M - N >= D; the earliest legal cycle is N + D, and exactly D issue opportunities are forbidden. The competing reading costs one extra idle cycle on every obligation in the system, forever, while looking completely correct.

timing_spec_resolver performs the resolution and reports which term governed. It is honestly classified as configuration-time arithmetic: in a real controller it is firmware, computed once per frequency change, and its divider is the reason.

16. What Comes Next

Two chapters have now built a single obligation completely. Chapter 13.1 gave it five parts; this one resolved its magnitude into a countable integer and fixed the convention for counting it.

One obligation is not the problem, though. Chapter 13.1 §5 observed that a candidate command is routinely the target of several edges at once, and §8's waveform there showed a candidate waiting on the last of three. What it did not do is say where those three came from, why they have different scopes, or how to combine them.

Chapter 13.3 does all three. It catalogues the classes of constraint — minimum separation, fixed latency, resource occupancy and rolling window — establishes which resource owns each obligation and why attaching one to the wrong resource produces two opposite hardware bugs, and derives the equation the whole module has been heading toward: the earliest legal issue time is the maximum over every applicable deadline.

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.