EN
← Back to Skills
AdvancedRTL Design

FIFO Generator

Generate synchronous or asynchronous FIFOs with proper empty/full detection, Gray-coded CDC pointers, almost-full/empty flags, and AXI-Stream interfaces. Includes depth calculation methodology.

💡 Copy this Skill description into your AI Agent's system prompt (Cursor, Claude Code, Copilot), then describe your design needs. The Agent will generate code following the rules defined in this Skill. Download the .md file at the bottom of this page.


name: fifo-generator description: > Generate a complete, synthesizable FIFO (First-In First-Out) implementation in Verilog/SystemVerilog. Supports synchronous and asynchronous FIFO architectures, configurable data width and depth, full/empty flag generation, almost-full/almost-empty flags, and AXI-Stream compatible valid/ready interfaces. Generates Gray-coded pointers for async FIFOs with proper CDC synchronization. Includes FIFO depth calculation methodology for worst-case back-to-back write scenarios. category: rtl-design level: advanced

Purpose

Generate a complete FIFO that is synthesis-ready, functionally verified, and correctly handles corner cases (empty, full, simultaneous read/write, reset). The FIFO must never overflow, never read invalid data, and never produce glitches on status flags.

Input Specification

Required Information

  • Data width (WIDTH): Number of bits per FIFO entry
  • Depth (DEPTH): Number of entries. If not specified, calculate based on write/read clock ratios and burst length

Optional Information

  • Type: Synchronous (default) or Asynchronous (different read/write clocks)
  • Write clock frequency and read clock frequency (for async depth calculation)
  • Maximum burst length (for depth calculation)
  • Almost-full / almost-empty thresholds (default: off)
  • Output register stage: combinatorial read (default) or registered read
  • Interface type: Native (wr_en/rd_en) or AXI-Stream (valid/ready)

Output Specification

Generate a complete module containing:

  1. Parameter declarations (DATA_WIDTH, DEPTH, derived PTR_WIDTH)
  2. Memory array (distributed RAM for shallow FIFOs, block RAM for deep FIFOs)
  3. Write pointer and read pointer (binary for sync, Gray for async)
  4. Word counter or pointer comparison logic for empty/full detection
  5. Read data output (combinatorial or registered)
  6. Status flags: empty, full, (optionally: almost_empty, almost_full)
  7. For async FIFO: pointer synchronization chain with proper CDC

Core Design Rules

Rule 1: Depth Calculation

Synchronous FIFO depth is user-specified; no calculation needed.

Asynchronous FIFO depth MUST account for the worst-case scenario:

  • Writer writes continuously at maximum rate (back-to-back)
  • Reader reads at minimum rate (accounting for idle cycles)
  • The FIFO must not overflow during the worst burst
code
MinDepth = BurstLength - (FreqRead / FreqWrite) × BurstLength × ReadEfficiency

Example:
  Write clock: 100 MHz, Read clock: 80 MHz
  Burst length: 80 words, Read efficiency: 100% (back-to-back reads)
  MinDepth = 80 - (80/100) × 80 = 80 - 64 = 16
  → Use DEPTH = 32 (round up to next power of 2)

Rule 2: Pointer Width and Empty/Full Detection

Synchronous FIFO:

  • Use binary pointers: [PTR_WIDTH-1:0] where PTR_WIDTH = $clog2(DEPTH)
  • Use a word counter: [PTR_WIDTH:0] (extra bit) to track fill level
  • Counter == 0 → Empty; Counter == DEPTH → Full

Asynchronous FIFO:

  • Use Gray-coded pointers: [PTR_WIDTH:0] (extra MSB to distinguish empty/full)
  • Empty: read pointer == synchronized write pointer (all bits match)
  • Full: write pointer and read pointer differ in 2 MSBs, match in remaining bits (Gray code wrap-around detection)
systemverilog
// Empty/full for async FIFO (Gray pointers)
// After gray-to-binary conversion:
assign empty  = (wr_ptr_gray_sync == rd_ptr_gray);
// Full: pointers differ in MSB, but next Gray write pointer == read pointer in MSB
assign full   = (wr_ptr_gray_next == {~rd_ptr_gray_sync[PTR_WIDTH:PTR_WIDTH-1],
                                       rd_ptr_gray_sync[PTR_WIDTH-2:0]});

Rule 3: Gray Code Pointer Synchronization (Async FIFO Only)

Critical: never synchronize binary pointers across clock domains! Always convert to Gray code first (only 1 bit changes per increment).

systemverilog
// Write clock domain
logic [PTR_WIDTH:0] wr_ptr_bin, wr_ptr_gray, wr_ptr_gray_next;
logic [PTR_WIDTH:0] rd_ptr_gray_sync;  // read pointer synced to write domain

