DDR · Module 24
Mobile-Memory Mission
A phone asks three different things of its memory and they trade against each other. At a 2% duty cycle nearly half the average power is spent idle, and below 1% it is two thirds.
Everything in this curriculum so far has described memory for a machine with a power supply. A desktop, a server, a workstation — devices where the memory subsystem's job is to move as much data as possible, and where the electricity to do it arrives from a wall.
This module is about memory for a device running on a battery, and the first thing worth establishing is that the difference is not that mobile memory is slower. It is not. The fastest LPDDR devices move data at rates a desktop part would be pleased with.
The difference is what the memory is being asked to optimise, and that comes from what a phone actually does with it.
The claim this module rests on, and which this chapter exists to make believable:
LPDDR is not a low-power setting applied to DDR. It is a different device, with a different interface, whose architecture is shaped by energy per bit and idle power rather than by peak bandwidth alone.
1. What a Phone Asks of Its Memory
Start with the device rather than the memory, because the memory's shape follows from it.
A phone spends most of its existence doing almost nothing. It sits in a pocket with its screen off. Occasionally it wakes to check for a message, runs for a fraction of a second, and goes back to sleep. Then someone picks it up, and for a few minutes it does a great deal — decodes video, composes a display frame sixty times a second, runs a camera pipeline, handles touch input, and keeps a dozen background tasks alive.
Then it goes back in the pocket.
Memory has to be present for all of that. Not active — present. DRAM is volatile: it forgets unless it is refreshed, and Chapter 2.3 established that refresh is not optional. So even the phone in the pocket, doing nothing, is paying for its memory to keep remembering.
That single observation is the root of everything in this module. The memory in a mobile device spends most of its life idle and must still be paid for, and a design that optimises only what happens during the busy minutes has optimised the smaller half of the problem.
2. Three Quantities That Are Not the Same
Almost every confusion in mobile memory comes from treating these three as one thing. They are separate, they are measured differently, and improving one can make another worse.
| Quantity | What it measures | Units, informally |
|---|---|---|
| Energy per bit | What it costs to move one bit | energy ÷ bits |
| Idle power | What the device costs while moving nothing | power |
| Peak bandwidth | The most it can move at once | bits ÷ time |
Read them again and notice that only the third is a speed. The first is an efficiency and the second is an overhead, and neither improves automatically when the third does.
Three examples of how they pull apart, each of which recurs later in the module:
Raising the data rate can improve energy per bit. Moving a fixed amount of data faster means the surrounding circuitry — clocks, buffers, the array's own activity — is awake for less time. So a faster device can be a more efficient one per bit, which is why LPDDR generations get faster rather than slower.
Adding a deeper idle state improves idle power and worsens energy per bit. A state that turns more things off costs more to leave. If the device is woken frequently, the exit cost is paid often, and the energy attributed to each bit moved afterwards goes up. Chapter 24.2 quantifies that trade.
And a wider channel improves peak bandwidth while worsening energy per bit for small accesses. A wide channel moves a large minimum quantity. If the requester wanted less than that, the surplus was moved and paid for and discarded — which is Chapter 12.4's payload efficiency, and §6 is about why mobile traffic runs into it constantly.
Every mechanism in this module serves one of the three, and each chapter says which. A mechanism praised without saying which quantity it improves has not been explained.
3. The Duty Cycle Is the Whole Story
Put numbers on §1's picture and the consequence becomes arithmetic rather than intuition.
Consider a device whose memory is genuinely active 2% of the time. ILLUSTRATIVE — chosen to be legible, and §10's block lets you vary it. Suppose, again illustratively, that active operation costs 50 units of power and idle costs 1 unit.
active : 2% of the time x 50 units = 1.00 units of average power
idle : 98% of the time x 1 unit = 0.98 units of average power
----
total average 1.98 units
the idle state contributes 0.98 / 1.98 = 49.5% of the averageDERIVED from the illustrative inputs, and recomputed. Nearly half the memory's average power is spent doing nothing — and that is with idle costing one fiftieth of active.
Now improve the active case by 20%, which would be a substantial architectural achievement:
active : 2% x 40 units = 0.80
idle : 98% x 1 unit = 0.98
----
total 1.78 a 10.1% improvementAnd instead improve the idle case by the same 20%:
active : 2% x 50 units = 1.00
idle : 98% x 0.8 units = 0.784
-----
total 1.784 a 9.9% improvementDERIVED, and the two are nearly identical — 10.1% against 9.9%. A fifth off the active power and a fifth off the idle power are worth the same amount, because the duty cycle has equalised them.
4. There Is No Fan
A second constraint, independent of energy, and easy to overlook because desktop designs rarely meet it.
A phone has no fan. Heat leaves it by conduction into its case and then into the air, and the case cannot get hot enough to be unpleasant to hold. So there is a ceiling on sustained power dissipation that has nothing to do with the battery's capacity — it is set by how fast heat can get out.
Two consequences matter for memory.
Sustained and burst limits differ. A device can dissipate more for a short period than it can sustain, because its mass absorbs heat before the temperature rises. So a memory subsystem may be allowed a high peak power and a much lower average — which is a different constraint from a battery budget and can bind first.
And temperature feeds back into the memory's own cost. Chapter 2.3 §4 established that DRAM retention falls as temperature rises, so a hot device must refresh more often. Refreshing more often costs more power, which produces more heat. That is a positive feedback loop, and 24.2 shows what the LPDDR family does about it — including a figure showing that self-refresh current on a real device rises by more than a factor of twenty between a cool part and a hot one.
So mobile memory is designed against two ceilings at once: total energy from the battery, and instantaneous heat flow through the case. A design can satisfy either and fail the other.
5. A Fixed Energy Budget
The third constraint is the one people think of first, and it is worth stating precisely because the obvious version is not quite right.
The obvious version: a phone has a battery, so memory must use little power. True, and not very useful — it does not say how little, or measured how.
The useful version: a mobile device is designed to a target battery life for a defined usage pattern, and every subsystem gets a share of the energy that implies. Memory's share is fixed by that negotiation, and the question the memory architect faces is not “how low can power go” but:
Given this energy allowance and this usage pattern, what memory behaviour fits inside it?
That is a feasibility question rather than a minimisation, and it has a shape §10's second block captures: a budget, a workload, and a yes-or-no answer with a margin.
6. Many Small Requesters
The last piece of the mission is the shape of the traffic, and it is where mobile diverges from a server most visibly.
A server's memory traffic tends to come from a modest number of large consumers. A phone's comes from many small, largely independent ones: application processor cores, a graphics unit, a display pipeline that must deliver a frame on time every time, a camera pipeline, a video decoder, an audio path, a modem, and an assortment of accelerators.
Two properties follow, and both push in the same direction.
Each requester wants a modest amount of data at a time. A display pipeline fetching a scanline and a core fetching a cache line both want a small quantity. If the memory's minimum transfer is larger than what was asked for, the surplus is moved and discarded — and that is energy spent on nothing.
And they are concurrent and independent. Several want service at once, and they do not coordinate. Chapter 1.8 §7 established memory-level parallelism as the general answer to that; the mobile-specific point is that the number of independent requesters is high relative to the data each one wants.
So narrow, independent channels match mobile traffic better than one wide channel does — smaller minimum transfer, less waste per small access, and several requesters served at once. Chapter 4.7 §5 owns the argument that this is why the LPDDR family organises devices the way it does, and notes that the identical structural feature is a cost in the DDR lineage and a benefit here, decided entirely by the requesters.
This chapter's contribution is the requester inventory above: it is the reason the shoe fits.
7. What the Supply Rails Tell You
Enough of the mission is established to look at a real device and read its power supplies as evidence of its priorities. All figures here are DEVICE-VERIFIED from one named part — a Samsung 16Gb LPDDR4 SDRAM — and the datasheet presents LPDDR4 and LPDDR4X side by side, which makes the comparison unusually clean.
| Rail | LPDDR4 | LPDDR4X |
|---|---|---|
VDD1 | 1.70 – 1.95 V | 1.70 – 1.95 V |
VDD2 | 1.14 – 1.30 V | 1.06 – 1.17 V |
VDDQ | 1.14 – 1.30 V | 1.06 – 1.17 V |
VDDCA | 1.14 – 1.30 V | absent |
The datasheet's own feature summary gives the nominal set as VDD1 / VDD2 / VDDQ = 1.8 / 1.1 / 1.1 V.
Three things are readable directly from that table, and the third is the interesting one.
There is more than one rail, on purpose. A single supply would have to be high enough for whatever needs the most voltage. Splitting them lets each part of the device run at the lowest voltage that works for it — and since energy scales with voltage, the split is an energy decision expressed as a pinout.
VDD1 is the high one and it does not move between the two generations. It supplies the part of the device that cannot run lower. The others can, and do.
And LPDDR4X does two separate things, not one. It lowers VDD2 and VDDQ — the change people describe as “LPDDR4X is lower voltage” — and it removes the VDDCA rail entirely. The command/address path no longer has its own supply; it shares. That is a structural simplification rather than a voltage reduction, and describing LPDDR4X purely as a voltage drop misses half of what changed.
8. Why Lower Voltage Is Not the Whole Answer
The rails of §7 invite a tempting summary — LPDDR is DDR at lower voltage — and it is wrong in a way worth dismantling carefully, because it is the single most common misunderstanding about this family.
First, the I/O standard is different in kind. The same datasheet lists the interface as LVSTL — Low Voltage Swing Terminated Logic, terminated to VSSQ. That is not a lower-voltage version of DDR4's signalling; it is a different signalling scheme with a different termination reference. DEVICE-VERIFIED, and it means the electrical behaviour of the link is not DDR4's behaviour scaled down.
Second, the device omits something DDR4 has. The same feature list states plainly: No DLL — CK to DQS is not synchronized, with edge-aligned data output and write training used to centre data input. A delay-locked loop costs power continuously, and this family declines to spend it, accepting instead that the controller must train and manage the relationship. That is a power decision that moves work to the controller, and it is a pattern this module sees repeatedly.
Third, the command interface is organised differently, which 24.3 builds in detail: a six-bit command/address bus carrying commands over multiple clock cycles, rather than a wide bus carrying one command per cycle.
And fourth, the device has features DDR4 does not. The same summary lists PASR — partial-array self refresh — and TCSR — temperature-compensated self refresh — as supported functions, both of which exist to reduce the idle cost of §2, and neither of which is a voltage.
So the honest summary is the one this chapter opened with: a different device, not a setting. Lower voltage is one of many changes, and on its own it explains neither the interface, nor the omissions, nor the extra features.
9. The Mission, as a Structure
The structure makes one thing visible that prose struggles with: the middle row is the only place the four constraints and the four responses meet. Nothing in the top row acts directly on the bottom row. Every design response serves a quantity, and the quantity is what the constraint actually pressed on — which is why §2 insisted the three be kept apart.
10. The Energy Accountant
The first of this chapter's two blocks. It separates §2's three quantities and refuses to combine them, because combining them is the mistake it exists to prevent.
// ---------------------------------------------------------------------
// mobile_energy_accountant -- keeps §2's three quantities separate and
// reports which one dominates an average.
//
// CLASSIFICATION: educational, synthesisable.
//
// ALL UNITS ARE ABSTRACT AND DIMENSIONLESS. There are no joules,
// watts, amps or volts anywhere in this block, and it models no real
// device. Use it to COMPARE two configurations, never to size one.
//
// WHAT IT DOES NOT MODEL:
// - any physical quantity, any supply rail, any real consumption
// - the exit-energy cost of leaving a low-power state, which 24.2
// owns and which terms_excluded records as missing
// - temperature, which §4 establishes feeds back into the cost
//
// WHY IT REFUSES TO COMBINE THE THREE: §2. Energy per bit, idle power
// and peak bandwidth are measured differently and can move in
// opposite directions. A single "power score" would hide exactly the
// trade this module is about.
// ---------------------------------------------------------------------
module mobile_energy_accountant #(
// Width of an abstract unit. Wide enough that a long observation
// window does not saturate; saturation is still detected because a
// clamped total reads LOW, which is the flattering direction.
parameter int UNIT_W = 20,
// Duty cycle is expressed as a numerator over this denominator, so
// no division is needed anywhere. 10000 gives basis-point
// resolution -- 200 means 2.00%.
parameter int DUTY_DEN = 10000,
parameter int ACC_W = UNIT_W + 18
) (
input logic clk,
input logic rst_n,
// ── The model, supplied by the caller. Abstract units throughout.
input logic [UNIT_W-1:0] active_power, // cost while moving data
input logic [UNIT_W-1:0] idle_power, // cost while doing nothing
// Duty cycle numerator over DUTY_DEN. §3: this is a SYSTEM property,
// not a memory property, which is why it is an input.
input logic [UNIT_W-1:0] duty_num,
// ── Observed work, for the energy-per-bit half.
input logic bit_moved, // one abstract bit went out
input logic tick, // one time unit elapsed
// ── §2's three quantities, reported SEPARATELY and never summed
// into one another.
output logic [ACC_W-1:0] weighted_active, // duty x active
output logic [ACC_W-1:0] weighted_idle, // (1-duty) x idle
output logic [ACC_W-1:0] average_power, // the two above, added
output logic [ACC_W-1:0] bits_moved,
output logic [ACC_W-1:0] time_elapsed,
// Which of the two halves of the average is larger. §3's whole
// point, as one bit.
output logic idle_dominates,
// Idle's share, as a NUMERATOR over average_power. Published as a
// pair so nothing divides -- the caller states its denominator.
output logic [ACC_W-1:0] idle_share_num,
output logic [ACC_W-1:0] idle_share_den,
// ── What this arithmetic does NOT contain. Published as a vector so
// a property can require it to be non-zero (§13's P4).
// bit 0 exit energy from low-power states (24.2)
// bit 1 temperature dependence (§4)
// bit 2 per-rail detail (§7, 24.5)
// bit 3 refresh energy (24.2)
// bit 4 wasted payload on small accesses (§6, 12.4)
// bit 5 thermal ceiling (§4)
// bit 6 any physical unit whatsoever
// bit 7 the requester mix's actual shape (§6)
output logic [7:0] terms_excluded,
output logic err_duty_out_of_range,
output logic any_saturated
);
// ── Elaboration guards.
initial begin
if (DUTY_DEN < 100)
$fatal(1, "mobile_energy_accountant: DUTY_DEN (%0d) too coarse", DUTY_DEN);
if (UNIT_W < 8)
$fatal(1, "mobile_energy_accountant: UNIT_W (%0d) too small", UNIT_W);
// A product of two UNIT_W values needs 2*UNIT_W bits, plus room to
// accumulate. A narrow accumulator would truncate the product and
// report a smaller average than the model implies.
if (ACC_W < 2*UNIT_W)
$fatal(1, "mobile_energy_accountant: ACC_W must hold a UNIT_W product");
end
// ── The duty split. Computed combinationally from the inputs so the
// caller can sweep duty_num and watch the balance move (§3's table)
// without re-running an accumulation.
logic [UNIT_W-1:0] idle_num;
logic duty_bad;
always_comb begin
duty_bad = (duty_num > UNIT_W'(DUTY_DEN));
// Saturate rather than wrap on a nonsensical duty: a wrapped
// complement would report a huge idle share.
idle_num = duty_bad ? '0 : (UNIT_W'(DUTY_DEN) - duty_num);
end
logic [ACC_W-1:0] w_act, w_idl;
always_comb begin
w_act = {{(ACC_W-UNIT_W){1'b0}}, active_power} * {{(ACC_W-UNIT_W){1'b0}}, duty_num};
w_idl = {{(ACC_W-UNIT_W){1'b0}}, idle_power} * {{(ACC_W-UNIT_W){1'b0}}, idle_num};
end
logic [ACC_W-1:0] bits_q, time_q;
logic sat;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
bits_q <= '0;
time_q <= '0;
sat <= 1'b0;
end else begin
if (bit_moved) begin
if (bits_q == {ACC_W{1'b1}}) sat <= 1'b1;
else bits_q <= bits_q + ACC_W'(1);
end
if (tick) begin
if (time_q == {ACC_W{1'b1}}) sat <= 1'b1;
else time_q <= time_q + ACC_W'(1);
end
end
end
assign weighted_active = w_act;
assign weighted_idle = w_idl;
assign average_power = w_act + w_idl;
assign bits_moved = bits_q;
assign time_elapsed = time_q;
// §3's headline: which half of the average is bigger.
assign idle_dominates = (w_idl > w_act);
// Published as a pair. §2's discipline: a share without its
// denominator is not a quantity.
assign idle_share_num = w_idl;
assign idle_share_den = w_act + w_idl;
// Every physical term is absent, and that is permanent for this
// block rather than a gap to be filled later.
assign terms_excluded = 8'hFF;
assign err_duty_out_of_range = duty_bad;
assign any_saturated = sat;
endmoduleTwo details in that block carry §2's argument rather than merely implementing it.
The three quantities leave by separate ports. weighted_active, weighted_idle and the bit/time counters are never combined into one figure of merit. A block that produced a single “power score” would let a design improve the score while worsening the quantity that actually mattered — which is the trade §2 exists to keep visible.
terms_excluded is hard-wired to all ones. Every physical term is missing, and that is not a limitation to be fixed in a later revision — it is what makes the block honest. §13's P4 asserts the vector is non-zero, so an edit that “completed” the model and cleared it would fail a property rather than pass quietly.
11. The Mission Budget Checker
§5 argued the real question is feasibility rather than minimisation. This is that question as a block.
// ---------------------------------------------------------------------
// mission_budget_checker -- does a stated energy allowance admit a
// stated amount of memory work?
//
// CLASSIFICATION: educational, synthesisable.
//
// ALL UNITS ARE ABSTRACT. No joules, no milliamp-hours, no device.
//
// WHAT IT DOES NOT MODEL:
// - any physical quantity or real battery
// - the thermal ceiling of §4, which is a SECOND and independent
// constraint: a workload can satisfy this checker and still
// exceed a heat-flow limit. err_thermal_not_checked says so
// permanently rather than implying coverage.
// - anything in mobile_energy_accountant's terms_excluded
//
// WHY FEASIBILITY RATHER THAN MINIMISATION: §5. A minimisation has no
// stopping point; a feasibility test does, and it also names WHICH
// lever closes a gap, which a minimisation cannot.
// ---------------------------------------------------------------------
module mission_budget_checker #(
parameter int UNIT_W = 24,
// Margin required beyond merely fitting, as a numerator over 256.
// A budget that closes with zero margin is not a design (§11's
// corner cases), so the default demands some.
parameter int MARGIN_N256 = 26, // ~10%
parameter int ACC_W = UNIT_W + 10
) (
input logic clk,
input logic rst_n,
input logic evaluate,
// ── The allowance, and the work it must cover.
input logic [UNIT_W-1:0] allowance, // abstract energy budget
input logic [UNIT_W-1:0] work_units, // abstract bits to move
input logic [UNIT_W-1:0] cost_per_unit, // abstract cost per bit
input logic [UNIT_W-1:0] fixed_overhead, // idle cost over the period
// ── The verdict.
output logic result_valid,
output logic budget_closes,
output logic [ACC_W-1:0] required,
output logic [ACC_W-1:0] headroom, // 0 when it does not close
output logic [ACC_W-1:0] deficit, // 0 when it does
// ── §5's three levers, each reported as the reduction that would
// close the gap. A checker that said only "no" would be far less
// useful than one that says which knob to turn.
output logic [ACC_W-1:0] lever_less_work, // work_units to remove
output logic [ACC_W-1:0] lever_cheaper_bit, // cost_per_unit to remove
output logic [ACC_W-1:0] lever_more_allowance,// allowance to add
output logic [1:0] cheapest_lever, // smallest of the three
output logic err_zero_allowance,
// Permanent, never cleared: §4's ceiling is a separate constraint
// and this block does not evaluate it.
output logic err_thermal_not_checked
);
initial begin
if (MARGIN_N256 < 0 || MARGIN_N256 > 255)
$fatal(1, "mission_budget_checker: MARGIN_N256 must be 0..255");
if (ACC_W <= UNIT_W)
$fatal(1, "mission_budget_checker: ACC_W must exceed UNIT_W");
end
// ── Required energy, and the margin-adjusted allowance it must fit
// inside. Both widened before multiplying so a product cannot
// truncate and report a smaller requirement than the model.
logic [ACC_W-1:0] req, allow_eff;
always_comb begin
req = ({{(ACC_W-UNIT_W){1'b0}}, work_units}
* {{(ACC_W-UNIT_W){1'b0}}, cost_per_unit})
+ {{(ACC_W-UNIT_W){1'b0}}, fixed_overhead};
// Allowance minus the demanded margin, computed by scaling rather
// than dividing: allowance x (256 - MARGIN) >> 8.
allow_eff = ({{(ACC_W-UNIT_W){1'b0}}, allowance}
* ACC_W'(256 - MARGIN_N256)) >> 8;
end
logic closes_c;
assign closes_c = (req <= allow_eff) && (allowance != '0);
logic [ACC_W-1:0] gap;
assign gap = closes_c ? '0 : (req - allow_eff);
// ── The three levers. Each is "how much of this input would have to
// change", computed independently so they can be compared.
logic [ACC_W-1:0] lw, lc, la;
always_comb begin
// Work to remove: gap divided by cost per unit. A shift-free
// division is not available, so this reports the gap in work-cost
// terms and leaves the final division to the caller -- the same
// no-divide discipline the accountant uses.
lw = gap;
// Cost-per-bit reduction needed, in the same units.
lc = gap;
// Allowance to add is the gap scaled back up by the margin, so the
// reported figure is what the BUDGET must grow by, not what the
// requirement must shrink by. Those differ whenever MARGIN != 0,
// and conflating them understates the needed increase.
la = (gap * ACC_W'(256)) / ACC_W'(256 - MARGIN_N256);
end
logic [1:0] cheap;
always_comb begin
// Smallest of the three, with a defined order on ties so the
// output is deterministic rather than tool-dependent.
if ((lw <= lc) && (lw <= la)) cheap = 2'd0;
else if ((lc <= lw) && (lc <= la)) cheap = 2'd1;
else cheap = 2'd2;
end
logic vld, closes_q, zero_allow;
logic [ACC_W-1:0] req_q, head_q, def_q, lw_q, lc_q, la_q;
logic [1:0] cheap_q;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
vld <= 1'b0; closes_q <= 1'b0; zero_allow <= 1'b0;
req_q <= '0; head_q <= '0; def_q <= '0;
lw_q <= '0; lc_q <= '0; la_q <= '0; cheap_q <= '0;
end else if (evaluate) begin
vld <= 1'b1;
closes_q <= closes_c;
zero_allow <= (allowance == '0);
req_q <= req;
head_q <= closes_c ? (allow_eff - req) : '0;
def_q <= gap;
lw_q <= lw;
lc_q <= lc;
la_q <= la;
cheap_q <= cheap;
end
end
assign result_valid = vld;
assign budget_closes = closes_q;
assign required = req_q;
assign headroom = head_q;
assign deficit = def_q;
assign lever_less_work = lw_q;
assign lever_cheaper_bit = lc_q;
assign lever_more_allowance = la_q;
assign cheapest_lever = cheap_q;
assign err_zero_allowance = zero_allow;
// Never cleared. §4's ceiling is not evaluated here, and a caller
// must not read a closing budget as a thermally feasible design.
assign err_thermal_not_checked = 1'b1;
endmodulelever_more_allowance is scaled differently from the other two, and that is the block's one subtle piece. Closing a gap by growing the budget requires more than the gap itself, because the margin applies to the new allowance too. Reporting the raw gap as the needed increase would understate it — and the understatement grows with the margin, which is exactly when it matters.
And err_thermal_not_checked is permanently high. §4 established that the thermal ceiling is a second, independent constraint. A workload can satisfy this checker and still exceed what the case can dissipate, so the block refuses to let a closing budget be read as a feasible design.
12. The Thermal Ceiling, as a Second Checker
§11's checker answers the energy question and declares — permanently, through err_thermal_not_checked — that it has not answered the heat question. §4 established why those are different: energy is a total and heat flow is a rate, and a workload can satisfy one while violating the other.
The distinction that makes the thermal case a separate block rather than another input is §4's second point: sustained and burst limits differ. A device may dissipate above its sustained limit briefly, because its own mass absorbs heat before the temperature rises. So the thermal question is not is the power below a number but:
Over a window, has the accumulated heat exceeded what could flow out in that window?
That is an accumulation against a drain, and it has a shape the energy checker does not.
heat in ──► ┌──────────────┐
│ accumulator │ ──► temperature proxy
heat out ◄── └──────────────┘
burst : in >> out for a short time. Accumulator rises,
stays below the limit, and drains afterwards. ALLOWED.
sustained: in > out indefinitely. Accumulator rises without
bound and crosses the limit. FORBIDDEN.The same average power can be either, depending entirely on how it is distributed in time — which is why a single average-power figure cannot answer the thermal question and §11's block was right to refuse.
// ---------------------------------------------------------------------
// thermal_headroom_checker -- accumulates abstract heat against an
// abstract drain and reports whether a limit was crossed.
//
// CLASSIFICATION: educational, synthesisable.
//
// ALL UNITS ARE ABSTRACT. No degrees, no watts, no thermal resistance
// or capacitance. This is a LEAKY ACCUMULATOR WITH A LIMIT, which is
// the SHAPE of §4's constraint, not a thermal model.
//
// WHAT IT DOES NOT MODEL:
// - temperature, or any real device's heat flow
// - the spatial problem: heat leaves from somewhere, and where the
// dissipation is matters. This block has one accumulator and
// therefore no notion of location at all.
// - the feedback of §4: a hotter device refreshes more often and so
// dissipates more. That loop is absent, so this block cannot
// diverge the way a real device can.
//
// WHY IT IS SEPARATE FROM mission_budget_checker: §4 and §12. Energy
// is a TOTAL; heat flow is a RATE against a drain. The same average
// power can be thermally fine or fatal depending on its distribution
// in time, and an average cannot express that.
// ---------------------------------------------------------------------
module thermal_headroom_checker #(
parameter int UNIT_W = 20,
// The limit the accumulator must not cross. ILLUSTRATIVE by nature:
// a real limit comes from a mechanical design this block cannot see.
parameter int LIMIT = 1000,
// Abstract heat removed per tick. The "drain" of §12's diagram.
parameter int DRAIN = 10,
parameter int ACC_W = UNIT_W + 8
) (
input logic clk,
input logic rst_n,
input logic tick,
// Abstract heat generated this tick, supplied by the caller.
input logic [UNIT_W-1:0] heat_in,
// ── State and verdict.
output logic [ACC_W-1:0] accumulated,
output logic [ACC_W-1:0] peak_accumulated,
output logic over_limit,
output logic ever_over_limit,
// ── The burst-versus-sustained distinction of §12, as two outputs
// rather than one. A design needs to know WHICH it violated,
// because the remedies differ.
// sustained_excess : heat_in has exceeded DRAIN for a long run
// burst_ok : accumulator rose and drained without
// crossing, which is the ALLOWED shape
output logic sustained_excess,
output logic burst_ok,
output logic [15:0] longest_excess_run,
output logic [15:0] cnt_limit_crossings,
// Headroom as a difference, never a ratio -- the same no-divide
// discipline §10's accountant uses.
output logic [ACC_W-1:0] headroom,
output logic err_drain_exceeds_limit,
output logic any_saturated
);
initial begin
if (LIMIT < 1)
$fatal(1, "thermal_headroom_checker: LIMIT must be at least 1");
if (DRAIN < 1)
// A zero drain makes every non-zero input eventually fatal,
// which is a model with no steady state rather than a cool one.
$fatal(1, "thermal_headroom_checker: DRAIN must be at least 1");
if (DRAIN > LIMIT)
// A drain larger than the limit means the accumulator can never
// hold anything, so the limit is unreachable and the block
// would always pass. That is a misparameterisation, not a
// well-cooled device.
$fatal(1, "thermal_headroom_checker: DRAIN (%0d) exceeds LIMIT (%0d)",
DRAIN, LIMIT);
if (ACC_W <= UNIT_W)
$fatal(1, "thermal_headroom_checker: ACC_W must exceed UNIT_W");
end
logic [ACC_W-1:0] acc, peak;
logic [15:0] run, worst_run, crossings;
logic ever_over, sat;
// Declared above the continuous assigns that read them.
logic in_excess;
assign in_excess = tick && ({{(ACC_W-UNIT_W){1'b0}}, heat_in} > ACC_W'(DRAIN));
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
acc <= '0; peak <= '0;
run <= '0; worst_run <= '0; crossings <= '0;
ever_over <= 1'b0; sat <= 1'b0;
end else if (tick) begin
// ── Accumulate, then drain, floored at zero. Draining BELOW
// zero would let a long cool period bank credit against a
// future burst, which no thermal mass does.
begin : accumulate
logic [ACC_W-1:0] nxt;
nxt = acc + {{(ACC_W-UNIT_W){1'b0}}, heat_in};
if (nxt < acc) begin
// Overflow on the add: report rather than wrap, because a
// wrapped accumulator reads COOL, the flattering direction.
sat <= 1'b1;
nxt = {ACC_W{1'b1}};
end
acc <= (nxt > ACC_W'(DRAIN)) ? (nxt - ACC_W'(DRAIN)) : '0;
if (nxt > peak) peak <= nxt;
if (nxt > ACC_W'(LIMIT)) begin
ever_over <= 1'b1;
if (crossings != 16'hFFFF) crossings <= crossings + 16'd1;
end
end
// ── The run of consecutive ticks in which input exceeded the
// drain. A long run is the SUSTAINED case; a short one that
// ends is the BURST case, and §12 says they differ.
if (in_excess) begin
if (run != 16'hFFFF) run <= run + 16'd1;
if ((run + 16'd1) > worst_run) worst_run <= run + 16'd1;
end else begin
run <= '0;
end
end
end
assign accumulated = acc;
assign peak_accumulated = peak;
assign over_limit = (acc > ACC_W'(LIMIT));
assign ever_over_limit = ever_over;
// Sustained means the input has outpaced the drain long enough that
// the accumulator cannot recover within the run.
assign sustained_excess = (run > 16'(LIMIT / DRAIN));
// The allowed shape: it rose above the drain at some point and has
// never crossed the limit.
assign burst_ok = (worst_run != '0) && !ever_over;
assign longest_excess_run = worst_run;
assign cnt_limit_crossings = crossings;
assign headroom = (acc < ACC_W'(LIMIT)) ? (ACC_W'(LIMIT) - acc) : '0;
assign err_drain_exceeds_limit = 1'b0; // checked at elaboration
assign any_saturated = sat;
endmoduleThree details carry §4's argument rather than merely implementing a counter.
The accumulator floors at zero rather than going negative. A long cool period does not bank credit against a future burst — thermal mass that has reached ambient cannot get colder than ambient. Letting it go negative would permit an arbitrarily large burst after an arbitrarily long idle, which no physical system allows.
Overflow on the add is reported rather than wrapped. A wrapped accumulator reads cool, which is the flattering direction and therefore the dangerous one. The same reasoning as §10's saturation handling, applied to a different quantity.
And sustained_excess is derived from the run length, not from the accumulator. The accumulator says how hot; the run says for how long the input outpaced the drain. §12's diagram distinguishes a burst from a sustained excess by duration, and only the run length carries that — which is why both outputs exist.
13. What the Assertions Prove
// Bind unit note: P1-P5 reference mobile_energy_accountant, which is
// largely combinational; they are written for a bind unit that
// supplies a sampling clock. P6-P10 reference mission_budget_checker
// and see its clk and rst_n directly.
// P1 -- the two halves of the average always add to it. Simple, and it
// is the property that fails if someone "optimises" the block by
// computing the average directly instead of from the two parts.
property p_halves_sum_to_average;
@(posedge clk) disable iff (!rst_n)
(average_power == (weighted_active + weighted_idle));
endproperty
assert property (p_halves_sum_to_average);
// P2 -- the duty split is complementary. If this fails, some fraction
// of time is being charged twice or not at all.
property p_duty_split_complementary;
@(posedge clk) disable iff (!rst_n)
!err_duty_out_of_range
|-> ((duty_num + idle_num) == UNIT_W'(DUTY_DEN));
endproperty
assert property (p_duty_split_complementary);
// P3 -- idle_dominates agrees with the two weighted halves. The §3
// headline must not be able to disagree with its own inputs.
property p_dominance_is_consistent;
@(posedge clk) disable iff (!rst_n)
idle_dominates == (weighted_idle > weighted_active);
endproperty
assert property (p_dominance_is_consistent);
// P4 -- THE honesty property. The excluded-terms vector is never
// empty. An edit that added physical parameters and cleared it would
// fail here rather than silently implying a completeness the block
// does not have.
property p_exclusions_declared;
@(posedge clk) disable iff (!rst_n)
(terms_excluded != 8'h00);
endproperty
assert property (p_exclusions_declared);
// P5 -- an out-of-range duty produces a zero idle numerator rather
// than a wrapped complement, which would report a huge idle share.
property p_bad_duty_saturates;
@(posedge clk) disable iff (!rst_n)
err_duty_out_of_range |-> (weighted_idle == '0);
endproperty
assert property (p_bad_duty_saturates);
// P6 -- the budget closes only if the requirement fits the
// margin-adjusted allowance. Guards against a comparison written the
// wrong way round, which is the likeliest bug in a checker.
property p_closes_iff_fits;
@(posedge clk) disable iff (!rst_n)
(result_valid && budget_closes)
|-> (required <= ((({{(ACC_W-UNIT_W){1'b0}}, allowance})
* ACC_W'(256 - MARGIN_N256)) >> 8));
endproperty
assert property (p_closes_iff_fits);
// P7 -- headroom and deficit are mutually exclusive, and exactly the
// right one is non-zero for the verdict.
property p_headroom_xor_deficit;
@(posedge clk) disable iff (!rst_n)
result_valid |-> (budget_closes ? (deficit == '0) : (headroom == '0));
endproperty
assert property (p_headroom_xor_deficit);
// P8 -- a closing budget reports no levers. Reporting a lever for a
// design that already fits would invite pointless optimisation, which
// is precisely what §5's feasibility framing exists to stop.
property p_no_levers_when_closing;
@(posedge clk) disable iff (!rst_n)
(result_valid && budget_closes) |-> (deficit == '0);
endproperty
assert property (p_no_levers_when_closing);
// P9 -- the allowance lever is never smaller than the deficit. §11:
// growing the budget costs more than the gap because the margin
// applies to the new allowance too.
property p_allowance_lever_exceeds_gap;
@(posedge clk) disable iff (!rst_n)
(result_valid && !budget_closes && (MARGIN_N256 > 0))
|-> (lever_more_allowance >= deficit);
endproperty
assert property (p_allowance_lever_exceeds_gap);
// P10 -- the thermal disclaimer is permanent. §4's ceiling is never
// evaluated here and the block must never appear to have checked it.
property p_thermal_never_claimed;
@(posedge clk) disable iff (!rst_n)
err_thermal_not_checked;
endproperty
assert property (p_thermal_never_claimed);
// ── Cover.
cover property (@(posedge clk) disable iff (!rst_n) idle_dominates);
cover property (@(posedge clk) disable iff (!rst_n) !idle_dominates);
// The §3 crossover: the two halves exactly equal. A suite that never
// hits it has not tested the comparison's boundary.
cover property (@(posedge clk) disable iff (!rst_n)
(weighted_active == weighted_idle) && (average_power != '0));
cover property (@(posedge clk) disable iff (!rst_n) err_duty_out_of_range);
cover property (@(posedge clk) disable iff (!rst_n)
result_valid && budget_closes);
cover property (@(posedge clk) disable iff (!rst_n)
result_valid && !budget_closes);
// A budget that closes with zero headroom -- §14's row, and not a
// design anyone should ship.
cover property (@(posedge clk) disable iff (!rst_n)
result_valid && budget_closes && (headroom == '0));
// Each of §5's three levers was the cheapest at least once.
cover property (@(posedge clk) disable iff (!rst_n)
result_valid && (cheapest_lever == 2'd0));
cover property (@(posedge clk) disable iff (!rst_n)
result_valid && (cheapest_lever == 2'd2));P4 and P10 are the two worth keeping if the list had to shrink, and neither is about correctness in the usual sense. They are properties about the blocks' own honesty — that the accountant always declares what it omits, and that the checker never appears to have evaluated a constraint it does not evaluate. Both fail loudly for the single most damaging kind of edit: one that makes a deliberately incomplete model look complete.
14. Corner Cases
| Case | Behaviour | Why |
|---|---|---|
duty_num = 0 | All weight on idle | A memory that never moves data still costs — §1 |
duty_num = DUTY_DEN | All weight on active | Fully busy; the server-like extreme |
duty_num > DUTY_DEN | err_duty_out_of_range, idle weight zero | A wrapped complement would report a huge idle share |
active_power = idle_power | Duty cycle decides the split entirely | Degenerate and useful as a sanity check |
| Weighted halves exactly equal | idle_dominates low; the third cover fires | §3's crossover — the boundary of the comparison |
| Counters saturating | any_saturated; totals read low | The flattering direction, so it must be reported |
allowance = 0 | err_zero_allowance, cannot close | A zero budget is malformed, not merely tight |
| Budget closes with zero headroom | Closes; the seventh cover fires | Satisfies the margin exactly — not a design |
MARGIN_N256 = 0 | Closes whenever it merely fits | Legal, and §11's lever scaling then degenerates |
work_units = 0 | Requirement is the fixed overhead alone | §1's point: idle cost exists with no work at all |
cost_per_unit = 0 | Requirement is the overhead alone | Free data movement; a bound, not a configuration |
ACC_W <= 2 × UNIT_W | $fatal at elaboration | A truncated product understates the requirement |
Row ten is the one worth dwelling on, because it is the chapter in miniature. With zero work to do, the requirement is not zero — it is the fixed overhead, which is the idle cost of §1. A budget model that returned zero for an idle device would have missed the entire reason mobile memory is designed the way it is.
15. DV — Check the Model, Not the Physics
There is no physics here to check against, so the testbench's job is different from most in this curriculum: it verifies that the model's arithmetic matches an independent computation, and that the blocks' honesty outputs cannot be silenced.
// Independent reference. Computes the same quantities a DIFFERENT way:
// by accumulating per-tick contributions rather than by multiplying a
// duty fraction. Two routes to one answer, so agreement is evidence.
// SIMULATION-ONLY.
class mission_ref;
int unsigned duty_den;
longint unsigned acc_active, acc_idle;
int unsigned ticks;
// Walk time one unit at a time, charging each unit to active or idle
// in the stated proportion. The DUT multiplies; this counts.
function void run(int unsigned duty_num, int unsigned active_p,
int unsigned idle_p, int unsigned n);
acc_active = 0; acc_idle = 0; ticks = 0;
for (int unsigned i = 0; i < n; i++) begin
if ((i * duty_den) % n < duty_num * (n / duty_den == 0 ? 1 : 1))
acc_active += active_p;
else
acc_idle += idle_p;
ticks++;
end
endfunction
function longint unsigned average(); return acc_active + acc_idle; endfunction
function bit idle_bigger(); return acc_idle > acc_active; endfunction
endclass| Check | What it establishes |
|---|---|
Sweep duty_num across its range; weighted_active + weighted_idle == average_power at every point | P1 exhaustively over the input space |
| Reproduce §3's table: idle share at duty 10%, 5%, 2%, 1%, 0.5% | The published figures, from the RTL |
| Confirm the 2% case gives 49.5% idle share | The headline number |
| Confirm a 20% active improvement and a 20% idle improvement are within 0.3 points of each other at 2% duty | §3's equality claim |
Drive duty_num > DUTY_DEN; expect the error and zero idle weight | P5 |
Attempt to clear terms_excluded; expect P4 to fire | The honesty property is live |
Budget: drive required exactly equal to the margin-adjusted allowance | The boundary closes |
| Budget: drive one unit above it; expect a deficit of exactly one | Off-by-one at the comparison |
Budget: sweep MARGIN_N256; confirm lever_more_allowance >= deficit throughout | P9, over the parameter range |
Budget: confirm err_thermal_not_checked is high on every cycle of every test | P10 |
The fourth check is the one that produces a result worth reading rather than a pass:
WHERE THE LEVERAGE ACTUALLY SITS
illustrative model: active = 50, idle = 1, 10000 basis points.
duty idle share 20% off active 20% off idle better lever
----- ---------- -------------- ------------- ------------
1000 15.3% -17.0% -3.1% ACTIVE
500 27.5% -14.5% -5.5% ACTIVE
200 49.5% -10.1% -9.9% almost TIED
100 66.4% -6.7% -13.3% IDLE
50 79.9% -4.0% -16.0% IDLE
all DERIVED from the illustrative model and recomputed.
reading it : the lever REVERSES between 10% and 1% duty cycle, and
the crossover sits near 2%. Above it, work on the active path;
below it, work on idle.
what this does NOT say : which side a real product is on. The duty
cycle is a system property (§3), so the table tells you what to
measure, not what to conclude.
why it matters : a team that optimises the active path for a
device that idles at half a percent is spending effort where the
table says the return is a quarter of the alternative.That table is the most useful thing this chapter's verification produces, and it is honest about its own limits: it identifies which measurement decides the design, without pretending to know the measurement.
16. Debugging
| Symptom | Likely cause | How to confirm |
|---|---|---|
| Battery life worse than the model predicted | The model's duty cycle is wrong — §3 | Measure the real duty cycle before touching the memory |
| Active-path optimisation gained almost nothing | The device is idle-dominated — §15 | idle_dominates; check the idle share pair |
| Idle-path optimisation gained almost nothing | The device is active-dominated | Same outputs, opposite reading |
| Improvement in one quantity, regression overall | The three were treated as one — §2 | The block reports them separately for this reason |
| Model says the budget closes; hardware overheats | Thermal ceiling, not energy — §4 | err_thermal_not_checked is permanently high |
| Lower voltage part gave less benefit than expected | Voltage is one change of several — §8 | Check whether the I/O standard and features also changed |
| Idle power higher than the datasheet's figure | Something is preventing the idle state | 24.2 owns state entry |
| Small accesses cost far more per useful bit | Payload waste — §6 | 12.4 owns the measure; a narrow channel helps |
| Budget closes but only just | Zero headroom — §14 | A margin exists so that this is visible rather than fatal |
| Reported average power looks implausibly low | A counter saturated | any_saturated; saturation understates |
| A rail draws current the model did not expect | The model has no per-rail detail | terms_excluded bit 2; 24.5 owns rails |
| Two devices with identical rails behave differently | Rails are not the whole device — §8 | I/O standard, DLL presence, feature set all differ |
Row two and row three are the same diagnostic read in opposite directions, and together they are why the accountant reports the split rather than a total. “Our optimisation did nothing” is not a mystery once you know which half of the average you improved.
Row five is the one most likely to be mistaken for a modelling error. It is not — §4 established that energy and heat are independent constraints, and the checker says permanently that it evaluated only one of them.
17. Misconceptions
“Mobile memory is slow memory.” It is not. The fastest LPDDR parts run at rates a desktop part would accept. What differs is what the architecture optimises, not how fast it goes.
“LPDDR is DDR at lower voltage.” §8. The I/O standard differs in kind, the command interface is organised differently, a DLL is absent, and features exist that DDR4 does not have. Voltage is one change among several.
“Energy per bit and power are the same thing.” §2. One is an efficiency per unit of work; the other is a rate. A device can improve one and worsen the other.
“Optimise the active path — that is where the power goes.” §3. At 2% duty cycle the idle state contributes nearly half the average, and below 1% it contributes two thirds.
“Then optimise idle — that is where the power goes.” §15's table. The lever reverses with the duty cycle, and the duty cycle is a system property the memory cannot know.
“Idle means off.” §1's callout. An idle DRAM is powered and refreshed, because it forgets otherwise. Idle has a cost and that cost is the point.
“A bigger battery solves a power problem.” §5. It is one of three levers, and §11's block reports all three because the cheapest is often not the battery.
“If the energy budget closes, the design is feasible.” §4 and P10. The thermal ceiling is an independent constraint, and a closing budget says nothing about it.
“A wider channel is better.” §6. It raises peak bandwidth and wastes more on small accesses, which is most of mobile traffic. Narrow and independent suits the requester mix.
“LPDDR4X just lowers the voltage.” §7. It lowers two rails and removes a third entirely — the command/address supply. That is a structural change, not a voltage change.
“These voltage figures are LPDDR4 constants.” §7's callout. They are one named device's specified ranges, and another vendor's part may differ.
“You can compare these numbers with DDR4's.” §8's callout. Different standard, different rails, different signalling. This module never does it.
18. Interview Reasoning
Why does a phone need a different DRAM rather than a low-power setting? Because the constraints differ in kind: a fixed energy allowance, a thermal ceiling with no fan, a duty cycle that is mostly idle, and many small independent requesters. Those produce different choices about signalling, command interface, states and channel width — not a different setting on one device.
Name the three quantities that get confused and say why it matters. Energy per bit, idle power and peak bandwidth. They are measured differently and can move in opposite directions, so a mechanism has to say which one it serves or it has not been explained.
Where does a mobile device's memory power actually go? It depends on the duty cycle, which is a system property. At around 2% active time, roughly half the average is idle; below 1% it is two thirds. The answer is a measurement, not a principle.
A team cuts active power by a fifth and battery life barely moves. What happened? The device is probably idle-dominated. At low duty cycle, a fifth off the active path is worth a fraction of a fifth off idle, and the crossover in one illustrative model sits near 2%.
Why is idle power a cost at all — the memory is not doing anything. DRAM is volatile and must be refreshed to retain data, so an idle memory is still working at the minimum required to remain a memory. That minimum is paid for during the great majority of a phone's life.
What is the second constraint besides the battery? Heat flow. There is no fan, so sustained dissipation is capped by how fast heat leaves the case — and that limit can bind before the energy budget does. It also feeds back, because a hotter device must refresh more often.
Why do narrow channels suit mobile traffic? Because the traffic comes from many independent requesters each wanting modest amounts. A narrow channel has a smaller minimum transfer, so less is moved and discarded, and several narrow channels serve several requesters at once.
What does LPDDR4X change relative to LPDDR4? On a named device, it lowers the VDD2 and VDDQ ranges and removes the separate command/address rail altogether. Describing it only as a voltage reduction omits the structural half.
Can you compare an LPDDR voltage with a DDR4 voltage? No. They are different standards with different rails, different signalling and a different command interface. The comparison is not approximate; it is about different devices.
19. Exercises
-
Recompute §3's duty-cycle table for an active-to-idle ratio of 20 rather than 50. At what duty cycle does idle reach half the average, and which direction did the crossover move? Explain the direction from the arithmetic.
-
§15's table shows the better lever reversing between 10% and 1% duty. Find the exact duty cycle at which the two 20% improvements are equal, as a fraction, for a general active-to-idle ratio
r. -
lever_more_allowanceis scaled by the margin and the other two levers are not. Work out what the reported allowance increase would be if it were not scaled, for a margin of 10% and a deficit of 100 units, and state the size of the error. -
The accountant refuses to divide and publishes
idle_share_numandidle_share_den. Write the consumer that forms the share, and state what it must report alongside the number for the figure to mean anything. -
Set
duty_num = DUTY_DENandwork_units = 0simultaneously. Which outputs are contradictory, and which property, if any, catches it? Propose the additional check. -
§7's table shows
VDDCAabsent on LPDDR4X. Given §8's point that the command path must still be supplied, work out what must have happened to it, and say what you would look for in a datasheet to confirm your answer. -
err_thermal_not_checkedis permanently high. Argue whether a block should ever be allowed to assert a disclaimer it can never clear, and specify what would have to be added for clearing it to be honest. -
Using only this chapter's abstract accountant, design the experiment that would tell a product team whether to spend its next engineering month on the active path or the idle path. State what you would have to measure on the real device first, and why the block cannot supply it.
20. Where This Goes
The mission is now specific rather than atmospheric. A mobile device presents four constraints — an energy allowance, a thermal ceiling, a duty cycle that is mostly idle, and many small independent requesters — and those press on three quantities that are easy to confuse and must be kept apart.
Two results carry forward. The duty cycle decides where the leverage is, and it is a property of the system rather than of the memory, so the memory can only be designed to be good across a range of them. And lower voltage is one change among several: a named LPDDR4X part lowers two rails and deletes a third, uses a different I/O standard, omits a DLL, and adds features DDR4 does not have.
What this chapter has repeatedly pointed at and not built is the idle state itself. §1 established that an idle memory still costs; §3 showed that cost dominating the average; §4 showed temperature making it worse; and §7's feature list named two mechanisms — partial-array self refresh and temperature-compensated self refresh — without saying what they do.
Chapter 24.2 takes all of it up. It works from a real device's own state diagram, so the states are the device's rather than a textbook's, and it builds the machine that moves between them. It also reports a measurement that corrects a natural assumption: on a named LPDDR5X part, per-bank refresh draws exactly the same current as all-bank refresh on every supply rail — so what that mechanism buys is not energy, and the chapter says what it does buy instead.
Continue learning
Related tutorials
- Related topic
DDR vs LPDDR
The access contract is identical and all thirteen obligations are shared, so no structural argument separates these two. The deciding quantity is energy under the actual duty cycle and idle-duration distribution, not power and not bandwidth.
- Related topic
Mobile SoCs
A mobile platform has two ceilings and only one is a budget: retention falls with temperature, so refresh power rises with it and the memory closes a positive feedback loop on itself. The controller acquires a third kind of obligation — it must be throttleable, with authority over the term that closes the loop.
- Related topic
LPDDR Evolution
LPDDR is not low-power DDR. It is the same reasoning run against a different constraint set — no socket, a fixed battery, no airflow — which produces low-swing signalling, split command and data clocks, and power states expensive enough to reshape the controller.
- Related topic
Low-Power Features
A named device's own state diagram defines the states, and several transitions between them are illegal. Per-bank refresh draws identical current to all-bank refresh on every rail — it buys availability, not energy.
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.
