December 22, 2024

Save Data to Files from Verilog and Vivado Simulations – FPGA Tutorial

In this FPGA, Verilog, and Vivado tutorial, we explain how to save simulation data to files from Verilog and Vivado simulations.

Motivation: The standard practice when developing and implementing FPGA algorithms is to first implement and test algorithms in MATLAB and Python. This is done since it is significantly easier to test algorithms in MATLAB or Python than in FPGAs. After MATLAB or Python simulation confirms that the algorithms work as expected, the next stage is to implement them in Verilog and simulate them in the Vivado development environment. The final stage is the FPGA implementation. During testing and simulation in Verilog and Vivado, it is often necessary to save simulation data to files such that the Verilog simulation data can be compared with data generated in Python and MATLAB. Consequently, we need to know how to save Verilog simulation data to files.

Outline:

We will first write a Verilog module. The Verilog module will calculate a complement of a 2-bit binary number. Then, we will write a Verilog simulation file (test bench file). The Verilog simulation will first load test data from a file. The test data is a two-bit binary sequence that should be inverted by the Verilog module. Then, we will save the inverted sequence to another data file.

    The YouTube tutorial is given below.

    How to Save Data to Files from Verilog and Vivado Simulations – FPGA Tutorial

    As a test case, we will create a simple module that will perform a bit-wise complement of the input sequence. The input sequence consists of 10 elements, where every element is a 2-bit binary number. For tests, create a file and save it as “input.data”. The content of the file is given below

    00
    01
    10
    11
    01
    01
    10
    11
    00
    11

    We will load this file in our Verilog simulation. The Verilog module that calculates the complement is given below.

    `timescale 1ns / 1ps
    module test_module(inputData, outputData);
    
    input [1:0] inputData;
    output [1:0] outputData;
    
    assign outputData=~inputData;
    
    endmodule

    This module contains two ports “inputData” and “outputData”. Both of these ports are two-bit long. The outputData is a bit-wise complement of inputData. Save this file as “test_module.v”

    The binary numbers from the created file called “input.data” will be sent sample-by-sample to the Verilog simulation. For that purpose, we define a testbench file (simulation file) given below.

    `timescale 1ns / 1ps
    module simulation;
    
    integer k;
    integer FILE1;
    
    reg [1:0] inputData;
    wire [1:0] outputData;
    
    reg CLK;
    
    reg [1:0] data[9:0];
    
    always #10 CLK=~CLK;
    
    test_module UUT(.inputData(inputData),
                    .outputData(outputData)
                    );
                    
    initial
    begin
    CLK=0;
    k=0;
    // read data from the file
    $readmemb("input.data",data);
    // open the file for saving data
    FILE1=$fopen("save.data","w");
    #20
    for (k=0;k<10;k=k+1)
    begin
    inputData<=data[k];
    @(posedge CLK);
    $fdisplay(FILE1,"%b",outputData);
    if(k==9)
        $fclose(FILE1);    
    end
    end             
    endmodule
    

    Next, we explain this file. We use the following statements to declare the variables:

    integer k;
    integer FILE1;
    
    reg [1:0] inputData;
    wire [1:0] outputData;
    
    reg CLK;
    
    reg [1:0] data[9:0];

    The integer variable “k” will be used to iterate through the input data file in a for loop that we will define later on. The integer variable “FILE1” will be used to save the simulation data to a file. Variables “inputData” and “outputData” are used for input and output data. the variable CLK is used to define the clock. We declare a register array for storing data from the input data file:

    reg [1:0] data[9:0];

    Later on, in the simulation file, the content of the file “input.data” is loaded and saved in this register array. That is every entry of this array stores a corresponding row from the input data file.

    We define the clock by using the statement

    always #10 CLK=~CLK;

    This will create a pulse clock with a pulse width of 10 nanoseconds.

    The unit under test is defined as follows

    test_module UUT(.inputData(inputData),
                    .outputData(outputData)
                    );

    This statement is used to connect the ports of the module with the variables defined in the simulation file.

    The simulation is defined below.

    initial
    begin
    CLK=0;
    k=0;
    // read data from the file
    $readmemb("input.data",data);
    // open the file for saving data
    FILE1=$fopen("save.data","w");
    #20
    for (k=0;k<10;k=k+1)
    begin
    inputData<=data[k];
    @(posedge CLK);
    $fdisplay(FILE1,"%b",outputData);
    if(k==9)
        $fclose(FILE1);    
    end
    end             

    We initialize the clock variable to zero. We use the following statement

    // read data from the file
    $readmemb("input.data",data);

    to read data from the file and to store it in the array register called “data”. Next, we open a file for storing the data. For that purpose, we use the following statement

    // open the file for saving data
    FILE1=$fopen("save.data","w");

    The name of the file is “save.data”. Initially, this file does not exist, and it is created in the first run of the simulation. In the next run of the simulation, the file is opened in the write mode. That is, the previous content of the file is erased next time the file is opened. Next, we define a for loop that will load the data from the input file and that will save the data in the output file:

    for (k=0;k<10;k=k+1)
    begin
    inputData<=data[k];
    @(posedge CLK);
    $fdisplay(FILE1,"%b",outputData);
    if(k==9)
        $fclose(FILE1);    
    end

    This loop will iterate through the entries of the array data, and it will pass every entry to the module to calculate the outputData. We use the following statement to save the data to the file:

    $fdisplay(FILE1,"%b",outputData);

    The file is specified by the integer FILE, “%b” means that we open the file in the binary mode, and “outputData” is the data that we save to the file. After the execution of the test bench. The output data file called “save.data” looks like this

    11
    10
    01
    00
    11
    10
    01
    00
    11
    00
    

    We can observe that the output data is a bit-wise complement of the input data. The simulation time diagram is shown below.