Synchronous FIFO Design via Counter-Based Status Tracking

In digital chip design, a First-In-First-Out (FIFO) buffer is an essetnial memory structure used to manage data flow between modules. Synchronous FIFOs, where both read and write operations share the same clock signal, rely on two critical status flags: full and empty. These signals prevent data loss (overflow) and erroneous reads (underflow).

A common approach to generating these status signals is using a dedicated counter to track the occupancy of the buffer. The counter increments during a write operation and decrements during a read operation. When the count hits zero, the FIFO is empty; when it reaches the maximum depth, the FIFO is full.

RTL Implementation

module sync_fifo #(
    parameter DATA_WIDTH = 8,
    parameter FIFO_DEPTH = 16,
    parameter ADDR_SIZE  = 4
)(
    input  wire                   clk,
    input  wire                   rst_n,
    input  wire                   w_en,
    input  wire [DATA_WIDTH-1:0]  w_data,
    input  wire                   r_en,
    output reg  [DATA_WIDTH-1:0]  r_data,
    output reg                    full,
    output reg                    empty
);

reg [ADDR_SIZE-1:0] w_ptr, r_ptr;
reg [DATA_WIDTH-1:0] mem [0:FIFO_DEPTH-1];
reg [ADDR_SIZE:0]    count;

wire can_write = w_en && !full;
wire can_read  = r_en && !empty;

// Status signal logic
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        empty <= 1'b1;
        full  <= 1'b0;
    end else begin
        empty <= (count == 0) || (count == 1 && can_read && !can_write);
        full  <= (count == FIFO_DEPTH) || (count == FIFO_DEPTH - 1 && can_write && !can_read);
    end
end

// Pointer and memory operations
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        w_ptr <= 0;
        r_ptr <= 0;
        count <= 0;
    end else begin
        if (can_write) begin
            mem[w_ptr] <= w_data;
            w_ptr      <= w_ptr + 1'b1;
        end
        if (can_read) begin
            r_data <= mem[r_ptr];
            r_ptr  <= r_ptr + 1'b1;
        end
        
        // Update occupancy counter
        case ({can_write, can_read})
            2'b10: count <= count + 1'b1;
            2'b01: count <= count - 1'b1;
            default: count <= count;
        endcase
    end
end
endmodule

Verification Testbench

`timescale 1ns/1ps

module tb_sync_fifo;
    reg clk = 0, rst_n = 0;
    reg w_en = 0, r_en = 0;
    reg [7:0] w_data = 0;
    wire full, empty;
    wire [7:0] r_data;

    sync_fifo dut (.*);

    always #10 clk = ~clk;

    initial begin
        #15 rst_n = 1;
        
        // Write sequence
        repeat(16) begin
            @(posedge clk) begin
                w_en   = 1;
                w_data = w_data + 1;
            end
        end
        
        // Read sequence
        @(posedge clk) w_en = 0;
        repeat(16) begin
            @(posedge clk) r_en = 1;
        end
        
        #50 $finish;
    end
endmodule

Tags: Verilog fifo RTL Digital Design FPGA

Posted on Fri, 05 Jun 2026 17:59:08 +0000 by almora