Introduction: Designing a Synchronous FIFO, LIFO/Stack in Verilog
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