Skip to content
VLSI Mentor

DDR · Module 24

LPDDR5 Architecture

A data clock at 4275 MHz against a command clock at a selectable 2:1 or 4:1 ratio, double-edge command entry that reverses LPDDR4's choice, and a burst length that is a configuration rather than a constant.

Chapter 4.7 §3 established that LPDDR5 separates the command clock from the data clock, explained why the decoupling is worth having, and said plainly that “Module 24 owns that machinery.” This is where it is owed.

Chapter 24.3 built LPDDR4's interface: six command pins per channel, single-edge sampling, two channels per die, a fixed 32-byte granule. LPDDR5 changes every one of those, and two of the changes go in the direction LPDDR4 had deliberately chosen against. §7 is about that reversal, and it is the most interesting thing in the chapter.

1. One Channel Where LPDDR4 Had Two

Begin with the change that is hardest to explain, because it appears to undo 24.3 §5.

DEVICE-VERIFIED, from a named LPDDR5/5X part: the die presents a single x16 channel. Chapter 24.3 §5 verified that the LPDDR4 part presents two independent x16 channels on one die.

LPDDR4 (named part)LPDDR5 (named part)
Channels per die2 × x161 × x16
Dies needed for a x32 interface12

So a x32 LPDDR5 interface is two dies, where a x32 LPDDR4 interface was one.

This is not the loss of channel independence. Two LPDDR5 dies give two independent x16 channels exactly as one LPDDR4 die did — the independence 24.3 §6 defines as an invariant is preserved, because it was always a property of the channels and not of the packaging. What changes is where the channel boundary sits relative to the die boundary.

What it does change is the per-die interface. One channel per die means the die's pins serve one command stream, which is what makes room for the additional clocking pins §3 introduces. A die carrying two channels and two clock pairs per channel would be a considerably harder pin-count problem.

So the single-channel die is not a step back from LPDDR4's organisation — it is what pays for LPDDR5's clocking, and §3 is the thing being paid for.

2. The Data Rate, and What It Implies

One verified bandwidth figure fixes every clock frequency in this chapter, so it is worth extracting carefully.

DEVICE-VERIFIED: 17.1 GB/s per channel on the named LPDDR5X part, with a x16 channel.

DERIVED, recomputed:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   17.1 GB/s x 8 bits/byte / 16 pins  =  8.550 Gbps per pin
   data is transferred on both WCK edges
   ->  WCK  =  8550 / 2  =  4275 MHz

So the data clock runs at roughly 4.3 GHz on this part. That number is what makes the rest of the chapter necessary: at 4.3 GHz there is no prospect of the command path sharing the same clock, which is precisely 4.7 §3's argument arriving as arithmetic.

DEVICE-VERIFIED, the clocks the part actually has:

SignalPurpose
CK_t / CK_ccommand and address clock
WCK_t / WCK_cdata clock, forwarded from the controller
RDQS_t / RDQS_cread strobe, optional

The third row is worth noting. The read strobe is optional, which means a controller may choose to time read data from WCK alone — one fewer signal pair, at the cost of whatever margin the returned strobe would have provided. Chapter 4.7 §3 makes the source-forwarded argument that permits this; the chapter does not claim to know which choice a given system should make.

3. Two Clock Domains at a Configurable Ratio

Here is the machinery. DEVICE-VERIFIED: the WCK to CK ratio is 2:1 or 4:1, selectable.

Not fixed. A configurable integer ratio between two clock domains is the central object of this chapter, and it has consequences the phrase does not suggest.

DERIVED from §2's 4275 MHz WCK, recomputed:

WCK:CKCK frequencyCommand slots per second (CK is DDR — §5)
2:12137.5 MHz4275 M
4:11068.75 MHz2137.5 M

Two things follow immediately.

The command clock's frequency is not a device constant. A controller configured at 4:1 runs its command domain at half the frequency it would at 2:1, for identical data bandwidth. Every command-path timing budget depends on a mode-register setting.

And the ratio is the only thing relating the two domains. There is no shared frequency, no common divisor other than the ratio itself, and no way to reason about a command's relationship to a data beat without knowing the ratio in force. §12's block exists because that relationship has to be maintained explicitly.

4. What the Ratio Actually Buys

The ratio table shows command slots halving. It is worth asking what the extra command bandwidth at 2:1 is for, because the obvious answer is wrong.

DERIVED, and the numbers come out exact:

WCK:CKData bits per channel per command slot
2:132
4:164

So at 4:1, one command slot accompanies 64 bits of channel data; at 2:1, 32 bits. The ratio is a choice about how much command bandwidth to provision per unit of data bandwidth, and 2:1 provisions twice as much.

Now the check that shows the obvious answer is wrong. Is the command bus the constraint on issuing bursts? DERIVED, using §6's BL32:

WCK:CKBL32 bursts per secondCommand slots per secondSlots available per burst
2:1267.2 M4275 M16
4:1267.2 M2137.5 M8

Even at 4:1 there are eight command slots per burst. The command bus is nowhere near saturated by bursts at either ratio, so the extra bandwidth at 2:1 is not buying the ability to issue more reads and writes.

What it buys is everything else a command stream carries. Activates, precharges, refreshes, mode-register writes, and the low-power entries and exits of 24.2 all consume slots and none of them transfers data. A workload with high bank turnover or frequent refresh pressure issues many non-data commands per burst, and that is what a 2:1 ratio serves.

And the cost of 2:1 is a command domain running at twice the frequency. Which is real power and real timing difficulty, for a benefit that only appears when the command stream is doing more than requesting data.

5. Command Entry Went DDR

Chapter 24.3 §1 verified that LPDDR4 samples commands on the positive edge only, and 24.3 §3 argued why: single-edge sampling trades edges for cycles, and without a DLL the negative edge is the expensive one.

DEVICE-VERIFIED: the named LPDDR5 part uses DDR command/address entry — both edges of CK.

LPDDR3LPDDR4LPDDR5
Command samplingboth edgespositive onlyboth edges

LPDDR5 reverses LPDDR4's choice. That is not a contradiction and it is worth seeing why not.

Chapter 24.3 §3's reasoning was that at LPDDR4's command-clock rate, the negative edge's placement was the harder problem. LPDDR5's command clock runs slower than LPDDR4's data rate by a large factor — at 4:1, CK is 1068.75 MHz against a data rate of 8550 Mbps. The command domain is no longer the fast domain; WCK is.

So decoupling the clocks made double-edge command sampling affordable again. A 1 GHz clock's negative edge is a far easier target than a 4 GHz clock's, and LPDDR4 had no separate slow command clock to sample against. The reversal is a consequence of §3's decoupling rather than a reconsideration of §3 of the previous chapter.

This is the clearest illustration of why 24.1 §8's rule against carrying parameters between generations matters. “LPDDR samples commands on one edge” was true of LPDDR4, false of LPDDR3, and false again of LPDDR5. It was never a family property.

6. Burst Length Is a Choice

