Thought Leadership

Starting Your UVM Simulation

Introduction

What happens when you start your simulation with a UVM test bench? Where should you put the uvm_config_db::set() calls to send the virtual interface to the test class? Are there potential race conditions? And what happens when your test is over?

Let’s Get This Started

At the start of the simulation, you need to put virtual interfaces in the uvm_config_db for the testbench. You could put this in an initial block in your RTL code.

module hdl_top;
  logic clk, rst;
  tx_ifc tx_if (clk, reset); // Instantiate the tx_ifc interface
  rx_ifc rx_if (clk, reset); // Instantiate the rx_ifc interface
  dut i0 (tx_if, rx_if);

  initial begin
    uvm_config_db#(virtual tx_ifc)::set(null, "uvm_test_top", "tx_if", tx_if);
    uvm_config_db#(virtual rx_ifc)::set(null, "uvm_test_top", "rx_if", rx_if);
  end
endmodule

Over in the test module, you start the UVM code.

module hvl_top;
  import uvm_pkg::*;
  import tx_pkg::*;   // tx definitions
  import rx_pkg::*;   // rx definitions
  initial run_test(); // Start UVM
endmodule

You can also put the uvm_config_db::set() calls in the testbench module. Just make sure they are before the call to run_test(), or in a separate initial block. The final argument to set() is the value of the virtual interface, which is a hierarchical name.

initial begin
  uvm_config_db#(virtual tx_ifc)::set(null, "uvm_test_top", "tx_if", hdl_top.tx_if);
  uvm_config_db#(virtual rx_ifc)::set(null, "uvm_test_top", "rx_if", hdl_top.rx_if);
  run_test();  // Start UVM
end

Going the Distance

If there are two initial blocks, is there a race condition?  Could your test class call uvm_config_db::get() before the set() call? Good news – the UVM developers anticipated this problem and added a #0 delay before your test code starts. As a result, all the initial and always blocks across the simulation set the uvm_config_db entries before the test class tries to get them.

Here is a little more detail. The UVM global method run_test() constructs a uvm_root object and calls its run_test() method. That gets the test name from the +UVM_TESTNAME command line switch and constructs the test object. It then starts the UVM phases, which includes a #0 delay, ensuring all your initial blocks have woken up.

The run_test() method never returns, so any code after it will never execute.

initial begin
  run_phase();
  $display("This will never print");
end

Extra, Extra, Extra

In my opinion, the run_test() method should have been called uvm_run_test(), but I’m a few decades late for that battle.

The method has an argument for the test name – don’t use it. UVM has the great concept of including all tests in one simulation image, so you don’t need to recompile between tests. The only time you might want to give a default test name is for a trivial example with a single test.

After UVM ends your test, it calls $finish(). If you need to run some code at the end of the test, put it in a final_phase() function. That way, your code runs with other testbench OOP code.

Conclusion

In UVM, call uvm_config_db::set() to send the virtual interface to the test class. Put these calls in an initial block. This are typically in the RTL module. If they are in the testbench module, put them before the call to run_test(), or in a separate initial block.

Learn More

You can learn more about these topics, including Oriented Programming with the Siemens SystemVerilog for Verification course. It is offered in instructor-led format by our industry expert instructors, or in a self-paced, on-demand format. It can also be tailored to address your specific design goals and show you how to set up an environment for reuse for additional designs.  Also, you can now earn a digital badge/level 1 certificate by taking our Advanced Topics Badging Exam. This will enable you to showcase your knowledge of this topic by displaying the badge on your social media and email signature.

Chris Spear

Chris brings over forty years of EDA expertise to Siemens customers. Holding a degree in electrical engineering from Cornell University, Chris has developed deep roots in the EDA industry, including as a Principal Application Consultant. Chris is also an industry author, writing the 2012 best-selling “SystemVerilog for Verification” and developing the IEEE standard for random seeding and File I/O PLI package that is part of SystemVerilog. Having taught thousands of engineers around the world, Chris is driven by a passion for learning new techniques and then helping others learn best practices for hardware verification. Outside of work, you may see Chris bicycling over 12,000-foot mountain passes.

More from this author

Leave a Reply

This article first appeared on the Siemens Digital Industries Software blog at https://blogs.sw.siemens.com/verificationhorizons/2023/10/10/starting-your-uvm-simulation/