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:
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.