What is a synchronous FIFO ?
A synchronous FIFO (First-In-First-Out) is a type of data buffer used in digital systems that operates under a single clock domain, meaning both read and write operations occur using the same clock signal. This design ensures that data is processed in the order it was received, which is critical for maintaining data integrity in various applications.
A synchronous FIFO is called "synchronous" because it uses synchronized clocks to control the read and write operations. The read and write pointers of the FIFO are updated synchronously with the clocks, and data is transferred between the FIFO and the external circuit synchronously with the clocks.
Synchronous FIFOs are primarily used to buffer data when the rate of data transfer exceeds the rate of data processing. This is particularly important in high-speed systems where timing discrepancies can lead to data loss or corruption.
What does depth and width indicate ?
The depth of a FIFO refers to the total number of data entries it can hold at any given time. It determines how much data can be buffered between the writing and reading processes.
Depth = (Writing Rate - Reading Rate)/Clock Frequency
The width of a FIFO refers to the number of bits that can be stored in each entry or slot within the FIFO. It essentially defines how much data can be written or read in one operation.
What are the main IO ports ?
- Data ports: It contains two ports, write and read, where the write port is used to write data into the FIFO, and the read port is used to read data from the FIFO.
- Pointers: It contains two pointers, write and read, where the write pointer tracks the position where new data will be written and the read pointer tracks the position from where data will be read. Both pointers are updated synchronously with the clock.
- Status Flags: When full , it indicates that no more data can be written until some is read and ]empty indicates that there is no data available to read.
How does it work ?
Data is written into the FIFO at each clock cycle when the write enable signal is active, and the FIFO is not full. The write pointer increments after each successful write.
Data can be read from the FIFO at each clock cycle when the read enable signal is active, and the FIFO is not empty. The read pointer increments after each successful read.
Synchronous FIFO Verilog Code
module sync_fifo #(parameter DEPTH=8, DWIDTH=16)
(
input rstn, // Active low reset
clk, // Clock
wr_en, // Write enable
rd_en, // Read enable
input [DWIDTH-1:0] din, // Data written into FIFO
output reg [DWIDTH-1:0] dout, // Data read from FIFO
output empty, // FIFO is empty when high
full // FIFO is full when high
);
reg [$clog2(DEPTH)-1:0] wptr;
reg [$clog2(DEPTH)-1:0] rptr;
reg [DWIDTH-1 : 0] fifo[DEPTH];
always @ (posedge clk) begin
if (!rstn) begin
wptr <= 0;
end else begin
if (wr_en & !full) begin
fifo[wptr] <= din;
wptr <= wptr + 1;
end
end
end
initial begin
$monitor("[%0t] [FIFO] wr_en=%0b din=0x%0h rd_en=%0b dout=0x%0h empty=%0b full=%0b",
$time, wr_en, din, rd_en, dout, empty, full);
end
always @ (posedge clk) begin
if (!rstn) begin
rptr <= 0;
end else begin
if (rd_en & !empty) begin
dout <= fifo[rptr];
rptr <= rptr + 1;
end
end
end
assign full = (wptr + 1) == rptr;
assign empty = wptr == rptr;
endmodule
The FIFO design is implemented using an internal memory array fifo and two pointers, rptr and wptr , that point to the read and write locations in the buffer. When data is written to the FIFO, it is stored in the memory location pointed to by wptr . The write pointer is then incremented to point to the next available memory location.
Similarly, when data is read from the FIFO, it is retrieved from the memory location pointed to by rptr . The read pointer is then incremented to point to the next available memory location. The number of queued items is represented by the difference between the write and read pointers. The empty and full flags are set based on the relative positions of wptr and rptr .
This is a simple example of a FIFO design with fixed capacity. There are other designs with additional features like programmable depth, programmable threshold for empty/full flags, and dual-clock operation, which are more suitable for use in complex systems.
Testbench
module tb;
reg clk;
reg [15:0] din;
wire [15:0] dout;
reg [15:0] rdata;
reg empty;
reg rd_en;
reg wr_en;
wire full;
reg rstn;
reg stop;
sync_fifo u_sync_fifo ( .rstn(rstn),
.wr_en(wr_en),
.rd_en(rd_en),
.clk(clk),
.din(din),
.dout(dout),
.empty(empty),
.full(full)
);
always #10 clk <= ~clk;
initial begin
clk <= 0;
rstn <= 0;
wr_en <= 0;
rd_en <= 0;
stop <= 0;
#50 rstn <= 1;
end
initial begin
@(posedge clk);
for (int i = 0; i < 20; i = i+1) begin
// Wait until there is space in fifo
while (full) begin
@(posedge clk);
$display("[%0t] FIFO is full, wait for reads to happen", $time);
end;
// Drive new values into FIFO
wr_en <= $random;
din <= $random;
$display("[%0t] clk i=%0d wr_en=%0d din=0x%0h ", $time, i, wr_en, din);
// Wait for next clock edge
@(posedge clk);
end
stop = 1;
end
initial begin
@(posedge clk);
while (!stop) begin
// Wait until there is data in fifo
while (empty) begin
rd_en <= 0;
$display("[%0t] FIFO is empty, wait for writes to happen", $time);
@(posedge clk);
end;
// Sample new values from FIFO at random pace
rd_en <= $random;
@(posedge clk);
rdata <= dout;
$display("[%0t] clk rd_en=%0d rdata=0x%0h ", $time, rd_en, rdata);
end
#500 $finish;
end
endmodule
Simulation Results
Note from the log below that there are times when the FIFO becomes empty and full depending on how fast the write the and read pointers operate.

