The entire testbench structure including the test class, interfaces and DUT are instantiated in the top module. So, this is a static container which will be used to hold everything else out there and hence is the root in our hierarchy.
How to write the top testbench module ?
The example below details the elements inside the top module tb_top.
module tb_top; import uvm_pkg::*; import test_pkg::*; // Complex testbenches will have multiple clocks and hence multiple clock // generator modules that will be instantiated elsewhere // For simple designs, it can be put into testbench top bit clk; always #10 clk <= ~clk; // Instantiate the Interface and pass it to Design dut_if dut_if1 (clk); dut_wrapper dut_wr0 (._if (dut_if1)); // At start of simulation, set the interface handle as a config object in UVM // database. This IF handle can be retrieved in the test using the get() method // run_test () accepts the test name as argument. In this case, base_test will // be run for simulation initial begin uvm_config_db #(virtual dut_if)::set (null, "uvm_test_top", "dut_if", dut_if1); run_test ("base_test"); end endmodule
Note the following :
- tb_top is a module and is a static container to hold everything else
- Since we have used UVM elements in this module, it is necessary to import
uvm_pkgusing the import keyword.
- Since we have explicitly passed base_test as an argument to run_test(), we also need to include test_pkg within which base_test is defined
- Clock generation is done here, and passed to the interface dut_if1
- The interface is set as an object in the UVM database table via
uvm_config_db::set ()which will be retrieved in the test class using
uvm_config_db::get ()method. Learn more on Using config database.
- Usage of the config database is a very convenient way to pass configuration objects to selected components within the testbench hierarchy
- The test is started by calling the function
run_test() starts the actual UVM test from this blog post.