Power gating changes what RTL means. When a block can be switched off, the design gains power switches, isolation cells, retention registers and level shifters that are usually not written in the RTL at all, plus control signals (switch enable, isolation enable, save and restore) that the RTL generates but that drive nothing until implementation adds those cells. Functional verification has to model all of that, in the right order, or the simulation proves nothing about the chip that will be built. This post walks through the RTL and micro-architecture questions to ask, and how verification handles them.
This article is about RTL in a multi-voltage environment and its implications for verification. In earlier posts I discussed multi-voltage design infrastructure. Here the focus is power gating, the most common design style for reducing leakage power.
Key takeaways
- Power gating needs a dedicated power controller that sequences save, isolation and switch-off, and the reverse on wake-up. Plan it in the micro-architecture, not after tape-out.
- Power down order: save state, enable isolation, open the power switches. Power up order: close the switches, wait for power good, restore state, release isolation.
- Switches, isolation cells, retention cells and level shifters are normally absent from RTL, so their control signals look unconnected in RTL simulation.
- Early teams modelled these cells with PLI routines. Today the power intent is written separately in IEEE 1801 (UPF), and simulators use it to corrupt, isolate and retain without a second RTL.
- Every sequencing rule should become an assertion or a checker so that a wrong order is flagged in simulation.
Characteristics of the power gating design style
- Some blocks in the design are shut down when not in use.
- Other blocks are always on.
- These blocks may run at the same voltage or at different voltages.
- The power structure used to shut down a block can be external (an off-chip regulator turned off) or internal (on-chip switch transistors). Internal switches are the most common.
- Either VDD (a header switch, usually PMOS) or ground (a footer switch, usually NMOS) can be cut off. Header switches are more common in practice because they keep a common ground.
A classic scenario
Consider a scenario where implementation and verification become a real challenge:
"We have a chip taped out and working fine in 65nm. We want to add more functionality and fit the logic within the same die area as before. To meet this area requirement, we decided to move to 45nm. Since the application as well as the technology node demands extremely low leakage, we want to shut down some blocks in the design."
Given this, it is very challenging to add power gating, because the chip was not architected for it. Leakage rises sharply at smaller nodes, so the motive is sound, but retrofitting power gating touches the control logic, the clocking, the reset scheme, every interface that crosses a domain boundary and the verification plan. The rest of this post lists the points to consider while micro-architecting such a design.
RTL and micro-architecture requirements
1. Who decides when a block shuts down?
Some blocks will be shut down. Does the design have control logic that generates the signals to shut a block down?
If the design was architected with power gating in mind from the start, it will have a control block that decides which blocks to shut down, when, and for how long. Is that sufficient? Do we need a separate power controller that takes requests from that control logic and generates the power-down signals in the right sequence? In my view this is good practice: keep the policy (which block, when) apart from the mechanism (the exact order and timing of control signals). The power controller itself must sit in an always-on domain.
2. The control signals and their order
The control signals required are:
- A control signal for the power switch (Switch_enable)
- A control signal for the isolation cells (Isolate_enable)
- A control signal for the retention flops (Save_Restore, often split into separate save and restore controls)
Ideally these control signals are derived from one another. They simply need to be generated in the right order for the circuit to behave as desired. On inactivity, the power-down sequence is:
- Generate Save_Restore (save): the retention flops copy their state into a retention latch that stays powered and go into sleep mode. Depending on the cell library, that latch is a separate balloon latch or the slave latch kept on the always-on supply.
- Generate Isolate_enable: the isolation cells become active and clamp the block's outputs to a known level (0, 1 or the last value).
- With every element informed of the shut-down, generate Switch_enable to turn off the power rails of that block.
- Other actions, such as reducing the frequency or the voltage, may also form part of this sequence.
- As part of the sequence definition, write the matching assertions, so that if the order is violated it is flagged up front.
Power-up is the mirror image: close the switches, wait for an acknowledge that the rail is good, restore the retained state (and apply reset to non-retained registers), then release isolation. Releasing isolation before the block is restored lets garbage reach the always-on logic.
The original assertion sketch, corrected
The first version of this post sketched the order as a SystemVerilog sequence:
Sequence power_sequence
Save_Restore && Isolate_enable && Switch_enable ==0
##1 Save_restore ==1
##1 Save_restore && Isolate_enable == 1;
##1 Save_restore && Isolate_enable && Switch_enable ==1;
endsequence
The intent is right, but it would not compile and does not check what was meant. The keyword is lowercase sequence; the semicolons in the middle end the sequence early; signal names change case (Save_Restore and Save_restore are different identifiers in Verilog); and a && b == 0 parses as a && (b == 0), not "all three are zero". It also fixes each step to exactly one clock, which real controllers rarely do. A more robust way to express the intent is as a set of rules that must always hold, rather than one fixed timeline:
- The switch may only be off while isolation is enabled.
- The switch may only turn off after state has been saved.
- Restore may only happen after the rail reports power good.
- Isolation may only be released after restore.
Each of these maps to one SVA property. Below they are written as a plain Verilog checker so the whole example runs on any simulator.
A working model: controller, domain and checker
The code below has three parts: a power controller state machine, a behavioural model of a switchable domain with one retained register (corrupted to X when the rail is off, clamped to 0 when isolated), and an order checker.
// Power controller for one switchable domain.
// Power down: save -> isolate -> switch off. Power up: switch on -> wait ack -> restore -> release isolation.
module pmu_ctrl (
input wire clk, rst_n,
input wire sleep_req, // 1 = request power down, 0 = request power up
input wire pwr_ack, // from the power switch network: 1 = domain rail is good
output reg save, // pulse: copy state into retention latches
output reg restore, // pulse: reload state from retention latches
output reg iso_en, // 1 = clamp domain outputs
output reg sw_off // 1 = open the power switches
);
localparam ON=3'd0, SAVE=3'd1, ISO=3'd2, OFF=3'd3, WAKE=3'd4, REST=3'd5, UNISO=3'd6;
reg [2:0] st;
always @(posedge clk or negedge rst_n)
if (!rst_n) begin st<=ON; save<=0; restore<=0; iso_en<=0; sw_off<=0; end
else begin
save<=0; restore<=0;
case (st)
ON: if (sleep_req) begin save<=1; st<=SAVE; end
SAVE: begin iso_en<=1; st<=ISO; end
ISO: begin sw_off<=1; st<=OFF; end
OFF: if (!sleep_req) begin sw_off<=0; st<=WAKE; end
WAKE: if (pwr_ack) begin restore<=1; st<=REST; end
REST: begin st<=UNISO; end
UNISO: begin iso_en<=0; st<=ON; end
default: st<=ON;
endcase
end
endmodule
// Behavioural model of the switchable domain: one 8-bit register with retention,
// corrupted to X while the rail is off, and an isolation clamp on its output.
module domain_model (
input wire clk, sw_off, save, restore, iso_en,
input wire [7:0] d,
output wire [7:0] q_iso,
output reg pwr_ack
);
reg [7:0] q, shadow; // shadow sits on the always-on supply
always @(posedge sw_off) begin pwr_ack = 0; q = 8'bx; end
always @(negedge sw_off) #12 pwr_ack = 1; // rail ramp time, illustrative
initial pwr_ack = 1;
always @(posedge clk)
if (!sw_off && pwr_ack) begin
if (save) shadow <= q;
else if (restore) q <= shadow;
else if (!iso_en) q <= d;
end
assign q_iso = iso_en ? 8'h00 : q; // clamp to 0 while isolated
endmodule
// Order checker in plain Verilog (an SVA version would express the same rules as properties).
module pwr_order_check (input wire clk, rst_n, save, iso_en, sw_off, pwr_ack, restore);
reg saved;
always @(posedge clk) if (rst_n) begin
if (save) saved <= 1; else if (restore) saved <= 0;
if (sw_off && !iso_en) $display("%0t ERROR: switch off while isolation is off", $time);
if (sw_off && !saved) $display("%0t ERROR: switch off before state was saved", $time);
if (restore && !pwr_ack) $display("%0t ERROR: restore before power is good", $time);
end
initial saved = 0;
endmodule
module tb;
reg clk=0, rst_n=0, sleep_req=0; reg [7:0] d=8'h00;
wire save, restore, iso_en, sw_off, pwr_ack; wire [7:0] q;
always #5 clk = ~clk;
pmu_ctrl u_pmu (clk, rst_n, sleep_req, pwr_ack, save, restore, iso_en, sw_off);
domain_model u_dom (clk, sw_off, save, restore, iso_en, d, q, pwr_ack);
pwr_order_check u_chk (clk, rst_n, save, iso_en, sw_off, pwr_ack, restore);
always @(posedge clk) $display("%0t req=%b save=%b iso=%b off=%b ack=%b rest=%b q=%h",
$time, sleep_req, save, iso_en, sw_off, pwr_ack, restore, q);
initial begin
#12 rst_n = 1; d = 8'hA5;
#20 sleep_req = 1; // power down after A5 is captured
#60 sleep_req = 0; // power up again
#100 $finish;
end
endmodule
Running it shows the expected order. The register holds A5, save pulses, isolation clamps the output to 00, the switch opens and the acknowledge drops. On wake-up, the switch closes, the acknowledge returns after the ramp time, restore pulses, and when isolation is released the output is A5 again. Forcing the switch off early, without save and isolation, makes the checker print both errors in the first cycle.
45 req=1 save=1 iso=0 off=0 ack=1 rest=0 q=a5
55 req=1 save=0 iso=1 off=0 ack=1 rest=0 q=00
65 req=1 save=0 iso=1 off=1 ack=0 rest=0 q=00
...
115 req=0 save=0 iso=1 off=0 ack=1 rest=0 q=00
125 req=0 save=0 iso=1 off=0 ack=1 rest=1 q=00
145 req=0 save=0 iso=0 off=0 ack=1 rest=0 q=a5
3. The control signals are driven but connected to nothing
If the control signals above exist in RTL, they are driven by the power management logic but are not connected to anything. Even though they are generated by control logic, they go nowhere outside it, for these reasons:
- The power switches that cut off power do not exist in RTL. They are added during power planning, by the floorplanning or power planning engineer, based on the architect's specification. Until the switches are in place, Switch_enable is floating.
- Nothing special is done in RTL for retention flops. They are coded like any other register and the synthesis tool maps them to retention cells based on constraints. Save_Restore ends up floating.
- Isolation cells do not exist in the RTL, so Isolate_enable is floating.
How do we simulate what is not in the RTL?
The question is how to simulate these cells, taken in order:
- Power switch behaviour
- Isolation behaviour
- Level shifter behaviour
- Retention flop behaviour
Remember, there is no representation of these cells in the RTL. Do we need to simulate all of them? In the earlier days, verification teams wrote PLI routines to model them.
Power switch behaviour
We can write a function or PLI routine with this specification:
$power(block_to_be_pd, type_of_pd (aon/shut-down), signal_used_for_shut_down (switch_enable), acknowledge)
The routine looks at type_of_pd, which is either always-on or shut-down, and acts accordingly. If the block is of type shut-down, then whenever it detects activity on switch_enable it corrupts all the signals of the block. Once all the signals are corrupted, it generates an acknowledge after a user-specified delay.
In my view it should also accept a staggered start, for example:
#0 $power(block1)
#20 $power(block2)
This lets us simulate power sequencing across blocks. Much more can be added to such a routine:
- Trace the fan-out of every output of the block, and flag an error if corrupted signals propagate to a receiver.
- When switch_enable goes inactive, set all the logic in the block either to X or to a random pattern.
- During power-up, stagger the power-up of different blocks randomly.
- Emulate the impact of IR drop and in-rush current using the same staggering principle.
Isolation behaviour
This is straightforward. All we need is a PLI routine or a simple Verilog function such as:
$isolate(input, early_switch_enable, output, output_sense)
If early_switch_enable is active, hold the output at the output_sense value (1 or 0), whatever the state of the input. In the model above this is the single line assign q_iso = iso_en ? 8'h00 : q;.
Level shifter behaviour
Functionally a level shifter is a buffer, so RTL simulation mostly needs to check that one exists on every crossing between different voltages, and that a shifter fed from a switched-off domain is also isolated. Static low-power checks catch missing shifters better than simulation does.
Retention behaviour
This is also straightforward from a simulation point of view. A PLI routine or simple function would look like:
$retain("register_names", early_early_switch_enable, wake_up)
Whenever early_early_switch_enable is active, copy the contents of register_names into a local shadow register or memory, and whenever wake_up goes active, reload register_names from the shadow register. The shadow register in the model above does exactly this.
Two RTLs, or a different approach?
There are more complex flavours of all these, depending on the circuit behaviour of the special cells. The big questions were: are we looking at two different RTLs, one for synthesis without any PLIs and one for simulation with PLIs? Can these PLIs be synthesised into hardware by the EDA tools? Is this the right approach, or can it be solved differently?
The industry did find a different approach. Power intent is now captured in a separate file, so the RTL stays a single source:
- IEEE 1801, the Unified Power Format (UPF), first published by the IEEE in 2009 and revised several times since, describes power domains, supply networks, switches, isolation, retention and level-shifter strategies. The Common Power Format (CPF) from Si2 served the same purpose and is now largely superseded by UPF.
- Simulators read the UPF alongside the RTL and apply corruption, isolation and retention automatically, which replaces the hand-written PLI models.
- Synthesis and place-and-route read the same UPF to insert the cells, so the floating control signals become real connections.
- Static low-power checkers verify that every crossing has the right isolation and level shifting, at RTL, after synthesis and after layout.
The controller logic and the sequencing rules are still the designer's job. UPF tells the tools what cells exist; it does not decide when to switch them.
More questions that need answers
- Is a proper sequence and timing for all the control signals defined? The first version of this post gave this example:
- Switch_enable @ 5ns
- Isolate_enable @ Switch_enable + 10ns
- Save_Restore @ Isolate_enable + 20ns
- The block being shut down needs to generate an acknowledge signal for power-up or power-down. In RTL this is again a floating output not driven by any logic, yet the power management logic waits for it. In practice it comes from the switch network (often the last switch in a daisy chain) or from a power-good detector, and verification must model it.
- Is there a requirement such as "the block must be powered up within n clock cycles"? What happens if no acknowledge arrives within n cycles? A timeout with an error flag or retry is usually needed.
- Even if all the above is handled during micro-architecture, a few more questions remain for logic synthesis and functional simulation:
- Are isolation cells and level shifters part of your RTL? How are you coding them? Are you instantiating them?
- Are retention flops part of the RTL? How are you coding them? Are you instantiating them?
- How will the implementation tool interpret the control signals, as they are floating?
- How will the acknowledge signal be generated, when power management logic needs it but no hardware in the RTL drives it?
- How will the functionality of all these be verified, given that some are floating or not generated?
- How will shut-down be simulated, when nothing special is done in RTL to model it?
- How will retention flop behaviour be simulated, when in RTL it is coded like any other register?
- When a block wakes up from shut-down, what should the state of its logic be? Is random better than X? Isn't X very pessimistic?
- How do we simulate the n-cycle requirement on the acknowledge from a powered-down block?
- If some always-on logic sits inside a shut-down block, how do we implement and verify it?
Most of these now have standard answers: power cells are described in UPF rather than instantiated, retention is chosen by UPF strategy, and the simulator corrupts and restores state. The X versus random question remains a judgement call. X corruption is pessimistic, but it is the safer default because it exposes any register that is used before it is reset or restored; random values can hide such bugs. Many teams run X by default and add random-value runs to catch logic that wrongly depends on X-propagation behaviour.
Common mistakes in power-gated designs
- Releasing isolation before the restored state is stable.
- Forgetting to isolate a signal that feeds the power controller itself, so the controller sees X from a domain it has switched off.
- Clocks still toggling into a domain during save or restore; gate the clock first.
- Reset not applied to non-retained registers on power-up.
- Switching many blocks on at once, causing in-rush current and IR drop that disturbs always-on logic.
- Testing only one power cycle. Bugs often appear on the second sleep or when a sleep request arrives during wake-up.
FAQ
Why not simply write isolation and retention cells in the RTL?
You can instantiate them, but it ties the RTL to one library and one power plan. Keeping power intent in UPF lets the same RTL be used with different domain arrangements and lets tools insert and check the cells consistently.
What does a simulator do when a domain is powered off?
With power-aware simulation enabled, every register and net driven from the domain is corrupted, normally to X, isolation cells drive their clamp value, and retention registers keep their saved value until restore.
Does power gating save dynamic power?
Its main purpose is to cut leakage. Clock gating is the usual tool for dynamic power. Power gating also removes dynamic power for the block while it is off, but adds energy for each save, restore and rail charge, so very short sleep periods can cost more than they save.


Your comments will be moderated before it appears here.