Fifo (first-in-first-out) are used to for serial transfer of information whenever there is a difference of Transfer rate. The Transfer rate may differ due to difference in number of ports, frequency or data-width between source and destination.
The FIFO width is chosen to compensate for the Transfer rate and is calculated as follows:
Fifo size = Source Freq. * ports * Data-with / Dest. Freq. * ports * Data-with
Ex: Source : Port = 1, Freq. = 100KHz, Data-Width = 20
Destination : Port = 2, Freq. = 50KHz, Data-Width = 10
FIFO Size : 1*100*20/ 1*50*10 = 4 Entries
If the FIFO size is a fractional number then we round-up the FIFO size to nearest largest whole number. For Ex 4.33 -> 5.
Here’s a SV reference for 8 Entry deep FIFO with Data-Width of 32 bits :

logic [3:0] wraddr, rdaddr; logic [7:0] wren, rden; logic [31:0] data_ff[7:0]; logic [31:0] data_in, data_out; logic wr_en, rd_en; logic fifo_empty, fifo_full; always_comb begin if(!fifo_full & wr_en) wraddr = wraddr + 1; else wraddr = wraddr_ff; end always_comb begin if(!fifo_empty & rd_en) rdaddr = rdaddr + 1; else rdaddr = rdaddr_ff; end always_ff @(posedge clk or negedge reset) begin if(!reset) rdaddr_ff <= 0; else if (rd_en) rdaddr_ff <= rdaddr; else rdaddr_ff <= rdaddr_ff; end always_ff @(posedge clk or negedge reset) begin if(!reset) wraddr_ff <= 0; else if (wr_en) wraddr_ff <= wraddr; else wraddr_ff <= wraddr_ff; end assign fifo_full = (wraddr[2:0] == rdaddr[2:0]) & (wraddr[3] != rdaddr[3]); assign fifo_empty = (wraddr[2:0] == rdaddr[2:0]) & (wraddr[3] == rdaddr[3]); assign wren = (wr_en == 1'b1) ? (1 << wraddr) : 8'b0; assign rden = (rd_en == 1'b1) ? (1 << rdaddr) : 8'b0; genvar i; generate for (i = 0; i < 8; i++) begin always_ff @(posedge clk or negedge reset) begin if(wren[i]) data_ff[i] <= data_in; else data_ff[i] <= data_ff[i]; end end endgenerate always_comb begin for (int j = 0; j < 8; j++) begin if(rden[j]) data_out = data_ff[j]; else data_out = 8'b0; end
If the Fifo Width is not binary multiples of 2 , detecting full and empty conditions is difficult using above method. Alternately, we can use below two implementations that are more scalable with Fifo Widths:
1st implementation can be followed with a use of Single Counter to track Full and Empty conditions :
logic [2:0] fifo_count, fifo_count_ff;
always_comb begin
unique casez ({rd_en, wr_en})
2'b00 : fifo_count = fifo_count_ff;
2'b01 : fifo_count = (fifo_count_ff == 3'b111) ? 3'b111
: (fifo_count_ff + 3'b001);
2'b10 : fifo_count = (fifo_count_ff == 3'b0) ? 3'b0
: (fifo_count_ff - 3'b001);
2'b11 : fifo_count = fifo_count_ff;
endcase
end
always_ff @(posedge clk or negedge reset) begin
if(!reset)
fifo_count_ff <= 3'b0;
else
fifo_count_ff <= fifo_count;
end
assign fifo_full = (fifo_count == 3'b111);
assign fifo_empty = (fifo_count == 3'b0);
2nd implementation is using read and write roll-over pointers on wraddr and rdaddr.
logic wr_rollover, wr_rollover_ff;
logic rd_rollover, rd_rollover_ff;
always_comb begin
if((wraddr == 3'b111) & (wr_en & !rd_en))
wr_rollover = ~wr_rollover_ff;
else
wr_rollover = wr_rollover_ff;
end
always_ff @(posedge clk or negedge reset) begin
if (!reset)
wr_rollover_ff <= 1'b0;
else
wr_rollover_ff <= wr_rollover;
end
always_comb begin
if((rdaddr == 3'b0) & (!wr_en & rd_en))
rd_rollover = ~rd_rollover_ff;
else
rd_rollover = rd_rollover_ff;
end
always_ff @(posedge clk or negedge reset) begin
if (!reset)
rd_rollover_ff <= 1'b0;
else
rd_rollover_ff <= rd_rollover;
end
assign fifo_full = (wraddr[2:0] == rdaddr[2:0]) &
(wr_rollover != rd_rollover);
assign fifo_empty = (wraddr[2:0] == rdaddr[2:0]) &
(wr_rollover == rd_rollover);
One thought on “Synchronous FIFO :”