Why Learn Power-Aware Simulation? Traditional RTL simulation assumes stable power supplies and doesn't model power-off behavior, X-propagation from powered-down domains, or retention cell operation. Power-aware simulation extends RTL simulation to verify that your UPF power intent correctly manages these low-power scenarios, catching critical bugs before tape-out that would be catastrophic in silicon. In this tutorial, you'll master power-aware simulation techniques to verify UPF designs thoroughly.

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:

SimulatorVendorUPF SupportKey Features
VCSSynopsysIEEE 1801-2018Fastest compile, comprehensive debug
QuestaSiemensIEEE 1801-2018Advanced waveform debug, power-aware coverage
XceliumCadenceIEEE 1801-2018Multi-step flow, low-power debug
Riviera-PROAldecIEEE 1801-2015Cost-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:

Output
# 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:

Output
# 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:

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

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.

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

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

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

  1. Processes one frame with ISP powered on
  2. Saves frame_buffer to retention cells
  3. Powers down PD_ISP (but keeps PD_ISP_MEM retention supply active)
  4. Waits 1000 cycles in powered-down state
  5. Powers up PD_ISP
  6. Restores frame_buffer from retention cells
  7. 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:

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
Did you find what you were looking for?

Quiz

No quiz questions available for this article.