Why Learn UPF Verification Strategies? Power-aware simulation verifies dynamic behavior, but it can't catch all UPF issues. Comprehensive verification requires multiple complementary techniques: static checking for syntax and semantic errors, linting for quality and best practices, consistency validation across RTL and UPF, and formal verification for exhaustive coverage. In this tutorial, you'll master a complete multi-layered verification strategy that catches UPF bugs before they reach silicon.

14 min read | Advanced Level

What You'll Learn

  • Apply static UPF checking to catch syntax and semantic errors
  • Use UPF linting tools to enforce quality and best practices
  • Validate UPF-RTL consistency and completeness
  • Build automated verification flows for continuous integration

The $5M UPF Bug

Your automotive SoC completed power-aware simulation with zero errors. The testbench exercised all power domains, tested retention and isolation, and achieved 100% power coverage. You proceeded to synthesis, which also completed successfully. Place-and-route inserted 45,000 isolation cells, 12,000 retention cells, and 380 power switches. Everything looked perfect.

During gate-level simulation (GLS), you discovered that 15% of your isolation cells were inserted on signals that don't actually cross power domain boundaries—they're entirely within a single domain. These unnecessary cells add 8% area overhead and 12mW leakage power. Removing them requires re-running synthesis and P&R, delaying tape-out by 3 weeks and consuming $200K in engineering resources.

Worse, you found that your retention strategy has incomplete coverage—127 flip-flops that should be retained are not included in any set_retention command. The simulation testbench happened to reinitialize these registers after power-up, masking the bug. In actual silicon use-cases, this causes intermittent failures after deep sleep.

A UPF linting tool would have caught both issues in 5 minutes, before synthesis. Bugs caught by linting cost hours to fix; bugs caught in GLS cost weeks; bugs caught in silicon cost months and millions of dollars.

The UPF Verification Pyramid

Effective UPF verification uses multiple layers, each catching different classes of bugs:

                +---------------------+
                ¦  Formal Verification¦  ? Exhaustive proofs
                ¦   (slowest, most    ¦
                ¦    comprehensive)   ¦
                +---------------------+
            +-----------------------------+
            ¦   Power-Aware Simulation    ¦  ? Dynamic behavior
            ¦   (medium speed, coverage-  ¦
            ¦      dependent)             ¦
            +-----------------------------+
        +---------------------------------------+
        ¦   Consistency Validation & Linting    ¦  ? Quality & completeness
        ¦   (fast, catches common bugs)         ¦
        +---------------------------------------+
    +-----------------------------------------------+
    ¦         Static Checking (UPF Syntax)          ¦  ? Basic correctness
    ¦         (fastest, catches syntax errors)      ¦
    +-----------------------------------------------+

Each layer builds on the previous, with faster checks at the bottom catching obvious errors, and slower checks at the top providing comprehensive validation.

Multi-Layered Verification in Practice

A mobile processor team integrated UPF linting into their CI pipeline, running SpyGlass LP on every commit. In the first week, the tool flagged 127 violations across their 3,500-line UPF codebase. After fixing critical issues and documenting acceptable warnings as waivers, the automated flow caught an average of 3.2 UPF bugs per week over 6 months. The estimated cost savings (avoided respins, reduced debug time) was $1.8M. Most importantly: first silicon worked correctly on all power management features with zero UPF-related bugs.

Layer 1: Static UPF Checking

Static checking validates UPF syntax and basic semantic correctness without simulating or synthesizing the design.

Built-in Simulator Checks

Most simulators provide UPF static checking during elaboration:


