You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# SvUTest: A Unit Testing Framework in System Verilog
2
2
3
-
System Verilog Unit Testing Framework (SvUTest) is a framework for performing unit testing of lightweight hardware modules written in System Verilog. This project is inspired by [CppUTest](https://cpputest.github.io/), [Google Test](https://google.github.io/googletest) and UVM.
3
+
System Verilog Unit Testing Framework (SvUTest) is an open source framework for performing unit testing of lightweight hardware modules written in System Verilog. This project is inspired by [CppUTest](https://cpputest.github.io/), [Google Test](https://google.github.io/googletest) and UVM.
4
4
5
5
## Introduction
6
6
@@ -10,26 +10,220 @@ SvUTest is an attempt at a tool that helps designers write basic sanity checks o
10
10
11
11
## Target Audience
12
12
13
-
SvUTest is meant to be used by RTL design engineers to ensure the correctness of their designs across a known set of input patterns.
13
+
SvUTest is meant to be used by RTL design engineers to ensure the correctness of their designs across a known set of input patterns. This framework not meant to be a replacement for UVM and is only recommended for small designs with a handful of input/output interfaces and a set of input workloads whose output is known. UVM would still be the go-to solution for large designs with complex stimuli.
14
14
15
-
## UVM
15
+
## Building a simple test case
16
16
17
-
SvUTest is not meant to replace UVM. SvUTest is only recommended for small designs with a handful of input and output interfaces and a set of input workloads whose output is known. UVM would still be the go-to solution for large designs with complex stimuli.
17
+
Let's take a look at the floating-point multiplier block in examples/001_floatmul that we need to unit-test. This block, or the Design Under Test, has two input channels and an output channel, all following the valid-data-ready protocol:
18
+
```
19
+
input logic a_valid,
20
+
input float32_t a_payload,
21
+
output logic a_ready,
22
+
23
+
input logic b_valid,
24
+
input float32_t b_payload,
25
+
output logic b_ready,
26
+
27
+
output logic o_valid,
28
+
output float32_t o_payload,
29
+
input logic o_ready,
30
+
```
31
+
The valid on the output interface shall be asserted if and only if both the inputs' valids a_valid and b_valid are high.
32
+
33
+
In order to unit test this block, we need to build a test_top. The test_top is a System Verilog module with no ports and a single type parameter that accepts a test_case class that we'll build later as the parameter argument:
34
+
```
35
+
module floatmul_test_top
36
+
// import any other packages needed by the DUT
37
+
import svutest_pkg::*;
38
+
#(
39
+
type T_test_case = bit // Need to be overriden during instantiation
40
+
);
41
+
...
42
+
endmodule
43
+
```
44
+
45
+
In addition to the test_case parameter, the test top needs to instantiate the following items:
46
+
* A ``busy`` signal to indicate that the DUT is operational. This signal may be driven by the DUT or may be generated by the test top. In this example, busy is driven from the DUT.
47
+
* An instance of ``svutest_if_test_ctrl``. This interface supplies the clock and reset to the interior of the test top and collects the busy signal.
48
+
* An instance of ``svutest_if_valid_ready`` interface for each channel on the DUT. These interfaces supply the input transactions to the DUT as well as collect the output transactions from the DUT. ``svutest_if_valid_ready`` need to be replaced with the right interface in case the protocol is different from valid-data-ready.
49
+
* An instance of the DUT, with the ports on the DUT hooked to the signals from the svutest_if_test_ctrl and svutest_if_valid_ready interfaces.
50
+
* Finally, an ``initial .. begin`` block where the T_test_case parameter is instantiated. ``T_test_case`` can have it's own constructor and may take any number of arguments. The arguments typically pass the interfaces declared in the test top to the test case.
51
+
52
+
With the above instantiations in place, the test top is now complete:
Once the test top is built, we now need to build a test_case class that drives the input interfaces and evaluates the output transactions. The test case class is derived from ``svutest_pkg::test_case`` and acts as a base class for all test sequences for current DUT. ``svutest_pkg::test_case`` class drives the clock and reset to the DUT while monitoring the busy, manages the interfaces which the drive the DUT and accepts the svutest_if_test_ctrl interface and a test name as constructor arguments:
``sv_utest_pkg`` provides drivers for 4 different interface protocols:
135
+
1. Simple data without any qualifier
136
+
2. Data with valid
137
+
3. Data with valid and ready
138
+
4. Data vector with valid_count and ready_count. The number of transfers that happens on a cycle will be equal to min(valid_count, ready_count)
139
+
140
+
The package also provides 3 different agent classes that can work with each of the above protocols:
141
+
1. Sender agent, which injects data into the DUT
142
+
2. Target agent, which drives response into the DUT, while also extracting output from the DUT
143
+
3. Monitor agent, which only monitors an output interface of the DUT without prodiving any response
18
144
19
-
## Requirements
145
+
The example above instantiates two sender agents and an output agent inside the class and their corresponding driver objects inside the constructor. The driver instances are not needed after the agents are formed and can be declared as local varaibles inside the constructor. The constructor connects the virtual interfaces to the drivers and passes the drivers to the agents. (Note: This syntax may change in future versions).
20
146
21
-
A System Verilog compatible compiler and simulator
147
+
Once the drivers and agents are set up, the user needs to extend the base class once again and populate two virtual functions ``test_case::inject()`` and ``test_case::check``. ``inject()`` is used to push transactions into different sender agents. The sender_agent class provides a function ``put()`` to pass a transaction to the DUT through the driver. Any number of ``put()`` calls may be made from the inject function.
22
148
23
-
## Example
149
+
The transactions emitted from the output channels of the DUT are collected by the target and monitor agents and populated into an internal queue called ``m_mon_queue``. This queue can be queried from the virtual function ``check()`` for correctness of output transactions. Two macros ``UTEST_ASSERT(expr)`` and ``UTEST_ASSERT_EQ(expr_lhs, expr_rhs)`` are provided by ``svutest_defines.svh`` to help the user with the timestamp, line number and a failure count summary for the given test.
Once the test_top and test cases are set up, we need to populate a top module where we instantiate the test top and trigger the start of the regression:
182
+
```
183
+
`include "svutest_defines.svh"
184
+
185
+
module regress_top;
186
+
import svutest_pkg::*;
187
+
import floatmul_test_pkg::*;
188
+
189
+
// Instantiate test cases floatmul_test_0_0 and floatmul_test_012_012
190
+
// for floatmul_test_top
191
+
`UTEST(floatmul_test_top, floatmul_test_0_0)
192
+
`UTEST(floatmul_test_top, floatmul_test_012_012)
193
+
194
+
initial begin
195
+
regress_suite::run_all_tests();
196
+
end
197
+
endmodule
198
+
```
199
+
200
+
The ``UTEST(module, test_case)`` macro creates an instance of ``module`` while passing ``test_case`` through the parameter list. Once all the required test cases are instantiated, we need to call ``regress_suite::run_all_tests();`` from an initial block as shown above.
201
+
202
+
The command runs all instantiated test cases, in no specific order, and prints a summary (pass +color to the simulation environment for print in color) on the console:
203
+
```
204
+
001_floatmul/floatmul_test_pkg.sv", 109: floatmul_test_pkg::\floatmul_test_012_012::check .unnamed$$_8: started at 15000s failed at 15000s
SvUTest framework contains only two source files: src/svutest_pkg.sv src/svutest_ctrl.sv and one header file: src/svutest_defines.svh, and can be built using any System Verilog compliant compiler. The source files need to be passed to your eda tool like SV modules while the header file is typically picked up by providing the include path. A typical invocation from the command line would be:
0 commit comments