// Binary to Gray
assign wr_ptr_gray = wr_ptr_bin ^ (wr_ptr_bin >> 1);
assign wr_ptr_gray_next = (wr_ptr_bin + 1) ^ ((wr_ptr_bin + 1) >> 1);

// Read clock domain
logic [PTR_WIDTH:0] rd_ptr_bin, rd_ptr_gray;
logic [PTR_WIDTH:0] wr_ptr_gray_sync;  // write pointer synced to read domain

// 2-stage synchronizer for Gray pointers
logic [PTR_WIDTH:0] wr_ptr_gray_sync1, wr_ptr_gray_sync2;

always_ff @(posedge rd_clk or negedge rd_rst_n) begin
    if (!rd_rst_n) begin
        {wr_ptr_gray_sync2, wr_ptr_gray_sync1} <= '0;
    end else begin
        {wr_ptr_gray_sync2, wr_ptr_gray_sync1} <=
            {wr_ptr_gray_sync1, wr_ptr_gray};
    end
end
assign wr_ptr_gray_sync = wr_ptr_gray_sync2;

Rule 4: Simultaneous Read/Write Handling

When read and write are both active in the same cycle and the FIFO is empty, the read data should be the newly written data (write-first behavior) or the old data (read-first behavior). Choose one and document it.

Write-first (recommended for simplicity):

systemverilog
always_ff @(posedge clk) begin
    if (wr_en && !full) begin
        mem[wr_ptr] <= wr_data;
    end
end

// Bypass: if reading from the address being written, use new data directly
assign rd_data = (rd_en && wr_en && (rd_ptr == wr_ptr))
                 ? wr_data
                 : mem[rd_ptr];

Rule 5: Reset Behavior

  • All pointers reset to 0
  • All synchronizer stages reset to 0 (for async FIFO)
  • All counters reset to 0
  • Read data output should NOT be reset (don't care after reset)
  • Empty flag = 1 after reset
  • Full flag = 0 after reset

Rule 6: Parameterized Implementation

systemverilog
module sync_fifo #(
    parameter int DATA_WIDTH  = 8,
    parameter int DEPTH       = 16,
    parameter bit REGISTERED_OUTPUT = 0,  // 0 = combinational, 1 = registered
    parameter bit ENABLE_ALMOST_FLAGS  = 0,
    parameter int ALMOST_FULL_THRESH   = DEPTH - 2,
    parameter int ALMOST_EMPTY_THRESH  = 2
) (
    input  logic                    clk,
    input  logic                    rst_n,
    // Write side
    input  logic                    wr_en,
    input  logic [DATA_WIDTH-1:0]   wr_data,
    output logic                    full,
    output logic                    almost_full,  // if enabled
    // Read side
    input  logic                    rd_en,
    output logic [DATA_WIDTH-1:0]   rd_data,
    output logic                    empty,
    output logic                    almost_empty  // if enabled
);

    localparam int PTR_WIDTH = $clog2(DEPTH);
    // ...implementation
endmodule

Anti-Patterns

Anti-Pattern 1: Binary Pointer CDC

code
NEVER synchronize binary pointers across clock domains.
ALWAYS convert to Gray code first.

Anti-Pattern 2: Missing Bypass Logic

code
Without bypass, writing to and reading from the same address in one cycle
returns stale data. Always implement read-data bypass.

Anti-Pattern 3: Glitchy Empty/Full

code
Empty/full flags must be registered, never purely combinational.
Combinational flags can glitch and cause downstream errors.

Anti-Pattern 4: FIFO Depth Too Shallow

code
Always add margin to calculated FIFO depth. Burst behavior in real systems
is rarely perfectly modeled. Round up to next power of 2 and add 25% margin.

Interaction with Other Skills

  • verilog-module: Foundation coding rules
  • cdc-synchronizer: Deeper CDC theory for async FIFO pointers
  • testbench-simple: Generate testbench covering all corner cases
  • rtl-reviewer: Verify FIFO correctness
  • sdc-writer: Generate timing constraints for async FIFO paths

Disclaimer

This skill is provided "as is" for reference and educational purposes only. Code generated using this skill should be independently reviewed and verified before use in any production design, tape-out, or safety-critical application. The author(s) and ICHDL assume no liability for any errors, omissions, or damages arising from the use of this skill or any code generated with it.

Usage Rights

You are free to use, modify, and distribute this skill for personal or commercial purposes. Attribution to ICHDL (ichdl.com) is appreciated but not required. Redistribution of this skill in substantially unmodified form must retain this notice.

Download this Skill as .md file

Copy into your AI Agent's system prompt to activate this Skill.