Why didn't Verilog testbench didn't 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 ();
  module tb_top;
    dut       d0   (...);
    driver    drvr (...);
    checker1  chk0 (...);
    checker2  chk1 (...);

Verilog testbenches are limited in functionality and are not scalable.

Why System-Verilog testbench require phases ?

System Verilog 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 could have their own hierarchy and arrangement of testbench environments and it would be very difficult to do plug-and-play of verification IPs. 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
    function init_mon ();
      mon0 = new ();
      // initialize
How do UVM phases help ?

All testbench components are derived from the same class uvm_component which follow a particular set of phases that are executed one after the other. So the flow of simulation is more structured and predictable in UVM. Do not confuse this with the phases of compilation, elaboration and simulation of an EDA tool. Those are steps required to prepare the necessary software environment to run a simulation. The kind of phases in UVM start when the simulator runs the code.

The first logical step is to create an object of all the required testbench components, and the next step would be to connect them together. And consequently, the first step is called build_phase followed by the connect_phase. Testbenches can grow in complexity and would be pretty useful to display the topography or its hierarchical structure in end_of_elaboration_phase. As the name suggests, this is the time when everything has been built and connected and the appropriate time to display the structure information. Note that build phase goes from the top of the hierarchy towards the bottom, while connect phase goes from bottom towards the top.

Remember that all of the above phases were executed at 0ns, and time has not advanced even one bit. So now it is time to run the stimulus and exercise different patterns on the design. This is achieved in the run_phase which can further be broken into sub-phases as shown in the diagram below. Simulation stops time advancement as soon as run_phase is over, and executes a couple of other steps like check_phase and report_phase to wrap up and exit gracefully.

  • build_phase - Used to instantiate and create component objects
  • connect_phase - Used to connect between various components
  • run_phase - Phase when simulation happens and consumes time
  • check_phase - Check the output with expected values
  • report_phase - Report failures, errors and passes

To add more flexibility, there are pre and post callbacks to all the runtime phases.

The table below summarizes the intent, expectation and result of each phase.

Phase Upon Entry Typical Uses Exit Criteria
build Top-Level components are instantiated under uvm_root
  • Instantiate sub-components
  • Instantiate register model
  • Get configuration values for components being built
  • Set configuration values for sub-components
  • All uvm_components have been instantiated.
connect All components have been instantiated
  • Connect TLM ports and exports, initiator sockets and target sockets
  • Connect register model to adapter components
  • Setup explicit phase domains
  • Connections between various components have been established
  • All independent phase domains are set
end_of_elaboration Verification environment has been assembled
  • Display environment topology
  • Open files
  • Define additional configuration settings
  • External tools/simulation engines have started and synced
  • Verification environment is configured and ready to be started
  • Display environment topology
  • Set debugger breakpoint
  • Set initial run-time configuration values
  • Indicates that power has been applied
  • No active edges of clock should have happened before (x->1)
Components run over various run-time phases
  • DUT no longer needs to be simulated
  • post_shutdown_phase is ready to end
  • DUT no longer needs to be simulated
  • Simulation time will no longer advance
  • Extract data/final state information from scoreboard/testbench components
  • Probe the DUT (hierarchical reference/backdoor access)
  • Calculate statistics and summaries
  • Display final state information
  • Close files
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
  • Report test results
  • Write results to file
End of test
final All test-related activity has completed
  • Close files
  • Terminate co-simulation engines
Ready to exit simulator

Was this article helpful ?