Verilog Coding Tips and Tricks: testbench
Showing posts with label testbench. Show all posts
Showing posts with label testbench. Show all posts

Sunday, November 5, 2017

File Reading and Writing(line by line) in Verilog - Part 2

File reading and writing is a very useful thing to know in Verilog. The possibility to read test input values from files, and write output values for later verification makes testbench codes easy to write and understand.

There are few ways to read or write files in Verilog. I have already explained one method in my last post, File Reading and Writing in Verilog - Part 1. The method described in this new post will help you to read the contents of a file line by line, instead of reading everything together. This is helpful when the size of file is too big.

In this post, we will learn:
  • How to read hexadecimal/binary/decimal values from a file using fopen and fscanf function.
  • How to write a file with hexadecimal/binary/decimal values using fopen and fdisplay.

Verilog code for File Read and Write:

`timescale 1ns / 1ps

module tb();

    reg [7:0] A; //register declaration for storing each line of file.
    integer outfile0,outfile1,outfile2,outfile3; //file descriptors

initial begin
    
    //The $fopen function opens a file and returns a multi-channel descriptor 
    //in the format of an unsized integer. 
    outfile0=$fopen("A_hex.txt","r");   //"r" means reading and "w" means writing
    outfile1=$fopen("A_write_dec.txt","w");
    outfile2=$fopen("A_write_bin.txt","w");
    outfile3=$fopen("A_write_hex.txt","w");
    
    //read the contents of the file A_hex.txt as hexadecimal values into register "A".
    while (! $feof(outfile0)) begin //read until an "end of file" is reached.
        $fscanf(outfile0,"%h\n",A); //scan each line and get the value as an hexadecimal
    //Write the read value into text files.
        $fdisplay(outfile1,"%d",A); //write as decimal
        $fdisplay(outfile2,"%b",A); //write as binary
        $fdisplay(outfile3,"%h",A); //write as hexadecimal
        #10;
    end 
    //once reading and writing is finished, close all the files.
    $fclose(outfile0);
    $fclose(outfile1);
    $fclose(outfile2);
    $fclose(outfile3);
    //wait and then stop the simulation.
    #100;
    $stop;
end    
      
endmodule

The above code is for reading a hexadecimal file and writing hex, binary and decimal numbers to a file. But with small changes we can make it read binary or decimal numbers too.

For Reading Binary numbers from the file,

Replace the line outfile0=$fopen("A_hex.txt","r");
with    outfile0=$fopen("A_bin.txt","r");     to change the name of the file.
Replace the line $fscanf(outfile0,"%h\n",A);
with    $fscanf(outfile0,"%b\n",A);     to change the type of data read from the file.


Similarly for Reading Decimal numbers from the file, 

Replace the line outfile0=$fopen("A_hex.txt","r");
with    outfile0=$fopen("A_dec.txt","r");     to change the name of the file.
Replace the line $fscanf(outfile0,"%h\n",A);
with    $fscanf(outfile0,"%d\n",A);     to change the type of data read from the file.


 That's all! The output files should look the same in both the cases.

A screenshot of input files and output files is given below:


The input files can be downloaded from here,



The code was tested using Xilinx ISE 14.6 tool.

File Reading and Writing in Verilog - Part 1

File reading and writing is a very useful thing to know in Verilog. The possibility to read test input values from files, and write output values for later verification makes testbench codes easy to write and understand.

There are few ways to read or write files in Verilog. So I will explain the whole thing in few different posts. In this first part we will learn the following things,

  • How to read hexadecimal values from a file using readmemh function.
  • How to read binary values from a file using readmemb function.
  • How to write a file with binary values using fopen and fdisplay.
  • How to write a file with decimal values using fopen and fdisplay.
  • How to write a file with hexadecimal values using fopen and fdisplay.

Verilog code for File Read and Write:

`timescale 1ns / 1ps

module tb();

    reg [7:0] A [0:15]; //memory declaration for storing the contents of file.
    integer outfile1,outfile2,outfile3; //file descriptors
    integer i;  //index used in "for" loop

initial begin
    //read the contents of the file A_hex.txt as hexadecimal values into memory "A".
    $readmemh("A_hex.txt",A);
    //The $fopen function opens a file and returns a multi-channel descriptor 
    //in the format of an unsized integer. 
    outfile1=$fopen("A_write_dec.txt","w");
    outfile2=$fopen("A_write_bin.txt","w");
    outfile3=$fopen("A_write_hex.txt","w");
    
    //Write one by one the contents of vector "A" into text files.
    for (= 0; i < 16; i = i +1) begin
        $fdisplay(outfile1,"%d",A[i]);  //write as decimal
        $fdisplay(outfile2,"%b",A[i]);  //write as binary
        $fdisplay(outfile3,"%h",A[i]);  //write as hexadecimal
    end 
    //once writing is finished, close all the files.
    $fclose(outfile1);
    $fclose(outfile2);
    $fclose(outfile3);
    //wait and then stop the simulation.
    #100;
    $stop;
