In digital chip design, a First-In-First-Out (FIFO) buffer is an essetnial memory structure used to manage data flow between modules. Synchronous FIFOs, where both read and write operations share the same clock signal, rely on two critical status flags: full and empty. These signals prevent data loss (overflow) and erroneous reads (underflow).
A common approach to generating these status signals is using a dedicated counter to track the occupancy of the buffer. The counter increments during a write operation and decrements during a read operation. When the count hits zero, the FIFO is empty; when it reaches the maximum depth, the FIFO is full.
RTL Implementation
module sync_fifo #(
parameter DATA_WIDTH = 8,
parameter FIFO_DEPTH = 16,
parameter ADDR_SIZE = 4
)(
input wire clk,
input wire rst_n,
input wire w_en,
input wire [DATA_WIDTH-1:0] w_data,
input wire r_en,
output reg [DATA_WIDTH-1:0] r_data,
output reg full,
output reg empty
);
reg [ADDR_SIZE-1:0] w_ptr, r_ptr;
reg [DATA_WIDTH-1:0] mem [0:FIFO_DEPTH-1];
reg [ADDR_SIZE:0] count;
wire can_write = w_en && !full;
wire can_read = r_en && !empty;
// Status signal logic
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
empty <= 1'b1;
full <= 1'b0;
end else begin
empty <= (count == 0) || (count == 1 && can_read && !can_write);
full <= (count == FIFO_DEPTH) || (count == FIFO_DEPTH - 1 && can_write && !can_read);
end
end
// Pointer and memory operations
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
w_ptr <= 0;
r_ptr <= 0;
count <= 0;
end else begin
if (can_write) begin
mem[w_ptr] <= w_data;
w_ptr <= w_ptr + 1'b1;
end
if (can_read) begin
r_data <= mem[r_ptr];
r_ptr <= r_ptr + 1'b1;
end
// Update occupancy counter
case ({can_write, can_read})
2'b10: count <= count + 1'b1;
2'b01: count <= count - 1'b1;
default: count <= count;
endcase
end
end
endmodule
Verification Testbench
`timescale 1ns/1ps
module tb_sync_fifo;
reg clk = 0, rst_n = 0;
reg w_en = 0, r_en = 0;
reg [7:0] w_data = 0;
wire full, empty;
wire [7:0] r_data;
sync_fifo dut (.*);
always #10 clk = ~clk;
initial begin
#15 rst_n = 1;
// Write sequence
repeat(16) begin
@(posedge clk) begin
w_en = 1;
w_data = w_data + 1;
end
end
// Read sequence
@(posedge clk) w_en = 0;
repeat(16) begin
@(posedge clk) r_en = 1;
end
#50 $finish;
end
endmodule