Skip to content
VLSI Mentor

DDR · Module 31

DDR vs GDDR

The graphics variant buys per-pin rate with topology and pays in replaceability and granularity. The deciding quantity is the consumer's latency tolerance, and a faster channel raises the concurrency bar rather than lowering it.

Chapter 31.3 §4 said there are exactly two ways past the pin-count wall: make the connection count scale like the area, or stop using the perimeter. This chapter is about the third thing people actually do, which is neither.

CURRICULUM-DERIVED from 26.1 §1: a cleverer interface raises the connections-per-millimetre or the rate each one carries, which multiplies the numerator by a constant — and it cannot turn 4/L into anything that does not fall.

The graphics variant is that constant factor, bought deliberately and pushed as far as a soldered point-to-point topology allows. Knowing that it is a constant factor is knowing its limit: it postpones the wall and never removes it.

And the deciding quantity is not on the memory side of the interface at all.

The deciding quantity is the consumer's latency tolerance. Once a requester can keep enough transactions in flight that the pipeline depth stops being observable, latency stops being an objective — and everything that was being spent to protect it becomes available to spend on rate instead.

So this chapter is organised around two lists: what becomes affordable to give up once latency stops mattering, and what must be added back once the per-pin rate rises far enough that the channel is no longer reliable on its own. The second list is the one that surprises people, and it is why this is not simply DDR with the clock turned up.

Axis A1 is identical for the third chapter running. Same destructive cell, same thirteen obligations. This module's recurring lesson is that comparisons between memory technologies almost never live at the cell.

1. What This Comparison Cannot Be About

Three chapters have established that axis A1 does not differ between DRAM technologies. Here it does not differ either, so the same arguments are unavailable — and one more is unavailable that was available before.

ClaimWhy it is not available
“It has lower latency”identical cell, restore, row buffer and constraint classes
“It has a simpler controller”§7: obligations are added, as in 31.2
“It uses a different surface”no — it uses the same perimeter, which is the whole point of §2
“It escapes the pin-count wall”it multiplies a constant; 26.1 §1 says the exponent is untouched
“It is for graphics”a market label, and §4 shows the deciding quantity is a workload shape that is not confined to one market

Row three is the structural point and it distinguishes this chapter from 31.3 completely. The previous chapter's technology changed which surface the connections land on, changing an exponent. This one keeps the surface and changes what each connection carries. Those are different kinds of answer to the same problem, and conflating them is the most common error in this comparison.

2. Establishing the Subject — What the Constraint Is

Since nothing in this curriculum owns this technology, derive it rather than describe it. Start from the question it answers.

The problem, stated in 26.1 §1's terms: a perimeter offers 4L connections and the capability to be fed scales as . If you are committed to the perimeter — because you cannot afford an interposer, or because the memory must be a separate replaceable component, or because the volume does not justify advanced packaging — then the only remaining variable is what each connection carries.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   carried bandwidth  =  available_connections  x  per_signal_rate

   31.3 §6 works on the first factor and the SURFACE decides it.
   This chapter works on the second, and the question becomes:

       what limits the rate one connection can carry?

And the answer is electrical, not logical. CURRICULUM-DERIVED from Module 22, which owns all of it: 22.2 owns where reflections come from and how termination mitigates them; 22.3 owns inter-symbol interference and crosstalk; 22.4 owns the effects that appear at the highest rates.

So raising the rate means removing electrical penalties, and the biggest available one is topological. STRUCTURAL, and the argument is worth following because it is the whole technology:

Topological featureWhat it costs electricallyOwner
A socket or connectoran impedance discontinuity on every signal22.2
Multi-drop — several devices on one netstubs, and loading that varies with population22.2
A variable device countthe channel's characteristics change with configuration22.3
Long board traces to a moduleattenuation and inter-symbol interference22.3, 22.4

Remove all four and the same signal can be driven faster. That is the trade this technology makes: it buys per-pin rate with topology — a soldered, point-to-point, fixed-population, short-trace channel.

So the derivation runs in the direction people do not expect. It is not a fast memory that happens to be soldered. It is a soldered point-to-point channel, and the rate is what that topology makes possible. Every consequence in §3 follows from the topology, not from the rate.

3. What the Topology Costs, Derived

Each row below is derived from §2's four removals rather than looked up, which is why this chapter can establish the technology without quoting a specification.

RemovalDirect consequenceAxis
No socketnot replaceable in the fieldA7
Point-to-point, fixed populationcapacity is fixed at board design — no adding modulesA7
Short tracesthe memory must sit physically adjacent, constraining floorplan and thermalsA7
No multi-dropone device per channel, so capacity per channel is a device propertyA3, A4
Higher per-pin ratethe channel is less reliable per bit, so §8's additions become mandatoryA2
Higher per-pin ratemore calibration, and more of it continuous rather than one-timeA2

Row two is the one that changes system design rather than memory design. A fixed population means the capacity decision is taken at board design and never revisited — which is 31.3 §16's irreversibility arriving by a completely different route. CURRICULUM-DERIVED structurally: the on-package technology fixes capacity at assembly; this one fixes it at board layout. Two different technologies, two different mechanisms, the same lost option.

And row five is the one people do not anticipate at all, because it runs against the intuition that a faster interface is simply better. A channel driven harder has less margin per bit22.4 owns the effects — so the interface must carry machinery to detect and survive errors that a slower channel did not need. §8 is that list, and it is why the obligation set grows.

4. The Deciding Quantity — Latency Tolerance, Made Arithmetic

The quantity is whether the consumer can keep the pipeline full, and it has a closed form that 30.4 §3 already established in a different context.

CURRICULUM-DERIVED from 30.4 §3 Q3: raising a delay costs latency and costs essentially no sustained bandwidth, and the exception is a requester with too few outstanding transactions to cover the longer pipeline — at which point the delay stops being hidden and starts being serialised.

That exception is this chapter's deciding quantity, so state it as the condition it is:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   Let  L    = the memory's completion latency, in cycles
        S    = the spacing between successive returns the channel
               can sustain, in cycles  (the OCCUPANCY, 30.4 §3)
        N    = the number of transactions the requester can keep
               outstanding

   the pipeline is FULL -- and therefore latency is INVISIBLE -- iff

       N  >=  ceil( L / S )

   DERIVED, and read it as a requirement on the REQUESTER:
   the memory states L and S; the consumer must supply N.

