Fading Coder

One Final Commit for the Last Sprint

Home > Tools > Content

Synchronous FIFO Design and Implementation in Verilog

Tools 2

A Synchronous FIFO (First-In, First-Out) is a digital storage structure where data is written and read using a single clock source. This component is essential for buffering data streams between logic blocks that operate within the same clock domain but may process data at different rates.

Core Design Components

Clock and Synchronous Logic

In a synchronous FIFO, all internal registers, including the memory array, write pointers, and read pointers, are driven by a single clock signal. This simplifies the design as there are no metastability issues typical of cross-clock domain transfers, allowing status flags like 'full' and 'empty' to be generated through direct comparison of pointers or occupancy counters.

Pointer Management

Two primary registers track the state of the buffer:

  • Write Pointer: Indicates the memory address for the next incoming data word. It increments after every successful write operation.
  • Read Pointer: Indicates the memory address of the next data word to be retrieved. It increments after every successful read operation. Both pointers typically wrap around to zero upon reaching the maximum depth of the FIFO.

Memory Array

The data is stored in a dual-port RAM or a register bank. This structure allows simultaneous read and write access, provided the FIFO is neither full nor empty.

Status Flags and Flow Control

  • Full Flag: Logic that prevents further writing when the buffer caapcity is reached. This is usually determined when the write pointer catches up to the read pointer (or when an occupancy counter equals the depth).
  • Empty Flag: Logic that prevents reading when there is no valid data. This occurs when the read pointer equals the write pointer.
  • Occupancy Counter: A counter that tracks the number of words current stored, facilitating the generation of 'almost full' or 'almost empty' signals.

Verilog Implementation

The following implementation utilizes a counter-based approach to manage the FIFO status flags and pointer increments.

module SynchronousFIFO #(
    parameter WIDTH = 8,
    parameter DEPTH = 16
)(
    input  wire              clk,
    input  wire              rst_n,
    input  wire              w_en,
    input  wire [WIDTH-1:0]  w_data,
    input  wire              r_en,
    output reg  [WIDTH-1:0]  r_data,
    output wire              full,
    output wire              empty,
    output reg  [$clog2(DEPTH):0] count
);

    reg [WIDTH-1:0] fifo_mem [0:DEPTH-1];
    reg [$clog2(DEPTH)-1:0] w_ptr;
    reg [$clog2(DEPTH)-1:0] r_ptr;

    // Status Flags
    assign full  = (count == DEPTH);
    assign empty = (count == 0);

    // Write Logic
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            w_ptr <= 0;
        end else if (w_en && !full) begin
            fifo_mem[w_ptr] <= w_data;
            w_ptr <= w_ptr + 1'b1;
        end
    end

    // Read Logic
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            r_ptr <= 0;
            r_data <= 0;
        end else if (r_en && !empty) begin
            r_data <= fifo_mem[r_ptr];
            r_ptr <= r_ptr + 1'b1;
        end
    end

    // Occupancy Counter
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            count <= 0;
        end else begin
            case ({w_en && !full, r_en && !empty})
                2'b10: count <= count + 1'b1;
                2'b01: count <= count - 1'b1;
                default: count <= count;
            endcase
        end
    end

endmodule

Verification Testbench

This testbench simulates basic write and read cycles to verify the integrity of the FIFO logic.

`timescale 1ns/1ps

module tb_SyncFIFO();
    parameter W = 8;
    parameter D = 8;

    reg clk;
    reg rst_n;
    reg w_en;
    reg [W-1:0] w_data;
    reg r_en;
    wire [W-1:0] r_data;
    wire full, empty;
    wire [$clog2(D):0] count;

    SynchronousFIFO #(.WIDTH(W), .DEPTH(D)) uut (
        .clk(clk), .rst_n(rst_n),
        .w_en(w_en), .w_data(w_data),
        .r_en(r_en), .r_data(r_data),
        .full(full), .empty(empty),
        .count(count)
    );

    always #5 clk = ~clk;

    initial begin
        clk = 0; rst_n = 0; w_en = 0; r_en = 0; w_data = 0;
        #20 rst_n = 1;

        // Write until full
        repeat (D) begin
            @(posedge clk);
            if (!full) begin
                w_en = 1;
                w_data = w_data + 1;
            end
        end
        @(posedge clk) w_en = 0;

        // Read until empty
        repeat (D) begin
            @(posedge clk);
            if (!empty) r_en = 1;
        end
        @(posedge clk) r_en = 0;

        #50 $finish;
    end
endmodule

Related Articles

Efficient Usage of HTTP Client in IntelliJ IDEA

IntelliJ IDEA incorporates a versatile HTTP client tool, enabling developres to interact with RESTful services and APIs effectively with in the editor. This functionality streamlines workflows, replac...

Installing CocoaPods on macOS Catalina (10.15) Using a User-Managed Ruby

System Ruby on macOS 10.15 frequently fails to build native gems required by CocoaPods (for example, ffi), leading to errors like: ERROR: Failed to build gem native extension checking for ffi.h... no...

Resolve PhpStorm "Interpreter is not specified or invalid" on WAMP (Windows)

Symptom PhpStorm displays: "Interpreter is not specified or invalid. Press ‘Fix’ to edit your project configuration." This occurs when the IDE cannot locate a valid PHP CLI executable or when the debu...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.