# VCS static UPF check (no simulation)
vcs -sverilog \
    -upf top_level.upf \
    -upf_check_only \        # Check UPF without simulation
    -upf_verbose \
    design/*.sv

# Output: UPF syntax and semantic errors
Output
UPF-CHECK: Analyzing top_level.upf
UPF-CHECK: Checking power domain definitions... OK
UPF-CHECK: Checking supply network definitions... OK
UPF-ERROR: Line 45: Supply net 'VDD_GPU' used before definition
UPF-ERROR: Line 67: Power domain 'PD_UNKNOWN' referenced but not created
UPF-WARN: Line 89: Isolation strategy 'ISO_CPU' has no library cell mapping
UPF-CHECK: Found 2 errors, 1 warning

Common Static Errors

Error TypeDescriptionExample
Undefined referencesUsing entities before definitionPower switch references non-existent supply net
Duplicate definitionsCreating same entity twiceTwo domains with same name
Scope errorsInvalid hierarchical pathsDomain created for non-existent instance
Syntax errorsTCL syntax mistakesMissing braces, invalid command options
Circular dependenciesLoad order issuesFile A loads File B, File B loads File A

Example: Catching Undefined Supply Reference


# Buggy UPF - supply net used before definition
create_power_domain PD_GPU -elements {gpu_subsystem}

# BUG: VDD_GPU not yet defined!
create_power_switch PSW_GPU -domain PD_GPU \
  -input_supply_port {vin VDD} \
  -output_supply_port {vout VDD_GPU}  # Error: VDD_GPU undefined

# Too late - should be before power switch
create_supply_net VDD_GPU
Output
UPF-ERROR: Line 5: Supply net 'VDD_GPU' used in power switch but not defined
UPF-ERROR: create_power_switch failed

Fix: Define supply nets before using them in power switches:


# Fixed UPF - supplies before power switches
create_power_domain PD_GPU -elements {gpu_subsystem}
create_supply_net VDD_GPU  # Define first
create_power_switch PSW_GPU -domain PD_GPU \
  -input_supply_port {vin VDD} \
  -output_supply_port {vout VDD_GPU}  # OK: VDD_GPU exists

Layer 2: UPF Linting

Linting goes beyond syntax checking to enforce quality, best practices, and detect common mistakes.

Industry UPF Linting Tools

ToolVendorKey Features
SpyGlass LPSiemensComprehensive rule library, custom rules, waivers
VC LPSynopsysIntegrated with VCS, synthesis correlation
JasperGoldCadenceFormal-based checks, completeness analysis
Meridian LPSiemensCross-domain analysis, hierarchical checking

SpyGlass Low Power Example


# SpyGlass LP project file (project.prj)
read_file -type verilog design/*.sv
read_file -type upf top_level.upf

# Select lint goals
current_goal lint/upf_lint_goal

# Run analysis
run_goal

# Generate report
write_report upf_lint_report.rpt

Common Linting Violations

Violation #1: Isolation Cells in Powered-Down Domain


# Violation: Isolation cells will lose power!
set_isolation ISO_GPU -domain PD_GPU \
  -isolation_supply_set SS_GPU \  # Uses GPU supply (powers down!)
  -location self                  # In GPU domain (powers down!)
Output
LINT-ERROR: ISO_01 - Isolation cells powered by same domain they isolate
LINT-ERROR: Isolation 'ISO_GPU' uses supply set 'SS_GPU' from domain 'PD_GPU'
LINT-ERROR: When PD_GPU powers off, isolation cells will also power off
RECOMMENDATION: Use always-on supply set and location=parent

Violation #2: Retention Without Always-On Supply


# Violation: Retention supply powers down!
set_retention RET_CPU -domain PD_CPU \
  -retention_supply_set SS_CPU  # Uses CPU supply (powers down!)
Output
LINT-ERROR: RET_01 - Retention cells require always-on supply
LINT-ERROR: Retention 'RET_CPU' uses supply set 'SS_CPU'
LINT-ERROR: Supply 'VDD_CPU' in SS_CPU has OFF state - retention will be lost
RECOMMENDATION: Use dedicated retention supply (e.g., VDD_RET at 0.6V) with no OFF state

Violation #3: Unnecessary Level Shifters (Same Voltage)


# Both domains use same voltage - level shifter not needed
add_power_state VDD_CPU -state {ON 1.0}
add_power_state VDD_GPU -state {ON 1.0}  # Same as CPU!

set_level_shifter LS_CPU_TO_GPU -domain PD_GPU \
  -applies_to inputs \
  -source PD_CPU  # Unnecessary - both at 1.0V
Output
LINT-WARN: LS_01 - Unnecessary level shifter between same voltage domains
LINT-WARN: Level shifter 'LS_CPU_TO_GPU' between PD_CPU and PD_GPU
LINT-WARN: Both domains operate at 1.0V in all states
RECOMMENDATION: Remove level shifter or verify voltage states
IMPACT: Adds 15% area, 8% delay, 12mW power overhead

Violation #4: Missing Isolation on Power Domain Outputs


# Power domain with outputs but no isolation
create_power_domain PD_MODEM -elements {modem_subsystem}
create_power_switch PSW_MODEM -domain PD_MODEM \
  -input_supply_port {vin VDD} \
  -output_supply_port {vout VDD_MODEM} \
  -control_port {ctrl modem_power_en}
# BUG: No set_isolation command!
Output
LINT-ERROR: ISO_02 - Switchable domain with outputs requires isolation
LINT-ERROR: Domain 'PD_MODEM' has power switch (can turn off)
LINT-ERROR: Domain has 47 output signals crossing to always-on domains
LINT-ERROR: No isolation strategy defined - X-propagation risk
RECOMMENDATION: Add set_isolation for domain outputs

Violation #5: Incomplete Retention Coverage


# Only some flip-flops retained
set_retention RET_CPU -domain PD_CPU \
  -retention_supply_set SS_CPU_RET \
  -elements {cpu_subsystem/control_regs}  # Only control registers!
# BUG: cpu_subsystem/data_path_regs not retained!
Output
LINT-WARN: RET_02 - Incomplete retention coverage
LINT-WARN: Domain 'PD_CPU' contains 2,048 sequential elements
LINT-WARN: Retention 'RET_CPU' covers only 256 elements (12.5%)
LINT-WARN: 1,792 flip-flops will lose state during power-down
RECOMMENDATION: Add retention for remaining flip-flops or document why excluded

Quick Check

Q: Why does a linting tool flag unnecessary level shifters as a warning rather than just accepting them?

Show Answer

Unnecessary level shifters add significant overhead: 15% area, 8% delay, and 12mW power consumption per shifter. In a large design, hundreds of unnecessary shifters can add 10-20mm² die area and 500mW+ power. Linting tools flag these to prevent waste, but allow waivers for cases where level shifters serve other purposes (e.g., voltage tolerance, noise immunity).

Layer 3: UPF-RTL Consistency Validation

Validates that UPF power intent is consistent with RTL structure and behavior.

Consistency Check #1: Power Domain Instance Existence

Verify that all instances referenced in create_power_domain exist in RTL:


# UPF creates domain for 'gpu_subsystem'
create_power_domain PD_GPU -elements {gpu_subsystem}

Check: Does RTL contain an instance named gpu_subsystem?

Output
CONSISTENCY-ERROR: Power domain instance mismatch
UPF defines: create_power_domain PD_GPU -elements {gpu_subsystem}
RTL instances: cpu_cluster, video_codec, modem_subsystem
ERROR: Instance 'gpu_subsystem' not found in RTL
POSSIBLE CAUSE: Renamed from 'gpu_subsystem' to 'graphics_processor'?

Consistency Check #2: Control Signal Existence

Verify that power control signals exist in RTL:


# UPF references control signals
create_power_switch PSW_GPU -domain PD_GPU \
  -control_port {ctrl gpu_power_enable}  # Must exist in RTL!

set_isolation ISO_GPU -domain PD_GPU \
  -isolation_signal gpu_iso_enable  # Must exist in RTL!

Check: Do signals gpu_power_enable and gpu_iso_enable exist?

Output
CONSISTENCY-ERROR: Control signal not found in RTL
UPF: -control_port {ctrl gpu_power_enable}
RTL: Signal 'gpu_power_enable' not found in design
AVAILABLE SIGNALS: gpu_pwren, gpu_clk_en, gpu_reset_n
RECOMMENDATION: Update UPF to use 'gpu_pwren' or add signal to RTL

Consistency Check #3: Cross-Domain Signal Analysis

Identify signals that cross power domain boundaries and verify they have appropriate isolation/level shifters:


// RTL: Signal crosses from PD_CPU to PD_GPU
module top;
  logic [31:0] cpu_to_gpu_data;  // Crosses domains!

  cpu_core cpu(.output_data(cpu_to_gpu_data));
  gpu_core gpu(.input_data(cpu_to_gpu_data));
endmodule
Output
CONSISTENCY-WARN: Cross-domain signal without isolation or level shifter
Signal: cpu_to_gpu_data [31:0]
Source: PD_CPU (instance 'cpu')
Destination: PD_GPU (instance 'gpu')
UPF: No isolation or level shifter defined for this signal
RECOMMENDATION: Add isolation if PD_GPU can power off, or level shifter if voltages differ

Consistency Check #4: Retention Element Coverage

Verify that all flip-flops in a switched domain are either retained or documented as non-critical:


// RTL: CPU subsystem with flip-flops
module cpu_subsystem(
  input logic clk,
  // ...
);
  logic [31:0] pc;           // Program counter - CRITICAL
  logic [31:0] regs [0:31];  // Register file - CRITICAL
  logic [7:0] pipeline_tag;  // Pipeline tag - NON-CRITICAL (can lose)

  always_ff @(posedge clk) begin
    pc <= next_pc;
    regs[rd] <= alu_result;
    pipeline_tag <= next_tag;
  end
endmodule

# UPF: Retention only for PC and register file
set_retention RET_CPU -domain PD_CPU \
  -retention_supply_set SS_CPU_RET \
  -elements {cpu_subsystem/pc cpu_subsystem/regs}
# pipeline_tag not retained (acceptable - non-critical state)

Check: Are there flip-flops without retention? Are they intentionally excluded?

Output
CONSISTENCY-INFO: Retention coverage analysis
Domain: PD_CPU
Total sequential elements: 1,057
Retained elements: 1,056 (99.9%)
Non-retained elements: 1
  - cpu_subsystem/pipeline_tag [7:0]

RECOMMENDATION: Document why 'pipeline_tag' is not retained, or add to retention set

Automated Verification Flow

Integrate UPF verification into continuous integration (CI) for automated checking on every commit.

Multi-Stage Verification Makefile


# Makefile for automated UPF verification
.PHONY: all static lint consistency simulate report

all: static lint consistency simulate report

# Stage 1: Static UPF syntax checking
static:
	@echo "=== Running UPF Static Checks ==="
	vcs -sverilog -upf top_level.upf -upf_check_only design/*.sv \
	  2>&1 | tee logs/upf_static.log
	@grep -q "ERROR" logs/upf_static.log && exit 1 || exit 0

# Stage 2: UPF linting
lint:
	@echo "=== Running UPF Linting ==="
	spyglass -project upf_lint.prj -batch \
	  2>&1 | tee logs/upf_lint.log
	@grep -q "LINT-ERROR" logs/upf_lint.log && exit 1 || exit 0

# Stage 3: UPF-RTL consistency validation
consistency:
	@echo "=== Running UPF-RTL Consistency Checks ==="
	python3 scripts/upf_consistency_check.py \
	  --upf top_level.upf \
	  --rtl design/*.sv \
	  --report logs/consistency.rpt
	@grep -q "ERROR" logs/consistency.rpt && exit 1 || exit 0

# Stage 4: Power-aware simulation
simulate:
	@echo "=== Running Power-Aware Simulation ==="
	vcs -sverilog -upf top_level.upf -full64 \
	  -debug_access+all design/*.sv testbench/power_tb.sv
	./simv +upf_iso_warn +upf_ret_warn \
	  2>&1 | tee logs/simulation.log
	@grep -q "UPF-ERROR" logs/simulation.log && exit 1 || exit 0

# Stage 5: Generate summary report
report:
	@echo "=== Generating UPF Verification Report ==="
	python3 scripts/generate_upf_report.py \
	  --static logs/upf_static.log \
	  --lint logs/upf_lint.log \
	  --consistency logs/consistency.rpt \
	  --simulation logs/simulation.log \
	  --output reports/upf_verification_summary.html
	@echo "Report available at: reports/upf_verification_summary.html"

clean:
	rm -rf logs/* reports/* simv* csrc ucli.key

Run make static first to catch syntax errors quickly (takes seconds), then make lint for quality checks (takes minutes), then make consistency, and finally make simulate for the slowest but most comprehensive checks. Fix errors at each layer before proceeding to the next.

Common Beginner Mistakes

Mistake #1: Skipping static checks before simulation


# WRONG: Jump straight to simulation
vcs -sverilog -upf buggy.upf design/*.sv
./simv  # Simulation crashes due to UPF syntax errors

# CORRECT: Static check first
vcs -sverilog -upf buggy.upf -upf_check_only design/*.sv
# Fix errors, then simulate
vcs -sverilog -upf fixed.upf design/*.sv
./simv

Why? Static checks take seconds and catch basic errors. Simulation takes minutes to hours. Always run fast checks first.

Mistake #2: Ignoring lint warnings

Output
LINT-WARN: Unnecessary level shifter (adds 12mW power)
LINT-WARN: Isolation cell location suboptimal (adds 5% delay)
LINT-WARN: Retention coverage incomplete (1,200 flip-flops not retained)
... 47 more warnings ...
Build: SUCCESS (with 50 warnings)

Problem: Warnings indicate real issues that cause area/power/performance overhead or functional bugs. Treat warnings as errors during development.

Mistake #3: Not verifying after UPF changes


# Developer makes quick UPF change
$ vi top_level.upf  # Change isolation location from self to parent
$ git commit -m "Fix isolation placement"
$ git push
# No verification run - breaks synthesis!

Fix: Always run verification after UPF changes, just like RTL changes:


$ vi top_level.upf
$ make all  # Runs static, lint, consistency, simulate
$ git commit -m "Fix isolation placement (verified)"
$ git push

Mistake #4: Not maintaining verification documentation

Document intentional exceptions to verification rules:

# upf_verification_waivers.txt
# Waiver: pipeline_tag flip-flops intentionally not retained
# Reason: Non-critical state, reinitialized on wake-up
# Approver: John Smith, 2024-01-15
WAIVE RET_02 cpu_subsystem/pipeline_tag

# Waiver: Level shifter LS_CPU_TO_NOC appears unnecessary (both 1.0V)
# Reason: NoC uses wide voltage range (0.8-1.1V), CPU uses narrow (0.95-1.05V)
#         Level shifter needed for voltage tolerance
# Approver: Jane Doe, 2024-01-20
WAIVE LS_01 LS_CPU_TO_NOC

Why? Waivers document design decisions and prevent the same warnings from being investigated repeatedly by different engineers.

Practice Exercise

Challenge: Build UPF Verification Script

You've inherited a UPF file from a previous engineer. Your task is to create a comprehensive verification script that checks for common issues.

Given buggy UPF file:


# legacy.upf - Inherited UPF with multiple issues
create_power_domain PD_TOP -include_scope
create_power_domain PD_CPU -elements {cpu_core}
create_power_domain PD_GPU -elements {graphics_processor}  # Renamed in RTL!

# Power switch for CPU (missing supply net definition!)
create_power_switch PSW_CPU -domain PD_CPU \
  -input_supply_port {vin VDD} \
  -output_supply_port {vout VDD_CPU} \
  -control_port {ctrl cpu_pwr_en}  # Signal name changed in RTL!

# Isolation in wrong location
set_isolation ISO_CPU -domain PD_CPU \
  -isolation_supply_set SS_CPU \  # Uses CPU supply (powers down!)
  -clamp_value 0 \
  -isolation_signal cpu_iso \
  -location self  # Wrong - should be parent!

# Retention with wrong supply
set_retention RET_CPU -domain PD_CPU \
  -retention_supply_set SS_CPU  # Uses switchable supply!

# GPU has no power management (missing isolation, switch, etc.)

Task: Create a verification script that detects:

  1. Undefined supply nets used before definition
  2. Power domain instances that don't exist in RTL
  3. Control signals that don't exist in RTL
  4. Isolation cells using powered-down supply
  5. Isolation cells in wrong location (self vs. parent)
  6. Retention cells using non-always-on supply
  7. Switchable domains without isolation
Hint 1

Parse the UPF file sequentially, tracking defined entities (supply nets, domains) in a dictionary. Flag any usage before definition.

Hint 2

For RTL consistency, you'll need to parse the RTL to extract instance names and signal names. Use regular expressions to match module_name instance_name ( and logic signal_name patterns.

Hint 3

For isolation location check: isolation in a switchable domain should use -location parent or -location fanout, not -location self.

Solution

A complete Python verification script would be approximately 200 lines. Key components:

  • Parse UPF sequentially, building sets of defined_supply_nets, defined_domains, switchable_domains
  • Parse RTL using regex to extract rtl_instances and rtl_signals
  • Check each UPF command for: undefined references, missing RTL elements, incorrect locations/supplies
  • Generate detailed error reports with line numbers and fix recommendations

The script should exit with non-zero status if errors are found, enabling CI integration.

Summary

In this tutorial, you learned:

  • UPF verification uses a multi-layered pyramid: static checking (fastest) ? linting ? consistency ? simulation ? formal (slowest, most comprehensive)
  • Static checking validates UPF syntax and basic semantic correctness using simulator built-in checks
  • UPF linting enforces quality and best practices, catching common mistakes like isolation in powered-down domains, retention without always-on supply, unnecessary level shifters, missing isolation, and incomplete retention coverage
  • Consistency validation ensures UPF matches RTL: instance existence, control signal existence, cross-domain coverage, retention coverage
  • Automated flows using Makefiles and CI pipelines enable verification on every commit, catching bugs early
  • Best practices: run static checks first, treat warnings as errors, verify after every UPF change, document waivers for intentional exceptions
  • Industry linting tools (SpyGlass LP, VC LP, JasperGold, Meridian LP) provide comprehensive rule libraries and customization
Did you find what you were looking for?

Quiz

No quiz questions available for this article.