Applications 1

De la WikiLabs
Jump to navigationJump to search

The Testbench Module

The testbench is a module used only in simulation for testing another module. It encloses the tested module (named DUT - Device Under Test) and generates the signals for the inputs of the DUT. Additionally the testbench may automatically check the correctness of the DUT outputs, and may report or log the values of various DUT signals and the events of the simulation. Simple testbenches generate all signals and do all checks and reports using verilog statements, but more complex testbenches may instantiate helper modules designed for various test functions and operations (signal drivers/generators, output checkers, signal monitors a.s.o.).

Dic lab1 testbench.png

For simple designs with few outputs that are easy to check visually on simulation waveforms, the testbench may be reduced to a DUT and the signals generation part.

For the first laboratory the testbench only generates some signals and does not instantiate any other module.

Exercise 1

One bit signal generation

Already a classical, the first program in any programming language tutorial displays the "Hello World" message. However, your first Vivado module will instead generate a very simple signal, a single binary pulse.

Dic lab1 one pulse.png

The simplest way to generate a signal in a testbench is to use a sequence of assignments that change the signal's value at predetermined steps of the simulation. The sequence of assignments is enclosed in a verilog procedural block of initial type. The initial procedural block starts its execution from the very beginning of simulation (hence its name, initial), and runs all its statements in order until the last one, after which it finishes.

module test; // module declaration. The testbench module is named test and has no ports (its generated signal is internal).

logic a;          // signal declaration. Signal named a is a variable of type logic. The default size is one bit.

initial begin     // start of an initial procedural block
        a = 0;    // a is set to 0 at the beginning of simulation
    #10 a = 1;    // after 10 simulation steps, a is set to 1
    #10 a = 0;    // after other 10 simulation steps, a is reset to 0. The delays are cumulative, therefore this change occurs after 20 simulation steps from the beginning.
    #20 $stop;    // $stop is a system function to stop the simulation. All system functions are prefixed by the dollar symbol, '''$'''.
end               // ends the procedural block

endmodule         // end of module description

The simulation advances in discrete steps. By default each step is counted as a 1 ns interval. Any SystemVerilog\Verilog statement may be preceded by a delay. The delay tells the simulator how many simulation steps should pass until the statement is executed. The delay is an integer constant value prefixed by the special hash symbol: #delay_value

Exercise 2

Two one-bit signals generation

Dic lab1 two pulse.png


module test;

logic a;          // first signal declaration
logic b;          // second signal declaration

initial begin     // initial procedural block for the generation of a
        a = 0;
    #10 a = 1;
    #10 a = 0;
    #20 $stop;    // Only one call is needed to stop the simulation 
end

initial begin     // initial procedural block for the generation of b
        b = 0;
     #5 b = 1;
    #10 b = 0;
end

endmodule

Each procedural block is treated as a thread by Vivado simulator. All procedural blocks are seen as parallel threads. When one thread finishes its execution, other threads may still have statements to execute. The initial block for generation of b executes its last statement at step 15. However the simulation goes forward (there are still statements in the other initial block to be executed). The stop should be called from the initial procedural block that is last to finish. Alternatively, you may employ a separate initial block to stop the simulation, using a delay whose value covers all steps of the simulation:

initial #40 $stop;

Note: When a procedural block has only one statement you may leave off the begin and end keywords. Otherwise, the begin and end keywords should be employed to enclose the sequence of statements of that procedural block.

Exercise 3

Two one-bit signals generation

Two different (or more) signals may be generated using the same procedural block. You should be careful to sequence and delay the assignments for different signals such that to achieve the desired waveform. Remember that delays are cumulative.

module test;

logic a, b;       // two variables of the same type (and width) may be declared in one declaration statement (not recommended)

initial begin
        a = 0;    // a is set to 0 at the beginning of simulation
        b = 0;    // b is set to 0 at the beginning of simulation
     #5 b = 1;    // after 5 simulation steps, b is set to 1             (at simulation step  5)
     #5 a = 1;    // after other 5 steps, the other variable is set to 1 (at simulation step 10)
                  // more statements to change a and b as desired                                  << COMPLETE THE MISSING CODE
    #20 $stop;
end

endmodule

Exercise 4

Multi-bit signal generation

Sometimes it is more convenient to generate multi-bit signals (such as data of various widths, vectors, bundled signals). Consider a 2 bit data whose value change in time as a sequence of integers.

Dic lab1 multibit.png

Multi-bit signal declaration sets the width of the signal using two indices. The left index is the index of the most significant bit (MSB), the right index is of the least significant bit (LSB)

module test;

logic [1:0] data; // A 2-bit variable of type reg, named data. MSB index is 1, LSB index is 0

initial begin
        data = 0; // data is set to 0 at the beginning of simulation
     #5 data = 1; // after 5 simulation steps, data changes to 1
     #5 data = 3; // after other 5 steps, data changes to 3
                  // more statements to change data value as desired                   << COMPLETE THE MISSING CODE
    #20 $stop;
end

endmodule

In the above code the data values are given as decimal integers. SystemVerilog integer constants may be written in 4 bases: decimal, hexadecimal, octal and binary. The decimal base is implicit if only a plain number literal is written. To write the value in another base you should prefix the number literal with a base specifier, which is the apostrophe sign followed by a letter that specifies the base: 'd for decimal, 'b for binary, 'h for hexadecimal.

Generate the same sequence of data values using a binary number format:

initial begin
        data = 2'b00; // data is set to 0 at the beginning of simulation
     #5 data = 2'b01; // after 5 simulation steps, data changes to 1
     #5 data = 2'b11; // after other 5 steps, data changes to 3, which in binary is writen as 11
                      // more statements to change data value as desired                                  << COMPLETE THE MISSING CODE
    #20 $stop;
end

Exercise 5

Signal bundle generation

The 2-bit data signal from Exercise 4 may be seen as a bundle of two one-bit signals. If you look carefully at the waveform of Exercise 2 you may recognize that a and b together may be bundled as a 2-bit number, with a as its most significant bit and b as the last bit:

Dic lab1 multibit ab.png

You may generate separate signals grouping them into a bundle and assigning multi-bit values to this bundle. Two or more signals can be bundled together using the concatenation operator, { }. The bundled signals are listed inside the curly brackets of the concatenation operator, separated by commas. The signals' bits are arranged from left to right, in the order in which the signals are listed.

initial begin
        {a, b} = 2'b00; // a and b are both set to 0 at the beginning of simulation
     #5 {a, b} = 2'b01; // after 5 simulation steps, b changes to 1. a value does not change
                        // more statements to change a and b values as desired                                  << COMPLETE THE MISSING CODE
end

Exercise 6

Ordered sequence of values

An ordered sequence of increasing/decreasing values may be generated using the for loop statement. Its syntax is the same as in C/C++:
for (initial_assignments; expression; step_assignments) statement or statement_group
Where

  • initial_assignments are assignments executed before the first iteration of the loop;
  • expression is a logic expression evaluated at the beginning of each iteration. If it's true the execution proceeds to the statements of the iteration, otherwise the loop is exited;
  • step_assignments are assignments executed at the end of an iteration after the last statement of the statement_group;
  • statement or statement_group is the body of an iteration.

Attention! There should be at least one statement with delay, otherwise the whole loop finishes in zero time!

A sequence of increasing 4 bit numbers from 0 to the maximum value:

logic [3:0] data;

initial begin
    for(data = 0; data < 15; data++) begin
        #10;
    end
end

The above loop starts with zero. For each iteration it waits 10 simulation steps and increments data value. At the end of the 15'th iteration data value is incremented to 15 after which the expression data < 15 is evaluated to false and the loop statement is exited.

A more traditional approach when the number of iterations is fixed and known is to use an index for the loop that runs from 0 to the desired value. The index is a classic (borrowed from C) type of integer variable int:

logic [3:0] data;
int i;

initial begin
    data = 0;
    for(i = 0; i < 16; i++) begin
        #10 data++;
    end
end
  • Note: This last example runs through 16 iterations, such that the loop is exited with zero for data. The previous example runs only for 15 iterations because there is no way to write a condition for data to exit the loop after one more iteration (since all data values are valid and incrementing from 4'b1111 gives 4'b0000), therefore after the loop is exited data remains at the maximum value.

Dic lab1 sequence.png

The last for loop may be condensed, grouping more assignments as initial_assignments and step_assignments:

logic [3:0] data;
int i;

initial begin
    for(i = 0, data = 0; i < 16; i++, data++) begin
        #10;
    end
end