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