end 

Now if you want to read a binary file instead of hex file you just have to replace a single line in the above code,

Replace the line $readmemh("A_hex.txt",A);
with $readmemb("A_bin.txt",A);

 That's all! The output files should look the same in both the cases.

A screenshot of input files and output files are given below:


The input files can be downloaded from here,

The code was tested using Xilinx ISE 14.6 tool.

Thursday, November 12, 2015

How to Write a simple Testbench for your Verilog Design

Once you complete writing the Verilog code for your digital design the next step would be to test it. First we have to test whether the code is working correctly in functional level or simulation level. A program written for testing the main design is called Testbench

In this post I want to show you, how to write a very simple testbench. Lets consider two types of designs. One without any clock(purely combinational) and one with clock(contains synchronous elements).

Without Clock:

The module takes two inputs A and B and returns the XOR of A and B as output on port C.

//XOR module
module test(A,B,C);

    input [7:0] A,B;
    output [7:0] C;
    
    assign C = A ^ B;
    
endmodule

The testbench code for the above module is given below. I have commented it well enough to show you the important sections.

//testbench module.
//Its a testbench, so we dont have any inputs or outputs for this module.
module tb;

    // Declare the Inputs as Reg's
    reg [7:0] A;
    reg [7:0] B;

    // Declare the Outputs as wire's
    wire [7:0] C;

    // Instantiate the design you want to test.
    //Read this for instantiation methods
    test uut (
        .A(A), 
        .B(B), 
        .C(C)
    );

//We will apply the inputs here.
    initial begin
        // Apply the first set of Inputs
        A = 100;
        B = 40;
        #100; //wait for some time before we apply the next input.
        //Apply the 2nd set of inputs and so on...
      A = 10;
        B = 30;
        #100; //wait for some time before we apply the next input.  
        A = 255;
        B = 0;
        #100;
    end
//end the simulation inputs here.   

//In case your simulator doesnt have a simulation waveform then display the inputs and outputs
//using display system command.
//whenever there is a change in A or B execute the always block.
always@(A,B)
    $display("A = %b,B = %b,C = A xor B = %b",A,B,C);
    
endmodule


The following lines will be printed after the codes were simulated:

A = 01100100,B = 00101000,C = A xor B = xxxxxxxx
A = 00001010,B = 00011110,C = A xor B = 01001100
A = 11111111,B = 00000000,C = A xor B = 00010100

I used Xilinx ISE 13.1 for simulation and I got the following waveform. For bigger designs with tons of variables and internal signals, I prefer using simulation waveform to debug the design.


With Clock:

For simplicity I will just add a clock to the above module. The XOR operation will take place at every positive edge of the clock. 

//XOR module with clock
module test(CLK,A,B,C);

    input CLK;
    input [7:0] A,B;
    output [7:0] C;
    reg [7:0] C;
    
    always@ (posedge CLK)
        C = A ^ B;
    
endmodule


The testbench for this type is almost the same as the old one. Just that, we have to add a clock here. Also we have generate a clock which keeps alternating between 0 and 1 till the end of simulation.


//testbench module.
//Its a testbench, so we dont have any inputs or outputs for this module.
module tb;

    // Declare the Inputs as Reg's
    reg [7:0] A;
    reg [7:0] B;
    reg CLK;

    // Declare the Outputs as wire's
    wire [7:0] C;

    // Instantiate the design you want to test.
    //Read this for instantiation methods
    test uut (
        .CLK(CLK),
        .A(A), 
        .B(B), 
        .C(C)
    );

//Generate the clock here:
    initial CLK = 0;  //initialize the clock value to zero
     //toggle the clock value every 10 ns. 
     //So the clock has a clock period of 20 ns.
    always #10 CLK = ~CLK; 

//We will apply the inputs here.
    initial begin
        // Apply the first set of Inputs
        A = 100;
        B = 40;
        #100; //wait for some time before we apply the next input.
        //Apply the 2nd set of inputs and so on...
      A = 10;
        B = 30;
        #100; //wait for some time before we apply the next input.  
        A = 255;
        B = 0;
        #100;
    end
//end the simulation inputs here.   

//In case your simulator doesnt have a simulation waveform then display the inputs and outputs
//using display system command.
//whenever there is a change in A or B execute the always block.
always@(A,B)
    $display("A = %b,B = %b,C = A xor B = %b",A,B,C);
    
endmodule


The display system function outputs the same results as in the last case. The simulation waveform obtained in Xilinx ISE 13.1 is pasted below:


Note how the output port C is only updated at the positive edge of the clock and not right after the change of inputs.

Notes:-

As with everything else, you can get creative with testbench designs too. For complex designs you might have to read the inputs from the files and write the outputs to the files. Here I just wanted to show you something very basic. I will cover many more aspects of testbench design in coming posts.