Skip to content
VLSI Mentor

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:

RailRangeNominalWhat it feeds
VDD11.70 – 1.95 V1.8 Vthe array's high-voltage needs
VDD21.14 – 1.30 V1.1 Vthe device's core logic
VDDQ1.14 – 1.30 V1.1 Vthe data I/O
VDDCA1.14 – 1.30 V1.1 Vthe 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:

RailLPDDR4LPDDR4XChange
VDD11.70 – 1.95 V1.70 – 1.95 Vunchanged
VDD21.14 – 1.30 V1.06 – 1.17 Vlowered
VDDQ1.14 – 1.30 V1.06 – 1.17 Vlowered
VDDCA1.14 – 1.30 Vabsentrail deleted
Rail count43

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:

ComparisonRatioDynamic-power change
VDD2 at range max, 1.17 / 1.300.8100−19.0%
VDD2 at range min, 1.06 / 1.140.8646−13.5%
VDDQ, identical rangessame−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 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 costsWhat deleting it saves
Package pins and ballspins, which a mobile package is extremely short of
A regulator outputa PMIC channel, its inductor, its capacitors, its board area
A sequencing constraintone fewer ordering relationship — §10
A monitoring obligationone 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:

RailNote
VDD1the array's high rail, still present
VDD2Ha high core rail
VDD2La low core rail
VDDQ0.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 model, recomputed:

ComparisonRatio²Dynamic-power change
0.50 / 1.300.1479−85.2%
0.50 / 1.140.1924−80.8%
0.45 / 1.140.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
VDD1no — unchanged across variants (§2)not while data must survivethe array's charge transfer is physical
VDD2 / VDD2Hmodestly — 10% on the X variantnot during self refresh (§7)the device must still refresh itself
VDD2Lyes in power-down self refresh (§7)verified unused there
VDDQdramatically — 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.

RailPower-down self refresh
VDD1in use — the array must be refreshed
VDD2Hin use — the self-refresh machinery runs on it
VDD2Lunused
VDDQunused

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:

Rail25 °C95 °CRatio
VDD10.25 mA3.70 mA14.8×
VDD2H0.45 mA12.00 mA26.7×
Sum0.70 mA15.70 mA22.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 needsWhy it costs
A regulator stagesilicon in the PMIC, and a pin out of it
External passivesan inductor or capacitors, occupying board area near the load
A control loopits own compensation, its own stability requirement
Quiescent currentthe 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 isWhy
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 leakagea 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 sSequenced exit cost eBreak-even d*
14040.0 ticks
44010.0
11010.0
4102.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:

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

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

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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   going FASTER :  raise voltage  ->  then raise frequency
   going SLOWER :  lower frequency ->  then lower voltage

Both 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

The mobile power-management architecture in four rows. The top row holds the four supply rails of a named LPDDR5X part: the array's high rail which cannot be lowered or gated, the high core rail which runs the self-refresh machinery, the low core rail which is unused during power-down self refresh, and the data rail at half a volt which is likewise unused there. The second row holds the four managing functions: the rail sequencer which enforces bring-up and shutdown order, the gating controller which decides which rails may be removed, the dynamic voltage and frequency guard which enforces the ordering between a voltage change and a frequency change, and the temperature input which constrains the operating point. The third row holds the three disciplines those functions produce: a bring-up order that must reverse on shutdown, a gateable rail set with per-rail exit costs, and a voltage-then-frequency ordering rule. The bottom row is idle power, which all three disciplines serve. Edges run from each rail to the function that manages it, from each function to the discipline it produces, and from all three disciplines to idle power.VDD1array — neither lowerednor gatedVDD2Hself-refresh machineryruns hereVDD2Lunused in PD self refresh— §7VDDQ0.50 V, and gateable —§5, §7Rail sequencerorder up, reverse down —§10Gating controlwhich rails may beremovedDVFS guardV before f, f before V —§11Temperature22.4x on the two liverails — §7Bring-up orderwrong in reverse, passesat bootGateable set2 of 4, separate exitcostsV-f orderingnever fast at low supplyIdle powerthe quantity all threeserve12

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

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

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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
S_UP : if (req_down) begin
  st <= S_FALL; k <= IDX_W'(RAILS - 1);   // start from the LAST rail

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

TestCorrect sequencerSame-order sequencer
Power up, check all rails goodpasspass
Power up, read and writepasspass
Power cycle, check it comes backpasspass
Power down, check the orderpassfail

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

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

The 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

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

cy_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

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

