This is a second trivial example, which tests a Multiply-Accumulate module with a configurable pipeline level. In this case, the generated testbench instantiates the DUT with a 3-stage pipeline (by setting a parameter in the Verilog module, or a generic in a VHDL entity). The testbench also creates a pipelined checker, which tests the Q output only when it is expected to be ready. The pipeline setup, test, and flush are handled automatically.
There are some other notable points in this test:
- variables declared as varn are 4-state variables (each bit can take on any of the 4 values 01XZ), while variables declared as bitn are conventional 2-state variables (each bit can be 0 or 1). Both are unsigned. bit variables initialise to 0 when created, while var variables initialise to X
- 'conventional' integers can be declared as int. These are signed, and are used primarily for general housekeeping operations, such as loop counting and indexing
- The default level of type checking is approximately C-like, and the 8-bit multiply is carried out using an explicily-sized unsigned operator (in this case, *$8). What this means is that the 4-bit a and b inputs are zero-extended and attached to a multiplier with an 8-bit output. The output is then zero-extended to the 10-bit destination size when it is assigned to sum. This behaviour differs from both VHDL and Verilog (12)
- Confused? If in doubt, and it's not hardware-related, then it probably does the same as C or C++
DUT { module MAC1 // declare the module interface... @(.stages(3)) // ...just paste in the Verilog module (input RST, CLK, // declaration (replacing '#' with '@') input [3:0] A, B, output [9:0] Q); [RST, CLK, A, B] -> [Q]; create_clock CLK; // default timing } void main() { var4 a=0, b=0; // vars initialise to x var10 sum=4; [1, .C, -, -] ->3 [0]; // check that Q resets to 0 after 3 cycles [0, .C, 2, 2] ->3 [sum]; // sanity check: 2*2=4 for(int i=0; i<16; i++) { for(int j=0; j<16; j++) { sum += (a *$8 b); [0, .C, a, b++] ->3 [sum]; // drive A and B, check that Q equals 'sum' 3 cycles later } ++a; } }
If this code is saved as test2.tv and the MAC1 module is in mac1.v, then the test can be run as:
$ rtv test2.tv mac1.v
If the DUT is correctly coded, the test reports:
(Log) (2600 ns) 258 vectors executed (258 passes, 0 fails)
The 258 passes refer to the reset test (Q resets to zero); the first MAC sanity test (Q=4); and the Q tests for the 256 input combinations.
The code can be made more compact and more readable by using the for all statement, rather than explicitly cycling through the values of i and j. See test2b.tv for the equivalent for all test:
for all a { for all b { sum += (a *$8 b); [0, .C, a, b] ->3 [sum]; } }