Introduction: Designing a Synchronous FIFO, LIFO/Stack in Verilog

About: Mitu Raj -- Just a Hobbyst and Learner -- Chip Designer -- Software Developer -- Physics and Mathematics Enthusiast

This is to inform that this blog is now archived and I have started a new website/blog of my own: Chipmunk Logic. I hope you guys follow/subscribe me for free content and knowledge and continue supporting me. Hereafter, I will publish all my future technical blogs there :)


FIFOs are one of the most useful modules that comes handy when designing a large digital system with lot of data path. FIFOs are popular choice among designers to transfer data from one module to the other. FIFOs provide simple handshaking and synchronization mechanism between the modules.

In this instructable I present a simple synchronous FIFO in RTL which you can configure and use directly in your designs. The design is fully synthesizable.

Step 1: Motivation Behind the Blog

So, why this blog? There are already lot of resources on Verilog/VHDL code of FIFOs. In past, I have myself used some of them. But frustratingly, most of them were buggy especially at the overflow and underflow conditions,. So I thought I would settle it for once and for all. Please feel free to use the code and comment if you have any suggestions in terms of performance/functionality while using in your designs. So far, I have been successfully using this FIFO in many of the digital systems I have designed till now. :-)

Step 2: Specifications of FIFO

  • Synchronous, single-clock.
  • Register based FIFO, ideal for small and medium FIFOs.
  • Full, Empty, Almost-full, Almost-empty flags.
  • Fully configurable data width, depth, and flags.
  • Fully synthesizable System Verilog code.
/*===============================================================================================================================
   Design       : Single-clock Synchronous FIFO

   Description  : Fully synthesisable, configurable Single-clock Synchronous FIFO based on registers.
                  - Configurable Data width.
                  - Configurable Depth.
                  - Configurable Almost-full and Almost-empty signals.
                                   
   Developer    : Mitu Raj, MR-CrEaTiONs, iammituraj@gmail.com
   Date         : Feb-12-2021
===============================================================================================================================*/

module my_fifo #(
                   parameter DATA_W           = 4      ,        // Data width
                   parameter DEPTH            = 8      ,        // Depth of FIFO                   
                   parameter UPP_TH           = 4      ,        // Upper threshold to generate Almost-full
                   parameter LOW_TH           = 2               // Lower threshold to generate Almost-empty
                )

                (
                   input                   clk         ,        // Clock
                   input                   rstn        ,        // Active-low Synchronous Reset
                   
                   input                   i_wren      ,        // Write Enable
                   input  [DATA_W - 1 : 0] i_wrdata    ,        // Write-data
                   output                  o_alm_full  ,        // Almost-full signal
                   output                  o_full      ,        // Full signal

                   input                   i_rden      ,        // Read Enable
                   output [DATA_W - 1 : 0] o_rddata    ,        // Read-data
                   output                  o_alm_empty ,        // Almost-empty signal
                   output                  o_empty              // Empty signal
                );


/*-------------------------------------------------------------------------------------------------------------------------------
   Internal Registers/Signals
-------------------------------------------------------------------------------------------------------------------------------*/

logic [DATA_W - 1        : 0] data_rg [DEPTH] ;        // Data array
logic [$clog2(DEPTH) - 1 : 0] wrptr_rg        ;        // Write pointer
logic [$clog2(DEPTH) - 1 : 0] rdptr_rg        ;        // Read pointer
logic [$clog2(DEPTH)     : 0] dcount_rg       ;        // Data counter
      
logic                         wren_s          ;        // Write Enable signal generated iff FIFO is not full
logic                         rden_s          ;        // Read Enable signal generated iff FIFO is not empty
logic                         full_s          ;        // Full signal
logic                         empty_s         ;        // Empty signal


/*-------------------------------------------------------------------------------------------------------------------------------
   Synchronous logic to write to and read from FIFO
-------------------------------------------------------------------------------------------------------------------------------*/
always @ (posedge clk) begin

   if (!rstn) begin     
          
      data_rg   <= '{default: '0} ;
      wrptr_rg  <= 0              ;
      rdptr_rg  <= 0              ;      
      dcount_rg <= 0              ;

   end

   else begin

      ready_rg <= 1'b1 ;
      
      /* FIFO write logic */            
      if (wren_s) begin                          
         
         data_rg [wrptr_rg] <= i_wrdata ;        // Data written to FIFO

         if (wrptr_rg == DEPTH - 1) begin
            wrptr_rg <= 0               ;        // Reset write pointer  
         end

         else begin
            wrptr_rg <= wrptr_rg + 1    ;        // Increment write pointer            
         end

      end

      /* FIFO read logic */
      if (rden_s) begin         

         if (rdptr_rg == DEPTH - 1) begin
            rdptr_rg <= 0               ;        // Reset read pointer
         end

         else begin
            rdptr_rg <= rdptr_rg + 1    ;        // Increment read pointer            
         end

      end

      /* FIFO data counter update logic */
      if (wren_s && !rden_s) begin               // Write operation
         dcount_rg <= dcount_rg + 1 ;
      end                    
      else if (!wren_s && rden_s) begin          // Read operation
         dcount_rg <= dcount_rg - 1 ;         
      end

   end

end


/*-------------------------------------------------------------------------------------------------------------------------------
   Continuous Assignments
-------------------------------------------------------------------------------------------------------------------------------*/

// Full and Empty internal
assign full_s      = (dcount_rg == DEPTH) ? 1'b1 : 0 ;
assign empty_s     = (dcount_rg == 0    ) ? 1'b1 : 0 ;

// Write and Read Enables internal
assign wren_s      = i_wren & !full_s                ;  
assign rden_s      = i_rden & !empty_s               ;

// Full and Empty to output
assign o_full      = full_s                          ;
assign o_empty     = empty_s                         ;

// Almost-full and Almost Empty to output
assign o_alm_full  = (dcount_rg > UPP_TH) ? 1'b1 : 0 ;
assign o_alm_empty = (dcount_rg < LOW_TH) ? 1'b1 : 0 ;

// Read-data to output
assign o_rddata    = data_rg [rdptr_rg]              ;   


endmodule

/*=============================================================================================================================*/

Step 3: Ram-based FIFO

So in the above step, we saw a synchronous FIFO based on registers. This time, we take a look at RAM-based FIFO. This FIFO implements its data array on RAM instead of registers. This is suitable for implementing large FIFO buffers on hardware; especially on FPGAs, where abundant block RAMs are available. This will reduce resource utilization and can achieve better timing performance as well.

Please find the Verilog code for RAM-based FIFO in my github. It infers block RAMs/distributed RAMs on FPGAs for FIFO data array (tested with Xilinx Vivado 18.3). Since block RAM reads are synchronous, the data and flags at de-queue side are cycle-delayed. If distributed RAM is used instead, the cycle-delay can be avoided because reads are asynchronous.

Step 4: Please Be Tuned In

Please visit my website/blog: Chipmunk Logic for more interesting articles and codes on RTL design. In fact, it is my new blog where I am active, instructables blog has been archived.


For queries: iammituraj@gmail.com

Regards,

Mitu Raj