DEVICE-VERIFIED: the part supports 16n or 32n prefetch, with burst length 16 or 32.

Chapter 24.3 §8 verified LPDDR4's prefetch as 16n, fixed. DERIVED, recomputed for a x16 channel:

PrefetchBurst lengthBits per burstMinimum transfer
16n1625632 B
32n3251264 B

BL16 reproduces LPDDR4's granule exactly; BL32 doubles it. So LPDDR5 does not choose a granularity — it offers both and makes the choice a configuration.

That is a genuinely different kind of answer from LPDDR4's, and §7 is about why the question needed reopening.

7. The Granularity Tension

Here is the chapter's most interesting point, and it requires holding two of this module's own arguments against each other.

Chapter 24.3 §9 argued, with numbers, that two narrow channels at a 32-byte granule beat one wide channel at 64 bytes for a mobile workload — smaller granule, more concurrency, and never worse. That argument was the justification for LPDDR4's whole organisation.

LPDDR5 offers a 64-byte granule as an option and one channel per die. Both of the things 24.3 §9 argued against.

LPDDR4LPDDR5
Channels per die21
Granule32 B, fixed32 B or 64 B

The resolution is in two parts, and the first is straightforward.

The channel count per die is not the channel count of the interface. §1 established this: a x32 LPDDR5 interface is two dies giving two channels, so the concurrency 24.3 §9 argued for is intact. The die changed; the interface did not.

The granule is a genuine reopening, and it is reopened as a choice rather than settled. BL32 halves the number of bursts needed for a large transfer, which reduces command overhead and improves bus efficiency — and wastes more on a small access, exactly as 24.3 §8's table shows. LPDDR5's answer is that the right granule depends on the workload and therefore belongs in a mode register, which is a different answer from LPDDR4's and not a contradiction of it.

8. Bank Groups or Sixteen Banks

DEVICE-VERIFIED: the part supports bank-group mode or 16-bank mode.

Chapter 5.3 owns bank groups as a structure and the classification they imply, and this chapter does not rebuild that. The LPDDR5-specific fact is that the organisation is selectable, which makes it the third configurable architecture dimension in this chapter after the clock ratio and the burst length.

Three configurable dimensions is the observation worth carrying rather than any one of them:

DimensionOptionsOwned by
WCK:CK ratio2:1, 4:1§3, §4
Burst length16, 32§6, §7
Bank organisationbank groups, 16 banks§8, and 5.3

LPDDR5's architecture is not one architecture. It is a small space of architectures selected by mode registers, and a controller that treats any dimension as a constant has hard-coded a choice it did not make deliberately. §15's guard exists because a configurable architecture needs a protocol for changing configuration, and that protocol is a part of the design most easily overlooked.

9. PASR, and PAAR as Well

DEVICE-VERIFIED: the part supports partial-array self refresh (PASR) and partial-array auto refresh (PAAR) with segment mask.

Chapter 24.2 §9 owns both mechanisms, established that PASR covers the idle case and PAAR the active case, and built the segment-mask block with its retention consequence. Nothing here changes that analysis.

The one thing worth adding is the architectural reading. PAAR extends the masking idea from the device's own refreshing to controller-issued refreshing, which means the abandoned-region hazard 24.2 §9 describes now applies while the device is active, not only while it idles. The failure mode is identical; its window is larger.

DEVICE-VERIFIED: the part supports link ECC.

This is worth a section because of the class of mechanism it is, and the distinction is one this curriculum has already done the work to make precise.

Chapter 22.4 built a write-CRC generator and an error-recovery block, and established from the polynomial itself what CRC can and cannot do: it detects, and recovery is a retry. A detected error means the transfer is repeated.

Link ECC corrects. An error within the code's correction capability is repaired at the receiver, and the transfer is not repeated.

Detection with retryCorrection
On an errorrepeat the transferrepair it in place
Cost when errors are rarenear zerothe check bits, always
Cost when errors are commonretries, which cost bandwidth and energyunchanged
Latency on an errora full retrynone

The third row is why a mobile device wants correction rather than detection. A retry spends the energy of the original transfer twice, and 24.1 §2 established that energy per bit is one of the three quantities that matters. A mechanism whose error response costs a second transfer is paying in the currency mobile devices have least of.

11. The Architecture as a Stack

The LPDDR5 architecture as five layers, top to bottom. At the top the controller holds three configurable dimensions: the write-clock to command-clock ratio, the burst length, and the bank organisation, each set by a mode register rather than fixed by the device. Below it the command domain runs on the command clock at both edges, at one thousand sixty-nine megahertz at the four-to-one ratio or twice that at two-to-one. Below that the domain crossing maintains the integer ratio between the two clocks and is the only relationship between them. Below that the data domain runs on the write clock at four thousand two hundred seventy-five megahertz, giving eight point five five gigabits per second per pin and seventeen point one gigabytes per second per channel. At the bottom the array delivers sixteen-n or thirty-two-n prefetch, a thirty-two or sixty-four byte granule, with bank groups or sixteen banks and partial-array refresh of both kinds.LPDDR5: a configurable architecture across two clock domainsController — three configurable dimensionsWCK:CK ratio, burst length, bank organisation — mode registers, not device constants (§8)WCK:CK ratio, burst length, bank organisation — mode registers, not device constants (§8)Command domain — CK, both edges1068.75 MHz at 4:1 or 2137.5 MHz at 2:1; DDR entry, reversing LPDDR4 (§5)1068.75 MHz at 4:1 or 2137.5 MHz at 2:1; DDR entry, reversing LPDDR4 (§5)Domain crossing — an integer ratio, 2:1 or 4:1the only relationship between the domains; a runtime variable, not a constant (§3)the only relationship between the domains; a runtime variable, not a constant (§3)Data domain — WCK at 4275 MHz8.55 Gbps per pin, 17.1 GB/s per x16 channel; RDQS optional (§2)8.55 Gbps per pin, 17.1 GB/s per x16 channel; RDQS optional (§2)Array — 16n or 32n prefetch, one channel per diea 32 B or 64 B granule; bank groups or 16 banks; PASR and PAAR (§6, §8, §9)a 32 B or 64 B granule; bank groups or 16 banks; PASR and PAAR (§6, §8, §9)

Two things the stack makes visible that the sections do not.

The crossing layer is the only thing holding the architecture together. Remove it and there are two unrelated clock domains with no defined relationship. Every command that references a data beat depends on that middle layer being correct, which is why §12's block is the chapter's centrepiece and §13 is about its hazard.

And configurability lives at the top and the bottom, with the crossing between them. The ratio is set above and the granule below, and both feed the middle layer's arithmetic: §4 showed that the data bits per command slot depend on the ratio, and §6 that the beats per burst depend on the burst length. A reconfiguration of either changes the crossing's parameters, which is the whole reason §15's guard exists.

