What are UVM phases ?
All testbench components are derived from
uvm_component and are aware of the phase concept. Each component goes through a pre-defined set of phases, and it cannot proceed to the next phase until all components finish their execution in the current phase. So UVM phases act as a synchronizing mechanism in the life cycle of a simulation.
Because phases are defined as callbacks, classes derived from
uvm_component can perform useful work in the callback phase method. Methods that do not consume simulation time are
functions and methods that consume simulation time are
tasks. All phases can be grouped into three categories:
- Build time phases
- Run time phases
- Clean-Up phases
run_phase is launched in parallel with other run-time phases.
Main UVM Phases
|Phase Category||UVM Phase Name||Method Type||Description|
|Build|| ||function||Used to build testbench components and create their instances|
| ||function||Used to connect between different testbench components via TLM ports|
| ||function||Used to display UVM topology and other functions required to be done after connection|
| ||function||Used to set initial run-time configuration or display topology|
|Run|| ||task||Actual simulation that consumes time happens in this UVM phase and runs parallel to other UVM run-time phases.|
|Clean|| ||function||Used to extract and compute expected data from scoreboard, an|
| ||function||Used to perform scoreboard tasks that check for errors between expected and actual values from design|
| ||function||Used to display result from checkers, or summary of other test objectives|
| ||function||Typically used to do last minute operations before exiting the simulation|
Logically, the first thing to be done is to create testbench component objects so that they can be connected together. This is the reason for the
build_phase. It is better to not start connecting them while other testbench components are still building their sub-components. So we have
connect_phase which will connect all the components that were built in the previous phase. Although the next two phases are rarely used or are typically used to display UVM hierhachy information. Test stimulus is driven to the design during the
run_phase which is launched in parallel with other run-time phases that are described shown below.
Why is build done top to bottom ?
Verification environment usually looks like a nested structure as shown in the image below. In order to build the agent, UVM environment is required to be defined and similarly in order to build the environment, the test class has to be defined. Hence, we can only go from top to bottom.
To add more flexibility, there are
post callbacks to all the runtime phases.
Why doesn't Verilog testbench need phases ?
A Verilog testbench would have all its components made of static containers or modules. So each module will have a set of ports or signals that it can utilize to communicate with other testbench components. Since a
module is static, all modules will be created at beginning of the simulation and we won't have to worry about any component getting called without it being created or initialized. It would be useful to have some run-time phases for resetting DUT, driving the actual stimulus and maybe another phase to enable and run checkers.
module driver (...); task reset_phase (...); task run_phase (...); task check_phase (...); initial begin reset_phase (); run_phase (); check_phase (); end endmodule module tb_top; dut d0 (...); driver drvr (...); checker1 chk0 (...); checker2 chk1 (...); endmodule
Verilog testbenches are limited in functionality and are not scalable.
Why SystemVerilog testbench require phases ?
SystemVerilog changes the whole game with the introduction of OOP (Object Oriented Programming) features. This enables the creation of well structured entities that can be reused and deployed when required. These entities are
class objects, and it is possible to create a brand new object in the middle of the entire simulation, say at time Xns. What this means is that testbench components can be created at different times, and hence you could end up calling a component while it hasn't been initialized yet leading to wrong testbench outputs. Also, different companies maintain their own hierarchy and arrangement of testbench environments and takes a lot of effort to plug in different verification IPs from other companies. So, synchronization between testbench components is a key requirement.
typedef class driver; typedef class monitor; class env; driver d0; monitor mon0; function init_drvr (); d0 = new (); // initialize endfunction function init_mon (); mon0 = new (); // initialize endfunction endclass
What should be done in each UVM Phase ?The table below summarizes the intent, expectation and result of each phase as documented in the UVM reference manual.
|Phase||Upon Entry||Typical Uses||Exit Criteria|
|build||Top-Level components are instantiated under || || |
|connect||All components have been instantiated|| || |
|end_of_elaboration||Verification environment has been assembled|| ||None|
|start_of_simulation|| || ||None|
|run|| ||Components run over various run-time phases|| |
|extract|| || ||All data has been collected and summarized|
|check||All data has been collected||Check that no unaccounted for data remain||Test is known to have passed or failed|
|report||Test is known to have passed or failed|| ||End of test|
|final||All test-related activity has completed|| ||Ready to exit simulator|