DERIVED, ILLUSTRATIVE, to show the shape:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   ILLUSTRATIVE L = 60 cycles, S = 4 cycles (a burst's occupancy).

       N* = ceil(60 / 4) = 15 outstanding transactions

   a consumer sustaining 15 or more : latency is invisible, and
       raising L to 80 changes NOTHING observable (N* becomes 20).
   a consumer sustaining 4          : it sees 60 cycles per access,
       serialised, and raising L hurts PROPORTIONALLY.

   recompute at S = 2 (a shorter burst on a faster channel):
       N* = ceil(60 / 2) = 30

   DERIVED, and note the direction: making the CHANNEL faster
   RAISES the concurrency the consumer must supply. A faster
   memory is harder to keep full, not easier.

That last result is the section's deliverable and it is counter-intuitive. Halving the return spacing doubles the outstanding transactions required to hide the same latency. So the technology that buys rate is simultaneously raising the bar its consumer must clear — and a consumer that could hide the latency of a slower channel may not be able to hide the same latency on a faster one.

And it explains why the market label is a proxy. CURRICULUM-DERIVED from 29.5, which owns accelerator access patterns: a consumer that streams predictably can prefetch deeply and supply a large N. The property being selected for is deep, predictable concurrency, and any workload with it qualifies — which is why the same interface reasoning applies to workloads far from the one the name suggests. A latency-bound consumer with a short dependency chain fails the test regardless of its market.

5. What Becomes Affordable to Give Up

Once §4's condition holds, the things a latency-optimised design spends to protect latency are available to spend elsewhere. Each row states what is given up and what the saving buys.

Given upWhy it is affordableWhat it buys
Low completion latency§4 — invisible when the pipeline is fullpipeline depth for rate and for error handling
Fine access granularitya streaming consumer wants the whole burst anywayfewer commands per byte — 12.4's command efficiency
A bypass path around the return buffernothing needs the first beat earlyarea, and a shorter critical path at high rate
In-order returna reorder buffer is affordable when latency is hiddenscheduling freedom — 23.4
Replaceability§3 — the topology is what buys the ratethe rate itself
Capacity flexibilityfixed population is inherent to point-to-pointthe rate itself

Row two has a cost that must be stated with it, because it is the one this curriculum measures. CURRICULUM-DERIVED from 12.4, which owns the four efficiency measures and insists they stay distinct: a coarser granularity improves command efficiency and worsens payload efficiency — bytes the requester did not want are still moved and paid for. So it is affordable only for a consumer that wants the whole burst, and a design that coarsens the granularity for a consumer with fine-grained accesses has traded a measure it was winning for one it was not.

Row four is the one §12's defect is about. A reorder buffer is not merely permitted by hidden latency — it is the point of it, because the freedom to return out of order is what lets the scheduler exploit 23.4's heuristics without a latency penalty. And it invalidates an assumption the latency-optimised design was entitled to make.

6. What Must Be Added Back

§3's row five said the channel becomes less reliable per bit. This is that, as an obligation list — and it is why the controller grows rather than shrinks.

Added obligationWhy the rate forces itOwner
Link-level error detection on the data patha channel with less margin per bit produces errors a slower one did not22.4
A retry or correction mechanism, and the state to support itdetection without a response is only a counter§8
Continuous calibration rather than one-timedrift consumes a larger fraction of a smaller margin19.5
Two-axis margining rather than onethe eye is small in both dimensions21.4
Per-pin trained state, not per-bytelane-to-lane variation matters more as the margin shrinks21.2
Deeper return tracking, with tags§5 row four: out-of-order return needs association10.5 §9
Thermal awarenessadjacent placement plus high activity concentrates heat24.1 §4

DERIVED: seven added obligations, and the module's arithmetic now reads deletion, addition, multiplication, addition again — but with a different set added than 31.2's. CURRICULUM-DERIVED from 31.2 §7: the low-power profile added power-state and refresh-ownership obligations. This profile adds integrity and calibration obligations instead. So adds obligations is not one pattern — the added set is the technology's fingerprint, and naming which set was added is more informative than counting them.

Row three deserves its distinction stated, because it is the one that changes the verification plan. CURRICULUM-DERIVED from 19.5, which owns the one-time versus continuous split and what it means for a trained value to become valid or stop being valid: a one-time calibration is a bring-up concern, and a continuous one is a runtime concern that interacts with traffic. So the calibration machinery becomes something the scheduler must accommodate, and 30.6 §8 owns the corresponding failure — the mechanism that exists to prevent drift failure can be the mechanism causing the failure, distinguished by whether the failure rate correlates with the calibration interval.

And row one has a consequence for the error model that is worth stating plainly. An interface that expects occasional errors and survives them is a different design contract from one that treats an error as a defect. Chapter 28.5 owns the ECC visibility problem — on-die correction means the observed structure is not the fault structure — and the same reasoning applies to link-level correction: a corrected error is invisible unless somebody counts it, so the corrected-error counter becomes a first-class debug requirement rather than an optional one. Chapter 30.10 §13 owns that as a design deliverable.

7. The Axes, Complete

The module's seven axes, for the last time, with all four chapters side by side. This table is the module's summary artifact and the reason the axes were fixed in 31.1 §2.

Axisvs SRAMvs LPDDRvs HBMvs GDDR
A1 cellDIFFERS — causalidenticalidenticalidentical
A2 obligationsdeleted 12 of 13added 9multiplied by channelsadded 7, a different set
A3 granularityone operationfinerfiner per channelcoarser
A4 channel shapeport-basedmore, narrowermany, narrow, semi-independentpoint-to-point, fixed
A5 latencyconstant+wake termsame termssame terms, deliberately traded
A6 rung movednot comparable1 and 21, by an order1, by a constant factor
A7 couplingon-diesolderedon-package, fixed at assemblysoldered, fixed at board design

Read the A1 row first. It differs in exactly one of the four comparisons. That is the module's central empirical finding: the cell is the most fundamental property a memory has, and it is almost never what the decision turns on. Chapter 31.1 is the exception, and it is the only chapter whose decision has a feasibility stage that the cell alone can close.

And read the A2 row second. Every comparison changes the obligation set, and in four different ways. So how much controller does this choice cost me is the question with the most reliable answer in the module — and it is a question almost never asked in a technology comparison.

8. The Ordering Assumption, in Principle

§5 row four gave up in-order return. State precisely what that invalidates, because §12's defect is the invalidation unnoticed.

CURRICULUM-DERIVED from 10.5 §9, which owns the back-to-back-return investigation and lists five mechanisms by which a read path with a single-transaction assumption fails under concurrency. Its third mechanism is exactly this: returns are interleaved or reordered, and the in-order assumption does not hold in this system.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   A LATENCY-OPTIMISED return path is entitled to assume:

       returns arrive in the order the requests were issued

   so association is FREE -- a FIFO suffices, and the head of the
   FIFO is the request the next return belongs to.

   A THROUGHPUT-OPTIMISED return path gives that up deliberately,
   because reordering is what hidden latency BUYS (§5). So:

       association must be by TAG, not by POSITION

   and the FIFO that was correct becomes a misattribution engine:
   it will pair every return with SOME request, and the pairing
   will be wrong, and nothing will report it.

The failure has no error signal, which makes it the fourth instance in this module of that shape — after 31.2 §7's PASR mask, 31.3 §12's straddling burst, and 30.4 §5's missed write deadline. A position-based association under reordering returns data; it simply returns the wrong data to the wrong requester.

And the configuration dependence is the hazard. With a shallow, bypass-equipped, in-order return path, position-based association is correct. With a deep reorder buffer it is not — and the parameter that flips it is the one a designer changes to improve throughput, which is a performance change nobody expects to affect correctness. Third instance of this module's signature bug class, and §12 is the code.

9. Four Technologies, One Stack

A stacked layer diagram summarising all four comparisons in the module from the consumer downwards. The top layer is the consumer's concurrency, which is the deciding quantity for this final chapter: latency becomes invisible only when the requester keeps at least the latency divided by the return spacing transactions outstanding, and a faster channel raises that bar rather than lowering it. The second layer is the integrity and calibration set, added by this chapter's technology because a channel driven harder has less margin per bit, requiring link-level error detection, a response to it, continuous rather than one-time calibration, two-axis margining and per-pin trained state. The third layer is return association, and it is the layer this chapter's defect lives in: a latency-optimised path may associate a return with its request by position, while a throughput-optimised path that reorders deliberately must associate by tag. The fourth layer is the obligation set, which every one of the four comparisons changes and in four different ways: deleted against static memory, added against the low-power variant, multiplied against the on-package variant, and added again here with a different set. The fifth layer is the connection surface and its rate, where the on-package variant changes the surface and therefore an exponent while this chapter's technology keeps the perimeter and multiplies a constant. The sixth layer is the access contract, identical for all four dynamic technologies. The bottom layer is the cell, which differs in exactly one of the module's four comparisons. The diagram is read as a summary: the cell is the most fundamental property a memory has and it is almost never the layer the decision turns on.The same cell four times — and the layer each comparison actually turns onConsumer concurrency — THIS CHAPTER'S DECIDING QUANTITYLatency is invisible iff N >= ceil(L/S). A faster channel RAISES the bar (§4).Latency is invisible iff N >= ceil(L/S). A faster channel RAISES the bar (§4).Integrity + calibration — ADDED BY RATELink-level detection, a response, CONTINUOUS calibration, two-axis margin, per-pin state (§6).Link-level detection, a response, CONTINUOUS calibration, two-axis margin, per-pin state (§6).Return association — POSITION or TAGLatency-optimised: a FIFO is correct. Reordering: only tags are. §12's defect is here.Latency-optimised: a FIFO is correct. Reordering: only tags are. §12's defect is here.The obligation set — CHANGED BY ALL FOUR COMPARISONSDeleted / added / multiplied / added-with-a-different-set. Four patterns, four chapters (§7).Deleted / added / multiplied / added-with-a-different-set. Four patterns, four chapters (§7).Connection surface and rateHBM changes the SURFACE and an exponent. This changes the RATE and a constant (26.1 §1).HBM changes the SURFACE and an exponent. This changes the RATE and a constant (26.1 §1).The access contract — identical for all four DRAM variantsHistory-dependent, deferrable, variable. Only the static comparison differs here.History-dependent, deferrable, variable. Only the static comparison differs here.The cell (A1) — differs in ONE of four comparisonsThe module's central finding: the most fundamental property is almost never the deciding one.The module's central finding: the most fundamental property is almost never the deciding one.

The bottom two layers are the module's conclusion. The access contract is identical across three of the four comparisons, and the cell across three of the fourso a comparison that begins at the cell has begun at the layer least likely to decide anything. Every one of these four decisions was made higher up the stack, and 31.1 §2's axes exist to say where.

10. RTL — One Return Path, Two Objectives

The comparative block for this chapter, and the parameter is the objective rather than a size. LATENCY_CRITICAL decides whether the path carries a bypass and a shallow in-order buffer, or a deep reorder buffer with tag-based association — §5's list and §8's requirement, in one module.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// read_return_path -- the comparative block of §10.
//
// CLASSIFICATION: synthesisable, ILLUSTRATIVE parameter values, and
// CORRECT as written. The intentionally defective block is §11.
//
// WHAT IT IS: the read return path under either objective. The
// generate boundaries are §5's "affordable to give up" list: the
// latency-critical arm carries a bypass and assumes in-order return;
// the throughput arm carries a deep tagged buffer and assumes
// nothing about order.
//
// WHY IT EXISTS HERE: §5 claims that hidden latency makes a reorder
// buffer affordable, and §8 that it simultaneously invalidates
// position-based association. Both claims are structural, and a
// generate boundary is how a structural claim is made checkable.
//
// HOW TO RUN IT: elaborate both arms and present returns OUT OF
// ORDER.
// EXPECTED RESULT: the throughput arm associates correctly by tag;
// the latency-critical arm REFUSES the stimulus via an assertion,
// because out-of-order return is outside its stated contract rather
// than a case it handles badly.
// EXPECTED TRACE: resp_tag must equal the tag of the request whose
// data is being returned, in both arms, for every return.
//
// SYNTHESIS: the latency-critical arm is a small FIFO plus a bypass
// mux. The throughput arm is a tag-indexed buffer of OUTSTANDING
// entries plus a valid vector.
//
// LIMITATIONS: models ASSOCIATION and BUFFERING. It does not model
// link-level error detection or retry -- §6 lists those as added
// obligations and 22.4 owns the electrical reason, and building a
// retry engine here would rebuild a subject this chapter does not
// own. That omission is STATED and is not the bug.
// ---------------------------------------------------------------------
module read_return_path #(
  // The OBJECTIVE parameter. It changes the CONTRACT (what may be
  // assumed about return order), so 31.1 §12's warning applies at
  // full force: the obligation must be re-derived, not inherited.
  parameter bit LATENCY_CRITICAL = 1'b0,

  parameter int OUTSTANDING      = 64,
  parameter int DATA_W           = 256,

  // COUNT versus INDEX, and both appear here for different things.
  // TAG_W indexes the OUTSTANDING tag space, so $clog2(OUTSTANDING)
  // is correct -- tags run 0 .. OUTSTANDING-1.
  parameter int TAG_W            = $clog2(OUTSTANDING),
  // OCC_W must REPRESENT an occupancy of 0 .. OUTSTANDING inclusive,
  // so it needs $clog2(OUTSTANDING + 1). Using TAG_W here would
  // wrap a full buffer to empty, and §11 is a block that makes a
  // related mistake in a place where it is harder to see.
  parameter int OCC_W            = $clog2(OUTSTANDING + 1)
)(
  input  logic                clk,
  input  logic                rst_n,

  // Issue side.
  input  logic                req_fire,
  input  logic [TAG_W-1:0]    req_tag,

  // Device side. `ret_tag` is meaningful only where the device
  // returns a tag; the latency-critical arm ignores it by contract.
  input  logic                ret_valid,
  input  logic [TAG_W-1:0]    ret_tag,
  input  logic [DATA_W-1:0]   ret_data,

  // Requester side.
  output logic                resp_valid,
  output logic [TAG_W-1:0]    resp_tag,
  output logic [DATA_W-1:0]   resp_data,
  input  logic                resp_ready,

  output logic [OCC_W-1:0]    occupancy,
  output logic                overflow,
  output logic [7:0]          added_obligations
);
  initial begin
    if (OUTSTANDING < 2)
      $fatal(1, "read_return_path: OUTSTANDING must be >= 2");
    if ((OUTSTANDING & (OUTSTANDING - 1)) != 0)
      $fatal(1, "read_return_path: OUTSTANDING must be a power of two for a tag-indexed buffer");
    // §4's condition, surfaced at elaboration as a WARNING rather
    // than a fatal, because it is a property of the CONSUMER and this
    // block cannot know it. What it can do is refuse to pretend: a
    // throughput arm with a shallow buffer cannot hide any latency,
    // and saying so at elaboration is cheaper than measuring it.
    if (!LATENCY_CRITICAL && OUTSTANDING < 8)
      $warning("read_return_path: throughput objective with OUTSTANDING = %0d cannot hide meaningful latency (§4)",
               OUTSTANDING);
  end

  generate
  // ===================================================================
  // THROUGHPUT OBJECTIVE. Latency is hidden (§4), so reordering is
  // AFFORDABLE (§5 row four) -- and association must therefore be by
  // TAG (§8). The buffer is indexed by tag, which is what makes
  // out-of-order return a non-event rather than a special case.
  // ===================================================================
  if (!LATENCY_CRITICAL) begin : g_throughput

    logic [DATA_W-1:0] buf_data [OUTSTANDING];
    logic              buf_full [OUTSTANDING];   // data has arrived
    logic              buf_live [OUTSTANDING];   // tag is outstanding
    logic [OCC_W-1:0]  occ;
    logic [TAG_W-1:0]  drain_ptr;

    always_ff @(posedge clk) begin
      if (!rst_n) begin
        for (int i = 0; i < OUTSTANDING; i++) begin
          buf_full[i] <= 1'b0;
          buf_live[i] <= 1'b0;
        end
        occ       <= '0;
        drain_ptr <= '0;
        overflow  <= 1'b0;
      end else begin
        // Issue: the tag becomes outstanding. A tag reissued while
        // still live is a requester error, and it is flagged rather
        // than silently overwritten -- 7.4's discipline that a
        // confident wrong answer is worse than a reported unknown.
        if (req_fire) begin
          if (buf_live[req_tag]) overflow <= 1'b1;
          buf_live[req_tag] <= 1'b1;
          buf_full[req_tag] <= 1'b0;
          if (occ != {OCC_W{1'b1}}) occ <= occ + 1'b1;
        end

        // Return: land the data at ITS OWN TAG. Order is irrelevant,
        // which is the whole point of this arm.
        if (ret_valid) begin
          buf_data[ret_tag] <= ret_data;
          buf_full[ret_tag] <= 1'b1;
        end

        // Drain: present completed entries to the requester. The
        // pointer sweeps the tag space rather than assuming the next
        // completion is the next tag.
        if (resp_valid && resp_ready) begin
          buf_full[drain_ptr] <= 1'b0;
          buf_live[drain_ptr] <= 1'b0;
          if (occ != '0) occ <= occ - 1'b1;
        end
        if (!(buf_live[drain_ptr] && buf_full[drain_ptr]))
          drain_ptr <= drain_ptr + 1'b1;
      end
    end

    assign resp_valid        = buf_live[drain_ptr] && buf_full[drain_ptr];
    assign resp_tag          = drain_ptr;
    assign resp_data         = buf_data[drain_ptr];
    assign occupancy         = occ;
    assign added_obligations = 8'd7;   // §6's count

  // ===================================================================
  // LATENCY-CRITICAL OBJECTIVE. A bypass so the first beat is not
  // delayed by a buffer, and a shallow in-order FIFO. Position-based
  // association is CORRECT HERE, and §8 states exactly why: the
  // contract includes in-order return, so position carries the
  // association. §12 is what happens when this code is reused where
  // that contract no longer holds.
  // ===================================================================
  end else begin : g_latency

    logic [DATA_W-1:0] fifo_data [OUTSTANDING];
    logic [TAG_W-1:0]  fifo_tag  [OUTSTANDING];
    logic [OCC_W-1:0]  wr_ptr, rd_ptr, occ;
    logic              bypass;

    // The bypass: when nothing is queued and the requester is ready,
    // the return goes straight through. This is what the throughput
    // arm gives up (§5 row three) and it is worth area only when the
    // first beat's arrival time is observable.
    assign bypass = ret_valid && (occ == '0) && resp_ready;

    always_ff @(posedge clk) begin
      if (!rst_n) begin
        wr_ptr <= '0; rd_ptr <= '0; occ <= '0; overflow <= 1'b0;
      end else begin
        if (ret_valid && !bypass) begin
          if (occ == OUTSTANDING[OCC_W-1:0]) overflow <= 1'b1;
          else begin
            fifo_data[wr_ptr[TAG_W-1:0]] <= ret_data;
            fifo_tag[wr_ptr[TAG_W-1:0]]  <= ret_tag;
            wr_ptr <= wr_ptr + 1'b1;
            occ    <= occ + 1'b1;
          end
        end
        if (resp_valid && resp_ready && !bypass) begin
          rd_ptr <= rd_ptr + 1'b1;
          occ    <= occ - 1'b1;
        end
      end
    end

    assign resp_valid = bypass || (occ != '0);
    assign resp_data  = bypass ? ret_data : fifo_data[rd_ptr[TAG_W-1:0]];
    // Even here the TAG is carried rather than reconstructed from
    // position. It costs TAG_W bits per entry and it is what makes
    // §14's association property writable at all -- a path that
    // discards the tag cannot be checked against one.
    assign resp_tag   = bypass ? ret_tag  : fifo_tag[rd_ptr[TAG_W-1:0]];
    assign occupancy  = occ;
    assign added_obligations = 8'd0;

  end
  endgenerate
endmodule

The latency-critical arm carries the tag even though position would serve. That is a deliberate design choice and it is the block's quietest lesson: carrying the tag costs TAG_W bits per entry and makes §14's association property writable in both arms. A path that reconstructs the tag from position cannot be checked against an independent one — which is 27.3's independence requirement applied to a design signal rather than to a monitor.

11. RTL Review — The Associator

The intended contract:

  1. Every return must be associated with the request whose data it carries.
  2. The association must be by tag, not by position, unless the contract guarantees in-order return.
  3. A return carrying a tag that is not outstanding is an error, and must be reported rather than absorbed.
  4. resp_tag must equal the tag of the request whose data resp_data holds — for every return, in every configuration.
  5. Occupancy must never exceed OUTSTANDING, and an attempt must set overflow.
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// return_associator -- INTENTIONALLY DEFECTIVE, for review (§11).
//
// CLASSIFICATION: synthesisable, ILLUSTRATIVE, and CONTAINS A BUG.
//
// WHAT IT IS MEANT TO DO: the five-clause contract above -- pair each
// return with its request.
//
// WHY IT EXISTS HERE: §8 establishes that a latency-optimised path is
// ENTITLED to associate by position, and that a throughput path which
// reorders deliberately is not. This block is that entitlement
// carried across a parameter change, and it is the third instance in
// this module of the same bug class -- 31.1 §12 (a contract
// parameter), 31.2 §12 (a duration), 31.3 §12 (a granularity).
//
// It is also a sixth mechanism for 10.5 §9's back-to-back-return
// investigation, whose third listed mechanism is exactly "returns
// are reordered and the in-order assumption does not hold". That
// chapter owns the DEBUG method; this block is the DESIGN that
// produces the symptom.
//
// HOW TO RUN IT: issue tags 0,1,2 and return them 2,0,1.
// EXPECTED RESULT under clause 4: resp_tag follows the DATA.
// EXPECTED TRACE: the return carrying tag 2's data must be presented
// with resp_tag = 2, not with resp_tag = 0.
//
// SYNTHESIS: two pointers and a data FIFO.
// LIMITATIONS: association only. Buffering policy is §10's.
// ---------------------------------------------------------------------
module return_associator #(
  parameter int OUTSTANDING = 64,
  parameter int DATA_W      = 256,
  parameter int TAG_W       = $clog2(OUTSTANDING),
  parameter int OCC_W       = $clog2(OUTSTANDING + 1)
)(
  input  logic                clk,
  input  logic                rst_n,

  input  logic                req_fire,
  input  logic [TAG_W-1:0]    req_tag,

  input  logic                ret_valid,
  input  logic [TAG_W-1:0]    ret_tag,     // on the interface, and...
  input  logic [DATA_W-1:0]   ret_data,

  output logic                resp_valid,
  output logic [TAG_W-1:0]    resp_tag,
  output logic [DATA_W-1:0]   resp_data,
  input  logic                resp_ready,

  output logic [OCC_W-1:0]    occupancy,
  output logic                overflow,
  output logic                assoc_error
);
  // The outstanding-request queue, in ISSUE order.
  logic [TAG_W-1:0]  pend_tag  [OUTSTANDING];
  logic [DATA_W-1:0] ret_buf   [OUTSTANDING];
  logic [OCC_W-1:0]  head, tail, occ;

  always_ff @(posedge clk) begin
    if (!rst_n) begin
      head <= '0; tail <= '0; occ <= '0;
      overflow <= 1'b0; assoc_error <= 1'b0;
    end else begin
      // Record the issue order. Correct, and necessary.
      if (req_fire) begin
        if (occ == OUTSTANDING[OCC_W-1:0]) overflow <= 1'b1;
        else begin
          pend_tag[tail[TAG_W-1:0]] <= req_tag;
          tail <= tail + 1'b1;
          occ  <= occ + 1'b1;
        end
      end

      // The return. Data is stored at the HEAD of the issue queue --
      // that is, the return is assumed to belong to the OLDEST
      // outstanding request.
      if (ret_valid) begin
        ret_buf[head[TAG_W-1:0]] <= ret_data;   // <-- THE DEFECT
      end

      if (resp_valid && resp_ready) begin
        head <= head + 1'b1;
        occ  <= occ - 1'b1;
      end
    end
  end

  assign resp_valid = (occ != '0);
  assign resp_tag   = pend_tag[head[TAG_W-1:0]];
  assign resp_data  = ret_buf[head[TAG_W-1:0]];
  assign occupancy  = occ;
endmodule

Before reading on: which clause, and what does ret_tag do in this module?

12. The Defect — Position Where the Contract Said Tag

The violated clause is 2, and clause 4 falls with it. ret_tag is declared on the port list and never read. The return's data is filed at the head of the issue-order queue, so the association is by position — correct only under the in-order guarantee that §8 says the throughput objective deliberately gives up.

The unread input is the static tell again — the third time in this module and the fifth in the curriculum, after 30.7 §9's trained offset, 30.8 §10's outstanding flag and 31.1 §12's access outcome. CURRICULUM-DERIVED from 30.9 §3: lint finds a declared-and-unread input before simulation starts, and naming the cheapest tool that would have caught a bug is a legitimate verification answer.

The trace, and it is the clearest failure in the module:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   ILLUSTRATIVE. Issue tags 0, 1, 2 in that order. The device
   returns them REORDERED as 2, 0, 1 -- which the throughput
   objective explicitly permits (§5 row four).

   issue:   pend_tag[0]=0  pend_tag[1]=1  pend_tag[2]=2   occ=3

   return tag 2 (data D2) :  ret_buf[head=0] <= D2
   return tag 0 (data D0) :  ret_buf[head=0] <= D0   overwritten!
   return tag 1 (data D1) :  ret_buf[head=0] <= D1   overwritten!

   drain: resp_tag = pend_tag[0] = 0,  resp_data = D1

   so request 0 is answered with request 1's data. Then head
   advances and request 1 is answered with whatever landed next.

   TWO failures at once, and the second is worse:
     - MISATTRIBUTION: every response carries the wrong data.
     - LOSS: three returns landed in one slot, so two were
       destroyed. The data is not merely misrouted; it is gone.

Clause 3 is also silently unmet. assoc_error is declared and never driven, so a return for a tag that was never outstanding is absorbed without a report — and that is the observable which would have made the misattribution diagnosable in a lab.

Now the review question: which parameter change makes a working design violate this, and why is that change not reviewed for correctness?

The change is enabling reordering — or equivalently, increasing OUTSTANDING past the point where the device starts returning out of order. That is a performance change. It is proposed to raise throughput, it is measured in bandwidth, it is reviewed by whoever owns performance, and its correctness consequence is in a different module that nobody re-read.

A performance parameter that changes what may be ASSUMED is a contract change wearing a performance parameter's clothing. The review question for any such change is not is it faster but which assumption did it invalidate, and who was relying on it.

And this is why the module's three defects are the same bug class with three different triggers: a contract parameter (31.1 §12), a residency (31.2 §12), a granularity (31.3 §12), and now a concurrency limit. In every case the code was correct before the change and the change was reviewed against the wrong criterion.

The correction, and the two additions are separate obligations:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
      // CORRECTED. Clause 2: file the data at ITS OWN TAG, so return
      // order is irrelevant. This is the same structure as §10's
      // throughput arm, which is the point -- the correct design
      // already existed in the module and this block did not use it.
      if (ret_valid) begin
        if (!pend_live[ret_tag]) begin
          // Clause 3: a return for a tag that is not outstanding is
          // an ERROR and must be REPORTED. Absorbing it is what
          // makes the misattribution undiagnosable in a lab --
          // 30.10 §13's argument that a debug observable is a design
          // deliverable rather than an optional extra.
          assoc_error <= 1'b1;
        end else begin
          ret_buf[ret_tag]  <= ret_data;
          ret_full[ret_tag] <= 1'b1;
        end
      end

      // And the drain must present a COMPLETED entry rather than the
      // oldest issued one, because under reordering the oldest issue
      // is not necessarily the first completion.
      // resp_valid = pend_live[drain_ptr] && ret_full[drain_ptr];
      // resp_tag   = drain_ptr;
      // resp_data  = ret_buf[drain_ptr];

Two interface additions the correction requires, stated rather than assumed: a pend_live vector — one bit per tag, marking the tag outstanding — and a ret_full vector marking data arrived. Neither existed in the defective block, because position-based association needs neither. A correction that silently references state the module does not have is not a correction, and the same note was needed in 31.1 §12.

The structural finding, and it is the module's last: the defective block and the correct one differ by which index is used to write one array. head against ret_tag. One identifier. That is what makes this class of bug worth a chapter: the wrong version is not badly written, it is a smaller amount of correct-looking code that was right under an assumption nobody restated when it stopped holding.

13. RTL — Measuring the Premise

§4's condition is a claim about the consumer, and the whole trade rests on it. A design that gave up latency and then discovered its requester cannot keep the pipeline full has traded away something it needed for something it cannot use. This block measures whether the premise holds.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// latency_tolerance_monitor -- verification/telemetry, CORRECT as written.
//
// CLASSIFICATION: synthesisable telemetry. Drives nothing.
//
// WHAT IT DOES: measures the outstanding-transaction count over a
// window against ceil(L / S), reporting whether the pipeline was
// actually full often enough for §4's condition to hold.
//
// WHY IT EXISTS HERE: every trade in §5 is licensed by §4's
// condition, and §4's condition is about the REQUESTER. This is the
// instrument that checks the licence. 30.8 §13 makes the same
// argument for the bandwidth ladder: rung 1 moving does not move
// rung 5, and only a measurement distinguishes them.
//
// HOW TO RUN IT: run the real access pattern and read
// `frac_full_num` / `frac_full_den`.
// EXPECTED RESULT: a deeply pipelined streaming consumer reports a
// high fraction; a dependency-chained one reports near zero, and
// that is the finding -- the technology's premise does not hold for
// that consumer.
//
// SYNTHESIS: two counters, a max tracker, one comparator.
//
// LIMITATIONS: measures CONCURRENCY, not throughput. A consumer can
// satisfy §4's condition and still be limited elsewhere -- 30.8's
// gaps 2-to-3 and 4-to-5 -- so a high fraction here licenses the
// latency trade and proves nothing about achieved bandwidth.
// ---------------------------------------------------------------------
module latency_tolerance_monitor #(
  parameter int WIN         = 65536,
  parameter int OUTSTANDING = 64,
  // L and S from §4. ILLUSTRATIVE, and supplied rather than derived
  // because S depends on the burst and L on the configuration --
  // 30.4 §2's unit discipline means neither can be assumed here.
  parameter int LAT_CYCLES  = 60,
  parameter int RET_SPACING = 4,
  // DERIVED at elaboration: N* = ceil(L / S). Integer ceiling,
  // written so it is obviously a ceiling rather than a truncation --
  // a floor here would UNDERSTATE the requirement and license a
  // trade the consumer cannot support.
  parameter int N_STAR      = (LAT_CYCLES + RET_SPACING - 1) / RET_SPACING,
  // COUNT, not INDEX: occupancy ranges 0 .. OUTSTANDING inclusive.
  parameter int OCC_W       = $clog2(OUTSTANDING + 1),
  parameter int CNT_W       = $clog2(WIN + 1)
)(
  input  logic              clk,
  input  logic              rst_n,

  input  logic              req_fire,
  input  logic              resp_fire,
  input  logic              win_tick,

  output logic [OCC_W-1:0]  r_max_outstanding,
  output logic [CNT_W-1:0]  frac_full_num,   // cycles with occ >= N*
  output logic [CNT_W-1:0]  frac_full_den,   // cycles with occ >  0
  output logic              premise_holds,
  output logic              result_valid,
  output logic              underflow
);
  initial begin
    if (RET_SPACING < 1) $fatal(1, "latency_tolerance_monitor: RET_SPACING >= 1");
    if (LAT_CYCLES  < 1) $fatal(1, "latency_tolerance_monitor: LAT_CYCLES >= 1");
    // The structural warning §4 earns: if the buffer cannot hold N*
    // transactions, the premise CANNOT hold however well the
    // requester behaves, and that is a design error rather than a
    // workload property. Flagged at elaboration because it is a
    // static fact -- 30.9 §3's cheapest-tool rule.
    if (OUTSTANDING < N_STAR)
      $fatal(1, "latency_tolerance_monitor: OUTSTANDING (%0d) < N* (%0d): the pipeline cannot be filled (§4)",
             OUTSTANDING, N_STAR);
  end

  logic [OCC_W-1:0] occ, mx;
  logic [CNT_W-1:0] c_full, c_busy;

  always_ff @(posedge clk) begin
    if (!rst_n) begin
      occ <= '0;  mx <= '0;  c_full <= '0;  c_busy <= '0;
      r_max_outstanding <= '0;
      frac_full_num <= '0;  frac_full_den <= '0;
      premise_holds <= 1'b0;  result_valid <= 1'b0;  underflow <= 1'b0;
    end else if (win_tick) begin
      r_max_outstanding <= mx;
      frac_full_num     <= c_full;
      frac_full_den     <= c_busy;
      // The verdict, cross-multiplied so no divider is needed:
      // the premise holds when the pipeline was full for at least
      // three quarters of the BUSY cycles. The threshold is a POLICY
      // and it is stated here rather than hidden -- 24.2 §5's
      // practice of naming a policy input as a policy input.
      premise_holds     <= (c_full * 4) >= (c_busy * 3);
      result_valid      <= 1'b1;
      mx <= '0;  c_full <= '0;  c_busy <= '0;
    end else begin
      result_valid <= 1'b0;

      // Occupancy tracking. The two events can coincide, so the
      // combined case is handled explicitly rather than by two
      // independent if-statements that would race.
      if (req_fire && !resp_fire) begin
        if (occ != OUTSTANDING[OCC_W-1:0]) occ <= occ + 1'b1;
      end else if (resp_fire && !req_fire) begin
        // A response with nothing outstanding is an ERROR, not a
        // saturating subtract. Absorbing it would make the counter
        // drift silently and every figure below it meaningless --
        // 30.8 §11's rule that an instrument which can report an
        // impossible value has never been checked.
        if (occ == '0) underflow <= 1'b1;
        else           occ <= occ - 1'b1;
      end

      if (occ > mx) mx <= occ;
      if (occ >= N_STAR[OCC_W-1:0] && c_full != {CNT_W{1'b1}})
        c_full <= c_full + 1'b1;
      if (occ != '0 && c_busy != {CNT_W{1'b1}})
        c_busy <= c_busy + 1'b1;
    end
  end
endmodule

The denominator is busy cycles, not window cycles, and that choice is the block's substance. Measuring against the whole window conflates the consumer cannot keep the pipeline full with the consumer had nothing to do — which is exactly the denominator error 30.8 §10 diagnoses, where an instrument reported 400% efficiency because it counted arrivals rather than outstanding work. This block avoids that specific mistake by construction, and says so.

And underflow exists because a drifting occupancy counter invalidates every other output. CURRICULUM-DERIVED from 30.8 §11: a measurement that can report an impossible value has never been checked, so the impossible case is reported rather than clamped.

14. SVA Review — What Survives a Contract Change

The property written for §11's block, and it is a genuinely good property:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // Offered as "proves every return is associated with a request".
  // Clause 1, and it PASSES on the defective block.
  property p_response_has_a_request;
    @(posedge clk) disable iff (!rst_n)
      resp_valid |-> (occupancy != '0);
  endproperty
  assert property (p_response_has_a_request)
    else $error("a response was presented with nothing outstanding");

  // And the in-order association property, which was CORRECT in the
  // latency-optimised design this code came from.
  property p_response_follows_issue_order;
    @(posedge clk) disable iff (!rst_n)
      (resp_valid && resp_ready) |-> (resp_tag == pend_tag[head]);
  endproperty
  assert property (p_response_follows_issue_order)
    else $error("response tag did not follow issue order");

Q. Both pass. What is wrong with the second one in particular?

It is 30.9 §6's variety 10 in its purest form yet. Under the latency-critical objective the contract includes in-order return, so the property is sound. Under the throughput objective the contract excludes it — and the property now asserts that responses must follow issue order, which is exactly the behaviour the objective was chosen to avoid. A corrected design that returns out of order fails it.

So the property does not merely miss the defect. It forbids the fix. And the first property is variety 2: it proves a response had some outstanding request and never mentions which.

The general repair, and it is the module's closing verification result:

When a parameter changes what may be ASSUMED, the assertion set partitions rather than generalises. Properties that encode the old assumption must be guarded by the configuration that licenses them, and a property with no such guard is claiming to hold in a configuration where it is false.

What actually covers clauses 1 to 4 across both objectives:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // Clause 4, and it is the property that is sound in BOTH arms
  // because it names the DATA's tag rather than a position. The
  // reference function is an independent model of the association --
  // 27.3's independence requirement, and 30.6 §11's rule that a
  // comparative obligation needs a model rather than a property.
  property p_response_tag_matches_its_data;
    @(posedge clk) disable iff (!rst_n)
      (resp_valid && resp_ready) |->
        (resp_data == ref_expected_data(resp_tag));
  endproperty
  assert property (p_response_tag_matches_its_data)
    else $error("response data does not belong to the response tag");

  // Clause 2, guarded by the configuration that licenses it. This is
  // the repair for variety 10: the parameter sits in the ANTECEDENT
  // as a condition on which contract is in force, and the claim
  // inside is unchanged in meaning -- 31.1 §14's test, passed.
  property p_in_order_only_when_licensed;
    @(posedge clk) disable iff (!rst_n)
      (LATENCY_CRITICAL && resp_valid && resp_ready) |->
        (resp_tag == pend_tag[head]);
  endproperty
  assert property (p_in_order_only_when_licensed)
    else $error("in-order association violated in the arm that guarantees it");

  // Clause 3. A return for a tag that is not outstanding must be
  // REPORTED. The defective block leaves assoc_error undriven, so
  // this property is what turns an undriven output into a failure.
  property p_unknown_tag_is_reported;
    @(posedge clk) disable iff (!rst_n)
      (ret_valid && !pend_live[ret_tag]) |-> assoc_error;
  endproperty
  assert property (p_unknown_tag_is_reported)
    else $error("a return for a non-outstanding tag was absorbed silently");

  // Clause 5, both directions. Occupancy is a derived count, so its
  // RANGE is asserted -- 30.8 §11's rule, and the direction matters:
  // an understated occupancy licenses an overflow.
  property p_occupancy_in_range;
    @(posedge clk) disable iff (!rst_n)
      occupancy <= OUTSTANDING;
  endproperty
  assert property (p_occupancy_in_range)
    else $error("occupancy exceeded OUTSTANDING");

  property p_overflow_is_reported;
    @(posedge clk) disable iff (!rst_n)
      (req_fire && occupancy == OUTSTANDING) |-> overflow;
  endproperty
  assert property (p_overflow_is_reported)
    else $error("an issue beyond OUTSTANDING did not set overflow");

  // No data may be OVERWRITTEN before it is delivered. This is the
  // second half of §12's failure -- the loss, which the
  // misattribution properties above do not catch, because a
  // destroyed return produces no wrong value at the interface, only
  // a missing one.
  property p_no_return_overwritten;
    @(posedge clk) disable iff (!rst_n)
      (ret_valid && ret_full[ret_tag]) |-> assoc_error;
  endproperty
  assert property (p_no_return_overwritten)
    else $error("a return overwrote undelivered data for the same tag");

  // §4's premise, asserted where it is a DESIGN fact rather than a
  // workload one: the buffer must be able to hold N*.
  property p_buffer_can_hold_n_star;
    @(posedge clk) disable iff (!rst_n)
      OUTSTANDING >= N_STAR;
  endproperty
  assert property (p_buffer_can_hold_n_star)
    else $error("the return buffer is too shallow to fill the pipeline");

  // ---- Covers. Per configuration, per order, and per depth.
  // The two objectives -- 31.1 §14's configuration rule.
  cover property (@(posedge clk) disable iff (!rst_n)
                  resp_valid && LATENCY_CRITICAL);
  cover property (@(posedge clk) disable iff (!rst_n)
                  resp_valid && !LATENCY_CRITICAL);
  // OUT-OF-ORDER return, which is the antecedent the whole defect
  // lives behind. In-order stimulus makes every property above hold
  // on the defective block, so this cover is the one that matters --
  // and an environment that models the device as in-order will
  // never reach it, however long it runs.
  cover property (@(posedge clk) disable iff (!rst_n)
                  ret_valid && (ret_tag != pend_tag[head]));
  // And the DEPTH regime, because §4 shows the requirement scales:
  // a pipeline that is full is a different test from one that is
  // merely non-empty, and only the first exercises reordering
  // pressure.
  cover property (@(posedge clk) disable iff (!rst_n)
                  occupancy >= N_STAR);
  cover property (@(posedge clk) disable iff (!rst_n)
                  occupancy == OUTSTANDING);

The out-of-order cover is the module's fourth distinct coverage-reachability problem, and the set is now worth stating as one rule.

ChapterThe defect scales withSo the cover must be on
31.1 §14a configurationeach configuration
31.2 §14a durationthe duration, thresholded by the failure mechanism
31.3 §15an index spaceeach index
This chaptera return ORDERthe reordered case, which an in-order model never produces

The coverage item must be on the dimension the defect scales with — and when that dimension is a property of the environment's MODEL rather than of the stimulus, no amount of running longer reaches it.

That last clause is the sharpest of the four. A longer run reaches more configurations, more durations and more indices. It never reaches an out-of-order return if the device model returns in order, because the unreached case is not rare — it is absent by construction. CURRICULUM-DERIVED from 27.3's independence requirement and 30.9 §12's escape taxonomy, whose fifth mechanism — the stimulus never reached the stateis the only one that more coverage addresses, and this is a case where the stimulus cannot reach it until the model is changed.

15. What the Return Path's Assertions Prove

§14 reviewed the associator and left §10's block unasserted. The gap matters here for a reason specific to this chapter: §10 is the module's only comparative block whose two arms have different interfaces in use — the throughput arm reads ret_tag, the latency arm reads it only to carry it — so a property set that does not distinguish the arms will either be unsound in one or vacuous in the other.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  // ---- The BYPASS, which exists only in the latency arm and is
  //      §5 row three's give-up made concrete. Two obligations, and
  //      they are separate: the bypass must be CORRECT when taken,
  //      and it must not be taken when it would reorder.
  property p_bypass_preserves_association;
    @(posedge clk) disable iff (!rst_n)
      (LATENCY_CRITICAL && bypass) |->
        (resp_tag == ret_tag && resp_data == ret_data);
  endproperty
  assert property (p_bypass_preserves_association)
    else $error("the bypass presented a tag or data that was not the return's");

  // A bypass while the FIFO is non-empty would deliver a NEWER
  // return ahead of an older queued one -- reordering, inside the
  // arm whose whole contract is that it does not reorder. This is
  // the property that makes the bypass safe rather than merely fast.
  property p_no_bypass_when_queued;
    @(posedge clk) disable iff (!rst_n)
      (LATENCY_CRITICAL && bypass) |-> (occupancy == '0);
  endproperty
  assert property (p_no_bypass_when_queued)
    else $error("bypass taken with data queued: the in-order contract is broken");

  // ---- Occupancy must agree with the events that change it. A
  //      derived count, so its RANGE is asserted (30.8 §11) and its
  //      ARITHMETIC is asserted separately -- a counter can be in
  //      range and still have drifted.
  property p_occupancy_tracks_events;
    @(posedge clk) disable iff (!rst_n)
      occupancy == ($past(occupancy)
                    + (($past(req_fire) && !$past(resp_valid && resp_ready)) ? 1 : 0)
                    - ((!$past(req_fire) && $past(resp_valid && resp_ready)) ? 1 : 0));
  endproperty
  assert property (p_occupancy_tracks_events)
    else $error("occupancy drifted from the issue and completion events");

  // ---- The throughput arm's defining property, and the one that is
  //      FALSE in the other arm -- so it is guarded by the
  //      configuration, per §14's repair for variety 10. A tag's
  //      data must be filed at that tag, independent of arrival
  //      order, which is the structural difference §8 names.
  property p_tag_indexed_when_reordering;
    @(posedge clk) disable iff (!rst_n)
      (!LATENCY_CRITICAL && ret_valid) |=> buf_full[$past(ret_tag)];
  endproperty
  assert property (p_tag_indexed_when_reordering)
    else $error("a return did not land at its own tag in the reordering arm");

  // ---- A tag reissued while still outstanding is a REQUESTER error
  //      and must be reported, not absorbed. 7.4's discipline: a
  //      confident wrong answer is worse than a reported unknown,
  //      and silently overwriting a live entry produces exactly the
  //      loss failure §12's second half describes.
  property p_live_tag_reissue_is_reported;
    @(posedge clk) disable iff (!rst_n)
      (!LATENCY_CRITICAL && req_fire && buf_live[req_tag]) |=> overflow;
  endproperty
  assert property (p_live_tag_reissue_is_reported)
    else $error("a live tag was reissued without report");

  // ---- Covers. Per arm, and then per the FEATURE each arm has that
  //      the other does not -- because a cover on the arm alone does
  //      not prove the arm's distinguishing path was exercised.
  // The bypass, taken. Absent from the throughput arm by
  // construction, so this must go DEAD there and be LIVE here --
  // two different instruments, 30.10 §12's distinction.
  cover property (@(posedge clk) disable iff (!rst_n)
                  LATENCY_CRITICAL && bypass);
  // The bypass NOT taken because data was queued: the path that
  // makes p_no_bypass_when_queued non-trivial.
  cover property (@(posedge clk) disable iff (!rst_n)
                  LATENCY_CRITICAL && ret_valid && (occupancy != '0));
  // A return landing at a tag that is NOT the drain pointer, which
  // is the reordering arm's distinguishing event and the antecedent
  // §14's out-of-order cover watches from the other side.
  cover property (@(posedge clk) disable iff (!rst_n)
                  !LATENCY_CRITICAL && ret_valid && (ret_tag != drain_ptr));
  // A full buffer in the reordering arm -- §4's N* condition
  // actually reached, rather than merely permitted by the parameter.
  cover property (@(posedge clk) disable iff (!rst_n)
                  !LATENCY_CRITICAL && (occupancy == OUTSTANDING));

p_no_bypass_when_queued is the property worth carrying out of this section, because it is the one a designer adding a bypass for speed would not think to write. A bypass is a reordering mechanism — it lets a newer return overtake older queued ones — so the arm whose contract forbids reordering must forbid the bypass in exactly the circumstances where it would reorder. The optimisation and the contract collide, and only a property states where.

And the two dead-or-live covers make the arm distinction auditable. CURRICULUM-DERIVED from 30.10 §12: a cover that must stay live and one that must go dead are different instruments, and naming which you are writing is the senior move. The bypass cover must be live in one arm and dead in the other, and a regression where it is dead in both has not run the latency arm at all.

16. What Would You Measure?

Q. You are considering this technology for a new design. What do you measure, in what order?

MeasurementWhat it settlesCostOwner
Is the memory required to be field-replaceable?§3 — closes the question with no further workhours§3
Is the capacity requirement settled at board design?§3 row two — point-to-point fixes it therehours§3
The requester's sustained outstanding count against N*§4's premise, which licenses every trade in §5days§4, §13
frac_full_num / frac_full_den from §13 on the real patternwhether the premise holds often enoughdays§13
The requester's access granularity against the burst§5 row two — payload efficiency, which coarsening costsdays12.4
Whether the device model in the environment returns in order§14 — whether the association was ever testedhours§14
The corrected-error counter's existence in the design§6 — a corrected error is invisible unless countedhours28.5
Thermal headroom at the adjacent placement§3 row three, and there may be no fanweeks24.1 §4
The five-rung ladder on a prototypewhether rung 1's constant factor moved rung 5weeks30.8 §3

Rows one and two cost hours and can close the question, which is the ordering discipline every chapter in this module has repeated. A replaceability requirement or an unsettled capacity requirement makes the topology unavailable, and no rate argument overrides a topology that is not on offer.

Row three is the premise and row four is its honest form. A peak outstanding count proves the requester can reach N*; the fraction proves it does. CURRICULUM-DERIVED from 23.1, which owns why the average is the wrong statistic — the same argument applies to a maximum, in the opposite direction.

And row six is free and almost never asked. If the environment's device model returns in order, §12's defect class was never testable, and that is a finding available in an afternoon by reading the model. CURRICULUM-DERIVED from 30.9 §12's escape analysis: four of its five mechanisms are plan or review defects that no additional stimulus would have found, and an in-order device model is precisely such a defect.

17. Common Wrong Answers

“It is DDR with the clock turned up.” §2, §6. The rate is bought with topology, and the topology costs replaceability and capacity flexibility while the rate forces seven added obligations back in.

“It escapes the pin-count wall.” §1, and 26.1 §1 owns the correction: a constant factor cannot change the 4/L exponent. It postpones and never removes.

“It uses a different surface, like HBM.” §1, §2. It uses the same perimeter. Changing the surface and changing the rate are different kinds of answer to the same problem.

“It has lower latency because it is faster.” §1, §5. Latency is what it trades away, deliberately, and §4 is the condition under which that is affordable.

“A faster memory is easier to keep full.” §4. Halving the return spacing doubles the outstanding transactions required to hide the same latency. The bar rises with the rate.

“It is for graphics.” §4. A market label. The property being selected for is deep, predictable concurrency, and any workload with it qualifies — while a latency-bound consumer fails regardless of its market.

“Bigger bursts are free for a streaming consumer.” §5. They improve command efficiency and worsen payload efficiency — 12.4 — so they are free only for a consumer that wants the whole burst.

“It has a simpler interface because it drops the socket.” §6. Seven obligations are added: link-level detection, a response to it, continuous calibration, two-axis margining, per-pin trained state, tagged return tracking, and thermal awareness.

“Calibration is a bring-up concern.” §6, and 19.5 owns the one-time-versus-continuous split. Continuous calibration is a runtime concern that the scheduler must accommodate, and 30.6 §8 owns the failure where the mechanism itself is the disturbance.

“Link-level correction makes errors a non-issue.” §6. A corrected error is invisible unless counted, and 28.5 owns why the observed structure is then not the fault structure.

“We can add capacity later.” §3. Point-to-point with a fixed population fixes capacity at board design — a different mechanism from 31.3 §16's assembly, and the same lost option.

“Reordering returns is an optimisation we can add later.” §8, §12. It is a contract change: position-based association becomes wrong, silently, and returns data to the wrong requester.

“A FIFO associates returns with requests.” §8. Only under a guaranteed in-order return. Otherwise it pairs every return with some request and every pairing is wrong.

ret_tag is on the interface, so the association is tag-based.” §12. Declared and never read — the fifth instance of that tell in the curriculum, and lint finds it before simulation starts.

“The misattribution would show up as wrong data.” §12. Partly: the second failure is loss, because three returns landed in one slot. A destroyed return produces a missing value rather than a wrong one, and §14 needs a separate property for it.

“The association assertion passes.” §14. One of them proves a response had some request, and the other forbids the fix — variety 10 in its purest form.

“We will find it by running the regression longer.” §14. Not if the device model returns in order. The unreached case is absent by construction, not rare.

“The consumer reached N* at peak, so the premise holds.” §15. A maximum proves it can; the busy-cycle fraction proves it does — and 23.1 owns why a single summary statistic is the wrong instrument.

18. Self-Check

  1. State which of 31.3 §4's two ways past the pin-count wall this technology takes, and explain in one sentence what that implies about its limit.

  2. Derive the four topological removals of §2 and, for each, the electrical penalty it eliminates and the chapter that owns that penalty.

  3. From §3, give three consequences of the topology that have nothing to do with speed, and name the axis each belongs to.

  4. Write §4's condition from memory. Compute N* for L = 100, S = 5 and for L = 100, S = 2, and state what the second result implies about making a channel faster.

  5. Give three items from §5's affordable to give up list, and for each the specific cost that must be stated alongside it.

  6. Give four items from §6's added back list, and say which one changes the verification plan rather than the design.

  7. Reproduce §7's axis table for the A1 and A2 rows across all four comparisons. State the module's central empirical finding in one sentence.

  8. Explain why position-based association is correct in one arm of §10 and wrong in the other, and identify the single identifier that differs between §11's defective block and its correction.

  9. Explain why p_response_follows_issue_order forbids the fix, and state the general repair for a property that encodes a superseded assumption.

  10. Name the four coverage-reachability problems this module produced, one per chapter, and say which one more running cannot solve and why.

  11. You are told the regression is green and the environment's device model returns in order. State what you now know, what you do not, and what you would ask for.

19. Where This Goes — and the Module Closes

The graphics variant buys per-pin rate with topology. It takes the constant-factor route past the pin-count wall, so it postpones and never removes it; the rate is purchased with a soldered point-to-point fixed-population channel, and the purchase price is replaceability and capacity flexibility; the deciding quantity is the consumer's latency tolerance, measured as whether the requester sustains ceil(L/S) transactions outstanding; making the channel faster raises that bar rather than lowering it; and what the rate forces back in — integrity, continuous calibration, per-pin state, tagged association — is why this is not DDR with the clock turned up.


And the module closes on the law it opened with.

A COMPARISON IS NOT TWO DESCRIPTIONS PLACED SIDE BY SIDE. IT IS A DECISION, AND A DECISION HAS A DECIDING QUANTITY.

Four comparisons, four deciding quantities, and not one of them is a specification number.

ComparisonThe deciding quantityWhere it is measured
vs SRAMtolerable completion spread, then capacity against a crossover carrying a controller termthe requester's deadline
vs LPDDRduty cycle and idle-duration distributionthe workload
vs HBMconnections per unit of capability, which the surface decidesthe package
vs GDDR — this chapterlatency tolerance, as sustained outstanding transactionsthe consumer

Every one of the four is measured outside the memory. That is the module's single most useful result: a memory-technology decision is not made by comparing memories. It is made by measuring the system that will use one, which is why 31.1 §1's two-stage ordering — feasibility before cost — recurs in every chapter, and why every what would you measure section begins with a row that costs hours and can close the question.

Three structural findings hold across all four.

Axis A1 differs in one comparison out of four. The cell is the most fundamental property a memory has and it is almost never the layer the decision turns on — §9's diagram is that result as a picture, and §7's table as arithmetic.

Every comparison changes the obligation set, in four different ways. Deleted, added, multiplied, and added-with-a-different-set. So how much controller does this cost me has the most reliable answer of any question in the module, and it is the question a technology comparison almost never asks.

And all three defects were the same bug class with different triggers. A contract parameter (31.1 §12), a residency (31.2 §12), a granularity (31.3 §12), and a concurrency limit (§12). In every case the code was correct before the change, the change was proposed for performance, and it was reviewed against the wrong criterion. The one review question that would have caught all four:

Which assumption did this change invalidate, and who was relying on it?

And the module contributed one new entry to 30.9 §6's taxonomy plus one refinement and one rule. Variety 10 — parameter-conditional soundness, where a property is sound under one configuration and, under another, certifies the defect and forbids its fix — three instances, in 31.1 §14, 31.3 §14 and §14. The scale-vacuity refinement of variety 6, from 31.2 §14. And the coverage rule the four chapters converge on: the coverage item must be on the dimension the defect scales with — and when that dimension belongs to the environment's model rather than to its stimulus, running longer never reaches it.

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.