12. The WCK / CK Domain Manager

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// wck_ck_domain_manager -- tracks the configurable integer ratio
// between the data clock and the command clock, and checks that a
// command's reference to a data beat is meaningful.
//
// CLASSIFICATION: educational, synthesisable.
//
// THIS IS THE MACHINERY 4.7 §3 DEFERRED TO MODULE 24.
//
// WHAT IT DOES NOT MODEL:
//   - either clock, any PLL, phase detection or clock generation.
//     19.1 §5 classifies those as outside portable RTL.
//   - jitter, skew, or any electrical property of the crossing
//   - a genuine asynchronous crossing. The block lives in the CK
//     domain with a WCK tick input (§12's header says so), because a
//     real synchroniser discipline would bury the ratio arithmetic
//     this chapter exists to show.
//
// WHY THE RATIO IS AN INPUT AND NOT A PARAMETER: §3. It is
// DEVICE-VERIFIED as selectable (2:1 or 4:1), so a design that fixed
// it at elaboration would have hard-coded a mode-register setting.
// ---------------------------------------------------------------------
module wck_ck_domain_manager #(
  // The largest supported ratio. Sets the counter WIDTH; the MODULUS
  // is a runtime value, which is the whole difficulty of §3.
  parameter int MAX_RATIO = 4,
  parameter int PH_W      = $clog2(MAX_RATIO),        // an INDEX into MAX_RATIO
  parameter int RCNT_W    = $clog2(MAX_RATIO + 1),    // a COUNT up to MAX_RATIO
  parameter int BEAT_W    = 6,
  parameter int CNT_W     = 24
) (
  input  logic                clk,            // CK domain
  input  logic                rst_n,

  // ── Configuration. DEVICE-VERIFIED as selectable, so an input.
  input  logic                cfg_valid,
  input  logic [RCNT_W-1:0]   cfg_ratio,      // 2 or 4

  // ── A WCK edge occurred. In a real design this arrives already
  //    synchronised; here it is simply an input (§12's header).
  input  logic                wck_tick,

  // ── A command that references a data beat.
  input  logic                cmd_valid,
  input  logic [BEAT_W-1:0]   cmd_beat_offset,
  input  logic [BEAT_W-1:0]   cmd_burst_beats,   // 16 or 32 (§6)

  // ── Phase state.
  output logic [PH_W-1:0]     wck_phase,
  output logic                phase_aligned,     // at phase 0
  output logic [RCNT_W-1:0]   ratio_in_force,
  output logic                ratio_valid,

  // ── Crossing verdict.
  output logic                crossing_legal,
  output logic [CNT_W-1:0]    cnt_crossings_ok,
  output logic [CNT_W-1:0]    cnt_crossings_bad,

  // §4's arithmetic, as an output rather than a comment: how many
  // data beats accompany one command slot at the ratio in force.
  output logic [RCNT_W:0]     beats_per_cmd_slot,

  // ── Errors, reported by kind.
  output logic                err_ratio_unsupported,
  output logic                err_cmd_before_config,
  output logic                err_beat_out_of_burst,
  output logic                err_tick_without_ratio,
  output logic                any_error
);

  initial begin
    if (MAX_RATIO != 4)
      $fatal(1, "wck_ck_domain_manager: MAX_RATIO must be 4 (device-verified)");
    if (PH_W < 2)
      $fatal(1, "wck_ck_domain_manager: PH_W too narrow for MAX_RATIO");
  end

  logic [PH_W-1:0]   ph;
  logic [RCNT_W-1:0] ratio;
  logic              have_ratio;
  logic [CNT_W-1:0]  n_ok, n_bad;
  logic              e_rat, e_pre, e_beat, e_tick;

  // A ratio is supported only if it is one of the two verified
  // values. Declared above the assigns that read it.
  logic cfg_ok;
  assign cfg_ok = (cfg_ratio == RCNT_W'(2)) || (cfg_ratio == RCNT_W'(4));

  // §4: at 4:1 a command slot accompanies twice the data of 2:1. CK
  // is DDR for commands (§5), so a CK period holds two slots and the
  // beats per slot is the ratio itself.
  logic [RCNT_W:0] bps;
  assign bps = have_ratio ? (RCNT_W+1)'(ratio) : '0;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      ph <= '0; ratio <= '0; have_ratio <= 1'b0;
      n_ok <= '0; n_bad <= '0;
      e_rat <= 1'b0; e_pre <= 1'b0; e_beat <= 1'b0; e_tick <= 1'b0;
    end else begin
      e_rat <= 1'b0; e_pre <= 1'b0; e_beat <= 1'b0; e_tick <= 1'b0;

      // ── Configuration.
      if (cfg_valid) begin
        if (!cfg_ok) begin
          // An unsupported ratio is REJECTED, not clamped. Clamping
          // would leave the controller believing a ratio is in force
          // that is not, and every crossing after that would be
          // computed against the wrong modulus.
          e_rat <= 1'b1;
        end else begin
          ratio      <= cfg_ratio;
          have_ratio <= 1'b1;
          // The phase resets on a ratio change: the modulus changed,
          // so a phase counted against the old one is meaningless.
          ph         <= '0;
        end
      end

      // ── Phase tracking. The MODULUS is a runtime value, which is
      //    §3's callout in one line of arithmetic.
      if (wck_tick) begin
        if (!have_ratio) begin
          e_tick <= 1'b1;
        end else if (ph == PH_W'(ratio - RCNT_W'(1))) begin
          ph <= '0;
        end else begin
          ph <= ph + PH_W'(1);
        end
      end

      // ── Crossing check.
      if (cmd_valid) begin
        if (!have_ratio) begin
          e_pre <= 1'b1;
          if (n_bad != {CNT_W{1'b1}}) n_bad <= n_bad + CNT_W'(1);
        end else if (cmd_beat_offset >= cmd_burst_beats) begin
          // §3's callout, second item: a beat offset must name a beat
          // that exists within the burst. The burst length is itself
          // configurable (§6), so this bound is a runtime value too.
          e_beat <= 1'b1;
          if (n_bad != {CNT_W{1'b1}}) n_bad <= n_bad + CNT_W'(1);
        end else begin
          if (n_ok != {CNT_W{1'b1}}) n_ok <= n_ok + CNT_W'(1);
        end
      end
    end
  end

  assign wck_phase      = ph;
  assign phase_aligned  = (ph == '0);
  assign ratio_in_force = ratio;
  assign ratio_valid    = have_ratio;
  // Computed combinationally from the CURRENT inputs, not from the
  // registered error flags -- a caller needs the verdict in the same
  // cycle it presents the command.
  assign crossing_legal = cmd_valid && have_ratio &&
                          (cmd_beat_offset < cmd_burst_beats);
  assign cnt_crossings_ok  = n_ok;
  assign cnt_crossings_bad = n_bad;
  assign beats_per_cmd_slot = bps;
  assign err_ratio_unsupported  = e_rat;
  assign err_cmd_before_config  = e_pre;
  assign err_beat_out_of_burst  = e_beat;
  assign err_tick_without_ratio = e_tick;
  assign any_error = e_rat | e_pre | e_beat | e_tick;
endmodule

13. The Hazard a Runtime Modulus Creates

One line of §12 is the whole difficulty of a configurable ratio, and it is worth isolating:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
end else if (ph == PH_W'(ratio - RCNT_W'(1))) begin
  ph <= '0;

The counter's width is fixed at elaboration and its modulus is a runtime value. PH_W is two bits, sized for the maximum ratio of 4. At a ratio of 2 the counter must wrap at 1, using only one of its two bits.

Three ways to get this wrong, and each fails differently.

Wrapping at the width instead of the ratio. Writing ph <= ph + 1 and letting the two-bit counter wrap naturally gives a modulus of 4 always. At 2:1 the phase would be wrong half the time — and correct the other half, which is what makes it hard to find. Every even phase agrees with the truth and every odd one does not.

Failing to reset the phase on a ratio change. A phase of 3 is valid at 4:1 and meaningless at 2:1. §12 resets ph on every accepted configuration precisely because a carried-over phase is not merely stale, it is out of range for the new modulus — and a comparison against ratio - 1 would then never match, leaving the counter stuck above its own wrap point forever.

And clamping an unsupported ratio instead of rejecting it. §12 rejects. Clamping a requested ratio of 3 to 4 would leave the controller computing crossings against 3 while the device ran at 4, and every crossing would be wrong by a factor that looks like a timing problem.

All three produce symptoms that point at the clocking circuit rather than at the counter, which is the shape worth recognising. Chapter 24.3 §12 found a different hazard with the same property — a value read on the edge a write to it lands — and in both cases the mechanism looks healthy while the value is wrong.

14. The Burst Granularity Selector

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// lp5_burst_selector -- runs the same request stream against BOTH
// burst lengths and reports what each would have moved.
//
// CLASSIFICATION: educational, synthesisable.
//
// WHY BOTH AT ONCE: §7's callout. BL32 is set once and applies to
// every access including the small ones, so the decision needs the
// access-size DISTRIBUTION rather than a peak-bandwidth benchmark.
// This block measures both arms against one stream, which is the
// counterfactual form 23.5's policy work uses.
//
// WHAT IT DOES NOT MODEL: energy, bandwidth in bytes per second, or
// command overhead in cycles. So it CANNOT say which is better -- the
// weighting between payload waste and command overhead is not in this
// chapter, and it reports the comparison instead of the verdict.
// ---------------------------------------------------------------------
module lp5_burst_selector #(
  // DEVICE-VERIFIED granules for a x16 channel (§6).
  parameter int GRAN16 = 32,
  parameter int GRAN32 = 64,
  parameter int SH16   = $clog2(GRAN16),
  parameter int SH32   = $clog2(GRAN32),
  parameter int REQ_W  = 16,
  parameter int ACC_W  = 40
) (
  input  logic              clk,
  input  logic              rst_n,

  input  logic              req_valid,
  input  logic [REQ_W-1:0]  req_bytes,

  // ── Both arms, over the same stream.
  output logic [ACC_W-1:0]  requested,
  output logic [ACC_W-1:0]  moved_bl16,
  output logic [ACC_W-1:0]  moved_bl32,
  output logic [ACC_W-1:0]  bursts_bl16,
  output logic [ACC_W-1:0]  bursts_bl32,

  // The difference, both ways, as a PAIR. BL32 always moves at least
  // as much data and always issues no more bursts, so the trade is
  // exactly these two numbers and never a single score.
  output logic [ACC_W-1:0]  extra_bytes_bl32,
  output logic [ACC_W-1:0]  fewer_bursts_bl32,

  // Distribution, which §7's callout says is what should decide.
  output logic [ACC_W-1:0]  cnt_le_32,
  output logic [ACC_W-1:0]  cnt_33_to_64,
  output logic [ACC_W-1:0]  cnt_gt_64,

  output logic              err_zero_length_req,
  output logic              any_saturated
);

  initial begin
    if (GRAN32 != 2 * GRAN16)
      $fatal(1, "lp5_burst_selector: GRAN32 must be twice GRAN16 (device-verified)");
    if (GRAN16 < 2 || (GRAN16 & (GRAN16 - 1)) != 0)
      // The power-of-two test alone ACCEPTS 0 and 1: both give
      // (g & (g-1)) == 0. Either would make $clog2 zero and the
      // round-up shift a no-op, so the lower bound is required and
      // not decorative. 24.3's accountant guards it the same way.
      $fatal(1, "lp5_burst_selector: GRAN16 must be a power of two >= 2");
  end

  logic [ACC_W-1:0] t_req, m16, m32, b16, b32, c_s, c_m, c_l;
  logic             e_zero, sat;

  // Round up by shifting -- no divide. Declared above the assigns.
  logic [REQ_W+1:0] n16, n32;
  assign n16 = (req_bytes == '0) ? '0
             : (REQ_W+2)'(((req_bytes - REQ_W'(1)) >> SH16) + 1);
  assign n32 = (req_bytes == '0) ? '0
             : (REQ_W+2)'(((req_bytes - REQ_W'(1)) >> SH32) + 1);

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      t_req <= '0; m16 <= '0; m32 <= '0; b16 <= '0; b32 <= '0;
      c_s <= '0; c_m <= '0; c_l <= '0;
      e_zero <= 1'b0; sat <= 1'b0;
    end else begin
      e_zero <= 1'b0;
      if (req_valid) begin
        if (req_bytes == '0) begin
          e_zero <= 1'b1;
        end else if (m32 > ({ACC_W{1'b1}} - ACC_W'(n32 << SH32))) begin
          sat <= 1'b1;
        end else begin
          t_req <= t_req + ACC_W'(req_bytes);
          m16   <= m16 + ACC_W'(n16 << SH16);
          m32   <= m32 + ACC_W'(n32 << SH32);
          b16   <= b16 + ACC_W'(n16);
          b32   <= b32 + ACC_W'(n32);
          if (req_bytes <= REQ_W'(GRAN16))
            c_s <= c_s + ACC_W'(1);
          else if (req_bytes <= REQ_W'(GRAN32))
            c_m <= c_m + ACC_W'(1);
          else
            c_l <= c_l + ACC_W'(1);
        end
      end
    end
  end

  assign requested   = t_req;
  assign moved_bl16  = m16;
  assign moved_bl32  = m32;
  assign bursts_bl16 = b16;
  assign bursts_bl32 = b32;
  // BL32 never moves less and never issues more bursts, so both
  // differences are non-negative by construction. P8 and P9 assert it.
  assign extra_bytes_bl32  = (m32 >= m16) ? (m32 - m16) : '0;
  assign fewer_bursts_bl32 = (b16 >= b32) ? (b16 - b32) : '0;
  assign cnt_le_32     = c_s;
  assign cnt_33_to_64  = c_m;
  assign cnt_gt_64     = c_l;
  assign err_zero_length_req = e_zero;
  assign any_saturated = sat;
endmodule

extra_bytes_bl32 and fewer_bursts_bl32 are reported separately and never combined. Combining them needs an exchange rate between a wasted byte and a saved burst, and this chapter does not have one — §14's header says so. A block that produced a single score would be smuggling in a weighting it could not justify, which is the discipline 24.1 §10's accountant established for the three quantities and this applies to two.

15. The Reconfiguration Guard

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// lp5_reconfig_guard -- a configurable architecture needs a protocol
// for changing configuration. This enforces its shape.
//
// CLASSIFICATION: educational, synthesisable.
//
// WHY IT EXISTS: §8. LPDDR5 has THREE configurable architecture
// dimensions, and every one of them changes arithmetic that in-flight
// work depends on -- §11's stack shows the ratio and the burst length
// both feeding the crossing layer. Changing one mid-flight does not
// produce a slightly wrong result; it produces a crossing computed
// against a modulus that no longer applies (§13).
//
// WHAT IT DOES NOT MODEL: any device's real reconfiguration sequence,
// its timing, or its mode-register encodings. It enforces the SHAPE:
// quiesce, change, resume.
// ---------------------------------------------------------------------
module lp5_reconfig_guard #(
  parameter int DIMS  = 3,          // ratio, burst length, bank org (§8)
  parameter int CNT_W = 20
) (
  input  logic              clk,
  input  logic              rst_n,

  // ── A request to change one dimension.
  input  logic              cfg_req,
  input  logic [DIMS-1:0]   cfg_dims,        // which dimensions change

  // ── Activity that must be absent before a change is permitted.
  input  logic              cmds_in_flight,
  input  logic              data_in_flight,
  input  logic              banks_open,

  // ── Verdict.
  output logic              cfg_grant,
  output logic              quiesced,
  output logic [CNT_W-1:0]  cnt_granted,
  output logic [CNT_W-1:0]  cnt_refused,

  // ── Refusals, by reason, so a controller learns WHAT to quiesce.
  output logic              err_cmds_in_flight,
  output logic              err_data_in_flight,
  output logic              err_banks_open,
  output logic              err_no_dim_selected,
  output logic              err_multi_dim_at_once,
  output logic              any_refusal
);

  initial begin
    if (DIMS < 1)
      $fatal(1, "lp5_reconfig_guard: DIMS must be at least 1");
  end

  logic [CNT_W-1:0] n_ok, n_no;
  logic             e_c, e_d, e_b, e_z, e_m, grant;

  // Declared above the assigns that read it.
  logic quiet;
  assign quiet = !cmds_in_flight && !data_in_flight && !banks_open;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      n_ok <= '0; n_no <= '0; grant <= 1'b0;
      e_c <= 1'b0; e_d <= 1'b0; e_b <= 1'b0; e_z <= 1'b0; e_m <= 1'b0;
    end else begin
      grant <= 1'b0;
      e_c <= 1'b0; e_d <= 1'b0; e_b <= 1'b0; e_z <= 1'b0; e_m <= 1'b0;

      if (cfg_req) begin
        // ── Every reason is reported, not just the first, so a
        //    controller that must quiesce three things learns all
        //    three in one attempt instead of three.
        if (cmds_in_flight) e_c <= 1'b1;
        if (data_in_flight) e_d <= 1'b1;
        if (banks_open)     e_b <= 1'b1;
        if (cfg_dims == '0) e_z <= 1'b1;
        // One dimension at a time. Two simultaneous changes make the
        // intermediate state ambiguous: there is no ordering in which
        // both old values and both new values are consistent.
        if (!$onehot0(cfg_dims)) e_m <= 1'b1;

        if (quiet && (cfg_dims != '0) && $onehot(cfg_dims)) begin
          grant <= 1'b1;
          if (n_ok != {CNT_W{1'b1}}) n_ok <= n_ok + CNT_W'(1);
        end else begin
          if (n_no != {CNT_W{1'b1}}) n_no <= n_no + CNT_W'(1);
        end
      end
    end
  end

  assign cfg_grant   = grant;
  assign quiesced    = quiet;
  assign cnt_granted = n_ok;
  assign cnt_refused = n_no;
  assign err_cmds_in_flight    = e_c;
  assign err_data_in_flight    = e_d;
  assign err_banks_open        = e_b;
  assign err_no_dim_selected   = e_z;
  assign err_multi_dim_at_once = e_m;
  assign any_refusal = e_c | e_d | e_b | e_z | e_m;
endmodule

Every refusal reason is reported, not just the first. A controller that must close banks and drain data learns both in one attempt. Reporting only the first reason would turn one diagnosis into a sequence of them, which is slower and easily mistaken for an intermittent fault.

And one dimension at a time is enforced rather than merely recommended. Two simultaneous changes have no consistent intermediate state — there is no ordering in which both old values and both new values hold together — so the guard refuses rather than choosing an order the controller did not specify.

16. What the Assertions Prove

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Bind unit note: P1-P7 reference wck_ck_domain_manager's internals,
// P8-P11 lp5_burst_selector's, P12-P15 lp5_reconfig_guard's.

// P1 -- the phase is always within the ratio in force. §13's first
// failure mode: a width-modulus counter violates this at 2:1.
property p_phase_within_ratio;
  @(posedge clk) disable iff (!rst_n)
    ratio_valid |-> (wck_phase < PH_W'(ratio_in_force));
endproperty
assert property (p_phase_within_ratio);

// P2 -- the phase advances by exactly one per WCK tick, or wraps to
// zero. Rules out a skipped or doubled phase.
property p_phase_advances_by_one;
  @(posedge clk) disable iff (!rst_n)
    ($past(wck_tick,1) && $past(ratio_valid,1) && !$past(cfg_valid,1))
      |-> ((wck_phase == $past(wck_phase,1) + PH_W'(1)) ||
           (wck_phase == '0));
endproperty
assert property (p_phase_advances_by_one);

// P3 -- §13's second failure mode. A ratio change RESETS the phase,
// because a phase counted against the old modulus is out of range for
// the new one and the wrap comparison would never match again.
property p_ratio_change_resets_phase;
  @(posedge clk) disable iff (!rst_n)
    ($past(cfg_valid,1) && !$past(err_ratio_unsupported,1) && ratio_valid)
      |-> (wck_phase == '0);
endproperty
assert property (p_ratio_change_resets_phase);

// P4 -- §13's third failure mode. An unsupported ratio is REJECTED,
// never clamped into force. Clamping would leave every subsequent
// crossing computed against the wrong modulus.
property p_bad_ratio_never_in_force;
  @(posedge clk) disable iff (!rst_n)
    ratio_valid |-> (ratio_in_force inside {RCNT_W'(2), RCNT_W'(4)});
endproperty
assert property (p_bad_ratio_never_in_force);

// P5 -- no crossing is judged legal before a ratio is configured.
// Without a ratio there is no relationship between the domains, so
// there is nothing to be legal with respect to.
property p_no_crossing_before_config;
  @(posedge clk) disable iff (!rst_n)
    !ratio_valid |-> !crossing_legal;
endproperty
assert property (p_no_crossing_before_config);

// P6 -- a beat offset outside the burst is never legal. §6: the
// burst length is itself configurable, so the bound is a runtime
// value and not a constant.
property p_beat_within_burst;
  @(posedge clk) disable iff (!rst_n)
    crossing_legal |-> (cmd_beat_offset < cmd_burst_beats);
endproperty
assert property (p_beat_within_burst);

// P7 -- §4's arithmetic, as an invariant: the beats accompanying one
// command slot equal the ratio in force.
property p_beats_per_slot_is_ratio;
  @(posedge clk) disable iff (!rst_n)
    ratio_valid |-> (beats_per_cmd_slot == (RCNT_W+1)'(ratio_in_force));
endproperty
assert property (p_beats_per_slot_is_ratio);

// P8 -- BL32 never moves LESS than BL16 over the same stream. The
// invariant that makes §14's subtraction safe.
property p_bl32_moves_at_least_bl16;
  @(posedge clk) disable iff (!rst_n) (moved_bl32 >= moved_bl16);
endproperty
assert property (p_bl32_moves_at_least_bl16);

// P9 -- BL32 never issues MORE bursts than BL16. The other half of
// §7's trade, and the reason the two differences are reported as a
// pair rather than combined into a score.
property p_bl32_issues_no_more_bursts;
  @(posedge clk) disable iff (!rst_n) (bursts_bl32 <= bursts_bl16);
endproperty
assert property (p_bl32_issues_no_more_bursts);

// P10 -- both arms move a whole number of their own granule.
property p_both_arms_whole_granules;
  @(posedge clk) disable iff (!rst_n)
    (((moved_bl16 & ACC_W'(GRAN16 - 1)) == '0) &&
     ((moved_bl32 & ACC_W'(GRAN32 - 1)) == '0));
endproperty
assert property (p_both_arms_whole_granules);

// P11 -- the three size buckets partition every counted request, so
// §7's distribution argument rests on an exhaustive classification.
property p_buckets_partition_requests;
  @(posedge clk) disable iff (!rst_n)
    (req_valid && (req_bytes != '0) && !any_saturated)
      |=> ((cnt_le_32 + cnt_33_to_64 + cnt_gt_64) ==
           ($past(cnt_le_32,1) + $past(cnt_33_to_64,1) +
            $past(cnt_gt_64,1) + ACC_W'(1)));
endproperty
assert property (p_buckets_partition_requests);

// P12 -- FORBIDDEN. Reconfiguration is never granted while anything
// depends on the old value. §15: the change does not produce a
// slightly wrong result, it produces one computed against a modulus
// that no longer applies.
property p_no_reconfig_while_busy;
  @(posedge clk) disable iff (!rst_n)
    (cmds_in_flight || data_in_flight || banks_open) |=> !cfg_grant;
endproperty
assert property (p_no_reconfig_while_busy);

// P13 -- FORBIDDEN. Two dimensions never change together, because no
// ordering makes both old and both new values consistent.
property p_one_dimension_at_a_time;
  @(posedge clk) disable iff (!rst_n)
    (cfg_req && !$onehot0(cfg_dims)) |=> !cfg_grant;
endproperty
assert property (p_one_dimension_at_a_time);

// P14 -- a grant implies the device was quiesced on the request
// cycle. The converse of P12, closing the gap.
property p_grant_implies_was_quiet;
  @(posedge clk) disable iff (!rst_n)
    cfg_grant |-> $past(quiesced, 1);
endproperty
assert property (p_grant_implies_was_quiet);

// P15 -- every refused request is counted, and every request is
// either granted or refused. No request is silently dropped.
property p_every_request_accounted;
  @(posedge clk) disable iff (!rst_n)
    cfg_req |=> ((cnt_granted + cnt_refused) ==
                 ($past(cnt_granted,1) + $past(cnt_refused,1) + CNT_W'(1)));
endproperty
assert property (p_every_request_accounted);

// ── Cover.
// BOTH verified ratios actually in force. A suite testing one has
// tested a fixed-ratio design, which is not what §3 describes.
cover property (@(posedge clk) disable iff (!rst_n)
                  ratio_valid && (ratio_in_force == RCNT_W'(2)));
cover property (@(posedge clk) disable iff (!rst_n)
                  ratio_valid && (ratio_in_force == RCNT_W'(4)));
// A ratio CHANGE, in both directions. §13's second failure mode only
// appears on a change, so a suite that configures once never sees it.
cover property (@(posedge clk) disable iff (!rst_n)
                  (ratio_in_force == RCNT_W'(4)) ##[1:$]
                  (ratio_in_force == RCNT_W'(2)));
cover property (@(posedge clk) disable iff (!rst_n)
                  (ratio_in_force == RCNT_W'(2)) ##[1:$]
                  (ratio_in_force == RCNT_W'(4)));
// The full phase sweep at each ratio.
cover property (@(posedge clk) disable iff (!rst_n)
                  (ratio_in_force == RCNT_W'(4)) && (wck_phase == PH_W'(3)));
cover property (@(posedge clk) disable iff (!rst_n)
                  (ratio_in_force == RCNT_W'(2)) && (wck_phase == PH_W'(1)));
// Each error kind attempted.
cover property (@(posedge clk) disable iff (!rst_n) err_ratio_unsupported);
cover property (@(posedge clk) disable iff (!rst_n) err_cmd_before_config);
cover property (@(posedge clk) disable iff (!rst_n) err_beat_out_of_burst);
// Both burst lengths exercised, and a stream where they differ.
cover property (@(posedge clk) disable iff (!rst_n)
                  cmd_valid && (cmd_burst_beats == BEAT_W'(16)));
cover property (@(posedge clk) disable iff (!rst_n)
                  cmd_valid && (cmd_burst_beats == BEAT_W'(32)));
cover property (@(posedge clk) disable iff (!rst_n)
                  (extra_bytes_bl32 != '0) && (fewer_bursts_bl32 != '0));
// Each refusal reason, and a grant.
cover property (@(posedge clk) disable iff (!rst_n) err_banks_open);
cover property (@(posedge clk) disable iff (!rst_n) err_multi_dim_at_once);
cover property (@(posedge clk) disable iff (!rst_n) cfg_grant);

17. Corner Cases

CaseBehaviourWhy
Command before any ratio configurederr_cmd_before_config; not legalP5 — no relationship exists yet
WCK tick before any ratio configurederr_tick_without_ratioThe modulus is unknown, so the phase is meaningless
Ratio of 3 requestederr_ratio_unsupported; not clampedP4, and §13's third failure mode
Ratio changed from 4:1 to 2:1Phase resets to 0P3 — phase 3 is out of range at 2:1
Phase counter wrapping at its widthWrong half the time at 2:1§13's first failure mode; P1 catches it
Beat offset 20 with BL16err_beat_out_of_burstP6 — the bound is the configured burst length
Beat offset 20 with BL32LegalThe same offset, a different configured bound
BL32 with a stream of 4 B requestsTwice the waste of BL16§7's callout — the trap
BL32 with a stream of 64 B requestsSame bytes, half the bursts§7's benefit, in its best case
Reconfigure with banks openRefused; err_banks_openP12 — open rows depend on the bank organisation
Reconfigure with data in flightRefused; err_data_in_flightIn-flight beats were computed against the old ratio
Two dimensions requested togetherRefused; err_multi_dim_at_onceP13 — no consistent intermediate state
Reconfigure requested with no dimension selectedRefused; err_no_dim_selectedA no-op request is a controller bug, not a no-op
Three reasons to refuse at onceAll three reportedOne diagnosis instead of three attempts
GRAN32 not twice GRAN16$fatal at elaborationThe device-verified relationship; a wrong pair invalidates every comparison
GRAN16 of 0 or 1$fatal at elaborationA power-of-two test alone accepts both; the lower bound is required

Rows six and seven are the pair worth studying together. The same beat offset is illegal at BL16 and legal at BL32, which means the legality of a crossing depends on a mode-register setting. A controller that caches a legality decision across a reconfiguration has cached something that is no longer true — and §15's guard is what prevents the reconfiguration from happening while such a decision is in flight.

18. DV

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Independent ratio reference. Models the phase as an explicit
// modular counter with the ratio passed in each call, rather than as
// registered state, so agreement with the DUT is evidence rather than
// a restatement. SIMULATION-ONLY.
class ratio_ref;
  int ratio;
  int phase;
  bit configured;

  function void reset();
    ratio = 0; phase = 0; configured = 0;
  endfunction

  function bit config_ratio(int r);
    if (r != 2 && r != 4) return 0;      // rejected, NOT clamped
    ratio = r; phase = 0; configured = 1;
    return 1;
  endfunction

  function void tick();
    if (!configured) return;
    phase = (phase + 1) % ratio;         // the modulus is the RATIO
  endfunction

  function bit crossing_ok(int beat, int burst);
    return configured && (beat < burst);
  endfunction

  function int beats_per_slot();
    return configured ? ratio : 0;
  endfunction
endclass
CheckWhat it establishes
Phase matches the reference every cycle, at both ratiosP1 and P2 against an independent modulus
Configure 4:1, sweep phase 0–3, switch to 2:1 mid-sweepP3, and §13's second failure mode
Configure 2:1, let the phase run for many ticksP1 — a width-modulus bug fires here and only here
Request ratios 0, 1, 3, 5; confirm none enters forceP4; the reference rejects the same set
Issue a command before configuringP5 and err_cmd_before_config
Sweep beat offsets 0–35 at BL16 and at BL32P6, and §17's rows six and seven
Check beats_per_cmd_slot against the reference at both ratiosP7 — §4's arithmetic
Random request stream; compare both arms against a referenceP8 through P11
All-small stream, then all-large, then mixed§7's trap and its benefit, both measured
Reconfigure under each of the three activity conditionsP12, each refusal reason
Reconfigure with all three conditions true at onceAll three reported in one attempt
Request two dimensions togetherP13
Reconfigure while quiescedP14 and the grant cover

The third check is the one that finds the bug §13 names first, and it is worth showing what its failure looks like because a suite run only at 4:1 reports nothing:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  THE SUITE THAT ONLY EVER RAN AT 4:1
    Phase counter written as ph <= ph + 1 on a 2-bit register,
    wrapping at its WIDTH rather than at the configured ratio.

    at 4:1  : width modulus 4 == ratio 4.
              phase sequence 0 1 2 3 0 1 2 3 ...   CORRECT
              P1 phase_within_ratio                PASS
              every property                       PASS

    at 2:1  : width modulus 4 != ratio 2.
              phase sequence 0 1 2 3 0 1 2 3 ...
              expected       0 1 0 1 0 1 0 1 ...
              P1 phase_within_ratio                FAIL at phase 2

    coverage of the 4:1-only suite:
              ratio_in_force == 4                  HIT
              ratio_in_force == 2                  NEVER HIT
              ratio change, either direction       NEVER HIT

    diagnosis : the bug is in the wrap condition and is invisible at
      the maximum ratio, because at the maximum ratio the width
      modulus and the configured modulus are the same number. It is a
      bug that only exists in the CONFIGURABILITY, so only a suite
      that exercises more than one configuration can see it.

    what makes it dangerous : 4:1 is the ratio that gives more data
      per command slot (§4) and is the likelier default. A design
      verified at its default and shipped configurable carries a bug
      in the non-default mode with a fully green regression.

    the fix : both ratio covers and both change-direction covers of
      §16 are REQUIRED goals. A configurable design verified in one
      configuration has been verified as a fixed design.

19. Debugging

SymptomLikely causeHow to confirm
Works at 4:1, fails at 2:1Phase wrapping at the counter width — §13P1 fires at phase 2; run the §18 third check
Fails only after a ratio changePhase not reset on reconfiguration — §13P3; check wck_phase immediately after cfg_valid
Every crossing wrong by a consistent factorAn unsupported ratio was clamped into force — §13P4; err_ratio_unsupported should have fired
Crossings fail intermittently at one offsetBeat offset valid at one burst length, not the other§17's rows six and seven; check the configured BL
Command path timing marginal2:1 doubles the command-domain frequency — §3Which ratio is in force; 4:1 halves it
Command bus appears to limit throughputUnlikely at either ratio — §4Eight slots per burst even at 4:1; look at banks instead
Bandwidth good, battery worse than LPDDR4BL32 with a small-access mix — §7's trapcnt_le_32 against cnt_gt_64; run §14's selector
Large transfers no faster at BL32Command overhead was not the limitfewer_bursts_bl32 is real but irrelevant to this workload
Reconfiguration silently has no effectRequest refused and the refusal ignoredcnt_refused; then the five reason flags
Reconfiguration appears to take effect, then corruptsChange granted while something was in flightP12 and P14 — if either fires, the guard is bypassed
Data corruption after a mode changeTwo dimensions changed together — §15err_multi_dim_at_once
A wrong-channel access returns valid data24.3 §6's failure, unchanged hereNot visible at the interface; check the mapping

Row one is the signature of this chapter. “Works at the default ratio, fails at the other” is the characteristic failure of a configurable design, and it is the one a regression suite that configures once will never report.

20. Misconceptions

“LPDDR5 dropped to one channel, so it gave up LPDDR4's independence.” §1. One channel per die. A x32 interface is two dies and two independent channels, exactly as before.

WCK and CK have a fixed relationship.” §3. The ratio is 2:1 or 4:1, selectable. Every command-path frequency in the design depends on a mode-register setting.

“2:1 gives more command bandwidth so it allows more bursts.” §4. Even at 4:1 there are eight command slots per BL32 burst. The extra bandwidth serves activates, precharges, refreshes and mode writes — not bursts.

“The higher ratio is the better one.” §4's callout. 2:1 helps a command-heavy workload and costs a command domain running at twice the frequency. Which is right depends on the address stream.

“LPDDR samples commands on one clock edge.” §5. True of LPDDR4 only. LPDDR3 used both edges and LPDDR5 uses both edges again. It was never a family property.

“LPDDR5 reversing LPDDR4's single-edge choice means one of them was wrong.” §5. Decoupling the clocks made the command clock slow, which made the negative edge affordable again. The reversal is a consequence of the decoupling.

“BL32 is the better burst length.” §7's callout. It is set once and applies to every access, so it doubles the waste on small ones — and a mobile mix is mostly small.

“LPDDR5 settled the granularity question.” §7. It reopened it and made it a configuration. BL16 reproduces LPDDR4's 32-byte granule exactly.

“LPDDR5 has one architecture.” §8. Three configurable dimensions — ratio, burst length, bank organisation. A controller treating any as constant has hard-coded a choice.

“Link ECC and write CRC do the same job.” §10. CRC detects and recovery is a retry; ECC corrects in place. On a device counting energy per bit, a retry spends the transfer twice.

“Link ECC's overhead is known to be worth it.” §10's callout. The code is not published in the source consulted, so the check-bit count is unknown and this chapter does not claim the trade.

“A phase counter sized for the maximum ratio is correct at every ratio.” §13. Its width is fixed and its modulus is not. Wrapping at the width is right at 4:1 and wrong at 2:1.

“Reconfiguration is a mode-register write.” §15. It is a protocol: quiesce, change one dimension, resume. A change made in flight is computed against a modulus that no longer applies.

21. Interview Reasoning

What does LPDDR5 change about clocking? It separates the command clock from the data clock. WCK times data at a ratio of 2:1 or 4:1 to CK, and that ratio is selectable rather than fixed.

Derive the data clock frequency from a channel bandwidth figure. At 17.1 GB/s on a x16 channel: 17.1 × 8 / 16 = 8.55 Gbps per pin; data is on both WCK edges, so WCK is 4275 MHz. At 4:1 that puts CK at 1068.75 MHz.

What does the ratio choice actually buy? Command bandwidth per unit of data bandwidth — 32 data bits per command slot at 2:1, 64 at 4:1. Since even 4:1 leaves eight slots per burst, the extra bandwidth serves non-data commands, not bursts.

Why did LPDDR5 go back to double-edge command sampling when LPDDR4 had abandoned it? Because the command clock is no longer the fast clock. Decoupling made CK around 1 GHz against a data rate of 8.55 Gbps, and a 1 GHz negative edge is a much easier target.

What is hard about a configurable clock ratio that a fixed one avoids? The phase counter's width is fixed at design time and its modulus is a runtime value, so wrapping at the width is correct at the maximum ratio and wrong at every other. The bug is invisible in the default configuration.

What must happen before a ratio can be changed? Everything depending on the old value must be drained — no commands in flight, no data in flight, no open banks — and only one dimension may change at a time, because no ordering makes two old and two new values consistent together.

When is BL32 the wrong choice? When the access-size distribution is dominated by transfers below 64 bytes. It is set once and applies to everything, so on a mobile mix of small accesses it doubles the payload waste.

How does link ECC differ from a write CRC, and why does a phone care? CRC detects and the recovery is a retry; ECC corrects in place. A retry spends the energy of the transfer a second time, which is the quantity a mobile device has least of.

How would you verify a configurable-ratio design? In every configuration and across every transition between them. A suite that configures once has verified a fixed design, and the characteristic bug lives only in the configurability.

22. Exercises

  1. Derive CK and the command slot rate for a hypothetical 6:1 ratio at §2's WCK, then explain why §12's block would reject it and what would have to change to support it.

  2. §4 shows eight command slots per BL32 burst at 4:1. Construct the access pattern that consumes all eight, stating every command you count, and say whether it is reachable on a real workload.

  3. A controller runs BL32 against a stream that is 70% 32-byte and 30% 128-byte requests. Compute total bytes moved under both burst lengths and both burst counts, then state what additional figure you would need to decide between them.

  4. §5 argues LPDDR5's reversal follows from the decoupling. Construct the counterargument — that double-edge entry would have been affordable on LPDDR4 too — using only facts from 24.3, and say which is better supported.

  5. Implement §13's first failure mode in §12's block and find the shortest stimulus that makes P1 fire. Then find the longest stimulus at 4:1 that does not fire it.

  6. §15's guard permits one dimension at a time. Design a sequence that changes all three safely, then show that no sequence of two-at-a-time changes is safe, identifying the inconsistent intermediate state in each case.

  7. §10 cannot state link ECC's overhead. Given a hypothetical code correcting one bit per 128 data bits, compute the check-bit overhead and the error rate at which correction beats detection-with-retry on energy, naming every assumption.

  8. Chapter 24.3 §14's accountant is parameterised on the granule. Run it conceptually at 32 and at 64 bytes against the same stream and show that its bytes_wasted output reproduces §14's extra_bytes_bl32. What does the agreement establish, and what does it not?

23. Where This Goes

LPDDR5's architecture is now built rather than described. Two clock domains related only by a selectable integer ratio, double-edge command entry that reverses LPDDR4's choice for a reason rather than against it, a burst length that is a configuration instead of a constant, and one channel per die that pays for the clocking.

Four results carry forward. The ratio provisions command bandwidth per unit of data bandwidth, 32 bits per slot at 2:1 and 64 at 4:1, and the extra serves non-data commands rather than bursts. The granularity question was reopened as a choice, not settled — BL16 reproduces LPDDR4's granule exactly and BL32 doubles both the efficiency and the waste. A configurable architecture needs a reconfiguration protocol, and the shape is quiesce, change one dimension, resume. And configurability creates a class of bug that is invisible in the default configuration, which makes both-ratio coverage a requirement rather than a thoroughness.

One thing this chapter established and deliberately did not pursue. §10 verified that the part supports link ECC and then declined to characterise it, because the code is not published in the source consulted. That is a real gap and it is recorded as one — the mechanism class is sound, the trade is not computable here, and §22's seventh exercise asks you to compute it under stated assumptions rather than pretending this chapter did.

What every chapter in this module has assumed is that the device has power. Chapter 24.1 §7 established that a mobile memory's supply rails tell you what the designers cared about, and this module has since verified that LPDDR4X lowers two rails and deletes a third, that two of four rails go unused during power-down self refresh, and that VDDQ on an LPDDR5X part sits at half a volt. None of those facts has been assembled into how a system actually manages them.

Chapter 24.5 finishes the module with that: which rails exist and what each one feeds, the sequencing a multi-rail device requires at power-up and at power-down, what it means that two rails can be gated in a low-power state, and the ordering discipline that changing a voltage and a frequency together demands. It is the system view the other four chapters have each deferred, and it is where the module's three quantities finally meet the hardware that supplies them.

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.