Generic Depth Synchronous FIFO
Synchronous FIFO (first-in-first-out) buffers are one of the most commonly used RTL designs in almost all digital systems. They are heavily used in communicating data/signals between two different logic blocks, interconnection interfaces, and storing data packets. These are just a few examples of their usage. Synchronous means the producer and the consumer work on the same clock, i.e. we use the same rate at which we write and read to/from FIFO.
In this part, I present the RTL design of Synchronous FIFO where the depth of the memory can be of any size, not necessarily power of 2.
GitHub link for full implementation and testbench: [generic-sync-fifo]
EDA-playground link: [eda-generic-fifo]
RTL Design and Implementation
FIFO is a sequential block that consists of a write (producer) interface and a read (consumer) interface. As our design is synchronous, so we will have one single clock and a reset port. Further, the device will also have two flags representing the fill state fo the FIFO they are called Empty and Full. Let's summarize the interface below.
System Inputs:
clk: System clock
rst_n: Asynchronous active low reset
Write Interface:
wr_data_i: [Input] Data to be written in the FIFO memory
wr_en_i: [Input] Indicating when we want to perform the write
full_o: [Output] Status if FIFO is full
Read Interface:
rd_data_o: [Output] Data read out of the FIFO
rd_en_i: [Input] Indicating when we want to perform the read
empty_o: [Output] Status if FIFO is empty
// Using above interface we can easily define our module
// Module Definition
module sync_fifo_sv #(WIDTH = 8,
DEPTH = 7 )
(
input logic clk,
input logic rst_n,
input logic wr_en_i,
input logic [WIDTH-1:0] wr_data_i,
output logic full_o,
input logic rd_en_i,
output logic [WIDTH-1:0] rd_data_o,
output logic empty_o
);
To capture the location to perform read and write operations we also define two pointers along with the memory block. The memory would be an SRAM but for now we define an array block to be used as memory.
Notice below that we are using an extra-bit in our read/write pointers. For example, if the depth is selected as 4 then we will use 2-bits to address the memory as usual, along with an extra third bit to help us generate full and empty flags.
logic [MSB:0] wr_ptr, rd_ptr;
logic [WIDTH-1:0] mem [DEPTH-1:0];
assign empty_o = (wr_ptr == rd_ptr);
assign full_o = (wr_ptr[MSB]^rd_ptr[MSB])
&& (wr_ptr[MSB-1:0] == rd_ptr[MSB-1:0]);
Now let us look at how to correctly set our pointers when we perform write to FIFO. First, we have to follow two golden rules in FIFO.
Never write to a FIFO when it is full.
Never read from a FIFO when it is empty.
That is even if write or read enable is asserted we should check in our logic that we don't perform write/read based on above two rules. Next, when we write or read to FIFO then we point to next location to be written or to be read. That is done by incrementing the our pointers. Now the tricky part is how to wrap our pointers if depth is not a power of 2?
For this, we enhance our logic by checking if we have reached the last location to be written/read, if we have then we wrap our pointers back to the starting address. Care must be taken that we still maintain the correct value for our extra-bit that we used in our pointers else we will produce incorrect full and empty flag. This is done by inverting the last bit in the pointers when we reach the last location. This is shown below for the write logic.
// Wrap the pointer when reached the required depth
// toggle the MSB bit of pointer for correct full/empty flags
else if (wr_en_i && !full_o) begin
if (wr_ptr[MSB-1:0] == DEPTH-1) begin
wr_ptr[MSB-1:0] <= '0;
wr_ptr[MSB] <= ~wr_ptr[MSB];
end
else begin
wr_ptr <= wr_ptr + 1;
end
// Write the data to memory
mem[wr_ptr[MSB-1:0]] <= wr_data_i;
end
Similarly, we can design the read logic for our FIFO and you can see the Github link for the full logic implementation.
GitHub link for full implementation and testbench: [generic-sync-fifo]
EDA-playground link: [eda-generic-fifo]
Full verification is a work-in-progress and will update it in this block soon :-)