DDR · Module 24
Mobile-Power Management
LPDDR4X lowers two rails and deletes a third. Two of four LPDDR5X rails go unused in self refresh — so gating them saves nothing in the memory, and a bad shutdown order survives ten thousand clean power cycles.
Chapter 24.1 §7 argued that a mobile memory's supply rails tell you what its designers cared about, and then read nothing off them. This module has since verified a great deal: that LPDDR4X lowers two rails and deletes a third, that an LPDDR5X part's data rail sits at half a volt, and that two of its four rails go unused during power-down self refresh.
None of those facts has been assembled into how a system manages them. That is this chapter, and it closes the module.
The organising observation is that every one of these facts is an ordering problem before it is a power saving. A rail that can be gated must be gated in the right order. A voltage that can be lowered must be lowered in the right relationship to a frequency. A rail that was deleted took its ordering constraint with it — which is a saving of a kind that has nothing to do with volts.
1. The Rails a Named LPDDR4 Part Has
Start with the set, because the chapter's whole argument is about its structure rather than its values.
DEVICE-VERIFIED, from a named Samsung 16Gb LPDDR4 part — operating ranges, and the nominal values the datasheet states:
| Rail | Range | Nominal | What it feeds |
|---|---|---|---|
VDD1 | 1.70 – 1.95 V | 1.8 V | the array's high-voltage needs |
VDD2 | 1.14 – 1.30 V | 1.1 V | the device's core logic |
VDDQ | 1.14 – 1.30 V | 1.1 V | the data I/O |
VDDCA | 1.14 – 1.30 V | 1.1 V | the command/address I/O |
Four rails, at two distinct voltages. That structure is the first thing worth reading, and it says something specific.
VDD1 is nearly twice the others and cannot be lowered with them. A DRAM array needs a high voltage to drive a wordline hard enough to transfer charge reliably — that requirement is physical and does not scale with the logic process. So VDD1 is the rail that resists every improvement in this chapter, and §3 shows it resisting.
And three separate rails sit at the same nominal voltage. VDD2, VDDQ and VDDCA are all 1.1 V, which raises an obvious question: why three rails rather than one? The answer is that they are separated for noise, not for voltage. I/O switching injects noise onto its supply, and keeping the core logic's rail separate from the I/O's keeps that noise out of the logic. Separation costs pins and package complexity, and it buys isolation.
That framing is what makes §2's finding legible. If three rails at one voltage exist for isolation rather than for their values, then deleting one is a statement about isolation — and §4 is about exactly that.
2. LPDDR4X Does Two Things, Not One
“LPDDR4X lowers the I/O voltage” is the standard summary. Comparing the two datasheets rail by rail shows it is half the story.
DEVICE-VERIFIED, both columns from the same manufacturer's documentation of the two variants:
| Rail | LPDDR4 | LPDDR4X | Change |
|---|---|---|---|
VDD1 | 1.70 – 1.95 V | 1.70 – 1.95 V | unchanged |
VDD2 | 1.14 – 1.30 V | 1.06 – 1.17 V | lowered |
VDDQ | 1.14 – 1.30 V | 1.06 – 1.17 V | lowered |
VDDCA | 1.14 – 1.30 V | absent | rail deleted |
| Rail count | 4 | 3 |
Three findings, and the third is the one the summary omits.
VDD1 is untouched. The array's high rail is exactly the same in both variants — which confirms §1's reading that it is a physical requirement rather than a design choice. Whatever LPDDR4X improved, it did not improve the array's voltage need.
Two rails came down by the same proportion. Both moved from 1.14–1.30 V to 1.06–1.17 V, a 10.0% reduction at the top of the range and 7.0% at the bottom. DERIVED, recomputed.
And VDDCA was removed entirely. The command/address I/O no longer has its own rail on the X variant. That is not a voltage reduction; it is a structural change, and §4 is about what it means.
3. What Lowering a Voltage Buys
The 10% figure needs converting into something meaningful, and the conversion requires a model that must be stated rather than assumed.
The model: dynamic power in CMOS logic scales with the square of the supply voltage. DERIVED under that model, recomputed at both ends of the range:
| Comparison | Ratio | Dynamic-power change |
|---|---|---|
VDD2 at range max, 1.17 / 1.30 | 0.8100 | −19.0% |
VDD2 at range min, 1.06 / 1.14 | 0.8646 | −13.5% |
VDDQ, identical ranges | same | −19.0% / −13.5% |
A 10% voltage reduction buys roughly a 19% dynamic-power reduction, at the top of the range, on the two rails that moved.
4. Deleting a Rail Is a Different Kind of Saving
VDDCA's removal does not fit the V² frame at all, and that is what makes it interesting.
§1 established that VDD2, VDDQ and VDDCA were separate rails at the same nominal voltage, kept apart for noise isolation. Removing VDDCA means the command/address I/O is now supplied from another rail — its isolation was given up.
So the saving is not in volts. It is in the rail itself. Four things go away with a rail, and none of them is a voltage:
| What a rail costs | What deleting it saves |
|---|---|
| Package pins and balls | pins, which a mobile package is extremely short of |
| A regulator output | a PMIC channel, its inductor, its capacitors, its board area |
| A sequencing constraint | one fewer ordering relationship — §10 |
| A monitoring obligation | one fewer rail to supervise |
The third row is this chapter's concern and the least obvious. §10 establishes that a multi-rail device must bring its rails up in a defined order. Three rails have fewer valid orderings to get right than four, and one fewer constraint in a sequence that must be correct every time the device powers up.
5. LPDDR5X and a Half-Volt Data Rail
The next generation's rail set is different again, and one value is dramatic.
DEVICE-VERIFIED, from a named Micron LPDDR5X part:
| Rail | Note |
|---|---|
VDD1 | the array's high rail, still present |
VDD2H | a high core rail |
VDD2L | a low core rail |
VDDQ | 0.50 V / 0.45 V TYP — 0.30 V permitted only with termination off |
Two structural changes from §2's three-rail LPDDR4X set.
The core rail split into two. VDD2H and VDD2L replace a single VDD2. That is the opposite direction from §4's deletion — LPDDR4X removed a rail and LPDDR5X added one back, in a different place.
And VDDQ is half a volt. Against LPDDR4's 1.14–1.30 V, DERIVED under §3's stated V² model, recomputed:
| Comparison | Ratio² | Dynamic-power change |
|---|---|---|
| 0.50 / 1.30 | 0.1479 | −85.2% |
| 0.50 / 1.14 | 0.1924 | −80.8% |
| 0.45 / 1.14 | 0.1558 | −84.4% |
Between roughly 80% and 85% less dynamic I/O power, under the same model and with every caveat of §3's callout still attached — including that these are two different manufacturers' parts of two different generations, which is a comparison across more variables than voltage alone.
Why the core rail split is the more interesting change, though, is that it answers a question §1 raised. If separate rails exist for isolation rather than for voltage, splitting a core rail into high and low halves means different parts of the core want different voltages — some logic can run at a lower supply than the rest. That is a finer-grained version of exactly the trade §3 describes, applied inside the device instead of across its interface.
And it has a consequence §7 makes concrete: more rails means more of them can be gated independently.
6. Rails Are Not Interchangeable
Before sequencing, one point that every subsequent section depends on.
A rail is not a quantity of power. It is a voltage supplied to a specific set of circuits with a specific requirement, and the rails in §1 and §5 are not substitutable in any direction:
| Can it be lowered? | Can it be gated? | Why | |
|---|---|---|---|
VDD1 | no — unchanged across variants (§2) | not while data must survive | the array's charge transfer is physical |
VDD2 / VDD2H | modestly — 10% on the X variant | not during self refresh (§7) | the device must still refresh itself |
VDD2L | — | yes in power-down self refresh (§7) | verified unused there |
VDDQ | dramatically — to 0.50 V (§5) | yes in power-down self refresh (§7) | no I/O happens while idle |
The pattern is that a rail's flexibility is inversely related to how close it sits to the stored charge. VDDQ supplies I/O, which stops entirely when the device is idle, so it can be both lowered dramatically and gated. VDD1 supplies the array, which must keep working for the data to exist, so it can be neither.
So “reduce the memory's voltage” is not an instruction that can be carried out. Each rail has its own answer, and 24.1 §8's claim that lower voltage is not the whole answer is, at the rail level, the observation that the rail with the most headroom supplies the least of the device's idle cost.
7. Two of Four Rails Go Unused
Here is the verified finding this chapter is built around, and it is a specific and actionable one.
DEVICE-VERIFIED: in the named LPDDR5X part's power-down self refresh current specification, VDD2L and VDDQ are not used. The condition draws current on VDD1 and VDD2H only.
| Rail | Power-down self refresh |
|---|---|
VDD1 | in use — the array must be refreshed |
VDD2H | in use — the self-refresh machinery runs on it |
VDD2L | unused |
VDDQ | unused |
Two of four rails can be gated in that state — not merely lowered, removed. And it follows directly from what the state is: 24.2 §4 established that in self refresh the device refreshes itself and the controller has no obligation, so nothing is being transferred and the I/O rail has no work. The low core rail evidently supplies logic that is likewise idle.
DEVICE-VERIFIED, the currents on the two rails that remain, at two temperatures — the figures 24.2 §7 established and this chapter now attributes per rail:
| Rail | 25 °C | 95 °C | Ratio |
|---|---|---|---|
VDD1 | 0.25 mA | 3.70 mA | 14.8× |
VDD2H | 0.45 mA | 12.00 mA | 26.7× |
| Sum | 0.70 mA | 15.70 mA | 22.4× |
All ratios DERIVED and recomputed. And the per-rail split adds something 24.2 §7 could not say: VDD2H is both the larger contributor and the faster-growing one. At 25 °C it is 64% of the total; at 95 °C, 76%. So the temperature problem is concentrated on the core rail rather than spread evenly — which matters, because it is the rail that cannot be gated while self refresh is running.
8. The Rail Count Is a PMIC Problem
§4 listed a regulator output among the things a deleted rail saves and moved on. It is worth returning to, because the component on the other side of these rails is where most of §4's saving actually lands.
A multi-rail device is supplied by a power-management IC — a part whose job is to take one battery voltage and produce several regulated rails at once. Every rail in §1's and §5's tables is a channel of such a part, and a channel is not free:
| What a channel needs | Why it costs |
|---|---|
| A regulator stage | silicon in the PMIC, and a pin out of it |
| External passives | an inductor or capacitors, occupying board area near the load |
| A control loop | its own compensation, its own stability requirement |
| Quiescent current | the channel draws to stay regulated even when the load draws nothing |
| A sequencing slot | §12's ordering must be implemented somewhere, and it is here |
The fourth row is the one that matters for §9, and it is the row most easily forgotten. A regulator channel is not a switch — it is a feedback loop that consumes current to hold its output steady, and it does so whether or not anything is drawing from it.
So §4's deleted rail is a deleted channel. The command/address rail's removal took a regulator stage, its passives, its board area, its control loop, its quiescent draw and its sequencing slot with it. On a phone, board area near the application processor is among the scarcest resources there is, which is why a rail deletion is a larger win in this context than the same deletion would be on a desktop part.
And the sequencing lives in the PMIC, not in the memory. §12's ordering discipline is implemented by whatever brings the rails up — the PMIC's own sequencer, or a controller driving its enables. §11's block is a model of that logic and not of anything inside the DRAM, which is worth being clear about: the memory does not sequence its own supplies. It has requirements, and something else has to meet them.
9. What Gating an Unused Rail Actually Saves
Here is the correction §7 set up without noticing, and it changes what rail gating is for.
§7 established, DEVICE-VERIFIED, that VDD2L and VDDQ are unused during power-down self refresh. Read that carefully: the device draws essentially nothing on them in that state already, without anything being gated.
So gating those two rails saves nothing in the device. The current that gating would eliminate is current the device was not drawing.
That is not a reason gating is pointless — it is a reason the saving is somewhere else:
| Where the saving is | Why |
|---|---|
| Not in the DRAM | §7 verified it draws nothing on those rails in that state |
| In the regulator channel | §8's fourth row — quiescent current flows regardless of load |
| Possibly in leakage | a rail at voltage across an idle circuit still leaks, weakly |
The dominant term is the regulator's own overhead, and §8's callout explains why this chapter cannot put a number on it.
This reframes the mechanism entirely. “Two of four rails can be gated” sounds like it removes half the device's idle draw and does not — §7's current table shows the remaining two rails carry all of it. What gating removes is the cost of keeping two regulator channels alive to supply circuits that are doing nothing, which is a system saving rather than a memory saving.
Now the break-even, in 24.2 §6's form. For a saving rate s and an exit cost e, the break-even idle duration is e / s. DERIVED, with ILLUSTRATIVE inputs:
Regulator overhead s | Sequenced exit cost e | Break-even d* |
|---|---|---|
| 1 | 40 | 40.0 ticks |
| 4 | 40 | 10.0 |
| 1 | 10 | 10.0 |
| 4 | 10 | 2.5 ticks |
A factor of four in each input moves the break-even by a factor of four, and the two extremes differ by sixteen. So whether rail gating is worth doing at all is decided entirely by two numbers this chapter does not have — which is why §14's controller reports the obligation and does not make the choice.
And the exit cost has a structural component worth isolating. §14 reports the obligation as both a sum and a maximum, and §8's ordering requirement means the sum applies. For the two verified gateable rails with ILLUSTRATIVE exit costs of 25 and 9:
simultaneous bring-up would cost max(25, 9) = 25
sequenced bring-up costs 25 + 9 = 34
the ordering constraint costs 9 ( 1.36x )A system that budgeted the maximum would underestimate its wake-up latency by 36%. With four rails and ILLUSTRATIVE costs of 12, 18, 25 and 9, the sum is 64 against a maximum of 25 — an underestimate of 61% of the true cost.
So §12's ordering discipline is not free, and its price is exactly this gap. Rails must be sequenced for correctness, sequencing makes exit costs add rather than overlap, and adding rather than overlapping lengthens every break-even in the table above. That is the honest accounting: the correctness requirement of §12 makes the saving of §9 harder to earn.
10. Sequencing Is an Ordering Problem
A device with three or four rails cannot have them applied in an arbitrary order, and this is where the chapter's organising claim earns itself.
The constraint's general form is that a circuit must not be driven by a rail that is up while a rail it depends on is down. An I/O driver supplied from VDDQ whose control logic runs on VDD2 must not have VDDQ present while VDD2 is absent — the driver's inputs are undefined, and it may drive an arbitrary level onto a bus, or conduct in a way it was never designed to.
That gives a natural ordering, and it reverses on the way down:
bring-up : VDD1 -> VDD2 (core) -> VDDQ (I/O)
supply the array, then the logic, then the drivers
shutdown : VDDQ -> VDD2 -> VDD1
exactly the reverse: remove the drivers firstThe reversal is the part most easily got wrong, and it is wrong in a way that does not show up at power-up. A sequencer that brings rails up correctly and takes them down in the same order removes the array's supply while the I/O is still powered — and the device passes every power-up test.
And §4's finding now has a concrete meaning. Four rails have more ordering relationships to respect than three. Deleting VDDCA removed one rail from this sequence permanently, which is a correctness simplification on every power cycle for the life of the device — and that is a form of saving no voltage arithmetic captures.
11. Changing Voltage and Frequency Together
The last ordering problem, and the one that belongs to the system rather than to the device.
A device running slower can run at a lower voltage; a device running faster needs a higher one. Changing both is two operations, and the order between them is not symmetric.
going FASTER : raise voltage -> then raise frequency
going SLOWER : lower frequency -> then lower voltageBoth orders have the same rule underneath: never run fast at a low voltage. The forbidden state is high frequency with low supply, because that is the combination where the logic cannot meet its timing — and the safe ordering is whichever one never passes through it.
The intermediate state is the whole point. Going faster, the intermediate state is high voltage, low frequency — wasteful for a moment, and safe. Going slower, it is low frequency, high voltage — the same wasteful, safe combination. In both directions the transition passes through the inefficient state and never through the unsafe one, and a controller that reverses either order passes through the unsafe one instead.
That is the same shape as §10's sequencing and as 24.4 §15's reconfiguration protocol: a change is safe in one order and unsafe in the other, and the safe order costs a moment of inefficiency. Three separate mechanisms in this module have that structure, which is why §16's guard is worth building even though the rule fits in one line.
12. The Power-Management Architecture
Two things the structure shows that the sections do not.
The two rails that feed the sequencer are exactly the two that cannot be gated. VDD1 and VDD2H connect to ordering, not to gating, because §7 verified they are in use during the deepest state the device offers. The rails a system can switch off and the rails it must sequence carefully are almost disjoint sets — and that is a useful division, because it means the hard correctness problem and the large saving live in different places.
And all three disciplines converge on idle power alone. None of them improves energy per bit or peak bandwidth — 24.1 §2's other two quantities are untouched by everything in this chapter. This is the third time in the module that a whole chapter's mechanisms serve exactly one of the three quantities, which is 24.1 §3's duty-cycle argument vindicated: on a device that is idle 98% of the time, the idle column is where the engineering goes.
13. The Rail Sequencer
// ---------------------------------------------------------------------
// rail_sequencer -- brings N rails up in a supplied order and down in
// the exact reverse, and reports every forbidden ordering.
//
// CLASSIFICATION: educational, synthesisable.
//
// THE ORDER IS A PARAMETER, NOT A CONSTANT. §10's callout: the
// specific sequence and its intervals are per-device, published in
// each datasheet's initialisation section. This block does not
// reproduce any device's sequence. A block with a hard-coded order
// would be wrong for every part it was not written against; one that
// enforces a supplied order is correct for all of them.
//
// WHAT IT DOES NOT MODEL:
// - any regulator, voltage, slew rate or settling time
// - any device's real initialisation intervals
// - what a device actually does when sequenced wrongly, which is
// not defined to be a clean refusal
//
// WHY SHUTDOWN IS THE DANGEROUS DIRECTION: §10. A sequencer that goes
// up correctly and down in the SAME order passes every power-up test
// and removes the array's supply while the I/O is still driven.
// ---------------------------------------------------------------------
module rail_sequencer #(
parameter int RAILS = 4,
parameter int IDX_W = (RAILS <= 1) ? 1 : $clog2(RAILS),
// A COUNT of rails up, 0..RAILS, needs clog2(RAILS+1).
parameter int CNT_W = $clog2(RAILS + 1),
parameter int TICK_W = 16
) (
input logic clk,
input logic rst_n,
// ── Direction request.
input logic req_up,
input logic req_down,
// ── Per-rail status from the supply, one bit each: the rail is up
// and settled. Settling itself is a regulator property this
// block does not model (§13's header).
input logic [RAILS-1:0] rail_good,
// ── Per-rail enable, driven in the required order.
output logic [RAILS-1:0] rail_enable,
// ── Progress.
output logic [CNT_W-1:0] rails_up,
output logic all_up,
output logic all_down,
output logic sequencing,
output logic [IDX_W-1:0] step,
output logic [TICK_W-1:0] cy_in_step,
// ── Forbidden orderings, reported by kind. §17 names this the
// highest-value property class in the chapter.
output logic err_out_of_order_up,
output logic err_out_of_order_down,
output logic err_down_same_order_as_up,
output logic err_dependent_without_base,
output logic err_both_directions,
output logic any_violation
);
localparam logic [1:0] S_DOWN = 2'd0;
localparam logic [1:0] S_UP = 2'd1;
localparam logic [1:0] S_RISE = 2'd2;
localparam logic [1:0] S_FALL = 2'd3;
initial begin
if (RAILS < 2)
$fatal(1, "rail_sequencer: RAILS must be at least 2 to have an order");
if (CNT_W <= IDX_W && RAILS > 1)
// A count up to RAILS needs one more bit than an index into it
// when RAILS is a power of two. Getting this wrong makes all_up
// unreachable, which is the silent form of the bug.
$display("rail_sequencer: NOTE -- CNT_W=%0d IDX_W=%0d; verify all_up is reachable",
CNT_W, IDX_W);
end
logic [1:0] st;
logic [IDX_W-1:0] k;
logic [RAILS-1:0] en;
logic [TICK_W-1:0] tck;
logic e_up, e_dn, e_same, e_dep, e_both;
// Rail k is the k-th in the supplied order. Index 0 is first up and
// therefore LAST down. Declared above the assigns that read them.
logic [CNT_W-1:0] n_up;
always_comb begin
n_up = '0;
for (int i = 0; i < RAILS; i++)
if (rail_good[i]) n_up = n_up + CNT_W'(1);
end
// A dependent rail is up while a rail earlier in the order is not.
// This is §10's constraint in its most direct form.
logic dep_bad;
always_comb begin
dep_bad = 1'b0;
for (int i = 1; i < RAILS; i++)
for (int j = 0; j < i; j++)
if (rail_good[i] && !rail_good[j]) dep_bad = 1'b1;
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
st <= S_DOWN; k <= '0; en <= '0; tck <= '0;
e_up <= 1'b0; e_dn <= 1'b0; e_same <= 1'b0;
e_dep <= 1'b0; e_both <= 1'b0;
end else begin
e_up <= 1'b0; e_dn <= 1'b0; e_same <= 1'b0;
e_dep <= 1'b0; e_both <= 1'b0;
// §10's constraint, checked every cycle regardless of state --
// a dependent rail up without its base is a violation whether
// or not this block caused it.
if (dep_bad) e_dep <= 1'b1;
if (req_up && req_down) begin
// Both directions at once has no safe interpretation, so it
// is refused rather than resolved by priority.
e_both <= 1'b1;
end else begin
unique case (st)
S_DOWN : if (req_up) begin
st <= S_RISE; k <= '0; tck <= '0;
en <= '0;
end
S_RISE : begin
if (tck != {TICK_W{1'b1}}) tck <= tck + TICK_W'(1);
// Enable rail k, then wait for its own good signal before
// enabling the next. Enabling two at once would abandon
// the ordering the block exists to enforce.
en[k] <= 1'b1;
if (rail_good[k]) begin
if (k == IDX_W'(RAILS - 1)) begin
st <= S_UP; tck <= '0;
end else begin
k <= k + IDX_W'(1); tck <= '0;
end
end
// A later rail good before the current one is out of order.
if ((k < IDX_W'(RAILS - 1)) && rail_good[k + 1] && !rail_good[k])
e_up <= 1'b1;
end
S_UP : if (req_down) begin
// §10: shutdown starts from the LAST rail in the order,
// not the first. This single line is the reversal, and
// getting it wrong passes every power-up test.
st <= S_FALL; k <= IDX_W'(RAILS - 1); tck <= '0;
end
S_FALL : begin
if (tck != {TICK_W{1'b1}}) tck <= tck + TICK_W'(1);
en[k] <= 1'b0;
if (!rail_good[k]) begin
if (k == '0) begin
st <= S_DOWN; tck <= '0;
end else begin
k <= k - IDX_W'(1); tck <= '0;
end
end
// An earlier rail down before the current one means the
// shutdown is running in the UP order -- §10's failure.
if ((k > '0) && !rail_good[k - 1] && rail_good[k]) begin
e_dn <= 1'b1;
e_same <= 1'b1;
end
end
endcase
end
end
end
assign rail_enable = en;
assign rails_up = n_up;
assign all_up = (n_up == CNT_W'(RAILS));
assign all_down = (n_up == '0);
assign sequencing = (st == S_RISE) || (st == S_FALL);
assign step = k;
assign cy_in_step = tck;
assign err_out_of_order_up = e_up;
assign err_out_of_order_down = e_dn;
assign err_down_same_order_as_up = e_same;
assign err_dependent_without_base = e_dep;
assign err_both_directions = e_both;
assign any_violation = e_up | e_dn | e_same | e_dep | e_both;
endmoduleFour decisions in that block are §10's argument rather than implementation detail.
The dependent-rail check runs every cycle, in every state. dep_bad is evaluated unconditionally rather than only while sequencing, because §10's constraint is a property of the rail state and not of this block's activity. A rail brought up by something else — a bootloader, a test fixture, a second controller — violates the constraint just as surely, and a checker that only watched its own transitions would miss exactly the cases it was not responsible for.
Rails are enabled one at a time and the next waits on the previous rail's own good signal. Enabling two together would be faster and would abandon the ordering the block exists to enforce. Waiting on rail_good rather than on a fixed delay is the same discipline: a settling time is a regulator property this block does not model, so it waits for the regulator to say so instead of assuming how long it takes.
Both directions at once is refused rather than resolved by priority. A sequencer that picked req_up when both were asserted would be choosing on the controller's behalf in a situation the controller clearly did not intend. There is no safe interpretation to pick, so the block reports and does nothing — the same choice 24.4 §15's guard makes about two simultaneous dimension changes.
And the count-versus-index widths carry an elaboration note rather than a $fatal. IDX_W indexes into RAILS and CNT_W counts up to it, which differ by a bit when RAILS is a power of two. Getting it wrong does not produce a wrong answer — it makes all_up unreachable, so the sequencer simply never reports completion. That is the silent form of the bug, which is why P6 asserts the relationship rather than trusting the parameterisation.
14. The Hazard the Reversal Creates
One line of §13 carries §10's whole argument:
S_UP : if (req_down) begin
st <= S_FALL; k <= IDX_W'(RAILS - 1); // start from the LAST railWrite k <= '0 there instead and the sequencer takes the rails down in the same order it brought them up.
What makes it a hazard rather than a bug is the test it survives. Consider what a power-up-only test sees:
| Test | Correct sequencer | Same-order sequencer |
|---|---|---|
| Power up, check all rails good | pass | pass |
| Power up, read and write | pass | pass |
| Power cycle, check it comes back | pass | pass |
| Power down, check the order | pass | fail |
Three of four pass. And the fourth test is the one least likely to exist, because a shutdown is the moment a system stops being observed — the device is going away, the test is over, and nothing reads a rail order on the way out.
What the bug actually does is remove VDD1 while VDDQ is still powered. §10 established the consequence: I/O drivers supplied and undriven, with undefined inputs, potentially driving an arbitrary level onto a shared bus. Whether that damages anything depends on the board, which is why the symptom is a field failure on some designs and not others — the worst possible distribution for finding a bug.
And §4's rail deletion reduces the exposure. Three rails have fewer ordering relationships than four, and one fewer chance for this class of mistake on every power cycle for the device's life. That is what §4 meant by a saving that is not a voltage.
15. The Gating Controller
// ---------------------------------------------------------------------
// rail_gating_controller -- which rails are gated, whether they may
// be, and what bringing them back will cost.
//
// CLASSIFICATION: educational, synthesisable.
//
// WHY THE GATEABLE SET IS AN INPUT: §7 verified that VDD2L and VDDQ
// are unused during power-down self refresh ON A NAMED PART IN THAT
// STATE. Hard-coding it would assert that finding about every device
// and every state, which the evidence does not support.
//
// WHY PER-RAIL EXIT COSTS: §7's callout. VDDQ must be up before any
// I/O; VDD2L before whatever logic it feeds. There is no reason to
// expect them equal, so a single "rails off" state would hide the
// difference that decides whether gating is worth it.
//
// WHAT IT DOES NOT MODEL: any regulator, real bring-up time, current,
// or the device. Exit costs are abstract ticks supplied by the caller
// -- the same discipline as 24.2 §6, and for the same reason: the
// datasheets publish latency and current, not the energy of a
// transition.
// ---------------------------------------------------------------------
module rail_gating_controller #(
parameter int RAILS = 4,
parameter int COST_W = 12,
parameter int ACC_W = COST_W + 6,
parameter int CNT_W = 20
) (
input logic clk,
input logic rst_n,
// ── Which rails MAY be gated in the state the device is in now.
input logic [RAILS-1:0] gateable_mask,
// 24.2 §4: in self refresh the DEVICE refreshes itself. Gating is
// only meaningful there, so the state is an input rather than
// something this block infers.
input logic in_self_refresh,
// ── Request.
input logic req_gate,
input logic [RAILS-1:0] req_mask,
input logic req_ungate,
// ── Per-rail exit cost in abstract ticks, caller supplied.
input logic [COST_W-1:0] exit_cost [RAILS],
// ── State.
output logic [RAILS-1:0] gated,
output logic [CNT_W-1:0] cy_gated,
output logic [CNT_W-1:0] cnt_gate_events,
// The obligation incurred: the cost of bringing back everything
// currently gated. Reported as the SUM and as the MAX, because
// rails brought up in sequence cost the sum and rails brought up
// together cost the max -- and §10 says they must be sequenced.
output logic [ACC_W-1:0] exit_obligation_serial,
output logic [COST_W-1:0] exit_obligation_worst,
output logic err_gate_unpermitted,
output logic err_gate_while_active,
output logic err_ungate_nothing_gated,
output logic any_violation
);
initial begin
if (RAILS < 2)
$fatal(1, "rail_gating_controller: RAILS must be at least 2");
if (ACC_W < COST_W + $clog2(RAILS))
$fatal(1, "rail_gating_controller: ACC_W too narrow to sum RAILS costs");
end
logic [RAILS-1:0] g;
logic [CNT_W-1:0] cy, nev;
logic e_perm, e_act, e_none;
// Declared above the assigns that read them.
logic [ACC_W-1:0] sum_cost;
logic [COST_W-1:0] max_cost;
always_comb begin
sum_cost = '0;
max_cost = '0;
for (int i = 0; i < RAILS; i++)
if (g[i]) begin
sum_cost = sum_cost + ACC_W'(exit_cost[i]);
if (exit_cost[i] > max_cost) max_cost = exit_cost[i];
end
end
// A request is permitted only if every rail in it is gateable.
logic req_ok;
assign req_ok = ((req_mask & ~gateable_mask) == '0) && (req_mask != '0);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
g <= '0; cy <= '0; nev <= '0;
e_perm <= 1'b0; e_act <= 1'b0; e_none <= 1'b0;
end else begin
e_perm <= 1'b0; e_act <= 1'b0; e_none <= 1'b0;
if (g != '0) begin
if (cy != {CNT_W{1'b1}}) cy <= cy + CNT_W'(1);
end
if (req_gate) begin
if (!in_self_refresh) begin
// Gating a rail the device is using is not a power saving,
// it is a fault. Reported and refused.
e_act <= 1'b1;
end else if (!req_ok) begin
// A rail outside the permitted set. NOT masked down to the
// permitted subset -- silently gating fewer rails than
// asked would leave the caller's exit accounting wrong.
e_perm <= 1'b1;
end else begin
g <= g | req_mask;
if (nev != {CNT_W{1'b1}}) nev <= nev + CNT_W'(1);
end
end
if (req_ungate) begin
if (g == '0) e_none <= 1'b1;
else g <= '0;
end
end
end
assign gated = g;
assign cy_gated = cy;
assign cnt_gate_events = nev;
assign exit_obligation_serial = sum_cost;
assign exit_obligation_worst = max_cost;
assign err_gate_unpermitted = e_perm;
assign err_gate_while_active = e_act;
assign err_ungate_nothing_gated = e_none;
assign any_violation = e_perm | e_act | e_none;
endmoduleThe exit obligation is reported as both a sum and a maximum, and the sum is the one that applies. Rails brought up simultaneously would cost the maximum; §10 establishes they must be sequenced, so the real obligation is the sum. Both are published because the gap between them is the price of the ordering constraint — and a system that assumed the maximum would underestimate its own wake-up latency by exactly that gap.
16. The DVFS Ordering Guard
// ---------------------------------------------------------------------
// dvfs_order_guard -- enforces §11: raise voltage before frequency,
// lower frequency before voltage, and never occupy the forbidden
// combination of high frequency at low supply.
//
// CLASSIFICATION: educational, synthesisable.
//
// WHAT IT DOES NOT MODEL: volts, hertz, timing closure, any regulator
// or PLL. Voltage and frequency are ORDERED LEVELS, and which pairs
// are compatible is an INPUT (min_v_for_f) because that is a timing
// closure result. 19.1 §5 classifies the machinery as outside
// portable RTL.
//
// THE SHAPE IS THE SAME AS §10 AND 24.4 §15: a change is safe in one
// order and unsafe in the other, and the safe order costs a moment of
// inefficiency rather than a moment of risk.
// ---------------------------------------------------------------------
module dvfs_order_guard #(
parameter int LEVELS = 4,
parameter int LV_W = (LEVELS <= 1) ? 1 : $clog2(LEVELS),
parameter int CNT_W = 20
) (
input logic clk,
input logic rst_n,
// ── The compatibility table: the minimum voltage level required
// for each frequency level. An INPUT, per §16's header.
input logic [LV_W-1:0] min_v_for_f [LEVELS],
// ── Current operating point.
input logic [LV_W-1:0] v_level,
input logic [LV_W-1:0] f_level,
// ── A requested step, one dimension at a time.
input logic step_v,
input logic step_f,
input logic [LV_W-1:0] step_target,
// ── Verdict.
output logic step_grant,
output logic at_safe_point,
output logic in_inefficient_point,
output logic [CNT_W-1:0] cy_inefficient,
output logic [CNT_W-1:0] cnt_granted,
output logic [CNT_W-1:0] cnt_refused,
// ── Forbidden combinations and orderings.
output logic err_unsafe_point, // fast at low supply
output logic err_f_raised_first,
output logic err_v_lowered_first,
output logic err_two_dims_at_once,
output logic err_step_not_adjacent,
output logic any_violation
);
initial begin
if (LEVELS < 2)
$fatal(1, "dvfs_order_guard: LEVELS must be at least 2");
end
logic [CNT_W-1:0] n_ok, n_no, cy_ineff;
logic e_uns, e_ff, e_vf, e_two, e_adj, grant;
// Declared above the assigns that read them.
logic safe_now, ineff_now;
assign safe_now = (v_level >= min_v_for_f[f_level]);
// Voltage above what the current frequency needs: wasteful, and the
// state every safe transition passes through (§11).
assign ineff_now = (v_level > min_v_for_f[f_level]);
// Would the requested step land somewhere safe?
logic next_safe;
always_comb begin
if (step_f) next_safe = (v_level >= min_v_for_f[step_target]);
else if (step_v) next_safe = (step_target >= min_v_for_f[f_level]);
else next_safe = safe_now;
end
// One level at a time, so no step can jump over an unsafe point.
logic adjacent;
always_comb begin
logic [LV_W-1:0] cur;
cur = step_f ? f_level : v_level;
adjacent = (step_target == cur + LV_W'(1)) ||
((cur != '0) && (step_target == cur - LV_W'(1)));
end
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_ok <= '0; n_no <= '0; cy_ineff <= '0; grant <= 1'b0;
e_uns <= 1'b0; e_ff <= 1'b0; e_vf <= 1'b0;
e_two <= 1'b0; e_adj <= 1'b0;
end else begin
grant <= 1'b0;
e_uns <= 1'b0; e_ff <= 1'b0; e_vf <= 1'b0;
e_two <= 1'b0; e_adj <= 1'b0;
// The forbidden point, checked every cycle whether or not this
// block put us there.
if (!safe_now) e_uns <= 1'b1;
if (ineff_now && (cy_ineff != {CNT_W{1'b1}}))
cy_ineff <= cy_ineff + CNT_W'(1);
if (step_v || step_f) begin
if (step_v && step_f) begin
e_two <= 1'b1;
end else if (!adjacent) begin
// A multi-level jump could pass over an unsafe point that
// neither endpoint reveals, so only adjacent steps are
// permitted.
e_adj <= 1'b1;
end else if (!next_safe) begin
// Name the violation by direction, so a controller learns
// WHICH rule it broke rather than that it broke one.
if (step_f && (step_target > f_level)) e_ff <= 1'b1;
if (step_v && (step_target < v_level)) e_vf <= 1'b1;
e_uns <= 1'b1;
end else begin
grant <= 1'b1;
end
if (!(step_v && step_f) && adjacent && next_safe) begin
if (n_ok != {CNT_W{1'b1}}) n_ok <= n_ok + CNT_W'(1);
end else begin
if (n_no != {CNT_W{1'b1}}) n_no <= n_no + CNT_W'(1);
end
end
end
end
assign step_grant = grant;
assign at_safe_point = safe_now;
assign in_inefficient_point = ineff_now;
assign cy_inefficient = cy_ineff;
assign cnt_granted = n_ok;
assign cnt_refused = n_no;
assign err_unsafe_point = e_uns;
assign err_f_raised_first = e_ff;
assign err_v_lowered_first = e_vf;
assign err_two_dims_at_once = e_two;
assign err_step_not_adjacent = e_adj;
assign any_violation = e_uns | e_ff | e_vf | e_two | e_adj;
endmodulecy_inefficient counts the state §11 says every safe transition must pass through. It is not an error and it is measured, because a controller that never occupies it is not being efficient — it is reversing the ordering. The inefficient state is the evidence that the guard is working, which is an unusual thing for a counter to mean and worth naming as such.
And only adjacent steps are granted. A jump from level 0 to level 3 might have safe endpoints and pass over an unsafe intermediate, which neither endpoint reveals. Forcing single steps makes every intermediate point checked.
17. What the Assertions Prove
// Bind unit note: P1-P7 reference rail_sequencer's internals, P8-P11
// rail_gating_controller's, P12-P16 dvfs_order_guard's.
// P1 -- FORBIDDEN. No rail is up while a rail earlier in the order is
// down. §10's constraint in its most direct form, and the property
// every other one in this group supports.
property p_no_dependent_without_base;
@(posedge clk) disable iff (!rst_n)
(rails_up != '0) |-> !err_dependent_without_base;
endproperty
assert property (p_no_dependent_without_base);
// P2 -- FORBIDDEN. Shutdown starts from the LAST rail in the order.
// §14: writing k <= 0 here passes every power-up test.
property p_shutdown_starts_at_last;
@(posedge clk) disable iff (!rst_n)
(all_up && req_down) |=> (step == IDX_W'(RAILS - 1));
endproperty
assert property (p_shutdown_starts_at_last);
// P3 -- FORBIDDEN. Shutdown never runs in the bring-up order.
property p_shutdown_never_same_order;
@(posedge clk) disable iff (!rst_n) !err_down_same_order_as_up;
endproperty
assert property (p_shutdown_never_same_order);
// P4 -- rails are enabled one at a time during bring-up. Enabling two
// together abandons the ordering the block exists to enforce.
property p_one_rail_per_step;
@(posedge clk) disable iff (!rst_n)
(sequencing && $past(sequencing,1))
|-> ($countones(rail_enable ^ $past(rail_enable,1)) <= 1);
endproperty
assert property (p_one_rail_per_step);
// P5 -- the step index is monotone within a direction. Rules out a
// restart that would re-enable a rail already brought up.
property p_step_monotone_rising;
@(posedge clk) disable iff (!rst_n)
((step != $past(step,1)) && sequencing && $past(sequencing,1)
&& !any_violation)
|-> ((step == $past(step,1) + IDX_W'(1)) ||
(step == $past(step,1) - IDX_W'(1)));
endproperty
assert property (p_step_monotone_rising);
// P6 -- all_up holds exactly when every rail is good. Guards the
// COUNT-vs-INDEX width trap the elaboration note warns about: a
// too-narrow count makes all_up unreachable, silently.
property p_all_up_iff_every_rail;
@(posedge clk) disable iff (!rst_n)
all_up == (rails_up == CNT_W'(RAILS));
endproperty
assert property (p_all_up_iff_every_rail);
// P7 -- both directions at once is refused, never resolved by
// priority. There is no safe interpretation to pick.
property p_both_directions_refused;
@(posedge clk) disable iff (!rst_n)
(req_up && req_down) |-> err_both_directions;
endproperty
assert property (p_both_directions_refused);
// P8 -- FORBIDDEN. A rail outside the permitted set is never gated.
// §7: the set is verified for one part in one state, so gating
// outside it has no evidence behind it at all.
property p_never_gate_unpermitted;
@(posedge clk) disable iff (!rst_n)
((gated & ~gateable_mask) == '0);
endproperty
assert property (p_never_gate_unpermitted);
// P9 -- FORBIDDEN. No rail is gated while the device is not in self
// refresh. Gating a rail in use is a fault, not a saving.
property p_never_gate_while_active;
@(posedge clk) disable iff (!rst_n)
(req_gate && !in_self_refresh) |=> (gated == $past(gated,1));
endproperty
assert property (p_never_gate_while_active);
// P10 -- an unpermitted request is refused ENTIRELY, not masked down
// to the permitted subset. A partial grant would leave the caller's
// exit accounting wrong while reporting success.
property p_unpermitted_request_not_partially_granted;
@(posedge clk) disable iff (!rst_n)
err_gate_unpermitted |-> (gated == $past(gated,1));
endproperty
assert property (p_unpermitted_request_not_partially_granted);
// P11 -- the serial obligation is never below the worst single rail,
// and is zero exactly when nothing is gated. §15: rails must be
// sequenced, so the sum is what applies.
property p_obligation_consistent;
@(posedge clk) disable iff (!rst_n)
((gated == '0) ? (exit_obligation_serial == '0)
: (exit_obligation_serial >= ACC_W'(exit_obligation_worst)));
endproperty
assert property (p_obligation_consistent);
// P12 -- FORBIDDEN. The unsafe point is never granted into. §11: high
// frequency at low supply is the one combination to avoid, and every
// ordering rule exists to route around it.
property p_never_grant_into_unsafe;
@(posedge clk) disable iff (!rst_n)
step_grant |-> (v_level >= min_v_for_f[f_level]);
endproperty
assert property (p_never_grant_into_unsafe);
// P13 -- FORBIDDEN. Frequency is never raised before voltage.
property p_f_never_raised_first;
@(posedge clk) disable iff (!rst_n)
(step_f && (step_target > f_level) &&
(v_level < min_v_for_f[step_target])) |-> !step_grant;
endproperty
assert property (p_f_never_raised_first);
// P14 -- FORBIDDEN. Voltage is never lowered before frequency.
property p_v_never_lowered_first;
@(posedge clk) disable iff (!rst_n)
(step_v && (step_target < v_level) &&
(step_target < min_v_for_f[f_level])) |-> !step_grant;
endproperty
assert property (p_v_never_lowered_first);
// P15 -- FORBIDDEN. Only adjacent steps are granted, so no step can
// jump over an unsafe intermediate point that neither endpoint shows.
property p_only_adjacent_steps;
@(posedge clk) disable iff (!rst_n)
step_grant |-> !err_step_not_adjacent;
endproperty
assert property (p_only_adjacent_steps);
// P16 -- every request is either granted or refused, never dropped.
property p_every_step_accounted;
@(posedge clk) disable iff (!rst_n)
(step_v || step_f) |=> ((cnt_granted + cnt_refused) ==
($past(cnt_granted,1) + $past(cnt_refused,1) + CNT_W'(1)));
endproperty
assert property (p_every_step_accounted);
// ── Cover.
// A full bring-up and a full shutdown, both completed.
cover property (@(posedge clk) disable iff (!rst_n) all_down ##[1:$] all_up);
cover property (@(posedge clk) disable iff (!rst_n) all_up ##[1:$] all_down);
// Each step index reached in BOTH directions -- a shutdown that never
// visits the low indices has not reversed.
cover property (@(posedge clk) disable iff (!rst_n)
sequencing && (step == '0));
cover property (@(posedge clk) disable iff (!rst_n)
sequencing && (step == IDX_W'(RAILS - 1)));
// Each forbidden ordering ATTEMPTED. A property forbidding something
// is vacuous until the testbench tries it.
cover property (@(posedge clk) disable iff (!rst_n) err_out_of_order_up);
cover property (@(posedge clk) disable iff (!rst_n) err_down_same_order_as_up);
cover property (@(posedge clk) disable iff (!rst_n) err_dependent_without_base);
cover property (@(posedge clk) disable iff (!rst_n) err_both_directions);
// Gating: the §7 case actually exercised -- two rails gated at once.
cover property (@(posedge clk) disable iff (!rst_n) $countones(gated) == 2);
cover property (@(posedge clk) disable iff (!rst_n) err_gate_while_active);
cover property (@(posedge clk) disable iff (!rst_n) err_gate_unpermitted);
// DVFS: both directions completed, and the inefficient point occupied.
cover property (@(posedge clk) disable iff (!rst_n)
step_grant && step_v && (step_target > v_level));
cover property (@(posedge clk) disable iff (!rst_n)
step_grant && step_f && (step_target < f_level));
// The state §11 says every safe transition passes through. NOT
// occupying it means the ordering was reversed.
cover property (@(posedge clk) disable iff (!rst_n) in_inefficient_point);
cover property (@(posedge clk) disable iff (!rst_n) err_f_raised_first);
cover property (@(posedge clk) disable iff (!rst_n) err_v_lowered_first);Twelve of these sixteen properties forbid something, which is the highest proportion in the module and appropriate to the subject. Every hazard in this chapter is an ordering hazard, and an ordering constraint is a statement about what must not happen.
18. Corner Cases
| Case | Behaviour | Why |
|---|---|---|
| A later rail good before the current one | err_out_of_order_up | Bring-up order violated |
| Shutdown from index 0 instead of the last | err_down_same_order_as_up | §14's hazard; P2 and P3 |
| Any rail up with an earlier rail down | err_dependent_without_base | §10's constraint; P1, checked every cycle |
req_up and req_down together | err_both_directions; no action | P7 — no safe interpretation to choose |
| Two rails enabled in one step | P4 fires | Abandons the ordering the block enforces |
A count width too narrow for RAILS | Elaboration note; all_up unreachable | The COUNT-vs-INDEX trap; P6 guards it |
| Gate requested outside the permitted set | Refused entirely, not masked down | P8 and P10 — a partial grant misstates the exit cost |
| Gate requested while not in self refresh | err_gate_while_active; nothing gated | P9 — gating a rail in use is a fault |
| Ungate with nothing gated | err_ungate_nothing_gated | A controller state-tracking error |
| Two rails gated, exit costs differ | Sum and max both reported; sum applies | §10 requires sequencing, so costs add |
| Short idle, rails gated anyway | No error; the loss is real | 24.2 §6 — the deepest state, the longest break-even |
| Gating an already-unused rail | No device-level current change | §9 — the saving is the regulator's, not the DRAM's |
| Frequency raised before voltage | err_f_raised_first; refused | P13 — the unsafe direction |
| Voltage lowered before frequency | err_v_lowered_first; refused | P14 — the other unsafe direction |
| Voltage above what the frequency needs | in_inefficient_point; counted, not an error | §11 — every safe transition passes through it |
| A two-level jump with safe endpoints | err_step_not_adjacent; refused | P15 — the intermediate may be unsafe |
| Both dimensions stepped at once | err_two_dims_at_once | No ordering was specified, so none can be enforced |
Row fourteen is the row worth remembering. The inefficient point is not a bug and a controller that never enters it has reversed the ordering — so an implementation whose cy_inefficient count is zero is not efficient, it is unsafe.
Row eleven is the honest one. Gating rails for a short idle produces no error at all, because nothing in the hardware is violated. The loss is real and invisible, which is precisely the shape 24.2 §6's selector exists to prevent.
19. DV
// Independent ordering reference. Holds the permitted rail order as
// an explicit list and validates an observed trace against it,
// rather than re-implementing the state machine -- a different
// representation, so agreement is evidence. SIMULATION-ONLY.
class rail_order_ref;
int order[$]; // order[0] is first up and LAST down
bit up[$]; // current per-rail state, indexed by rail id
function void build(int n);
order.delete(); up.delete();
for (int i = 0; i < n; i++) begin order.push_back(i); up.push_back(0); end
endfunction
// Returns 1 if the observed rail-good vector is a legal state:
// every rail that is up has all earlier rails up too.
function bit state_legal(bit [31:0] good, int n);
bit seen_down = 0;
for (int i = 0; i < n; i++) begin
if (!good[order[i]]) seen_down = 1;
else if (seen_down) return 0; // up after a down: illegal
end
return 1;
endfunction
// Returns 1 if a shutdown trace removed rails in reverse order.
function bit shutdown_legal(bit [31:0] prev, bit [31:0] now, int n);
for (int i = 0; i < n; i++)
if (prev[order[i]] && !now[order[i]]) begin
// The rail that went down must be the LAST one still up.
for (int j = i + 1; j < n; j++)
if (now[order[j]]) return 0;
end
return 1;
endfunction
endclass| Check | What it establishes |
|---|---|
Every observed rail state is state_legal() | P1 against an independent representation |
| Full bring-up, every step index visited in order | The two step covers, rising |
| Full shutdown, every step index visited in reverse | P2 and P3; the covers, falling |
Every shutdown transition is shutdown_legal() | §14's hazard, driven rather than assumed |
Force k <= 0 on shutdown; confirm P2 and P3 fire | The hazard's detectability |
| Power cycle repeatedly; confirm no state is ever illegal | The test §14's table says usually exists |
Assert req_up and req_down together | P7 |
Sweep RAILS from 2 to 8 | P4, P5, P6 over the parameter range |
| Gate each rail individually, then both gateable together | The two-rails-gated cover — §7's case |
| Request a non-gateable rail; confirm nothing is gated | P8 and P10 — no partial grant |
| Request gating while not in self refresh | P9 |
| Differing exit costs; check sum against max | P11, and the gap §15 names |
| Walk every adjacent step in the voltage-frequency grid | P12 through P15 across the whole space |
| Attempt each unsafe direction at each level | Both direction covers, non-vacuous |
Confirm cy_inefficient is non-zero after any transition | §18's row fourteen — a zero means reversal |
The sixth check deserves the report, because it is the one that shows why §14's hazard survives normal testing:
THE POWER-CYCLE TEST THAT MISSES THE SHUTDOWN BUG
Sequencer with k <= 0 on entry to shutdown, taking rails down in
the SAME order it brought them up.
test: power up, verify all rails good, read and write, power down,
repeat 10,000 times. Device works every time.
result: PASS. 10,000 clean cycles.
what the trace actually contained, every single cycle:
shutdown step 1 : VDD1 removed <- the ARRAY's supply
shutdown step 2 : VDD2 removed
shutdown step 3 : VDDQ removed <- I/O still powered for
two whole steps with its
control logic unpowered
assertions, if bound : P2 shutdown_starts_at_last FAIL
P3 shutdown_never_same_order FAIL
P1 no_dependent_without_base FAIL
diagnosis : the functional test cannot see it. Every power-up is
correct, every access succeeds, and the device comes back. The
violation happens entirely during teardown, when nothing is
being checked and the device is about to be unpowered anyway.
what makes it dangerous : whether it damages anything depends on
the board -- on what else shares the bus the undriven I/O may
drive. So it is a field failure on SOME designs and not others,
which is the worst possible distribution for finding a bug.
the fix : the shutdown-direction covers of §17 are required
goals, and P1 must be bound during teardown and not only during
operation. A sequencer verified only on the way up is verified
for half its job.20. Debugging
| Symptom | Likely cause | How to confirm |
|---|---|---|
| Device fails to initialise | A rail out of order on bring-up | err_out_of_order_up; step shows where it stopped |
| Initialisation stalls at one step | The rail's own good signal never asserts | cy_in_step growing without bound at a fixed step |
| Works at power-up, occasional field failures | Shutdown order — §14 | P1, P2, P3 bound during teardown; check the trace, not the result |
all_up never asserts though every rail is good | Count width too narrow for RAILS | P6; the elaboration note |
| Two rails come up together | The step gate is bypassed | P4 |
| Gating appears to do nothing | Request refused and the refusal ignored | cnt_gate_events is zero; then the three reason flags |
| Device misbehaves after gating | A rail in use was gated | err_gate_while_active; then whether the device was in self refresh |
| Fewer rails gated than requested | Should be impossible — the request is all-or-nothing | P10; if gated is a subset of req_mask, masking was introduced |
| Rail gating enabled, device current unchanged | Expected — §9; those rails were already unused | Measure at the regulator input, not at the DRAM |
| Wake-up slower than budgeted | Exit costs add because rails are sequenced — §15 | exit_obligation_serial against exit_obligation_worst |
| Battery worse after enabling rail gating | Short idles — the deepest state's break-even | cy_gated against cnt_gate_events; short average occupancy |
| Timing failures after a frequency change | Frequency raised before voltage — §11 | err_f_raised_first; P13 |
| Timing failures after a voltage change | Voltage lowered before frequency — §11 | err_v_lowered_first; P14 |
cy_inefficient is zero | The ordering is reversed — §18's row fourteen | Not an efficiency result; a safety finding |
| Idle current much worse when warm | Temperature, concentrated on VDD2H — §7 | The rail that cannot be gated is the one that grows fastest |
Row three is what this chapter exists to put in a debugging table, and row thirteen is the one that reads backwards. A zero inefficiency counter is a safety finding, not a good result — and an engineer who reads it as an optimisation will confirm a bug as a success.
21. Misconceptions
“LPDDR4X lowers the I/O voltage.” §2. It lowers two rails by about 10% and deletes a third entirely. The deletion is the half the summary omits.
“VDD1 came down too.” §2. It is identical in both variants — 1.70–1.95 V. The array's high rail resists every improvement in this chapter.
“A 10% voltage cut is a 10% power cut.” §3. Under the stated V² model it is about 19% of dynamic power on two of four rails, and leakage does not scale that way at all.
“LPDDR4X is 19% lower power.” §3's callout. The device-level figure needs per-rail weights no datasheet consulted publishes, and the 19% is dynamic power on two rails under a model this module cannot check.
“Three rails at the same voltage is redundant.” §1. They are separated for noise isolation, not for their values — which is why §4's deletion is a statement about isolation.
“Deleting a rail saves voltage.” §4. It saves pins, a regulator channel, board area, a sequencing constraint and a monitoring obligation. None of those is a volt.
“More rails is always worse.” §5. LPDDR5X splits the core rail into high and low — because different parts of the core want different voltages, and more rails means more of them can be gated independently.
“Reduce the memory's voltage.” §6. Not an executable instruction. Each rail has its own answer, and the rail with the most headroom supplies the least of the idle cost.
“Self refresh means everything is off.” §7. Two of four rails are unused; the other two are drawing, and one of them is both the larger and the faster-growing contributor with temperature.
“Gating two of four rails removes half the idle draw.” §9. Those two rails are already unused in that state — §7's current table shows the other two carry all of it. Gating removes the regulator's cost of keeping two channels alive to supply nothing.
“Rail gating is a memory-level optimisation.” §9. The saving is in the PMIC channel, not in the DRAM, which is why a DRAM datasheet cannot tell you its size.
“Sequencing is free once it is correct.” §9. Sequencing makes exit costs add instead of overlap — 34 against 25 for two rails, 64 against 25 for four — which lengthens every break-even.
“Two gateable rails should always be gated.” §7's callout. Rail gating is the deepest state with the longest break-even. For a short idle it loses, and nothing reports an error.
“Shutdown is bring-up in any order.” §10. It is bring-up in exactly reverse order, and getting it wrong passes every power-up test.
“A power-cycle test would catch a bad shutdown order.” §19. Ten thousand clean cycles with the violation present in every single one — the teardown is when nothing is being checked.
“Change voltage and frequency together to save time.” §11. They must be ordered, and the order reverses with direction. Doing both at once passes through the one forbidden combination.
“Running at a higher voltage than needed is a bug.” §18's row fourteen. It is the state every safe transition passes through, and never occupying it means the ordering was reversed.
22. Interview Reasoning
What rails does an LPDDR4 device have, and what does the structure tell you? Four on the named part: a high array rail near 1.8 V and three at 1.1 V for core logic, data I/O and command I/O. Three rails at one voltage exist for noise isolation rather than for their values.
What did LPDDR4X change? Two rails down about 10%, and the command/address rail removed entirely. The removal is the structural change and the one usually left out.
Convert a 10% voltage reduction into a power figure, and state your model. Under dynamic power scaling with V², 1.17/1.30 squared is 0.81 — about 19% less dynamic power, on the two rails that moved, with leakage unaffected and no per-rail weighting available.
Why is deleting a rail a saving if the voltage is unchanged? It removes pins, a regulator channel with its passives and board area, one ordering constraint from every power cycle, and one monitoring obligation. It costs the noise isolation the rail provided.
Which rails can be gated on the LPDDR5X part, and why those? The low core rail and the data rail are unused during power-down self refresh — two of four. The data rail because no I/O happens while idle; the array and high core rails must stay up because the device is refreshing itself.
Is rail gating always worth it? No. It is the deepest low-power state, with the largest saving and the largest exit cost, so it has the longest break-even duration. For a short idle it loses, and no hardware reports that.
Two rails are unused during power-down self refresh. What does gating them save? Nothing in the DRAM — it already draws nothing on them. The saving is the regulator channels' own quiescent overhead, so it is a system saving that no DRAM datasheet can size.
What does the sequencing requirement cost you at wake-up? Exit costs add rather than overlap, because rails must come up in order. For two rails costing 25 and 9 ticks the obligation is 34 rather than 25 — a 36% underestimate if you budget the worst single rail.
What order do rails come up in, and what order do they come down in? Up: the rail everything depends on first, then core, then I/O. Down: exactly the reverse. The rule underneath is that no circuit may be powered while what it depends on is not.
Why is a bad shutdown order hard to find? A functional test exercises power-up and access, both of which pass. The violation happens during teardown when nothing is checked, and whether it causes damage depends on the board — so it fails in the field on some designs and not others.
Which order do you change voltage and frequency in? Raise voltage before frequency, lower frequency before voltage. Both avoid the one forbidden combination — high frequency at low supply — by passing through the wasteful-but-safe one instead.
Your DVFS controller never runs at a voltage above what the frequency needs. Good or bad? Bad. That state is what every safe transition passes through, so never occupying it means the ordering is reversed and the design is passing through the unsafe combination instead.
23. Exercises
-
§3 gives −19.0% at the range maximum and −13.5% at the minimum. Explain why the two differ, and say which figure a system designer should use and why.
-
Compute the device-level dynamic-power saving of LPDDR4X under the
V²model for three hypothetical per-rail weightings:VDD1at 20%, 50% and 80% of the total. What does the spread tell you about quoting a single figure? -
§4 lists four things a rail costs. Rank them by what they cost a mobile design specifically, and justify the ordering against a desktop design where the ranking would differ.
-
Using §7's per-rail currents, compute what fraction of self-refresh current is on the rail that cannot be gated, at both temperatures. What does the trend say about the value of further gating work?
-
Implement §14's hazard in §13's sequencer. Write the minimal stimulus that makes P2 fire, then the minimal stimulus that makes P1 fire, and explain why they differ.
-
For a 3-rail and a 4-rail device, count the ordering relationships that must hold simultaneously during bring-up. Express the count in terms of
nand evaluate §4's sequencing claim quantitatively. -
§15 reports the exit obligation as both a sum and a maximum. Construct the exit-cost vector that maximises the gap between them for four rails, and say what the gap costs a system that budgeted the maximum.
-
§16 grants only adjacent steps. Construct a 4-level compatibility table in which a two-level voltage step has safe endpoints and an unsafe intermediate, then show that the adjacency rule is necessary rather than merely cautious.
-
§9 shows the sequencing requirement lengthens every gating break-even. Derive the general relationship between the number of gated rails, their cost distribution, and the break-even duration, then state the rail-cost distribution for which sequencing costs least.
-
Every ordering mechanism in this module — §10's sequencing, §11's DVFS discipline, and 24.4 §15's reconfiguration protocol — passes through an inefficient intermediate state to avoid an unsafe one. State the general principle, and find one mechanism elsewhere in the DDR curriculum with the same shape.
24. Where the Module Ends
Mobile memory is now a complete picture rather than a set of features, and it is worth stating what the five chapters together established.
Chapter 24.1 established the mission — three quantities that must never be collapsed, and a duty cycle that makes idle power dominant. 24.2 established the idle mechanisms from a named device's own state diagram, with the forbidden transitions between them, and found that per-bank refresh buys availability rather than energy. 24.3 built LPDDR4's interface and found that a x32 interface uses two more command pins than the previous generation, spent on a second independent command stream. 24.4 built LPDDR5's two clock domains at a selectable ratio, and found that the granularity question LPDDR4 settled was deliberately reopened as a configuration. And this chapter established the rails — what each feeds, why two of four can be gated, and why every one of those facts is an ordering problem before it is a saving.
Four results from this chapter carry beyond it.
A verified “unused” changes what a mechanism is for. §9: because the two gateable rails are already unused in the state where gating applies, gating saves nothing in the memory at all. The saving is the regulator's cost of keeping two channels alive to supply circuits doing nothing — a system-level saving no DRAM datasheet can size, and a conclusion that only appears if you read the current table rather than the feature list.
The rails that can be switched off and the rails that must be sequenced carefully are almost disjoint sets. §12's diagram shows it: the hard correctness problem and the large saving live in different places, which is a useful division rather than a coincidence.
A saving can be structural rather than numerical. §4's deleted rail saves pins, a regulator channel, one ordering constraint per power cycle and one monitoring obligation — and no voltage arithmetic captures any of it.
And every ordering mechanism in this module routes through an inefficient state to avoid an unsafe one. Rail sequencing, DVFS ordering and 24.4 §15's reconfiguration protocol all have that shape, and in all three the inefficient state is the evidence the discipline is working.
Two things this module verified and could not finish, recorded rather than passed on.
Energy per bit is measured in payload and not in joules. Chapter 24.1 §11 excluded wasted payload on small accesses because nothing could compute it; 24.3 §14 computes the bytes exactly, and 24.4 §14 compares both burst lengths. Converting bytes into joules needs a per-bit energy figure no datasheet consulted in this module publishes, so the term is fully measured in payload and still excluded from any energy total. That is as far as the available evidence reaches.
And two mechanisms are named but not characterised. Chapter 24.2 §8 could not establish whether per-bank refresh reduces peak current, because only average currents are published. Chapter 24.4 §10 could not characterise link ECC, because the code is not published in the source consulted. Both are real gaps in the evidence rather than gaps in the exposition, and the honest thing was to say so at each point rather than supply a plausible number.
What every chapter here has in common is that its most useful findings contradicted a reasonable expectation — per-bank refresh saving no energy, a narrower bus using more pins, a later generation reopening a settled question, and a shutdown bug that survives ten thousand clean power cycles. None of those came from reasoning about how memory ought to work. Each came from reading what a specific named device actually says and then doing the arithmetic, which is the only method this module has used and the one worth carrying into whatever you read next.
Continue learning
Related tutorials
- 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
Training Algorithms
Four searches exist and nothing has said in what order they run. The order is not a convention: it follows from which observable each step needs — a derivation that predicts the sequence real controllers document.
- Related topic
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.
- 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.
