0
0
Verilogprogramming~10 mins

FIFO buffer design concept in Verilog

Choose your learning style9 modes available
Introduction

A FIFO buffer helps store data in the order it arrives and gives it back in the same order. It works like a line where the first person in is the first person out.

When you want to hold data temporarily between two parts of a system working at different speeds.
When you need to make sure data is processed in the exact order it was received.
When connecting two devices that send and receive data asynchronously.
When buffering data streams like audio or video to avoid glitches.
When designing communication between hardware modules that use different clocks.
Syntax
Verilog
module fifo_buffer #(parameter DATA_WIDTH = 8, parameter DEPTH = 16) (
    input wire clk,
    input wire reset,
    input wire write_enable,
    input wire read_enable,
    input wire [DATA_WIDTH-1:0] data_in,
    output reg [DATA_WIDTH-1:0] data_out,
    output wire full,
    output wire empty
);

    reg [DATA_WIDTH-1:0] memory [0:DEPTH-1];
    reg [$clog2(DEPTH)-1:0] write_pointer = 0;
    reg [$clog2(DEPTH)-1:0] read_pointer = 0;
    reg [$clog2(DEPTH):0] count = 0;

    assign full = (count == DEPTH);
    assign empty = (count == 0);

    always @(posedge clk or posedge reset) begin
        if (reset) begin
            write_pointer <= 0;
            read_pointer <= 0;
            count <= 0;
            data_out <= 0;
        end else begin
            if (write_enable && !full) begin
                memory[write_pointer] <= data_in;
                write_pointer <= (write_pointer + 1) % DEPTH;
            end
            if (read_enable && !empty) begin
                data_out <= memory[read_pointer];
                read_pointer <= (read_pointer + 1) % DEPTH;
            end
            if (write_enable && !full && !(read_enable && !empty)) begin
                count <= count + 1;
            end else if (read_enable && !empty && !(write_enable && !full)) begin
                count <= count - 1;
            end
        end
    end
endmodule

This FIFO uses a circular buffer with read and write pointers.

The 'full' and 'empty' signals help control when to write or read data safely.

Examples
This example shows a small FIFO with depth 4. It handles the empty case by not reading when empty, and the full case by not writing when full.
Verilog
module fifo_buffer #(parameter DATA_WIDTH = 8, parameter DEPTH = 4) (
    input wire clk,
    input wire reset,
    input wire write_enable,
    input wire read_enable,
    input wire [DATA_WIDTH-1:0] data_in,
    output reg [DATA_WIDTH-1:0] data_out,
    output wire full,
    output wire empty
);

    reg [DATA_WIDTH-1:0] memory [0:DEPTH-1];
    reg [$clog2(DEPTH)-1:0] write_pointer = 0;
    reg [$clog2(DEPTH)-1:0] read_pointer = 0;
    reg [$clog2(DEPTH):0] count = 0;

    assign full = (count == DEPTH);
    assign empty = (count == 0);

    always @(posedge clk or posedge reset) begin
        if (reset) begin
            write_pointer <= 0;
            read_pointer <= 0;
            count <= 0;
            data_out <= 0;
        end else begin
            if (write_enable && !full) begin
                memory[write_pointer] <= data_in;
                write_pointer <= (write_pointer + 1) % DEPTH;
            end
            if (read_enable && !empty) begin
                data_out <= memory[read_pointer];
                read_pointer <= (read_pointer + 1) % DEPTH;
            end
            if (write_enable && !full && !(read_enable && !empty)) begin
                count <= count + 1;
            end else if (read_enable && !empty && !(write_enable && !full)) begin
                count <= count - 1;
            end
        end
    end
endmodule
These comments explain what happens when the FIFO is empty or full. It prevents errors by blocking reads or writes.
Verilog
// Edge case: FIFO empty - no read allowed
// read_enable is high but empty is true, so data_out stays the same.

// Edge case: FIFO full - no write allowed
// write_enable is high but full is true, so new data is ignored.
This shows how the FIFO behaves when it has just one element. The pointers wrap around correctly.
Verilog
// Edge case: Only one element in FIFO
// write_pointer and read_pointer point to the same location.
// Reading removes the element, making FIFO empty.
// Writing adds an element, making FIFO full if depth is 1.
Sample Program

This testbench creates a FIFO with depth 4 and 8-bit data. It writes three values, reads two, writes two more, then reads all remaining values. The monitor prints the FIFO status at each step.

Verilog
`timescale 1ns / 1ps

module test_fifo;
    reg clk = 0;
    reg reset = 1;
    reg write_enable = 0;
    reg read_enable = 0;
    reg [7:0] data_in = 0;
    wire [7:0] data_out;
    wire full;
    wire empty;

    fifo_buffer #(8, 4) my_fifo (
        .clk(clk),
        .reset(reset),
        .write_enable(write_enable),
        .read_enable(read_enable),
        .data_in(data_in),
        .data_out(data_out),
        .full(full),
        .empty(empty)
    );

    always #5 clk = ~clk; // 10ns clock period

    initial begin
        $display("Time | Reset | Write | Read | Data In | Data Out | Full | Empty");
        $monitor("%4t |   %b   |   %b   |  %b  |   %d    |    %d    |  %b  |  %b", $time, reset, write_enable, read_enable, data_in, data_out, full, empty);

        #10 reset = 0;

        // Write 3 values
        data_in = 10; write_enable = 1; read_enable = 0; #10;
        data_in = 20; #10;
        data_in = 30; #10;

        write_enable = 0;

        // Read 2 values
        read_enable = 1; #20;

        read_enable = 0;

        // Write 2 more values
        data_in = 40; write_enable = 1; #10;
        data_in = 50; #10;

        write_enable = 0;

        // Read all values until empty
        read_enable = 1; #40;

        $finish;
    end
endmodule

module fifo_buffer #(parameter DATA_WIDTH = 8, parameter DEPTH = 4) (
    input wire clk,
    input wire reset,
    input wire write_enable,
    input wire read_enable,
    input wire [DATA_WIDTH-1:0] data_in,
    output reg [DATA_WIDTH-1:0] data_out,
    output wire full,
    output wire empty
);

    reg [DATA_WIDTH-1:0] memory [0:DEPTH-1];
    reg [$clog2(DEPTH)-1:0] write_pointer = 0;
    reg [$clog2(DEPTH)-1:0] read_pointer = 0;
    reg [$clog2(DEPTH):0] count = 0;

    assign full = (count == DEPTH);
    assign empty = (count == 0);

    always @(posedge clk or posedge reset) begin
        if (reset) begin
            write_pointer <= 0;
            read_pointer <= 0;
            count <= 0;
            data_out <= 0;
        end else begin
            if (write_enable && !full) begin
                memory[write_pointer] <= data_in;
                write_pointer <= (write_pointer + 1) % DEPTH;
            end
            if (read_enable && !empty) begin
                data_out <= memory[read_pointer];
                read_pointer <= (read_pointer + 1) % DEPTH;
            end
            if (write_enable && !full && !(read_enable && !empty)) begin
                count <= count + 1;
            end else if (read_enable && !empty && !(write_enable && !full)) begin
                count <= count - 1;
            end
        end
    end
endmodule
OutputSuccess
Important Notes

Time complexity for read and write is O(1) because pointers move directly.

Space complexity is O(depth) for the memory array.

Common mistake: not checking full or empty before writing or reading can cause data loss or errors.

Use FIFO when you need ordered data buffering; use other buffers if order is not important.

Summary

FIFO stores data in the order it arrives and outputs in the same order.

It uses read and write pointers to track where to read and write next.

Signals 'full' and 'empty' help control safe reading and writing.