FIFO buffer design concept in Verilog - Time & Space Complexity
When designing a FIFO buffer in Verilog, it's important to understand how the time to process data grows as the buffer size changes.
We want to know how the number of operations changes when we add more data to the buffer.
Analyze the time complexity of the following FIFO buffer code snippet.
module fifo #(parameter DEPTH = 16, WIDTH = 8) (
input clk, reset,
input wr_en, rd_en,
input [WIDTH-1:0] data_in,
output reg [WIDTH-1:0] data_out,
output reg full, empty
);
reg [WIDTH-1:0] mem [0:DEPTH-1];
reg [$clog2(DEPTH)-1:0] wr_ptr = 0, rd_ptr = 0;
always @(posedge clk) begin
if(reset) begin
wr_ptr <= 0; rd_ptr <= 0;
full <= 0; empty <= 1;
end else begin
if(wr_en && !full) begin
mem[wr_ptr] <= data_in;
wr_ptr <= wr_ptr + 1;
end
if(rd_en && !empty) begin
data_out <= mem[rd_ptr];
rd_ptr <= rd_ptr + 1;
end
full <= (wr_ptr == DEPTH-1);
empty <= (wr_ptr == rd_ptr);
end
end
endmodule
This code implements a FIFO buffer that stores data in memory and uses pointers to track where to write and read.
Look for operations that happen repeatedly as data moves through the FIFO.
- Primary operation: Writing to and reading from memory at pointer positions.
- How many times: Each write or read happens once per clock cycle when enabled.
The FIFO processes one data item per clock cycle when enabled, regardless of buffer size.
| Input Size (DEPTH) | Approx. Operations per Data Item |
|---|---|
| 10 | 1 write + 1 read per item |
| 100 | 1 write + 1 read per item |
| 1000 | 1 write + 1 read per item |
Pattern observation: The number of operations per data item stays the same no matter how big the buffer is.
Time Complexity: O(1)
This means each data item is handled in a fixed amount of time, no matter how large the FIFO buffer is.
[X] Wrong: "The time to write or read data grows as the buffer size increases because the pointers have to move through the whole buffer."
[OK] Correct: The pointers just move by one position each cycle, and the memory access is direct, so the time per operation stays constant.
Understanding FIFO time complexity shows you can reason about hardware efficiency and how designs scale, a key skill in hardware and embedded system roles.
What if we changed the FIFO to use a software loop to shift all data on each write? How would the time complexity change?
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