xcelium> run [0] [FIFO] wr_en=0 din=0xx rd_en=0 dout=0xx empty=x full=x [10] clk i=0 wr_en=0 din=0xx [10] [FIFO] wr_en=0 din=0x5e81 rd_en=1 dout=0xx empty=1 full=0 [30] clk i=1 wr_en=0 din=0x5e81 [30] clk rd_en=1 rdata=0xx [30] FIFO is empty, wait for writes to happen [30] [FIFO] wr_en=1 din=0x7b0d rd_en=0 dout=0xx empty=1 full=0 [50] clk i=2 wr_en=1 din=0x7b0d [50] FIFO is empty, wait for writes to happen [50] [FIFO] wr_en=1 din=0x8465 rd_en=0 dout=0xx empty=0 full=0 [70] clk i=3 wr_en=1 din=0x8465 [70] [FIFO] wr_en=0 din=0xe301 rd_en=1 dout=0xx empty=0 full=0 [90] clk i=4 wr_en=0 din=0xe301 [90] clk rd_en=1 rdata=0xx [90] [FIFO] wr_en=0 din=0xcd3d rd_en=1 dout=0x7b0d empty=0 full=0 [110] clk i=5 wr_en=0 din=0xcd3d [110] clk rd_en=1 rdata=0xx [110] [FIFO] wr_en=0 din=0xe9f9 rd_en=0 dout=0x8465 empty=1 full=0 [130] clk i=6 wr_en=0 din=0xe9f9 [130] clk rd_en=0 rdata=0x7b0d [130] FIFO is empty, wait for writes to happen [130] [FIFO] wr_en=1 din=0xd2aa rd_en=0 dout=0x8465 empty=1 full=0 [150] clk i=7 wr_en=1 din=0xd2aa [150] FIFO is empty, wait for writes to happen [150] [FIFO] wr_en=1 din=0x7277 rd_en=0 dout=0x8465 empty=0 full=0 [170] clk i=8 wr_en=1 din=0x7277 [170] [FIFO] wr_en=0 din=0xdb8f rd_en=0 dout=0x8465 empty=0 full=0 [190] clk i=9 wr_en=0 din=0xdb8f [190] clk rd_en=0 rdata=0x8465 [190] [FIFO] wr_en=0 din=0x7ae8 rd_en=1 dout=0x8465 empty=0 full=0 [210] clk i=10 wr_en=0 din=0x7ae8 [210] clk rd_en=1 rdata=0x8465 [210] [FIFO] wr_en=0 din=0x28bd rd_en=1 dout=0xd2aa empty=0 full=0 [230] clk i=11 wr_en=0 din=0x28bd [230] clk rd_en=1 rdata=0x8465 [230] [FIFO] wr_en=1 din=0x6263 rd_en=0 dout=0x7277 empty=1 full=0 [250] clk i=12 wr_en=1 din=0x6263 [250] clk rd_en=0 rdata=0xd2aa [250] FIFO is empty, wait for writes to happen [250] [FIFO] wr_en=0 din=0x2120 rd_en=0 dout=0x7277 empty=0 full=0 [270] clk i=13 wr_en=0 din=0x2120 [270] [FIFO] wr_en=0 din=0xcc9d rd_en=0 dout=0x7277 empty=0 full=0 [290] clk i=14 wr_en=0 din=0xcc9d [290] clk rd_en=0 rdata=0x7277 [290] [FIFO] wr_en=1 din=0x380d rd_en=1 dout=0x7277 empty=0 full=0 [310] clk i=15 wr_en=1 din=0x380d [310] clk rd_en=1 rdata=0x7277 [310] [FIFO] wr_en=1 din=0x2ad5 rd_en=0 dout=0x6263 empty=0 full=0 [330] clk i=16 wr_en=1 din=0x2ad5 [330] clk rd_en=0 rdata=0x7277 [330] [FIFO] wr_en=0 din=0xe91d rd_en=1 dout=0x6263 empty=0 full=0 [350] clk i=17 wr_en=0 din=0xe91d [350] clk rd_en=1 rdata=0x6263 [350] [FIFO] wr_en=1 din=0x650a rd_en=0 dout=0x380d empty=0 full=0 [370] clk i=18 wr_en=1 din=0x650a [370] clk rd_en=0 rdata=0x6263 [370] [FIFO] wr_en=0 din=0xbdf2 rd_en=0 dout=0x380d empty=0 full=0 [390] clk i=19 wr_en=0 din=0xbdf2 [390] clk rd_en=0 rdata=0x380d [390] [FIFO] wr_en=1 din=0x34d8 rd_en=0 dout=0x380d empty=0 full=0 [410] clk i=20 wr_en=1 din=0x34d8 [410] clk rd_en=0 rdata=0x380d [410] [FIFO] wr_en=1 din=0xdeb rd_en=0 dout=0x380d empty=0 full=0 [430] clk rd_en=0 rdata=0x380d [490] [FIFO] wr_en=1 din=0xdeb rd_en=0 dout=0x380d empty=0 full=1 Simulation complete via $finish(1) at time 930 NS + 0