Design and Verification of Synchronous
FIFO Using Verilog HDL
Abstract
This project presents the design and verification of a parameterized Synchronous First-In-
First-Out (FIFO) memory using Verilog HDL. The FIFO operates under a single clock
domain and supports configurable data width and depth, making it scalable and reusable for
various digital system applications. The architecture is implemented using a circular buffer
mechanism consisting of a memory array, write pointer, and read pointer. To accurately
differentiate between Full and Empty conditions, an additional Most Significant Bit (MSB) is
included in both pointers. Overflow and underflow protection mechanisms are incorporated
to ensure reliable data transfer. The functionality of the FIFO is verified using a structured
testbench covering reset, write, read, overflow, underflow, and simultaneous read/write
conditions. The design is fully synthesizable and suitable for FPGA and ASIC
implementation.
1. Introduction
A FIFO (First-In-First-Out) is a memory structure where the first data written into the
memory is the first data to be read out. FIFO buffers are widely used in digital systems for
temporary data storage and data rate matching between producer and consumer modules.
Synchronous FIFOs operate under a single clock domain and are commonly used in
microcontrollers, communication interfaces, embedded systems, and digital signal processing
applications.
2. Objectives
To design a parameterized synchronous FIFO using Verilog HDL
To implement proper Full and Empty flag detection logic
To prevent overflow and underflow conditions
To verify the design using a structured testbench
To ensure the design is synthesizable for hardware implementation
1|Page
3. FIFO Architecture
0 1 2 3 4 5 6 7
7777777
Wr_ptr 0 rd_ptr
clk 2
rst_n 4
full
5
wr_en
7
FIFO_ empty
8 DEPTH
9
rd_en
10
11
read_data
12
13
write_data 14
15
DATA_WIDTH
The synchronous FIFO consists of the following components:
1. Memory Array
o Stores the input data
o Depth = 16 (default)
o Data width = 8 bits (default)
2. Write Pointer (wr_ptr)
o Points to the next memory location for writing
o Includes one extra MSB bit for Full detection
3. Read Pointer (rd_ptr)
o Points to the next memory location for reading
o Includes one extra MSB bit for Full detection
4. Control Logic
o Generates Full and Empty flags
o Controls write and read operations
The FIFO operates as a circular buffer where pointers wrap around after reaching the
maximum depth.
2|Page
4. Design Description
4.1 RTL Implementation (Design Code)
The synthesizable Verilog RTL code for the Synchronous FIFO is provided below:
module Sync_FIFO #(
parameter DATA_WIDTH = 8,
parameter FIFO_DEPTH = 16
)(
clk, rst_n, wr_en, rd_en, write_data, read_data, empty, full);
input wire clk;
input wire rst_n;
input wire wr_en;
input wire rd_en;
input wire [DATA_WIDTH-1:0] write_data;
output reg [DATA_WIDTH-1:0] read_data;
output empty;
output full;
// Calculate ADDRESS WIDTH
parameter ADDR_WIDTH = $clog2(FIFO_DEPTH);
// Declare a by-dimensional array to store the data
reg [DATA_WIDTH-1:0] mem [0:FIFO_DEPTH-1]; // depth 16 => [0:15] 8 bit
elements
// Wr/Rd pointer have 1 extra bits at MSB
reg [ADDR_WIDTH:0] wr_ptr;
reg [ADDR_WIDTH:0] rd_ptr;
// Write operation
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
wr_ptr <= 0;
else if (wr_en && !full) begin
mem[wr_ptr[ADDR_WIDTH-1:0]] <= write_data;
wr_ptr <= wr_ptr + 1;
end
end
// Read operation
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rd_ptr <= 0;
read_data <= 0;
end
else if (rd_en && !empty) begin
read_data <= mem[rd_ptr[ADDR_WIDTH-1:0]];
rd_ptr <= rd_ptr + 1;
end
end
// Full/Empty Logic
// Full: MSB is different, but rest of the bits are same
// Empty: Both pointers are identical
assign full = (wr_ptr[ADDR_WIDTH] != rd_ptr[ADDR_WIDTH]) &&
(wr_ptr[ADDR_WIDTH-1:0] == rd_ptr[ADDR_WIDTH-1:0]);
3|Page
assign empty = (wr_ptr == rd_ptr);
endmodule
4.2 Parameters
DATA_WIDTH = 8
FIFO_DEPTH = 16
ADDR_WIDTH = $clog2(FIFO_DEPTH)
4.3 Memory Declaration
reg [DATA_WIDTH-1:0] mem [0:FIFO_DEPTH-1];
4.4 Pointer Declaration
reg [ADDR_WIDTH:0] wr_ptr;
reg [ADDR_WIDTH:0] rd_ptr;
The extra MSB bit is used to distinguish between Full and Empty conditions when lower
address bits are equal.
5. Working Principle
5.1 Write Operation
Triggered on positive edge of clock
Executed when wr_en is high and FIFO is not full
Data is written into memory at location indexed by wr_ptr
wr_ptr increments after write
5.2 Read Operation
Triggered on positive edge of clock
Executed when rd_en is high and FIFO is not empty
Data is read from memory at location indexed by rd_ptr
rd_ptr increments after read
6. Full and Empty Logic
6.1 Empty Condition
The FIFO is empty when:
wr_ptr == rd_ptr
4|Page
6.2 Full Condition
The FIFO is full when:
MSB of wr_ptr and rd_ptr are different
Lower bits of wr_ptr and rd_ptr are equal
This logic ensures correct detection in circular buffer operation.
7. Testbench Description
The testbench verifies FIFO functionality under different conditions.
7.1 Testbench Implementation
The verification testbench used to validate FIFO functionality is provided below:
`timescale 1ns/1ps
module tb_Sync_FIFO ();
// Testbench variables
parameter DATA_WIDTH = 8;
parameter FIFO_DEPTH = 16;
reg clk, rst_n, wr_en, rd_en;
reg [DATA_WIDTH-1:0] write_data;
wire [DATA_WIDTH-1:0] read_data;
wire empty, full;
integer i;
// Instantiate the Unit Under Test (UUT)
Sync_FIFO #(DATA_WIDTH, FIFO_DEPTH) UUT
(clk, rst_n, wr_en, rd_en, write_data, read_data, empty, full);
// Clock Generation (100MHz)
always #5 clk = ~clk;
// Task for Reset
task reset_fifo();
begin
rst_n = 0; wr_en = 0; rd_en = 0; write_data = 0;
repeat(2) @(posedge clk);
rst_n = 1;
$display("--- Reset Complete ---");
end
endtask
// Task for Write_data
task Write_data(input [DATA_WIDTH-1:0] d_in);
begin
@(posedge clk);
wr_en = 1;
write_data = d_in;
$display("%0t Writing: %0d", $time, write_data);
5|Page
@(posedge clk);
wr_en = 0;
end
endtask
// Task for Read_data
task Read_data();
begin
@(posedge clk);
rd_en = 1;
$display("%0t Reading: %0d", $time, read_data);
@(posedge clk);
rd_en = 0;
end
endtask
initial begin
//1. Setup
clk = 0;
reset_fifo();
//2. Test Empty Flag
if (empty)
$display("Success: FIFO is empty after reset.");
//3. Fill the FIFO to the brim
$display("--- Starting Fill Test ---");
for (i = 0; i <= FIFO_DEPTH; i = i+1) begin
Write_data(i);
end
//4. Test Full Flag & Overflow Protection
#5;
if (full)
$display("Success: FIFO is Full.");
Write_data(8'hFF);
$display("Note: Attempted overflow write (should be ignored).");
//5. Read everything back
$display("--- Starting Read Test ---");
for (i = 0; i < FIFO_DEPTH; i = i+1) begin
Read_data();
end
//6. Test Underflow Protection
#5;
if (empty)
$display("Success: FIFO is Empty.");
Read_data();
$display("Note: Attempted underflow read (should be ignored).");
//7. Simultaneous Read and Write (Throughput Test)
$display("--- Starting Simultaneous Read/Write Test ---");
reset_fifo();
for (i = 0; i < FIFO_DEPTH; i = i+1) begin
Write_data(i**2);
Read_data();
end
$display("--- All Tests Finished ---");
6|Page
#50 $finish;
end
initial begin
$dumpfile("[Link]");
$dumpvars();
end
endmodule
7.2 Clock Generation
100 MHz clock
Generated using always #5 clk = ~clk
7.3 Reset Task
Active low reset
Initializes pointers and control signals
7.4 Write Task
Applies write enable
Sends input data
Displays written values
7.5 Read Task
Applies read enable
Reads data from FIFO
Displays read values
8. Verification Scenarios
1. Reset Verification
o FIFO should be empty after reset
2. Fill Test
o Write until FIFO becomes full
3. Overflow Test
o Attempt to write when full
o Write operation should be ignored
4. Read Test
o Read all stored values
o FIFO should become empty
5. Underflow Test
o Attempt to read when empty
o Read operation should be ignored
6. Simultaneous Read/Write Test
7|Page
o Tests throughput and circular operation
9. Simulation Results
Simulation confirms:
Correct write and read sequencing
Proper Full and Empty flag generation
Successful overflow and underflow protection
Correct circular pointer behavior
8|Page
9|Page
Waveform output file: [Link]
Zoom image of Waveform
10. Applications
UART Transmit/Receive Buffers
SPI Communication Interfaces
Embedded Data Logging Systems
DSP Streaming Systems
Microprocessor Data Buffers
11. Advantages
Fully synthesizable RTL design
Parameterized and scalable
Simple and efficient architecture
Reliable Full/Empty detection
10 | P a g e
Structured verification methodology
12. Limitations
Operates in single clock domain only
No Almost-Full or Almost-Empty flags
No occupancy counter output
13. Future Enhancements
Design of Asynchronous FIFO (dual clock)
Addition of Almost-Full and Almost-Empty flags
Addition of FIFO occupancy counter
Implementation using SystemVerilog assertions
UVM-based verification environment
14. Conclusion
The Synchronous FIFO was successfully designed and verified using Verilog HDL. The
implementation demonstrates correct circular buffer operation, accurate Full and Empty flag
generation, and protection against overflow and underflow conditions. The design is fully
synthesizable and suitable for FPGA and ASIC applications. This project strengthens
understanding of RTL design, memory structures, pointer arithmetic, and verification
methodologies in digital system design.
Thank you
11 | P a g e