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

    plaintext
    +------------------+
    |  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.