16 min read | Advanced Level
What You'll Learn
- Configure power-aware simulation in industry-standard simulators
- Verify X-propagation prevention with isolation cells
- Validate retention cell operation during power state transitions
- Debug common power-aware simulation issues and errors
The Hidden Power Bug
Your mobile SoC has passed 50,000 regression tests in traditional RTL simulation. Functionality is perfect. You tape out the chip, and first silicon arrives 4 months later. During power-up testing, the GPU randomly corrupts system memory after waking from deep sleep.
After weeks of expensive silicon debug, you discover the issue: when the GPU powers down, its output signals float to X (unknown) values. These X's propagate through the NoC interconnect to the memory controller, causing undefined writes. Your UPF specified isolation cells, but they were placed on the wrong side of the power domain boundary—they lost power along with the GPU.
A power-aware simulation would have caught this immediately. When the simulator modeled the GPU power-down, it would have driven X's on GPU outputs, the incorrectly-placed isolation cells would have become X's themselves, and X-propagation warnings would have flagged the bug during verification.
Power-related bugs caught in silicon are 100-1000× more expensive to fix than bugs caught in simulation. Power-aware simulation is not optional for designs with power management—it's essential for catching bugs that traditional simulation cannot detect.
What is Power-Aware Simulation?
Power-aware simulation (also called low-power simulation or UPF simulation) extends traditional RTL simulation to model power management behavior defined in UPF:
- Powered-down domains: Drive X (unknown) on all outputs
- Isolation cells: Clamp signals to 0 or 1 when enabled
- Retention cells: Save state before power-down, restore after power-up
- Level shifters: Convert voltage levels (modeled as assertions/warnings)
- Power switches: Control domain power with enable signals
- Power state transitions: Check legal state changes per Power State Table
The simulator reads your UPF file along with RTL and applies power management semantics during simulation, allowing you to verify that your power intent correctly manages all low-power scenarios.
Power-Aware Simulation in Practice
Modern smartphone SoC verification teams run power-aware simulation on every regression test. A typical flow: compile RTL with UPF using power-aware mode, run functional tests with power cycling injected at random intervals, check for X-propagation violations. One team caught 37 power bugs before tape-out this way, including isolation placement errors, retention timing violations, and illegal power state transitions. The cost to fix these bugs in silicon would have exceeded $5M and added 6 months to schedule.
Industry-Standard Power-Aware Simulators
All major EDA simulators support UPF-based power-aware simulation:
| Simulator | Vendor | UPF Support | Key Features |
|---|---|---|---|
| VCS | Synopsys | IEEE 1801-2018 | Fastest compile, comprehensive debug |
| Questa | Siemens | IEEE 1801-2018 | Advanced waveform debug, power-aware coverage |
| Xcelium | Cadence | IEEE 1801-2018 | Multi-step flow, low-power debug |
| Riviera-PRO | Aldec | IEEE 1801-2015 | Cost-effective, good for smaller designs |
Configuring Power-Aware Simulation
Each simulator requires specific compilation and runtime options to enable power-aware mode. It goes something like what is shown below, but check your vendor's tool manual or user guide for exact arguments.
VCS (Synopsys) Configuration
# Compile RTL with UPF support
vcs -sverilog \
-full64 \
-debug_access+all \
-upf top_level.upf \ # Load UPF file
-upf_elab_mode mixed \ # Mixed elaboration mode
-upf_power_state_checks on \ # Enable power state checks
top_tb.sv \
design/*.sv
# Run simulation with power-aware options
./simv \
+upf_verbose \ # Verbose UPF messages
+upf_dump_supply_status \ # Dump supply status
+upf_iso_warn \ # Warn on isolation violations
+upf_ret_warn # Warn on retention violations
Questa (Siemens) Configuration
# Compile RTL with UPF
vlog -sv \
-hazards \
-upf top_level.upf \
design/*.sv
# Elaborate with power-aware mode
vopt top_tb \
-o top_opt \
-debug \
-pa \ # Enable power-aware mode
-pa_checks=s \ # Standard power-aware checks
-pa_upf=top_level.upf
# Run simulation
vsim top_opt \
-pa \
-pa_highlight \ # Highlight power-aware objects
-pa_enable=config+check+x # Enable all PA features
Xcelium (Cadence) Configuration
# Compile with UPF
xrun -sv \
-elaborate \
-upf top_level.upf \
-upf_enable_hdl_compile \ # Enable UPF-aware HDL compile
-upf_enable_supply_check \ # Enable supply checks
design/*.sv \
top_tb.sv
Start with Minimal UPF: For your first power-aware simulation, start with a minimal UPF file containing just domains and supplies. Once that simulates cleanly, incrementally add power switches, isolation, and retention. This makes debug much easier than loading a complete UPF all at once.
X-Propagation Verification
The most critical verification task: ensuring that X's from powered-down domains don't corrupt active logic.
Without Isolation: X-Propagation Bug
// GPU module (will be powered down)
module gpu(
input logic clk,
input logic [31:0] request,
output logic [31:0] response
);
always_ff @(posedge clk)
response <= request + 32'd1;
endmodule
// Top-level integration (NoC interconnect)
module top;
logic gpu_power_enable;
logic [31:0] gpu_to_noc; // Signal from GPU to NoC
gpu gpu_inst(
.clk(clk),
.request(noc_to_gpu),
.response(gpu_to_noc) // Will become X when GPU powers down!
);
// NoC uses GPU response - will see X's!
noc_router router(
.input_data(gpu_to_noc), // X-propagation here!
.output_data(noc_output)
);
endmodule
# UPF WITHOUT isolation (buggy)
create_power_domain PD_GPU -elements {gpu_inst}
create_power_domain PD_TOP -include_scope
# Power switch for GPU
create_power_switch PSW_GPU -domain PD_GPU \
-input_supply_port {vin VDD} \
-output_supply_port {vout VDD_GPU} \
-control_port {ctrl gpu_power_enable} \
-on_state {on vin {ctrl}}
# NO ISOLATION SPECIFIED - BUG!
Simulation output when GPU powers down:
# Time 1000ns: GPU power disabled UPF-INFO: Power domain PD_GPU transitioning to OFF state UPF-WARN: Signal 'gpu_to_noc' driven by powered-off domain PD_GPU UPF-WARN: X-propagation detected from PD_GPU to PD_TOP UPF-ERROR: Signal 'gpu_to_noc' = 32'hXXXXXXXX propagating to active domain UPF-ERROR: X-propagation to always-on logic at noc_router.input_data FATAL: X-state detected in memory write address - simulation cannot continue
With Isolation: Correct Behavior
# UPF WITH isolation (correct)
create_power_domain PD_GPU -elements {gpu_inst}
create_power_domain PD_TOP -include_scope
# Power switch for GPU
create_power_switch PSW_GPU -domain PD_GPU \
-input_supply_port {vin VDD} \
-output_supply_port {vout VDD_GPU} \
-control_port {ctrl gpu_power_enable} \
-on_state {on vin {ctrl}}
# Isolation cells - CRITICAL for X-prevention
set_isolation ISO_GPU -domain PD_GPU \
-isolation_supply_set SS_TOP \ # Always-on supply!
-clamp_value 0 \ # Clamp to 0 when isolated
-isolation_signal gpu_iso_enable \
-isolation_sense high \ # Active high
-location parent # Place in parent (always-on) domain
Simulation output with isolation:
# Time 1000ns: Enable isolation before powering down GPU UPF-INFO: Isolation ISO_GPU enabled (gpu_iso_enable=1) UPF-INFO: Signal 'gpu_to_noc' clamped to 32'h00000000 # Time 1020ns: GPU power disabled UPF-INFO: Power domain PD_GPU transitioning to OFF state UPF-INFO: GPU outputs isolated - no X-propagation # Time 5000ns: GPU power re-enabled UPF-INFO: Power domain PD_GPU transitioning to ON state # Time 5020ns: Disable isolation after GPU stable UPF-INFO: Isolation ISO_GPU disabled (gpu_iso_enable=0) UPF-INFO: Signal 'gpu_to_noc' driven by PD_GPU (active)
Most simulators support multiple X-checking levels: warning (reports X's but continues), error (reports X's and may stop), fatal (stops immediately on X). Start with warning level during initial debug, then increase to error for regression testing.
Quick Check
Q: Why must isolation cells be placed in the parent domain rather than the powered-down domain?
Show Answer
Isolation cells must remain powered to actively drive the clamp value (0 or 1) when the source domain is off. If they were in the powered-down domain, they would lose power along with that domain, leaving outputs floating (the exact problem isolation is meant to prevent). Isolation cells must use always-on supply from the parent domain.
Retention Verification
Retention cells must correctly save state before power-down and restore it after power-up. Power-aware simulation models this behavior.
Retention Testbench Pattern
// Testbench for retention verification
module retention_tb;
logic clk, rst_n;
logic cpu_power_enable;
logic cpu_save_enable;
logic cpu_restore_enable;
logic [31:0] cpu_state;
// CPU with retention
cpu_core cpu(
.clk(clk),
.rst_n(rst_n),
.state_out(cpu_state)
);
initial begin
// 1. Normal operation - write known pattern
cpu_power_enable = 1;
cpu_save_enable = 0;
cpu_restore_enable = 0;
repeat(100) @(posedge clk);
// Capture state before power-down
logic [31:0] saved_state = cpu_state;
$display("Time %0t: State before power-down = 0x%h", $time, saved_state);
// 2. Assert save signal (retention cells capture state)
cpu_save_enable = 1;
@(posedge clk);
cpu_save_enable = 0;
$display("Time %0t: Retention save triggered", $time);
// 3. Power down CPU
cpu_power_enable = 0;
@(posedge clk);
$display("Time %0t: CPU powered down", $time);
// 4. Wait in power-down state (retention cells hold state)
repeat(1000) @(posedge clk);
// 5. Power up CPU
cpu_power_enable = 1;
repeat(10) @(posedge clk); // Wait for power stable
$display("Time %0t: CPU powered up", $time);
// 6. Assert restore signal (retention cells restore state)
cpu_restore_enable = 1;
@(posedge clk);
cpu_restore_enable = 0;
$display("Time %0t: Retention restore triggered", $time);
// 7. Verify state was retained
repeat(5) @(posedge clk);
if (cpu_state === saved_state) begin
$display("PASS: State correctly retained (0x%h)", cpu_state);
end else begin
$error("FAIL: State corrupted! Expected 0x%h, got 0x%h",
saved_state, cpu_state);
end
end
endmodule
UPF for Retention Verification
# Retention strategy for CPU
set_retention RET_CPU -domain PD_CPU \
-retention_supply_set SS_CPU_RET \ # Always-on retention supply
-save_signal {cpu_save_enable high} \
-restore_signal {cpu_restore_enable high}
# Map to retention cells in library
map_retention_cell RET_CPU \
-lib_cells {DFFR_X1} # Retention flip-flop from liberty
# Define retention supply (always-on, low voltage)
create_supply_net VDD_RET
add_power_state VDD_RET \
-state {ON 0.6} # Retention voltage
Simulation output for retention:
Time 1000ns: State before power-down = 0xDEADBEEF UPF-INFO: Retention save signal asserted (cpu_save_enable=1) UPF-INFO: Retention cells in RET_CPU capturing state UPF-INFO: 1024 flip-flops saved to retention cells Time 1020ns: Retention save triggered Time 1040ns: CPU powered down UPF-INFO: Power domain PD_CPU transitioning to OFF state UPF-INFO: Primary supply VDD_CPU = 0.0V UPF-INFO: Retention supply VDD_RET = 0.6V (active) Time 10000ns: CPU powered up UPF-INFO: Power domain PD_CPU transitioning to ON state UPF-INFO: Primary supply VDD_CPU = 1.0V Time 10100ns: Retention restore triggered UPF-INFO: Retention restore signal asserted (cpu_restore_enable=1) UPF-INFO: Retention cells in RET_CPU restoring state UPF-INFO: 1024 flip-flops restored from retention cells PASS: State correctly retained (0xDEADBEEF)
Retention Signal Timing is Critical: Save must be asserted before power-down, and restore must be asserted after power-up stabilizes. Incorrect timing will cause state corruption. Power-aware simulation checks this timing and reports violations.
Power State Transition Verification
Verify that power state transitions follow legal paths defined in your Power State Table (PST).
Power State Table Example
# Define power states for GPU
add_port_state VDD_GPU \
-state {ON 1.0} \
-state {SLOW 0.8} \
-state {SLEEP 0.6} \
-state {OFF off}
# Define legal state transitions
add_pst GPU_PST -supplies {VDD_GPU}
add_pst_state ACTIVE -state {VDD_GPU ON}
add_pst_state DVFS -state {VDD_GPU SLOW}
add_pst_state STANDBY -state {VDD_GPU SLEEP}
add_pst_state POWEROFF -state {VDD_GPU OFF}
# Legal transitions (directed graph)
# ACTIVE <-> DVFS <-> STANDBY -> POWEROFF -> ACTIVE
Illegal Transition Detection
// Testbench that attempts illegal transition
initial begin
// Start in ACTIVE state
set_gpu_voltage(1.0); // ON
#1000;
// Legal: ACTIVE -> DVFS
set_gpu_voltage(0.8); // SLOW - OK
#1000;
// ILLEGAL: DVFS -> POWEROFF (must go through STANDBY)
set_gpu_voltage(0.0); // OFF - VIOLATION!
#1000;
end
Simulation output:
Time 0ns: GPU state = ACTIVE (VDD_GPU = 1.0V) Time 1000ns: GPU state transition ACTIVE -> DVFS UPF-INFO: Legal state transition Time 2000ns: GPU state transition DVFS -> POWEROFF UPF-ERROR: Illegal power state transition! UPF-ERROR: PST GPU_PST does not allow DVFS -> POWEROFF UPF-ERROR: Legal path: DVFS -> STANDBY -> POWEROFF ERROR: Simulation stopped due to illegal power state transition
Common Simulation Issues and Debug
Common Beginner Mistakes
Mistake #1: Isolation cells powered down with domain
Symptom: X-propagation warnings even though isolation is specified.
UPF-WARN: Isolation cell ISO_GPU_response_0 in powered-off domain UPF-ERROR: Isolation cell cannot function - supply OFF UPF-ERROR: X-propagation from gpu_to_noc
Root cause: Isolation cells placed in wrong domain (GPU instead of parent).
# WRONG: Isolation in GPU domain (powers down with GPU!)
set_isolation ISO_GPU -domain PD_GPU \
-isolation_supply_set SS_GPU \ # BUG: Uses GPU supply!
-location self # BUG: In GPU domain!
# CORRECT: Isolation in parent domain with always-on supply
set_isolation ISO_GPU -domain PD_GPU \
-isolation_supply_set SS_TOP \ # Always-on supply
-location parent # In parent (always-on) domain
Mistake #2: Retention restore before power stable
Symptom: Retained state corrupted after power-up.
UPF-WARN: Retention restore asserted while VDD_CPU ramping UPF-ERROR: Restore occurred at VDD_CPU = 0.73V (below 0.9V threshold) UPF-ERROR: Retention data corrupted during restore
Root cause: Restore signal asserted too early, before supply voltage stabilized.
// WRONG: Restore immediately after power enable
cpu_power_enable = 1;
@(posedge clk);
cpu_restore_enable = 1; // BUG: Too early! Supply not stable yet
// CORRECT: Wait for power stable before restore
cpu_power_enable = 1;
repeat(20) @(posedge clk); // Wait for VDD ramp-up
cpu_restore_enable = 1; // Now safe to restore
Mistake #3: Missing supply definitions
Symptom: Simulator fails to elaborate UPF.
UPF-ERROR: Supply net 'VDD_GPU' not defined in domain PD_GPU UPF-ERROR: Cannot create power switch PSW_GPU UPF-FATAL: UPF elaboration failed
Root cause: Supply nets must be defined before use in power switches.
# WRONG: Power switch before supply net defined
create_power_switch PSW_GPU -domain PD_GPU \
-output_supply_port {vout VDD_GPU} # Error: VDD_GPU doesn't exist!
create_supply_net VDD_GPU # Too late!
# CORRECT: Define supplies first
create_supply_net VDD_GPU
create_power_switch PSW_GPU -domain PD_GPU \
-output_supply_port {vout VDD_GPU} # OK: VDD_GPU exists
Mistake #4: Running traditional simulation instead of power-aware
# WRONG: Normal simulation (no UPF)
vcs -sverilog top_tb.sv design/*.sv
./simv
# UPF ignored - no power-aware behavior!
# CORRECT: Power-aware simulation
vcs -sverilog -upf top_level.upf top_tb.sv design/*.sv
./simv +upf_iso_warn +upf_ret_warn
Why? Without power-aware mode, the simulator treats UPF as comments and doesn't model power management behavior.
Practice Exercise
Challenge: Camera ISP Retention Verification
You're verifying a camera ISP (Image Signal Processor) with two power domains: PD_ISP (main processing) and PD_ISP_MEM (image buffer memory). The ISP can power down between frames, but the memory must retain the previous frame for motion detection.
Given RTL:
module camera_isp(
input logic clk,
input logic rst_n,
input logic isp_power_enable,
input logic [23:0] pixel_in,
output logic [23:0] pixel_out,
output logic frame_ready
);
// Image buffer (should be retained)
logic [23:0] frame_buffer [0:1023];
// Processing pipeline (can lose state)
logic [23:0] pipeline_stage1, pipeline_stage2;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
pipeline_stage1 <= '0;
pipeline_stage2 <= '0;
end else begin
pipeline_stage1 <= pixel_in;
pipeline_stage2 <= pipeline_stage1;
pixel_out <= pipeline_stage2;
end
end
endmodule
Task: Create a power-aware testbench that:
- Processes one frame with ISP powered on
- Saves frame_buffer to retention cells
- Powers down PD_ISP (but keeps PD_ISP_MEM retention supply active)
- Waits 1000 cycles in powered-down state
- Powers up PD_ISP
- Restores frame_buffer from retention cells
- Verifies that frame_buffer contents match pre-power-down values
Hint 1
Use a dynamic array to save frame_buffer contents before power-down: logic [23:0] saved_frame [0:1023]; foreach(frame_buffer[i]) saved_frame[i] = frame_buffer[i];
Hint 2
The retention save/restore sequence should be: 1) assert save, 2) wait 1 cycle, 3) deassert save, 4) power down, 5) wait, 6) power up, 7) wait for stable, 8) assert restore, 9) wait 1 cycle, 10) deassert restore.
Hint 3
Use === (case equality) instead of == to detect X's in comparison: if (frame_buffer[i] !== saved_frame[i]) will catch both value mismatches and X-corruption.
Solution
module camera_isp_power_tb;
logic clk, rst_n;
logic isp_power_enable;
logic isp_save_enable;
logic isp_restore_enable;
logic [23:0] pixel_in;
logic [23:0] pixel_out;
logic frame_ready;
// Reference frame buffer for verification
logic [23:0] saved_frame [0:1023];
integer errors = 0;
// DUT
camera_isp dut(
.clk(clk),
.rst_n(rst_n),
.isp_power_enable(isp_power_enable),
.pixel_in(pixel_in),
.pixel_out(pixel_out),
.frame_ready(frame_ready)
);
// Clock generation
initial clk = 0;
always #5 clk = ~clk;
// Main test
initial begin
// Initialize
rst_n = 0;
isp_power_enable = 1;
isp_save_enable = 0;
isp_restore_enable = 0;
pixel_in = 0;
repeat(10) @(posedge clk);
rst_n = 1;
// 1. Process one frame (1024 pixels) with ISP powered on
$display("Time %0t: Processing frame with ISP powered on", $time);
for (int i = 0; i < 1024; i++) begin
pixel_in = $random();
@(posedge clk);
end
// Wait for pipeline to flush
repeat(10) @(posedge clk);
// 2. Save frame_buffer for later verification
$display("Time %0t: Saving frame buffer contents", $time);
for (int i = 0; i < 1024; i++) begin
saved_frame[i] = dut.frame_buffer[i];
end
// 3. Assert retention save signal
$display("Time %0t: Asserting retention save", $time);
isp_save_enable = 1;
@(posedge clk);
isp_save_enable = 0;
@(posedge clk);
// 4. Power down ISP
$display("Time %0t: Powering down ISP (memory retention active)", $time);
isp_power_enable = 0;
@(posedge clk);
// 5. Wait in powered-down state
$display("Time %0t: Waiting 1000 cycles in power-down", $time);
repeat(1000) @(posedge clk);
// 6. Power up ISP
$display("Time %0t: Powering up ISP", $time);
isp_power_enable = 1;
// 7. Wait for power supply to stabilize
repeat(20) @(posedge clk);
// 8. Assert retention restore signal
$display("Time %0t: Asserting retention restore", $time);
isp_restore_enable = 1;
@(posedge clk);
isp_restore_enable = 0;
// 9. Wait for restore to complete
repeat(5) @(posedge clk);
// 10. Verify frame_buffer was retained
$display("Time %0t: Verifying frame buffer retention", $time);
errors = 0;
for (int i = 0; i < 1024; i++) begin
if (dut.frame_buffer[i] !== saved_frame[i]) begin
$error("Frame buffer[%0d] mismatch! Expected 0x%h, got 0x%h",
i, saved_frame[i], dut.frame_buffer[i]);
errors++;
end
end
// Report results
if (errors == 0) begin
$display("========================================");
$display("PASS: All 1024 pixels correctly retained!");
$display("========================================");
end else begin
$display("========================================");
$error("FAIL: %0d/%0d pixels corrupted", errors, 1024);
$display("========================================");
end
$finish;
end
// Timeout watchdog
initial begin
#1000000;
$error("TIMEOUT: Test did not complete in 1ms");
$finish;
end
endmodule
Expected simulation output:
Time 100ns: Processing frame with ISP powered on Time 10340ns: Saving frame buffer contents Time 10340ns: Asserting retention save UPF-INFO: Retention save signal asserted (isp_save_enable=1) UPF-INFO: Capturing 1024 words to retention cells Time 10360ns: Powering down ISP (memory retention active) UPF-INFO: Power domain PD_ISP transitioning to OFF UPF-INFO: Retention supply VDD_RET remains active (0.6V) Time 10360ns: Waiting 1000 cycles in power-down Time 20360ns: Powering up ISP UPF-INFO: Power domain PD_ISP transitioning to ON Time 20560ns: Asserting retention restore UPF-INFO: Retention restore signal asserted (isp_restore_enable=1) UPF-INFO: Restoring 1024 words from retention cells Time 20610ns: Verifying frame buffer retention ======================================== PASS: All 1024 pixels correctly retained! ========================================
Summary
In this tutorial, you learned:
- Power-aware simulation extends RTL simulation to model power-down, X-propagation, isolation, retention, and state transitions defined in UPF
- All major simulators (VCS, Questa, Xcelium) support IEEE 1801 UPF with specific compilation and runtime flags
- X-propagation verification ensures isolation cells prevent X's from powered-down domains from corrupting active logic
- Retention verification validates save-before-powerdown and restore-after-powerup timing and correctness
- Power state transition verification checks legal paths defined in Power State Tables (PST)
- Common issues include misplaced isolation cells (in powered-down domain), early retention restore (before power stable), and missing supply definitions
- Power-aware simulation catches critical bugs that traditional simulation cannot detect, preventing expensive silicon respins