Why Learn Advanced Level Shifters? While basic level shifter concepts were introduced earlier, real-world designs require sophisticated level shifter placement strategies, bidirectional handling, and optimization for timing and power. Understanding advanced level shifter techniques enables you to minimize delay overhead, manage complex multi-voltage hierarchies, and ensure reliable voltage domain crossing in production SoCs. In this tutorial, you'll master advanced level shifter strategies in UPF.

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:

  1. Tool analyzes supply voltage for each domain
  2. Detects signals crossing between different voltage levels
  3. Automatically inserts level shifters at crossings
  4. 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

ScenarioRuleRationale
Unidirectional interfacelow_to_high or high_to_lowSignals only flow one direction
Bidirectional busbothData flows both directions
Asymmetric voltage designSpecific direction onlyOne 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 VDest VΔVThreshold 0.2VShifter Inserted?
1.0V1.05V0.05VBelowNo
1.0V1.2V0.2VAt thresholdYes
0.9V1.2V0.3VAboveYes
1.0V1.8V0.8VAboveYes

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:

  1. Minimize critical path impact (CORE ↔ CACHE)
  2. Optimize area for non-critical paths (CACHE ↔ PERIPH)
  3. 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
Did you find what you were looking for?

Quiz

No quiz questions available for this article.