Search code examples
simulationsystem-verilogfifo

Undefined output in Ring FIFO simulation


I have been working on a FIFO for SystemVerilog. The first simulations turned out good. However, upon expanding the simulation to try to bring it to its limit and account for corner cases, I have encountered a few issues. Although I have been able to solve most of them there is one that I have not been able to solve.

//This fifo implements a Circular Buffer topology
//Note: The way the fifo is defined it is impossible to have a depth of 1
//This is because the pointers will always be on the same position
//The fifo is therefore always "empty"
module i2s_fifo #(
    parameter WIDTH = 16,
    parameter DEPTH = 10
)(
    input logic clk_i,
    input logic rst_ni,

    //Write Communication Signals
    output logic wready,
    input logic wvalid,
    input logic [WIDTH-1:0] fifo_data_in,

    //Read Communication Signals
    input logic rready,
    output logic rvalid,
    output logic [WIDTH-1:0] fifo_data_out,

    //Debuging
    output logic [$clog2(DEPTH)-1:0]wptr_tb,
    output logic [$clog2(DEPTH)-1:0] rptr_tb
);
    //This returns how many bits are needed to add and address of X size
    //For example. $clog2(7) = 3. We need three bits to access a memory of 7 values.
    //Basically converts from an integer to bits
    localparam int unsigned byte_depth = $clog2(DEPTH);

    //Pointers
    logic [byte_depth-1:0] fifo_rptr, fifo_wptr; 
    assign wptr_tb = fifo_wptr;
    assign rptr_tb = fifo_rptr;
    
    //We cannot read if the FIFO is empty
    //We cannot write if the FIFO is full
    logic fifo_empty;
    logic fifo_full;

    always_comb begin : fifo_state   
        if(fifo_rptr == fifo_wptr) begin
            assign fifo_empty = 1'b1;
        end else begin
            assign fifo_empty = 1'b0;
        end

        if(fifo_wptr == DEPTH)begin //Pointer is at the edge of fifo
            if (fifo_rptr == 0) begin 
                fifo_full <= 1'b1;
            end else begin
                fifo_full <= 1'b0;
            end
        end else if (fifo_wptr+1 == fifo_rptr)begin
            fifo_full <= 1'b1;
        end else begin
            fifo_full <= 1'b0;
        end
    end

    //Handshake
    logic revent, wevent;

    assign rvalid = !fifo_empty;
    assign revent = rvalid & rready;

    assign wready = !fifo_full;
    assign wevent = wvalid & wready;

    //INCREASE POINTER POSSITION
    always_ff @(posedge clk_i) begin
        if(!rst_ni)begin
            fifo_rptr <= {(byte_depth){1'b0}};
        end else if (revent) begin
            if (fifo_rptr == DEPTH) begin
                fifo_rptr <= {(byte_depth){1'b0}};
            end else begin
                fifo_rptr <= fifo_rptr+1;
            end
        end
    end

    always_ff @(posedge clk_i) begin
        if(!rst_ni)begin
            fifo_wptr <= {(byte_depth){1'b0}};
        end else if (wevent) begin
            if (fifo_wptr == DEPTH) begin
                fifo_wptr <= {(byte_depth){1'b0}};
            end else begin
                fifo_wptr <= fifo_wptr+1;
            end
        end
    end

    //Write and Save data
    //data_type  [rows][columns] array_name;
    logic [DEPTH-1:0] [(WIDTH-1):0] fifo_storage;

    always_ff @(posedge clk_i)begin
        if(wevent) begin
            //This access the row dictated by the pointer
            fifo_storage[fifo_wptr] <= fifo_data_in;
        end
    end

    always_ff @(posedge clk_i)begin
        if(revent) begin
            fifo_data_out <= fifo_storage[fifo_rptr];
        end
    end    
endmodule

And this is the testbench

module i2s_fifo_tb(

);
    localparam WIDTH = 16;
    localparam DEPTH = 10;
    logic clk;
    logic rst;
    logic wready;
    logic wvalid;
    logic [WIDTH-1:0]fifo_data_in;
    logic rready;
    logic rvalid;
    logic [WIDTH-1:0]fifo_data_out;
    logic [$clog2(DEPTH)-1:0]rptr_tb;
    logic [$clog2(DEPTH)-1:0]wptr_tb;
    i2s_fifo #(
        .WIDTH(16),
        .DEPTH(10)
    )UUT(
        .clk_i(clk),
        .rst_ni(rst),
        .wready,
        .wvalid,
        .fifo_data_in,
        .fifo_data_out,
        .rready,
        .rvalid,
        .rptr_tb,
        .wptr_tb
    );
    always begin
        clk = '1;
        #10000;
        clk = '0;
        #10000;
    end
    
    initial begin
        rst = '0;
        rready = '0;
        wvalid = 0;
        fifo_data_in = 16'd0;
        #20000;
        //Fill FIFO
        rst = 1;
        wvalid = 1;
        fifo_data_in = 16'h1111;
        #20000;
        fifo_data_in = 16'h2222;
        #20000;
        fifo_data_in = 16'h3333;
        #20000;
        fifo_data_in = 16'h4444;
        #20000;
        fifo_data_in = 16'h5555;
        #20000;
        fifo_data_in = 16'h6666;
        #20000;
        fifo_data_in = 16'h7777;
        #20000;
        fifo_data_in = 16'h8888;
        #20000;
        fifo_data_in = 16'h9999;
        #20000;
        fifo_data_in = 16'haaaa;
        #20000;
        fifo_data_in = 16'hbbbb;
        #20000;
        fifo_data_in = 16'hcccc;
        #20000;
        //Read FIFO
        wvalid = '0;
        rready = '1;
        #100000;
        //Pause Reading
        rready= 0;
        #100000;
        //Resume Reading
        rready = 1;
        #160000;
        //Check correct writing
        rready = 0;
        fifo_data_in = 16'h1111;
        #20000;
        fifo_data_in = 16'h2222;
        #20000;
        fifo_data_in = 16'h3333;
        #20000;
        fifo_data_in = 16'h4444;
        #20000;
        wvalid = 1;
        fifo_data_in = 16'h5555;
        #20000;
        fifo_data_in = 16'h6666;
        #20000;
        fifo_data_in = 16'h7777;
        #20000;
        wvalid = 0;
        rready = 1;
        #80000;
        //Multiple Writing and reading
        rready = 0;
        wvalid = 1;
        fifo_data_in = 16'h1111;
        #20000;
        fifo_data_in = 16'h2222;
        #20000;
        fifo_data_in = 16'h3333;
        #20000;
        rready = 1;
        fifo_data_in = 16'h4444;
        #20000;
        fifo_data_in = 16'h5555;
        #20000;
        fifo_data_in = 16'h6666;
        #20000;
        wvalid = 0;
        //Fill it again, different point
        #60000;
        rready = 0;
        wvalid = 1;
        fifo_data_in = 16'h1111;
        #20000
        fifo_data_in = 16'h2222;
        #20000
        fifo_data_in = 16'h3333;
        #20000
        fifo_data_in = 16'h4444;
        #20000
        fifo_data_in = 16'h5555;
        #20000
        fifo_data_in = 16'h6666;
        #20000
        fifo_data_in = 16'h7777;
        #20000
        fifo_data_in = 16'h8888;
        #20000
        fifo_data_in = 16'h9999;
        #20000
        fifo_data_in = 16'haaaa;
        #20000
        fifo_data_in = 16'hbbbb;
        #20000
        fifo_data_in = 16'hcccc;
        #20000
        wvalid = 0;
        rready = 1;
        #600000
        $finish;
    end

endmodule

Basically on the testbench I simply input a lot of data and check whether the fifo behaves accordingly. However. On the simulation I have observed that whenever the read pointer points to the zero position, the output is not define.

At first I thought it may have been an issue with timing, that at the beginning of the simulation I simply did not write anything on the position zero and thus, when attempting to read int for the first time I have an undefined state. Thus, I decided to further the simulation. However, I have observed that any time the read pointer looks at the zero position, the state is undefined.

My hypothesis is that there is some problem in this part

    //Write and Save data
    //data_type  [rows][columns] array_name;
    logic [DEPTH-1:0] [(WIDTH-1):0] fifo_storage;

    always_ff @(posedge clk_i)begin
        if(wevent) begin
            //This acces the row dictated by the pointer
            fifo_storage[fifo_wptr] <= fifo_data_in;
        end
    end

    always_ff @(posedge clk_i)begin
        if(revent) begin
            fifo_data_out <= fifo_storage[fifo_rptr];
        end
    end    

namely that the vectors I'm using to assign the data are wrong. But, I cannot pinpoint the exact issue.

I would appreciate some help.

Here are two captures from the simulation:

Simulation 1

Simulation 2


Solution

  • The problem is that your read pointer goes out of range when you are reading from the FIFO, which results in unknown values when you read from the FIFO.

    You set the FIFO DEPTH to 10, which means your read pointer needs at least 4 bits, and you properly declared the pointer as [3:0]. However, some of the possible values of the pointer are illegal, namely 10 through 15. Only pointer values from 0 to 9 are legal. Your simulation waveforms clearly show that the pointer reaches the value 10 (hexadecimal a) when revent is 1. This is why the fifo_data_out signals becomes unknown.

    Note that the output signal is unknown because it was updated when the pointer was sampled as 10.

    To prevent that from happening, you could consider changing:

            if (fifo_rptr == DEPTH) begin
    

    to:

            if (fifo_rptr == (DEPTH-1)) begin
    

    But, you need to make sure your design works properly.