HOME

    Electronics Directory Articles/ Tutorials eBooks

About Us

FORUM Links Contact Us
   

 

Implementing a FIFO using Verilog
 

 

A FIFO or Queue is an array of memory commonly used in hardware to transfer transfer data between two circuits with different clocks. There are many other use of FIFO also. FIFO uses a dual port memory and there will be two pointers to point read and write addresses. Here is a generalized block diagram of FIFO. 

 

 

 

 

Generally fifos are implemented using rotating pointers. We can call write and read pointers of a FIFO as head and tail of data area. Initially read and write pointers of the FIFO will point to the same location. In a fifo with n locations, after writing data to 'n'th location, write pointer points to 0th location.

Here is an example to explain how FIFO uses the memory. This is a fifo of length 8, WP and RP are the locations where write pointer and read pointer points. Shaded area in the diagram is filled with data.

 

 One data pushed to fifo:

RP

WP

           
 5 more data pushed to fifo

RP

       

 

WP

 
 3 data popped from fifo
     

RP

   

WP

 
 3 more data pushed to fifo
 

WP

 

RP

       
 2 more data pushed to fifo: fifo full
     

WP, RP

       
 6 data popped from fifo
 

RP

 

WP

       

A FIFO may be synchronous or asynchronous. There is no clock in asynchronous fifo. In synchronous fifo, there may be 1 or 2 clocks since some FIFOs have separate clocks for read and write. Asynchronous fifos are not used commonly now a days because synchronous FIFOs have improved interface timing.

`define BUF_WIDTH 3    // BUF_SIZE = 16 -> BUF_WIDTH = 4, no. of bits to be used in pointer
`define BUF_SIZE ( 1<<`BUF_WIDTH )

module fifo( clk, rst, buf_in, buf_out, wr_en, rd_en, buf_empty, buf_full, fifo_counter );

input                 rst, clk, wr_en, rd_en;   
// reset, system clock, write enable and read enable.
input [7:0]           buf_in;                   
// data input to be pushed to buffer
output[7:0]           buf_out;                  
// port to output the data using pop.
output                buf_empty, buf_full;      
// buffer empty and full indication 
output[`BUF_WIDTH :0] fifo_counter;             
// number of data pushed in to buffer   

reg[7:0]              buf_out;
reg                   buf_empty, buf_full;
reg[`BUF_WIDTH :0]    fifo_counter;
reg[`BUF_WIDTH -1:0]  rd_ptr, wr_ptr;           // pointer to read and write addresses  
reg[7:0]              buf_mem[`BUF_SIZE -1 : 0]; //  

always @(fifo_counter)
begin
   buf_empty = (fifo_counter==0);
   buf_full = (fifo_counter== `BUF_SIZE);

end

always @(posedge clk or posedge rst)
begin
   if( rst )
       fifo_counter <= 0;

   else if( (!buf_full && wr_en) && ( !buf_empty && rd_en ) )
       fifo_counter <= fifo_counter;

   else if( !buf_full && wr_en )
       fifo_counter <= fifo_counter + 1;

   else if( !buf_empty && rd_en )
       fifo_counter <= fifo_counter - 1;
   else
      fifo_counter <= fifo_counter;
end

always @( posedge clk or posedge rst)
begin
   if( rst )
      buf_out <= 0;
   else
   begin
      if( rd_en && !buf_empty )
         buf_out <= buf_mem[rd_ptr];

      else
         buf_out <= buf_out;

   end
end

always @(posedge clk)
begin

   if( wr_en && !buf_full )
      buf_mem[ wr_ptr ] <= buf_in;

   else
      buf_mem[ wr_ptr ] <= buf_mem[ wr_ptr ];
end

always@(posedge clk or posedge rst)
begin
   if( rst )
   begin
      wr_ptr <= 0;
      rd_ptr <= 0;
   end
   else
   begin
      if( !buf_full && wr_en )    wr_ptr <= wr_ptr + 1;
          else  wr_ptr <= wr_ptr;

      if( !buf_empty && rd_en )   rd_ptr <= rd_ptr + 1;
      else rd_ptr <= rd_ptr;
   end

