Bird
Raised Fist0
Verilogprogramming~10 mins

FIFO buffer design concept in Verilog

Choose your learning style10 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.

Practice

(1/5)
1. What is the main purpose of a FIFO buffer in Verilog designs?
easy
A. To perform arithmetic operations on data
B. To store data randomly and output in reverse order
C. To store data in the order it arrives and output it in the same order
D. To compress data before storage

Solution

  1. Step 1: Understand FIFO behavior

    A FIFO (First-In-First-Out) buffer stores data in the order it arrives and outputs it in the same order, preserving sequence.
  2. Step 2: Compare options

    Options A, B, and D describe behaviors unrelated to FIFO buffers.
  3. Final Answer:

    To store data in the order it arrives and output it in the same order -> Option C
  4. Quick Check:

    FIFO = First-In-First-Out [OK]
Hint: FIFO means first data in is first data out [OK]
Common Mistakes:
  • Confusing FIFO with LIFO (stack)
  • Thinking FIFO changes data order
  • Assuming FIFO compresses data
2. Which of the following is the correct Verilog syntax to declare a FIFO memory array of 16 elements, each 8 bits wide?
easy
A. wire [7:0] fifo_mem [15:0];
B. reg [7:0] fifo_mem [0:15];
C. reg fifo_mem [7:0] [0:15];
D. wire [15:0] fifo_mem [7:0];

Solution

  1. Step 1: Understand Verilog array declaration

    To declare a memory array of 16 elements each 8 bits wide, use reg [7:0] fifo_mem [0:15]; where [7:0] is the bit width and [0:15] is the index range.
  2. Step 2: Check options

    reg [7:0] fifo_mem [0:15]; matches the correct syntax. Options A, B, and C have incorrect bit widths or array dimensions.
  3. Final Answer:

    reg [7:0] fifo_mem [0:15]; -> Option B
  4. Quick Check:

    Memory array = reg [bit_width] name [index_range] [OK]
Hint: Declare memory as reg [width-1:0] name [size-1:0] [OK]
Common Mistakes:
  • Swapping bit width and array size
  • Using wire instead of reg for memory
  • Incorrect array dimension order
3. Given this simplified FIFO write pointer update code snippet in Verilog:
always @(posedge clk) begin
  if (write_enable && !full) begin
    write_ptr <= (write_ptr + 1) % 8;
  end
end

What will be the value of write_ptr after 10 write cycles starting from 0, assuming full is always 0?
medium
A. 10
B. 7
C. 0
D. 2

Solution

  1. Step 1: Understand pointer increment with modulo

    The write pointer increments by 1 each cycle and wraps around modulo 8, so it cycles through 0 to 7 repeatedly.
  2. Step 2: Calculate pointer after 10 increments

    Starting at 0, after 10 increments: (0 + 10) % 8 = 2.
  3. Final Answer:

    2 -> Option D
  4. Quick Check:

    (0 + 10) mod 8 = 2 [OK]
Hint: Use modulo to find pointer wrap-around [OK]
Common Mistakes:
  • Ignoring modulo wrap-around
  • Assuming pointer grows beyond size
  • Confusing write and read pointers
4. Identify the error in this FIFO read pointer update code snippet:
always @(posedge clk) begin
  if (read_enable && !empty) begin
    read_ptr = read_ptr + 1;
  end
end
medium
A. Using blocking assignment (=) instead of non-blocking (<=) inside always block
B. Missing reset condition for read_ptr
C. Not checking full signal before increment
D. Using read_enable instead of write_enable

Solution

  1. Step 1: Check assignment type in sequential always block

    Inside an always @(posedge clk) block, non-blocking assignment (<=) should be used to update registers to avoid race conditions.
  2. Step 2: Identify the error

    The code uses blocking assignment (=), which is incorrect for sequential logic.
  3. Final Answer:

    Using blocking assignment (=) instead of non-blocking (<=) inside always block -> Option A
  4. Quick Check:

    Sequential logic uses <=, not = [OK]
Hint: Use <= for registers in clocked always blocks [OK]
Common Mistakes:
  • Using = instead of <=
  • Forgetting to reset pointers
  • Confusing full and empty signals
5. You want to design a FIFO buffer with depth 4 and 8-bit data width in Verilog. Which of the following correctly describes how to detect the FIFO full condition using write and read pointers write_ptr and read_ptr?
hard
A. FIFO is full when (write_ptr + 1) % 4 == read_ptr
B. FIFO is full when write_ptr == read_ptr
C. FIFO is full when write_ptr == (read_ptr + 1) % 8
D. FIFO is full when write_ptr > read_ptr

Solution

  1. Step 1: Understand FIFO full condition logic

    In a circular FIFO, the buffer is full when the write pointer is one position behind the read pointer, considering wrap-around.
  2. Step 2: Apply modulo with depth 4

    So, full condition is when (write_ptr + 1) % 4 == read_ptr.
  3. Final Answer:

    FIFO is full when (write_ptr + 1) % 4 == read_ptr -> Option A
  4. Quick Check:

    Full = next write_ptr equals read_ptr [OK]
Hint: Full when next write_ptr equals read_ptr [OK]
Common Mistakes:
  • Confusing full with empty condition
  • Using wrong modulo base
  • Checking write_ptr > read_ptr without wrap
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard