A synchronous FIFO (First-In-First-Out) is a digital circuit that is used to transfer data between the same clock domain and the main function is to buffer data when the rate of data transfer is faster than the rate of data processing.
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 typically consist of two basic parts: a write port and a read port. The write port is used to write data into the FIFO, while the read port is used to read data from the FIFO. The write and read pointers are used to keep track of the data that has been written and read, respectively, and to ensure that the FIFO operates in a correct and efficient manner.
module sync_fifo #(parameter DEPTH=8, DWIDTH=16) ( input rstn, // Active low reset Wr_clk, // Write clock rd_clk, // Read 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 wr_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 rd_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.
module tb; reg wr_clk; //reg rd_clk; wire rd_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), .wr_clk(wr_clk), .rd_clk(rd_clk), .din(din), .dout(dout), .empty(empty), .full(full) ); always #10 wr_clk <= ~wr_clk; //always #20 rd_clk <= ~rd_clk; assign rd_clk = wr_clk; initial begin wr_clk <= 0; //rd_clk <= 0; rstn <= 0; wr_en <= 0; rd_en <= 0; stop <= 0; #50 rstn <= 1; end initial begin @(posedge wr_clk); for (int i = 0; i < 50; i = i+1) begin // Wait until there is space in fifo while (full) begin @(posedge wr_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] wr_clk i=%0d wr_en=%0d din=0x%0h ", $time, i, wr_en, din); // Wait for next clock edge @(posedge wr_clk); end stop = 1; end initial begin @(posedge rd_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 rd_clk); end; // Sample new values from FIFO at random pace rd_en <= $random; @(posedge rd_clk); rdata <= dout; $display("[%0t] rd_clk rd_en=%0d rdata=0x%0h ", $time, rd_en, rdata); end #500 $finish; end endmodule