end
endmodule

 

When ever FIFO counter becomes zero or BUF_LENGTH, empty or full flags will beset.

fifo_counter is incremented if write takes place and buffer is not full and will be decremented id read takes place and buffer is not empty. If both read and write takes place, counter will remain the same.

rd_ptr and wr_ptr are read and write pointers. Since we selected the bits in these registers same as address width of buffer, when buffer overflows, values will overflow and become 0.

Following testbench can be used to test the fifo code. Some push and pop are made to test normal full and empty conditions. We can observe in the output that we popped the values in the order we pushed. Simultaneous push and pop is tested using fork-join once.

`define BUF_WIDTH 3

module fifo_test();
reg clk, rst, wr_en, rd_en ;
reg[7:0] buf_in;
reg[7:0] tempdata;
wire [7:0] buf_out;
wire [`BUF_WIDTH :0] fifo_counter;

fifo ff( .clk(clk), .rst(rst), .buf_in(buf_in), .buf_out(buf_out), 
         .wr_en(wr_en), .rd_en(rd_en), .buf_empty(buf_empty), 
         .buf_full(buf_full), .fifo_counter(fifo_counter) );

initial
begin
   clk = 0;
   rst = 1;
        rd_en = 0;
        wr_en = 0;
        tempdata = 0;
        buf_in = 0;


        #15 rst = 0;
  
        push(1);
        fork
           push(2);
           pop(tempdata);
        join              //push and pop together   
        push(10);
        push(20);
        push(30);
        push(40);
        push(50);
        push(60);
        push(70);
        push(80);
        push(90);
        push(100);
        push(110);
        push(120);
        push(130);

        pop(tempdata);
        push(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
   push(140);
        pop(tempdata);
        push(tempdata);//
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        push(5);
        pop(tempdata);
end

always
   #5 clk = ~clk;

task push;
input[7:0] data;


   if( buf_full )
            $display("---Cannot push: Buffer Full---");
        else
        begin
           $display("Pushed ",data );
           buf_in = data;
           wr_en = 1;
                @(posedge clk);
                #1 wr_en = 0;
        end

endtask

task pop;
output [7:0] data;

   if( buf_empty )
            $display("---Cannot Pop: Buffer Empty---");
   else
        begin

     rd_en = 1;
          @(posedge clk);

          #1 rd_en = 0;
          data = buf_out;
           $display("-------------------------------Poped ", data);

        end
endtask

endmodule

 

Tasks 'push' and 'pop' are used to write and read data from the queue. After placing the data and address in the appropriate port, task waits for a clock to come. Then data is read from the port. In the output, you can notice that it displays 'cannot push' message when data pushed is BUF_SIZE and displays 'cannot pop' when all the data are popped.

Here is the output of displayed:

# Pushed 1
# Pushed 2
# -------------------------------Poped 1
# Pushed 10
# Pushed 20
# Pushed 30
# Pushed 40
# Pushed 50
# Pushed 60
# Pushed 70
# ---Cannot push: Buffer Full---
# ---Cannot push: Buffer Full---
# ---Cannot push: Buffer Full---
# ---Cannot push: Buffer Full---
# ---Cannot push: Buffer Full---
# ---Cannot push: Buffer Full---
# -------------------------------Poped 2
# Pushed 2
# -------------------------------Poped 10
# -------------------------------Poped 20
# -------------------------------Poped 30
# -------------------------------Poped 40
# Pushed 140
# -------------------------------Poped 50
# Pushed 50
# -------------------------------Poped 60
# -------------------------------Poped 70
# -------------------------------Poped 2
# -------------------------------Poped 140
# -------------------------------Poped 50
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# Pushed 5
# -------------------------------Poped 5

 

 

 

Home   |    About Us   |   Articles/ Tutorials   |   Downloads   |   Feedback   |   Links   |   eBooks   |   Privacy Policy
Copyright 2005-2007 electroSofts.com.
webmaster@electroSofts.com