Skip to content
VLSI Mentor

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.

QuantityWhat it measuresUnits, informally
Energy per bitWhat it costs to move one bitenergy ÷ bits
Idle powerWhat the device costs while moving nothingpower
Peak bandwidthThe most it can move at oncebits ÷ 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   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 average

DERIVED 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   active :  2%  x  40 units  =  0.80
   idle   : 98%  x   1 unit   =  0.98
                                 ----
   total                         1.78     a 10.1% improvement

And instead improve the idle case by the same 20%:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   active :  2%  x  50 units  =  1.00
   idle   : 98%  x  0.8 units =  0.784
                                 -----
   total                         1.784    a 9.9% improvement

DERIVED, 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.

RailLPDDR4LPDDR4X
VDD11.70 – 1.95 V1.70 – 1.95 V
VDD21.14 – 1.30 V1.06 – 1.17 V
VDDQ1.14 – 1.30 V1.06 – 1.17 V
VDDCA1.14 – 1.30 Vabsent

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 mobile memory mission shown as a structure, in four rows. The top row holds the four things a mobile system imposes on its memory: a battery energy allowance, a thermal ceiling set by having no fan, a duty cycle that is mostly idle, and a mix of many small independent requesters. The second row holds the three quantities those constraints act on, kept deliberately separate: energy per bit, idle power, and peak bandwidth. The third row holds the four device-level responses this module covers: multiple supply rails at different voltages, a low-swing terminated input-output standard, low-power states the device can enter when idle, and narrow independent channels matched to the requester mix. The bottom row is the feasibility question the whole chapter builds toward, namely whether a stated energy allowance admits a stated amount of work. Edges run from each system constraint to the quantity it acts on, from each quantity to the responses that serve it, and from the responses into the feasibility question.Battery budgeta fixed allowance — §5No fanheat-flow ceiling — §4Duty cyclemostly idle — §3Many requesterseach wanting little — §6Energy per bitcost to move one bitIdle powercost of doing nothingPeak bandwidththe most it can moveMultiple rails1.8 / 1.1 / 1.1 V — §7Low-swing I/OLVSTL, VSSQ-terminatedLow-power states24.2 owns themNarrow channels24.3 owns themDoes it fit?the question — §512

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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// 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;
endmodule

Two 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// 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;
endmodule

lever_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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------
// 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;
endmodule

Three 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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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

CaseBehaviourWhy
duty_num = 0All weight on idleA memory that never moves data still costs — §1
duty_num = DUTY_DENAll weight on activeFully busy; the server-like extreme
duty_num > DUTY_DENerr_duty_out_of_range, idle weight zeroA wrapped complement would report a huge idle share
active_power = idle_powerDuty cycle decides the split entirelyDegenerate and useful as a sanity check
Weighted halves exactly equalidle_dominates low; the third cover fires§3's crossover — the boundary of the comparison
Counters saturatingany_saturated; totals read lowThe flattering direction, so it must be reported
allowance = 0err_zero_allowance, cannot closeA zero budget is malformed, not merely tight
Budget closes with zero headroomCloses; the seventh cover firesSatisfies the margin exactly — not a design
MARGIN_N256 = 0Closes whenever it merely fitsLegal, and §11's lever scaling then degenerates
work_units = 0Requirement is the fixed overhead alone§1's point: idle cost exists with no work at all
cost_per_unit = 0Requirement is the overhead aloneFree data movement; a bound, not a configuration
ACC_W <= 2 × UNIT_W$fatal at elaborationA 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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
CheckWhat it establishes
Sweep duty_num across its range; weighted_active + weighted_idle == average_power at every pointP1 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 shareThe 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 weightP5
Attempt to clear terms_excluded; expect P4 to fireThe honesty property is live
Budget: drive required exactly equal to the margin-adjusted allowanceThe boundary closes
Budget: drive one unit above it; expect a deficit of exactly oneOff-by-one at the comparison
Budget: sweep MARGIN_N256; confirm lever_more_allowance >= deficit throughoutP9, over the parameter range
Budget: confirm err_thermal_not_checked is high on every cycle of every testP10

The fourth check is the one that produces a result worth reading rather than a pass:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  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

SymptomLikely causeHow to confirm
Battery life worse than the model predictedThe model's duty cycle is wrong — §3Measure the real duty cycle before touching the memory
Active-path optimisation gained almost nothingThe device is idle-dominated — §15idle_dominates; check the idle share pair
Idle-path optimisation gained almost nothingThe device is active-dominatedSame outputs, opposite reading
Improvement in one quantity, regression overallThe three were treated as one — §2The block reports them separately for this reason
Model says the budget closes; hardware overheatsThermal ceiling, not energy — §4err_thermal_not_checked is permanently high
Lower voltage part gave less benefit than expectedVoltage is one change of several — §8Check whether the I/O standard and features also changed
Idle power higher than the datasheet's figureSomething is preventing the idle state24.2 owns state entry
Small accesses cost far more per useful bitPayload waste — §612.4 owns the measure; a narrow channel helps
Budget closes but only justZero headroom — §14A margin exists so that this is visible rather than fatal
Reported average power looks implausibly lowA counter saturatedany_saturated; saturation understates
A rail draws current the model did not expectThe model has no per-rail detailterms_excluded bit 2; 24.5 owns rails
Two devices with identical rails behave differentlyRails are not the whole device — §8I/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

  1. 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.

  2. §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.

  3. lever_more_allowance is 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.

  4. The accountant refuses to divide and publishes idle_share_num and idle_share_den. Write the consumer that forms the share, and state what it must report alongside the number for the figure to mean anything.

  5. Set duty_num = DUTY_DEN and work_units = 0 simultaneously. Which outputs are contradictory, and which property, if any, catches it? Propose the additional check.

  6. §7's table shows VDDCA absent 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.

  7. err_thermal_not_checked is 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.

  8. 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

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.