CaseBehaviourWhy
A later rail good before the current oneerr_out_of_order_upBring-up order violated
Shutdown from index 0 instead of the lasterr_down_same_order_as_up§14's hazard; P2 and P3
Any rail up with an earlier rail downerr_dependent_without_base§10's constraint; P1, checked every cycle
req_up and req_down togethererr_both_directions; no actionP7 — no safe interpretation to choose
Two rails enabled in one stepP4 firesAbandons the ordering the block enforces
A count width too narrow for RAILSElaboration note; all_up unreachableThe COUNT-vs-INDEX trap; P6 guards it
Gate requested outside the permitted setRefused entirely, not masked downP8 and P10 — a partial grant misstates the exit cost
Gate requested while not in self refresherr_gate_while_active; nothing gatedP9 — gating a rail in use is a fault
Ungate with nothing gatederr_ungate_nothing_gatedA controller state-tracking error
Two rails gated, exit costs differSum and max both reported; sum applies§10 requires sequencing, so costs add
Short idle, rails gated anywayNo error; the loss is real24.2 §6 — the deepest state, the longest break-even
Gating an already-unused railNo device-level current change§9 — the saving is the regulator's, not the DRAM's
Frequency raised before voltageerr_f_raised_first; refusedP13 — the unsafe direction
Voltage lowered before frequencyerr_v_lowered_first; refusedP14 — the other unsafe direction
Voltage above what the frequency needsin_inefficient_point; counted, not an error§11 — every safe transition passes through it
A two-level jump with safe endpointserr_step_not_adjacent; refusedP15 — the intermediate may be unsafe
Both dimensions stepped at onceerr_two_dims_at_onceNo 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

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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
CheckWhat it establishes
Every observed rail state is state_legal()P1 against an independent representation
Full bring-up, every step index visited in orderThe two step covers, rising
Full shutdown, every step index visited in reverseP2 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 fireThe hazard's detectability
Power cycle repeatedly; confirm no state is ever illegalThe test §14's table says usually exists
Assert req_up and req_down togetherP7
Sweep RAILS from 2 to 8P4, P5, P6 over the parameter range
Gate each rail individually, then both gateable togetherThe two-rails-gated cover — §7's case
Request a non-gateable rail; confirm nothing is gatedP8 and P10 — no partial grant
Request gating while not in self refreshP9
Differing exit costs; check sum against maxP11, and the gap §15 names
Walk every adjacent step in the voltage-frequency gridP12 through P15 across the whole space
Attempt each unsafe direction at each levelBoth 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:

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

SymptomLikely causeHow to confirm
Device fails to initialiseA rail out of order on bring-uperr_out_of_order_up; step shows where it stopped
Initialisation stalls at one stepThe rail's own good signal never assertscy_in_step growing without bound at a fixed step
Works at power-up, occasional field failuresShutdown order — §14P1, P2, P3 bound during teardown; check the trace, not the result
all_up never asserts though every rail is goodCount width too narrow for RAILSP6; the elaboration note
Two rails come up togetherThe step gate is bypassedP4
Gating appears to do nothingRequest refused and the refusal ignoredcnt_gate_events is zero; then the three reason flags
Device misbehaves after gatingA rail in use was gatederr_gate_while_active; then whether the device was in self refresh
Fewer rails gated than requestedShould be impossible — the request is all-or-nothingP10; if gated is a subset of req_mask, masking was introduced
Rail gating enabled, device current unchangedExpected — §9; those rails were already unusedMeasure at the regulator input, not at the DRAM
Wake-up slower than budgetedExit costs add because rails are sequenced — §15exit_obligation_serial against exit_obligation_worst
Battery worse after enabling rail gatingShort idles — the deepest state's break-evency_gated against cnt_gate_events; short average occupancy
Timing failures after a frequency changeFrequency raised before voltage — §11err_f_raised_first; P13
Timing failures after a voltage changeVoltage lowered before frequency — §11err_v_lowered_first; P14
cy_inefficient is zeroThe ordering is reversed — §18's row fourteenNot an efficiency result; a safety finding
Idle current much worse when warmTemperature, concentrated on VDD2H — §7The 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 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 , 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

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

  2. Compute the device-level dynamic-power saving of LPDDR4X under the model for three hypothetical per-rail weightings: VDD1 at 20%, 50% and 80% of the total. What does the spread tell you about quoting a single figure?

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

  4. 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?

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

  6. 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 n and evaluate §4's sequencing claim quantitatively.

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

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

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

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.