14 min read | Advanced Level
What You'll Learn
- Implement automatic vs explicit level shifter insertion strategies
- Optimize level shifter placement for timing and power
- Handle bidirectional signals and special cases
- Apply advanced mapping and cell selection techniques
Automatic vs Explicit Level Shifter Insertion
UPF provides two approaches to level shifter insertion: automatic (tool-inferred) and explicit (designer-controlled).
Automatic Level Shifter Insertion
With automatic insertion, synthesis tools detect voltage boundaries and insert level shifters based on UPF supply definitions:
# Automatic mode: Tool infers where shifters are needed
set_level_shifter LS_AUTO -domain PD_CPU \
-applies_to outputs \
-location automatic
How it works:
- Tool analyzes supply voltage for each domain
- Detects signals crossing between different voltage levels
- Automatically inserts level shifters at crossings
- Chooses placement based on built-in heuristics
Benefits:
- Simpler UPF (minimal designer effort)
- Tool handles complexity of large designs
- Automatic updates if netlist changes
Drawbacks:
- Less control over placement
- May not optimize for critical timing paths
- Tool-dependent behavior
Explicit Level Shifter Insertion
With explicit insertion, designer specifies exact placement strategy:
# Explicit mode: Designer controls placement
set_level_shifter LS_CPU_OUT -domain PD_CPU \
-applies_to outputs \
-location self \ # Specific placement
-rule low_to_high # Specific direction
Benefits:
- Precise control over placement
- Can optimize critical paths
- Predictable behavior across tools
Drawbacks:
- More UPF code to maintain
- Designer must understand all voltage crossings
- Requires updates if design changes
Use automatic insertion for initial design exploration and non-critical paths. Switch to explicit insertion for timing-critical signals or when you need precise control over level shifter placement.
Hybrid Approach in Production
Large SoCs typically use a hybrid strategy: automatic insertion for the bulk of signals (thousands of voltage crossings), with explicit overrides for critical paths like high-speed interfaces, clock domain crossings, and performance-sensitive control signals. This balances designer productivity with timing optimization.
Advanced Placement Strategies
Level shifter placement significantly impacts timing, power, and routability.
Self Placement (Source Domain)
set_level_shifter LS_CPU_SELF -domain PD_CPU \
-applies_to outputs \
-location self
PD_CPU (1.0V) PD_TOP (1.2V) ┌──────────────────────┐ ┌─────────────┐ │ Logic │ │ │ │ ↓ │ │ │ │ [Level Shifter] ────┼────────>│ Logic │ │ (1.0V → 1.2V) │ 1.2V │ │ └──────────────────────┘ └─────────────┘
When to use:
- Source domain has slack (not timing-critical)
- Minimize routing distance in destination domain
- Source domain has available area
Parent Placement (Destination Domain)
set_level_shifter LS_CPU_PARENT -domain PD_CPU \
-applies_to outputs \
-location parent
PD_CPU (1.0V) PD_TOP (1.2V)
┌────────────┐ ┌───────────────────────┐
│ Logic ────┼───────>│ [Level Shifter] │
│ │ 1.0V │ (1.0V → 1.2V) │
└────────────┘ │ ↓ │
│ Logic (receives 1.2V)│
└───────────────────────┘
When to use:
- Destination domain has timing slack
- Source domain is area-constrained
- Better for isolation (shifter in always-on domain)
Fanout Placement (At Each Sink)
set_level_shifter LS_HIGH_FANOUT -domain PD_CPU \
-applies_to outputs \
-location fanout
PD_CPU (1.0V) PD_TOP (1.2V) ┌────────────┐ ┌───────────────────────┐ │ │ │ [LS] → Instance A │ │ │ │ │ │ Logic ────┼───────>│ [LS] → Instance B │ │ (high │ │ │ │ fanout) │ │ [LS] → Instance C │ └────────────┘ └───────────────────────┘
When to use:
- Signal has very high fanout (>20 sinks)
- Individual path timing optimization needed
- Minimize loading on source driver
Tradeoff: More area (multiple shifters) but better timing and drive strength per path.
Fanout placement creates one level shifter per sink, significantly increasing area. Use only when high fanout causes timing issues with single shifter approaches. For most signals, self or parent placement suffices.
Quick Check
Q: A critical signal crosses from 1.0V to 1.2V domain with 2ns timing margin in source and 0.1ns margin in destination. Where should the level shifter be placed?
Show Answer
Place the level shifter in the source domain (self). The source has 2ns slack to absorb the level shifter delay (typically 200-500ps), while the destination has only 0.1ns slack. Placing the shifter in the destination would likely cause a timing violation. Always place level shifters in the domain with more timing margin.
Rule-Based Level Shifter Insertion
The -rule option specifies which voltage transitions require shifters.
Unidirectional Rules
# Only low-to-high transitions (1.0V → 1.2V)
set_level_shifter LS_L2H -domain PD_CPU \
-applies_to outputs \
-rule low_to_high \
-location self
# Only high-to-low transitions (1.2V → 1.0V)
set_level_shifter LS_H2L -domain PD_IO \
-applies_to outputs \
-rule high_to_low \
-location self
Bidirectional Rule
# Both directions (default behavior)
set_level_shifter LS_BOTH -domain PD_CPU \
-applies_to both \
-rule both \
-location automatic
Use Cases for Directional Rules
| Scenario | Rule | Rationale |
|---|---|---|
| Unidirectional interface | low_to_high or high_to_low | Signals only flow one direction |
| Bidirectional bus | both | Data flows both directions |
| Asymmetric voltage design | Specific direction only | One direction is same voltage |
Threshold-Based Level Shifter Insertion
The -threshold option controls minimum voltage difference for shifter insertion.
# Only insert shifters if ΔV > 0.2V
set_level_shifter LS_THRESHOLD -domain PD_GPU \
-applies_to outputs \
-threshold 0.2 \
-location automatic
Example: Conditional Insertion
| Source V | Dest V | ΔV | Threshold 0.2V | Shifter Inserted? |
|---|---|---|---|---|
| 1.0V | 1.05V | 0.05V | Below | No |
| 1.0V | 1.2V | 0.2V | At threshold | Yes |
| 0.9V | 1.2V | 0.3V | Above | Yes |
| 1.0V | 1.8V | 0.8V | Above | Yes |
Conservative practice: Set threshold to 0.0V (insert shifters for any voltage difference) or very low value (0.05V). Small voltage differences can still cause reliability and timing issues. Only use higher thresholds if process characterization proves it's safe.
Advanced Library Cell Mapping
Mapping level shifters to library cells with drive strength and voltage range selection.
Multi-Cell Mapping
# Provide multiple cell options with different drive strengths
map_level_shifter_cell LS_CPU_OUT -domain PD_CPU \
-lib_cells {
LS_LH_1V0_1V2_X1 # Low drive (small loads)
LS_LH_1V0_1V2_X2 # Medium drive
LS_LH_1V0_1V2_X4 # High drive (high fanout)
LS_LH_1V0_1V2_X8 # Very high drive
}
Synthesis tools select appropriate drive strength based on:
- Fanout (number of sinks)
- Capacitive load
- Timing requirements
- Power budget
Voltage-Specific Cell Mapping
# Different cells for different voltage transitions
map_level_shifter_cell LS_CPU_OUT -domain PD_CPU \
-lib_cells {LS_LH_1V0_1V2_X1 LS_LH_1V0_1V2_X2}
map_level_shifter_cell LS_IO_OUT -domain PD_IO \
-lib_cells {LS_HL_1V8_1V2_X1 LS_HL_1V8_1V2_X2}
Each voltage combination (1.0V→1.2V, 1.8V→1.2V) uses appropriate library cells designed for that range.
Drive Strength Selection
In a production SoC, level shifter drive strength is critical. A clock signal crossing voltage domains might require X8 drive strength (high fanout to hundreds of flip-flops), while a single control signal uses X1 (minimal area/power). Providing multiple cell options in map_level_shifter_cell allows synthesis tools to optimize each signal individually, balancing timing, area, and power.
Handling Bidirectional Signals
Bidirectional signals (inout ports) require special level shifter handling.
Bidirectional Challenge
Domain A (1.0V) Bidirectional Bus Domain B (1.2V) ┌────────────┐ ←─────────────→ ┌────────────┐ │ │ │ │ │ Logic ────┼──────[LS both ways?]────┼──── Logic │ │ │ │ │ └────────────┘ └────────────┘
Problem: Single wire must support voltage translation in both directions.
Solution 1: Separate Directional Signals
Convert bidirectional to separate input/output with control:
# Wrapper converts bidir to input + output + direction control
# Then apply normal level shifters to each direction
set_level_shifter LS_BUS_OUT -domain PD_CPU \
-applies_to outputs \
-location self
set_level_shifter LS_BUS_IN -domain PD_CPU \
-applies_to inputs \
-location parent
Solution 2: Bidirectional Level Shifter Cells
# Use special bidirectional level shifter cells (if available)
map_level_shifter_cell LS_BIDIR -domain PD_CPU \
-lib_cells {LS_BIDIR_1V0_1V2_X1 LS_BIDIR_1V0_1V2_X2}
Bidirectional level shifter cells contain:
- Two level shifters (one per direction)
- Direction control logic
- Tri-state buffers for bus control
Bidirectional level shifter cells are complex and not available in all libraries. When possible, redesign interfaces to avoid true bidirectional signals at voltage boundaries, using separate input/output paths with direction control instead.
Level Shifter Optimization Techniques
1. Cluster Shifters for Related Signals
# Group related signals (e.g., data bus) for efficient placement
set_level_shifter LS_DATA_BUS -domain PD_MEM \
-applies_to outputs \
-location self \
-elements {mem_inst/data_out[*]} # All data bus bits together
Benefits: Better routing, reduced area, consistent timing across bus.
2. Minimize Shifter Count on Critical Paths
BAD (Multiple Shifters): CPU (1.0V) → [LS] → MEM (1.2V) → [LS] → IO (1.8V) Total delay: 2× shifter delay GOOD (Single Shifter): CPU (1.0V) → MEM (1.0V) → [LS] → IO (1.8V) Total delay: 1× shifter delay (50% reduction)
Design tip: Keep critical path blocks at same voltage to minimize level shifter overhead.
3. Use Lower-Delay Cells for Critical Paths
# Map critical path to fast level shifter cells
map_level_shifter_cell LS_CRITICAL -domain PD_CPU \
-lib_cells {LS_LH_FAST_1V0_1V2} # Fast but higher power variant
Some libraries offer low-delay level shifters (higher power, larger area) for timing-critical signals.
Complete Advanced Example
# ===================================================================
# Advanced Level Shifter Configuration
# ===================================================================
# Domain structure
create_power_domain PD_TOP -include_scope
create_power_domain PD_CPU -elements {cpu_inst}
create_power_domain PD_MEM -elements {mem_ctrl}
create_power_domain PD_IO -elements {io_pads}
# Supply definitions (voltages: TOP=1.2V, CPU=1.0V, MEM=1.0V, IO=1.8V)
# ... (supply nets, sets, states omitted for brevity)
# CPU Level Shifters (1.0V ↔ 1.2V)
# Critical path: Use self placement for timing control
set_level_shifter LS_CPU_CRITICAL -domain PD_CPU \
-applies_to outputs \
-location self \
-rule low_to_high \
-elements {cpu_inst/critical_path/*}
# Non-critical CPU outputs: Automatic placement
set_level_shifter LS_CPU_GENERAL -domain PD_CPU \
-applies_to outputs \
-location automatic \
-threshold 0.05
# CPU inputs: Parent placement
set_level_shifter LS_CPU_IN -domain PD_CPU \
-applies_to inputs \
-location parent
# MEM stays at 1.0V (same as CPU) - no shifters needed between CPU-MEM
# IO Level Shifters (1.8V ↔ 1.2V)
# High-voltage interface requires specific cells
set_level_shifter LS_IO_OUT -domain PD_IO \
-applies_to outputs \
-location self \
-rule high_to_low
set_level_shifter LS_IO_IN -domain PD_IO \
-applies_to inputs \
-location parent \
-rule low_to_high
# Library Cell Mapping
# Critical path: Multiple drive strengths + fast variant
map_level_shifter_cell LS_CPU_CRITICAL -domain PD_CPU \
-lib_cells {
LS_LH_FAST_1V0_1V2_X2
LS_LH_FAST_1V0_1V2_X4
LS_LH_FAST_1V0_1V2_X8
}
# General paths: Standard cells
map_level_shifter_cell LS_CPU_GENERAL -domain PD_CPU \
-lib_cells {
LS_LH_1V0_1V2_X1
LS_LH_1V0_1V2_X2
LS_LH_1V0_1V2_X4
}
map_level_shifter_cell LS_CPU_IN -domain PD_CPU \
-lib_cells {LS_HL_1V2_1V0_X1 LS_HL_1V2_1V0_X2}
# IO cells: High-voltage range
map_level_shifter_cell LS_IO_OUT -domain PD_IO \
-lib_cells {LS_HL_1V8_1V2_X1 LS_HL_1V8_1V2_X2 LS_HL_1V8_1V2_X4}
map_level_shifter_cell LS_IO_IN -domain PD_IO \
-lib_cells {LS_LH_1V2_1V8_X1 LS_LH_1V2_1V8_X2}
# ===================================================================
# Strategy Summary:
# - Critical paths: Explicit control with fast cells
# - General paths: Automatic insertion with standard cells
# - CPU-MEM: Same voltage, no shifters (optimized)
# - Voltage-specific cell mapping for each transition
# ===================================================================
Common Beginner Mistakes
Mistake #1: Using automatic placement for all signals including critical paths
Problem: Tool may place level shifters on critical paths without considering timing impact.
Solution: Use explicit placement (-location self/parent) for critical paths with -elements to control timing. Reserve automatic for non-critical signals.
Mistake #2: Not providing multiple drive strength options
# SUBOPTIMAL: Only one drive strength
map_level_shifter_cell LS_CPU -domain PD_CPU \
-lib_cells {LS_LH_1V0_1V2_X1}
# BETTER: Multiple options for synthesis optimization
map_level_shifter_cell LS_CPU -domain PD_CPU \
-lib_cells {
LS_LH_1V0_1V2_X1
LS_LH_1V0_1V2_X2
LS_LH_1V0_1V2_X4
}
Why? Different signals have different fanout and timing requirements. Providing multiple drive strengths allows synthesis to optimize each signal individually.
Mistake #3: Forgetting level shifters for both input and output directions
# INCOMPLETE: Only output shifters
set_level_shifter LS_CPU_OUT -domain PD_CPU \
-applies_to outputs \
-location self
# COMPLETE: Both directions
set_level_shifter LS_CPU_OUT -domain PD_CPU \
-applies_to outputs \
-location self
set_level_shifter LS_CPU_IN -domain PD_CPU \
-applies_to inputs \
-location parent
Why? Voltage boundaries require level shifting in both directions. Signals entering the domain (inputs) need shifters just as signals leaving (outputs) do.
Practice Exercise
Challenge: Optimize Level Shifters for Performance
Given a design with three domains:
- PD_CORE: 1.2V (critical timing, 0 slack on outputs)
- PD_CACHE: 1.0V (moderate timing, 500ps slack)
- PD_PERIPH: 0.9V (non-critical, 2ns slack)
CORE and CACHE communicate frequently on critical paths. Design level shifter strategies that:
- Minimize critical path impact (CORE ↔ CACHE)
- Optimize area for non-critical paths (CACHE ↔ PERIPH)
- Provide appropriate cell mappings
Hint
For critical CORE-CACHE signals, keep them at the same voltage (no shifters) by running both at 1.2V, or place shifters in CACHE domain (which has slack). For CACHE-PERIPH (non-critical), use automatic insertion to minimize UPF complexity.
Solution
# ===================================================================
# Optimized Level Shifter Strategy
# ===================================================================
# OPTION 1: Run critical domains at same voltage (Best for timing)
# Change CACHE to 1.2V (same as CORE) - eliminates shifters on critical path
# This is the preferred solution for zero shifter delay on critical path
# OPTION 2: If CACHE must stay at 1.0V for power reasons:
# CACHE outputs to CORE (critical path)
# Place shifters in CACHE domain (has 500ps slack, can absorb LS delay)
set_level_shifter LS_CACHE_TO_CORE -domain PD_CACHE \
-applies_to outputs \
-location self \ # In CACHE domain (has slack)
-rule low_to_high \
-elements {cache_inst/critical_out[*]}
# CACHE inputs from CORE
# Place shifters in CACHE domain to keep CORE critical path clean
set_level_shifter LS_CORE_TO_CACHE -domain PD_CACHE \
-applies_to inputs \
-location self \ # In CACHE domain
-rule high_to_low
# CACHE to PERIPH (non-critical, 2ns slack)
# Use automatic insertion for simplicity
set_level_shifter LS_CACHE_PERIPH -domain PD_CACHE \
-applies_to both \
-location automatic \
-threshold 0.05
set_level_shifter LS_PERIPH_CACHE -domain PD_PERIPH \
-applies_to both \
-location automatic \
-threshold 0.05
# Cell Mapping
# Critical path: Fast, multiple drive strengths
map_level_shifter_cell LS_CACHE_TO_CORE -domain PD_CACHE \
-lib_cells {
LS_LH_FAST_1V0_1V2_X2
LS_LH_FAST_1V0_1V2_X4
}
map_level_shifter_cell LS_CORE_TO_CACHE -domain PD_CACHE \
-lib_cells {
LS_HL_FAST_1V2_1V0_X2
LS_HL_FAST_1V2_1V0_X4
}
# Non-critical: Standard cells, smaller drive
map_level_shifter_cell LS_CACHE_PERIPH -domain PD_CACHE \
-lib_cells {LS_LH_1V0_0V9_X1 LS_LH_1V0_0V9_X2}
map_level_shifter_cell LS_PERIPH_CACHE -domain PD_PERIPH \
-lib_cells {LS_LH_0V9_1V0_X1 LS_LH_0V9_1V0_X2}
# ===================================================================
# Key Optimizations:
# 1. Critical path shifters in CACHE (has slack) not CORE (0 slack)
# 2. Use FAST level shifter cells for critical signals
# 3. Automatic insertion for non-critical (less UPF maintenance)
# 4. Optimal solution: Run CORE and CACHE at same 1.2V (zero LS delay)
# ===================================================================
Summary
In this tutorial, you learned:
- Automatic level shifter insertion simplifies UPF but sacrifices control; explicit insertion optimizes critical paths with precise placement
- Placement strategies (self, parent, fanout) impact timing differently—place shifters in domains with slack to preserve critical path performance
- Rule-based insertion (
-rule low_to_high/high_to_low/both) and thresholds control which voltage crossings get shifters - Multi-cell mapping with various drive strengths enables synthesis optimization for individual signal requirements
- Bidirectional signals require special handling—separate directional paths or dedicated bidirectional level shifter cells
- Optimization techniques: cluster related signals, minimize shifters on critical paths, use fast cells for timing-sensitive signals