The First In First Out (FIFO) is a data arrangement structure in which the data that enters first is the one that is removed first. Let us see how to implement Synchronous FIFO in Verilog in this post.
Procedure to implement FIFO:
Testbench:
Simulation Result:
Procedure to implement FIFO:
- Create a normal memory in Verilog.
- When the data and push signal is given, write to the memory starting from first address.
- When pop signal is given, read from the memory from the first address.
- When FIFO becomes empty, assert empty and if it becomes full, assert full signal.
We require a write pointer as well as a read pointer to control a FIFO because we keep incrementing the address while writing, whereas for read, we have to start from the first address.
Empty and Full:
Empty and full condition assertion in synchronous FIFO are not as simple as in stack. Here we have rdptr and wrptr. When both the pointers are equal, the FIFO could either be empty or full.
If the last operation was write and the pointers become equal, then the FIFO is full.
If the last operation was read and the pointers become equal, then the FIFO is empty.
So we use another variable 'fullchk' to check if the last operation was write and the FIFO became full. Then this can be used to assert full signal.
Similarly if 'emptychk' is there, then we can say that the FIFO is empty.
Verilog Code Logic:
Empty and Full:
Empty and full condition assertion in synchronous FIFO are not as simple as in stack. Here we have rdptr and wrptr. When both the pointers are equal, the FIFO could either be empty or full.
If the last operation was write and the pointers become equal, then the FIFO is full.
If the last operation was read and the pointers become equal, then the FIFO is empty.
So we use another variable 'fullchk' to check if the last operation was write and the FIFO became full. Then this can be used to assert full signal.
Similarly if 'emptychk' is there, then we can say that the FIFO is empty.
Verilog Code Logic:
- Use variables rdptr to traverse read location and wrptr to traverse write locations in memory.
- During push signal, write to the memory and increment wrptr.
- During pop, read from the memory and decrement rdptr.
- Empty is asserted when pointers are equal and there is emptychk. Deasserted when a push occurs.
- Full occurs when there is fullchk. Deasserted when a pop occurs.
Verilog Code:
module sync_fifo( clk, rstn, pop, push, empty, full, din, dout ); parameter PTR_WIDTH = 3; parameter DATA_WIDTH = 8; parameter DEPTH = 8; input clk; input rstn; input pop; input push; input [DATA_WIDTH-1:0]din; output [DATA_WIDTH-1:0]dout; output empty; output full; reg [DATA_WIDTH-1:0]fifo[DEPTH-1:0]; reg [PTR_WIDTH-1:0]rdptr, next_rdptr; reg [PTR_WIDTH-1:0]wrptr, next_wrptr; reg [DATA_WIDTH-1:0]dout, next_dout; reg empty, next_empty; reg full, next_full; assign fullchk = push && !(|(wrptr^(rdptr-1'b1))); assign emptychk = pop && !(|(rdptr^(wrptr-1'b1))); always @ (posedge clk) //Sequential block begin if(!rstn) begin dout <= 8'd0; empty <= 1'b1; full <= 1'b0; rdptr <= 1'b0; wrptr <= 1'b0; end else begin dout <= next_dout; empty <= next_empty; full <= next_full; rdptr <= next_rdptr; wrptr <= next_wrptr; end end always @ (*) //Combinational Block begin next_dout = dout; next_empty = emptychk ? 1'b1 : push ? 1'b0 : empty; next_full = fullchk ? 1'b1 : pop ? 1'b0 : full; next_rdptr = rdptr; next_wrptr = wrptr; if(push) //write begin fifo[wrptr] = din; next_wrptr = wrptr+1; end else if(pop) //read begin next_dout = fifo[rdptr]; next_rdptr = rdptr+1; end else begin next_dout = dout; next_rdptr = rdptr; next_wrptr = wrptr; end end endmodule
Testbench:
module Fifo_tb; // Inputs reg clk; reg rstn; reg pop; reg push; reg [7:0] din; // Outputs wire empty; wire full; wire [7:0] dout; // Instantiate the Unit Under Test (UUT) Fifo uut ( .clk(clk), .rstn(rstn), .pop(pop), .push(push), .empty(empty), .full(full), .din(din), .dout(dout) ); always #5 clk = ~clk; task reset(); begin clk = 1'b1; rstn = 1'b0; pop = 1'b0; push = 1'b0; din = 8'd0; #30; rstn = 1'b1; end endtask task read_fifo(); begin pop = 1'b1; #10; pop = 1'b0; end endtask task write_fifo([7:0]din_tb); begin push = 1'b1; din = din_tb; #10 push = 1'b0; end endtask // Main code initial begin reset(); #10; repeat(2) begin write_fifo(8'h11); #10; write_fifo(8'h22); #10; write_fifo(8'h33); #10; write_fifo(8'h44); #10; end #10; repeat(2) begin read_fifo(); #10; end #10; $finish; end endmodule
Simulation Result:
From the simulation result,
- The FIFO is initially empty as there is no data.
- Data is pushed into it until it becomes full.
- After that all the data have been read, indicating empty. Since it is a FIFO, the first data which entered is the one that is removed.
Thus we have verified the basic operation of a Synchronous FIFO.
Note: Depth of the FIFO must be in powers of 2 and the ptr_width parameter must be set to log to the base 2 of FIFO depth.
Further Steps:
Next you can look into an Asynchronous FIFO. This kind of FIFO works on two different clocks and so we require synchronization. This makes its implementation a little more complex.
However here is a good article on the design of an Asynchronous FIFO on verilogpro:
it helped me ...
ReplyDeletethank you