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.
FIFO buffer design concept in 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.
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
// 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.
// 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.
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.
`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
endmoduleTime 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.
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
Solution
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.Step 2: Compare options
Options A, B, and D describe behaviors unrelated to FIFO buffers.Final Answer:
To store data in the order it arrives and output it in the same order -> Option CQuick Check:
FIFO = First-In-First-Out [OK]
- Confusing FIFO with LIFO (stack)
- Thinking FIFO changes data order
- Assuming FIFO compresses data
Solution
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.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.Final Answer:
reg [7:0] fifo_mem [0:15]; -> Option BQuick Check:
Memory array = reg [bit_width] name [index_range] [OK]
- Swapping bit width and array size
- Using wire instead of reg for memory
- Incorrect array dimension order
always @(posedge clk) begin
if (write_enable && !full) begin
write_ptr <= (write_ptr + 1) % 8;
end
endWhat will be the value of
write_ptr after 10 write cycles starting from 0, assuming full is always 0?Solution
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.Step 2: Calculate pointer after 10 increments
Starting at 0, after 10 increments: (0 + 10) % 8 = 2.Final Answer:
2 -> Option DQuick Check:
(0 + 10) mod 8 = 2 [OK]
- Ignoring modulo wrap-around
- Assuming pointer grows beyond size
- Confusing write and read pointers
always @(posedge clk) begin
if (read_enable && !empty) begin
read_ptr = read_ptr + 1;
end
endSolution
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.Step 2: Identify the error
The code uses blocking assignment (=), which is incorrect for sequential logic.Final Answer:
Using blocking assignment (=) instead of non-blocking (<=) inside always block -> Option AQuick Check:
Sequential logic uses <=, not = [OK]
- Using = instead of <=
- Forgetting to reset pointers
- Confusing full and empty signals
write_ptr and read_ptr?Solution
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.Step 2: Apply modulo with depth 4
So, full condition is when (write_ptr + 1) % 4 == read_ptr.Final Answer:
FIFO is full when (write_ptr + 1) % 4 == read_ptr -> Option AQuick Check:
Full = next write_ptr equals read_ptr [OK]
- Confusing full with empty condition
- Using wrong modulo base
- Checking write_ptr > read_ptr without wrap
Ready to prove it?
Pass the test and this pattern moves unknown
