XOR Data Driven waveform

Test Bench Data Files in Verilog

In this post I am going to look at test bench data files. As circuits become more complex there will be more test cases for inputs. It will become increasingly tedious to write code for each of the possible test cases for inputs. So I want to see if I can simply testing before then. It would be nice if I could read in the various test case inputs and have them applied to the circuit. It turns out the creators of Verilog anticipated this need and provided some tools to help. Let’s take a look!

Reading Test Bench Data Files

The first thing we will need is a way to read data in from a file. Verilog provides $readmemb and $readmemh for retrieving data from an external file. These functions read in binary or hexadecimal formatted data respectively. In Verilog terms these functions are called system tasks. The syntax of these calls are as follows:

$readmemb("[filename]", [memory_variable]);
$readmemh("[filename]", [memory_variable]);

But what is a memory variable? We haven’t talked about this topic yet. In Verilog you can specify a block of memory as a two dimensional array. The first array specifier determines the width of each memory element in bits. The second specifier determines how many memory elements are in the array. Here is how this looks in Verilog:

// address width is 10 bits (2^10 is 1024)
localparam W = 10;

// specify 1K of 8-bit memory
reg [7:0] memory_block [2**W - 1 : 0];

This tells Verilog to reserve an array of 8-bit (2^8) memory with 1024 (2^10) entries. Specifying memory uses us logic cells. Depending on the capability of your device you may be significantly limited on how much memory can be used in this way. However, for simulation purposes you will in practice be subject only to the limitations of your simulation software and development PC.

Testing XOR With Data Files

To learn more about using data files I am going to rewrite the test bench for the Exclusive Or circuit using user-defined primitives. Rather than explicitly write code to pass in the various input signals I’ll read the inputs from a file.

For our purposes we only need enough memory to hold 4 elements of 2-bit data. One 2-bit input for each of the four (2**2) possible input combinations. We can declare a memory variable to hold this data using:

// specify 2-bit memory with 4 elements
reg [1:0] test_data [3:0];

Now that we have a way to read in data we need some data to read. I’ll put the input data in a file called test_vectors.txt. To create this file in Vivado you add it as a new simulation source file. Below are the contents of the test_vectors.txt file.

00
01
10
11

As you can see, the file is pretty simple and contains just four lines of binary data. If our circuit was more complex we would have many more input patterns to test. Now that we have a data file, a way to read it in and a memory variable to hold it we have what we need. The re-written test bench code is shown below.

module xor_test();

    localparam T=20;

    reg clk, a, b;
    wire d;
    integer i;

    // memory to hold our test vectors
    reg [1:0] test_data [2:0];
            
    xor_gate uut(.a(a), .b(b), .d(d));

    // generate clock signal    
    always
    begin
        clk = 1'b1;
        #(T/2);
        clk = 1'b0;
        #(T/2);
    end

    initial
    begin
        // read in our test vectors
        $readmemb("test_vectors.txt", test_data);
        
        // for each test vector apply inputs
        for (i = 0; i < 4; i = i + 1)
        begin
            {a, b} = test_data[i];
            #(T);
        end

        $stop; 
    end
endmodule

On line 7 we declare an integer variable i that we will use to iterate over our test vector. Then on line 10 we declare our memory variable test_data. And on line 26 you can see the call to read in our test data and put it into our memory variable.

Finally, starting on line 29 you can see that I use a for loop to iterate over the four elements of our test data array. I use concatenation on line 31 to assign the test data to the circuit inputs a and b.

Simulation Results

Running the simulation shows that we see the same inputs and outputs as the earlier non-data-driven simulations. And the results match the truth table for exclusive-or. You can see the simulation waveform below.

XOR Data Driven waveform
XOR Data Driven waveform

And that’s it for now. The source code for this project is on github.

Please like and share these posts. And remember if you have feedback, questions or suggestions regarding this post, please leave a comment!

Discover more from FPGA Coding

Subscribe now to keep reading and get access to the full archive.

Continue reading