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 die | 2 × x16 | 1 × x16 |
| Dies needed for a x32 interface | 1 | 2 |
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:
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 MHzSo 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:
| Signal | Purpose |
|---|---|
CK_t / CK_c | command and address clock |
WCK_t / WCK_c | data clock, forwarded from the controller |
RDQS_t / RDQS_c | read 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:CK | CK frequency | Command slots per second (CK is DDR — §5) |
|---|---|---|
| 2:1 | 2137.5 MHz | 4275 M |
| 4:1 | 1068.75 MHz | 2137.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:CK | Data bits per channel per command slot |
|---|---|
| 2:1 | 32 |
| 4:1 | 64 |
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:CK | BL32 bursts per second | Command slots per second | Slots available per burst |
|---|---|---|---|
| 2:1 | 267.2 M | 4275 M | 16 |
| 4:1 | 267.2 M | 2137.5 M | 8 |
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.
| LPDDR3 | LPDDR4 | LPDDR5 | |
|---|---|---|---|
| Command sampling | both edges | positive only | both 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:
| Prefetch | Burst length | Bits per burst | Minimum transfer |
|---|---|---|---|
| 16n | 16 | 256 | 32 B |
| 32n | 32 | 512 | 64 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.
| LPDDR4 | LPDDR5 | |
|---|---|---|
| Channels per die | 2 | 1 |
| Granule | 32 B, fixed | 32 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:
| Dimension | Options | Owned by |
|---|---|---|
WCK:CK ratio | 2:1, 4:1 | §3, §4 |
| Burst length | 16, 32 | §6, §7 |
| Bank organisation | bank 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.
10. Link ECC — Correction, Not Detection
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 retry | Correction | |
|---|---|---|
| On an error | repeat the transfer | repair it in place |
| Cost when errors are rare | near zero | the check bits, always |
| Cost when errors are common | retries, which cost bandwidth and energy | unchanged |
| Latency on an error | a full retry | none |
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
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
// ---------------------------------------------------------------------
// 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;
endmodule13. The Hazard a Runtime Modulus Creates
One line of §12 is the whole difficulty of a configurable ratio, and it is worth isolating:
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
// ---------------------------------------------------------------------
// 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;
endmoduleextra_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
// ---------------------------------------------------------------------
// 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;
endmoduleEvery 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
// 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
| Case | Behaviour | Why |
|---|---|---|
| Command before any ratio configured | err_cmd_before_config; not legal | P5 — no relationship exists yet |
WCK tick before any ratio configured | err_tick_without_ratio | The modulus is unknown, so the phase is meaningless |
| Ratio of 3 requested | err_ratio_unsupported; not clamped | P4, and §13's third failure mode |
| Ratio changed from 4:1 to 2:1 | Phase resets to 0 | P3 — phase 3 is out of range at 2:1 |
| Phase counter wrapping at its width | Wrong half the time at 2:1 | §13's first failure mode; P1 catches it |
| Beat offset 20 with BL16 | err_beat_out_of_burst | P6 — the bound is the configured burst length |
| Beat offset 20 with BL32 | Legal | The same offset, a different configured bound |
| BL32 with a stream of 4 B requests | Twice the waste of BL16 | §7's callout — the trap |
| BL32 with a stream of 64 B requests | Same bytes, half the bursts | §7's benefit, in its best case |
| Reconfigure with banks open | Refused; err_banks_open | P12 — open rows depend on the bank organisation |
| Reconfigure with data in flight | Refused; err_data_in_flight | In-flight beats were computed against the old ratio |
| Two dimensions requested together | Refused; err_multi_dim_at_once | P13 — no consistent intermediate state |
| Reconfigure requested with no dimension selected | Refused; err_no_dim_selected | A no-op request is a controller bug, not a no-op |
| Three reasons to refuse at once | All three reported | One diagnosis instead of three attempts |
GRAN32 not twice GRAN16 | $fatal at elaboration | The device-verified relationship; a wrong pair invalidates every comparison |
GRAN16 of 0 or 1 | $fatal at elaboration | A 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
// 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| Check | What it establishes |
|---|---|
| Phase matches the reference every cycle, at both ratios | P1 and P2 against an independent modulus |
| Configure 4:1, sweep phase 0–3, switch to 2:1 mid-sweep | P3, and §13's second failure mode |
| Configure 2:1, let the phase run for many ticks | P1 — a width-modulus bug fires here and only here |
| Request ratios 0, 1, 3, 5; confirm none enters force | P4; the reference rejects the same set |
| Issue a command before configuring | P5 and err_cmd_before_config |
| Sweep beat offsets 0–35 at BL16 and at BL32 | P6, and §17's rows six and seven |
Check beats_per_cmd_slot against the reference at both ratios | P7 — §4's arithmetic |
| Random request stream; compare both arms against a reference | P8 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 conditions | P12, each refusal reason |
| Reconfigure with all three conditions true at once | All three reported in one attempt |
| Request two dimensions together | P13 |
| Reconfigure while quiesced | P14 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:
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
| Symptom | Likely cause | How to confirm |
|---|---|---|
| Works at 4:1, fails at 2:1 | Phase wrapping at the counter width — §13 | P1 fires at phase 2; run the §18 third check |
| Fails only after a ratio change | Phase not reset on reconfiguration — §13 | P3; check wck_phase immediately after cfg_valid |
| Every crossing wrong by a consistent factor | An unsupported ratio was clamped into force — §13 | P4; err_ratio_unsupported should have fired |
| Crossings fail intermittently at one offset | Beat offset valid at one burst length, not the other | §17's rows six and seven; check the configured BL |
| Command path timing marginal | 2:1 doubles the command-domain frequency — §3 | Which ratio is in force; 4:1 halves it |
| Command bus appears to limit throughput | Unlikely at either ratio — §4 | Eight slots per burst even at 4:1; look at banks instead |
| Bandwidth good, battery worse than LPDDR4 | BL32 with a small-access mix — §7's trap | cnt_le_32 against cnt_gt_64; run §14's selector |
| Large transfers no faster at BL32 | Command overhead was not the limit | fewer_bursts_bl32 is real but irrelevant to this workload |
| Reconfiguration silently has no effect | Request refused and the refusal ignored | cnt_refused; then the five reason flags |
| Reconfiguration appears to take effect, then corrupts | Change granted while something was in flight | P12 and P14 — if either fires, the guard is bypassed |
| Data corruption after a mode change | Two dimensions changed together — §15 | err_multi_dim_at_once |
| A wrong-channel access returns valid data | 24.3 §6's failure, unchanged here | Not 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
-
Derive
CKand the command slot rate for a hypothetical 6:1 ratio at §2'sWCK, then explain why §12's block would reject it and what would have to change to support it. -
§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.
-
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.
-
§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.
-
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.
-
§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.
-
§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.
-
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_wastedoutput reproduces §14'sextra_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
Related tutorials
- Related topic
Burst Length
Burst length counts transfer positions. It is not a byte count, not a bus width, not a cache line, and not a number of clock cycles — and it is not a free menu you pick from.
- Related topic
The Refresh Requirement
Leakage produces a rule about the passage of time rather than about any operation. What the maintenance operation actually does, why it costs device availability, and how a digital design tracks a deadline, arbitrates it against traffic, and proves it never silently drops the obligation.
- Related topic
Restore Operations
Sensing consumed the stored state, so something must put it back. What restoration drives, why it covers a whole row, why a restored row is then cheap to access again, and an educational control model that cannot skip a prerequisite the array is unable to enforce.
- Related topic
Rows
A DRAM row is not an address range. It is the group of cells one shared selection conductor connects at the same instant — and that physical fact is where row granularity, controller-visible row state and state-dependent access cost all come from.
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.
