In a normal project, the development of the DUT is done separately from the development of the testbench, so there are two components that connects both of them:
- The top block of the testbench
- A virtual interface
The top block will create instances of the DUT and of the testbench and the virtual interface will act as a bridge between them.
The interface is a module that holds all the signals of the DUT. The monitor, the driver and the DUT are all going to be connected to this module.
The code for the interface can be seen in Code 3.1.
1 2 3 4 5 6 7 8 | interface simpleadder_if; logic sig_clock; logic sig_ina; logic sig_inb; logic sig_en_i; logic sig_out; logic sig_en_o; endinterface: simpleadder_if |
Code 3.1: Interface module – simpleadder_if.sv
After we have an interface, we will need the top block. This block will be a normal SystemVerilog module and it will be responsible for:
- Connecting the DUT to the test class, using the interface defined before.
- Generating the clock for the DUT.
- Registering the interface in the UVM factory. This is necessary in order to pass this interface to all other classes that will be instantiated in the testbench. It will be registered in the UVM factory by using the uvm_resource_db method and every block that will use the same interface, will need to get it by calling the same method. It might start to look complex, but for now we won’t need to worry about it too much.
- Running the test.
The source for the top block is represented in Code 3.2.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | `include "simpleadder_pkg.sv" `include "simpleadder.v" `include "simpleadder_if.sv" module simpleadder_tb_top; import uvm_pkg::*; //Interface declaration simpleadder_if vif(); //Connects the Interface to the DUT simpleadder dut(vif.sig_clock, vif.sig_en_i, vif.sig_ina, vif.sig_inb, vif.sig_en_o, vif.sig_out); initial begin //Registers the Interface in the configuration block //so that other blocks can use it uvm_resource_db#(virtual simpleadder_if)::set(.scope("ifs"), .name("simpleadder_if"), .val(vif)); //Executes the test run_test(); end //Variable initialization initial begin vif.sig_clock = 1'b1; end //Clock generation always #5 vif.sig_clock = ~vif.sig_clock; endmodule |
Code 3.2: Top block – simepladder_tb_top.sv
A brief explanation of the code will follow:
- The lines 2 and 3 include the DUT and the interface into the top block, the line 5 imports the UVM library, lines 11 to 16 connect the interface signals to the DUT.
- Line 21 registers the interface in the factory database with the name simpleadder_if.
- Line 24 runs one of the test classes defined at compilation runtime. This name is specified in the Makefile.
- Line 34 generates the clock with a period of 10 timeunits. The timeunit is also defined in the Makefile.
For more information about interfaces, you can consult the book “SystemVerilog for Verification: A Guide to Learning the TestBench Language Features“, chapter 5.3.
The files can be found here: