UVM Phases

UVM phases are the backbone of the UVM simulation lifecycle. They provide a standardized, deterministic order of execution for different parts of the testbench, ensuring consistent and predictable behavior. Understanding UVM phases is crucial for writing robust and reliable verification environments.

Why UVM Phases?

  • Standardization: Phases provide a well-defined order of execution, making it easier to understand and debug complex testbenches.
  • Automation: The UVM scheduler automatically executes the phases in the correct order, reducing manual intervention.
  • Synchronization: Phases help synchronize the execution of different components in the testbench.
  • Reusability: Phases enable the creation of reusable verification components that can be easily integrated into different testbenches.

Types of UVM Phases:

UVM defines two main categories of phases:

  1. Build Phases: These phases are responsible for constructing the testbench hierarchy. They execute in a bottom-up fashion.

  2. Run-Time Phases: These phases are responsible for the actual simulation and verification process. They execute in a top-down fashion.

Key UVM Phases

1. Build Phases (Bottom-Up)

This is the primary phase for constructing the testbench hierarchy. Components are instantiated, connections are made, and configurations are applied.

Functions: build_phase(), connect_phase(), end_of_elaboration_phase()

Diagram 1: Build Phase Execution (Bottom-Up)

        Top Environment
            /   |   \
       Env 1   Env 2   Env 3
      /   \   /   \   /   \
Comp 1 Comp 2 Comp 3 Comp 4 Comp 5 Comp 6

Execution Order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 (Top Environment)
  • Diagram:
    +-------------------+
    |   Build Phase     |
    +-------------------+
            |
            v
    +-------------------+
    | Construct & Init  |
    +-------------------+
    

2. Run-Time Phases (Top-Down)

These phases execute in the following order:

  • connect_phase: This phase establishes connections between components, such as connecting ports and exports.
  • end_of_elaboration_phase: This phase is called after all connections are made and before the simulation starts. It’s typically used for final setup and configuration.
  • start_of_simulation_phase: This phase is called at the very beginning of the simulation.
  • run_phase: This is the main phase for running the simulation. It’s where sequences are executed, drivers drive stimulus, and monitors collect data.

 

  • Functions: run_phase()
  • Diagram:
    +-------------------+
    |    Run Phase      |
    +-------------------+
            |
            v
    +-------------------+
    | Generate Stimulus |
    | Check Responses   |
    +-------------------+
    

 

  • extract_phase: This phase is used to extract data from the simulation, such as coverage information.
  • Functions: extract_phase()
  • Diagram:
    +-------------------+
    |  Extract Phase    |
    +-------------------+
            |
            v
    +-------------------+
    | Extract Data      |
    +-------------------+
    

  • check_phase: This phase is used to check the results of the simulation and report any errors.
  • Functions: check_phase()
  • Diagram:
    +-------------------+
    |   Check Phase     |
    +-------------------+
            |
            v
    +-------------------+
    | Perform Checks    |
    +-------------------+

     

  • final_phase: This phase is called at the very end of the simulation. It’s typically used for final cleanup and reporting.
  • Functions: report_phase()
  • Diagram:
    +-------------------+
    |  Report Phase     |
    +-------------------+
            |
            v
    +-------------------+
    | Generate Report   |
    +-------------------+
    
  • Diagram 2: Run-Time Phase Execution (Top-Down)

            Top Environment
                /   |   \
           Env 1   Env 2   Env 3
          /   \   /   \   /   \
    Comp 1 Comp 2 Comp 3 Comp 4 Comp 5 Comp 6
    
    Execution Order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 (Top Environment)
    (But each component executes connect_phase, end_of_elaboration_phase, etc. before moving to the next phase.)
    

    Diagram 3: Overall UVM Phase Execution

    +-----------------+     +-----------------------------------------------------------------+
    | Build Phases    |---->| Run-Time Phases                                                 |
    +-----------------+     +-----------------------------------------------------------------+
    | build_phase     |     | connect_phase -> end_of_elaboration_phase -> start_of_simulation_phase|
    | (Bottom-Up)     |     | -> run_phase -> extract_phase -> check_phase -> final_phase        |
    +-----------------+     +-----------------------------------------------------------------+
                                                                        ^
                                                                        |
                                                                        | Simulation Time

    Detailed UVM Phase Diagram

    +------------------+
    |  Build Phase     |
    +------------------+
              |
              v
    +------------------+
    |  Run Phase       |
    +------------------+
              |
              v
    +------------------+
    |  Extract Phase   |
    +------------------+
              |
              v
    +------------------+
    |  Check Phase     |
    +------------------+
              |
              v
    +------------------+
    |  Report Phase    |
    +------------------+
    

    Phase Jumping:

    UVM provides mechanisms for jumping between phases or skipping phases altogether. This is typically done using the phase.raise_objection() and phase.drop_objection() methods within the run_phase.

    Example of raising and dropping objections in run_phase:

    task run_phase(uvm_phase phase);
      phase.raise_objection(this); // Raise objection to keep simulation running
    
      // Perform simulation activities (e.g., run sequences)
      seqr.start_item(seq);
      seqr.finish_item(seq);
    
      phase.drop_objection(this); // Drop objection to allow simulation to finish
    endtask
    

    Example Code for UVM Phases

    Here’s an example of how to implement these phases in a UVM testbench:

    class my_env extends uvm_env;
      `uvm_component_utils(my_env)
    
      function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        // Build components
      endfunction
    
      task run_phase(uvm_phase phase);
        super.run_phase(phase);
        // Generate stimulus and check responses
      endtask
    
      function void extract_phase(uvm_phase phase);
        super.extract_phase(phase);
        // Extract data
      endfunction
    
      function void check_phase(uvm_phase phase);
        super.check_phase(phase);
        // Perform checks
      endfunction
    
      function void report_phase(uvm_phase phase);
        super.report_phase(phase);
        // Generate report
      endfunction
    endclass
    

    Benefits of UVM Phases

    • Modularity: Different tasks are separated into distinct phases, making the testbench more modular.
    • Reusability: Testbenches can be reused for different designs with minimal changes.
    • Scalability: Facilitates the creation of large and complex verification environments.
    • Synchronization: Ensures that all components are synchronized during execution.

  • Key Considerations:

    • Phase Synchronization: Ensure that different components in your testbench are properly synchronized using phases and objections.
    • Phase Domains: UVM supports phase domains, which allow you to create separate sets of phases for different parts of your testbench.
    • Objections: Objections are used to control the duration of the run_phase. A component raises an objection to indicate that it is still active and drops the objection when it is finished. The simulation ends when all objections have been dropped.
Scroll to Top