CodiodeCodiode
Home
Problem Solving
Skill Tracks
My Assignments
Contests
Leaderboard
Community
Settings
Codiode/Problems/Sequential Logic

Parameterizable Depth Asynchronous FIFO

HardVerilog / SystemVerilogBuild

Modern SoCs consist of multiple independent clock domains interacting constantly. When a high-speed PCIe controller needs to pass data to a slower system bus, an asynchronous FIFO acts as the critical elastic buffer between these domains. Hardcoding FIFO dimensions leads to massive code duplication across a large chip; parameterizable IP is essential for scalable and maintainable hardware design.

This module implements a robust asynchronous FIFO with parameterized data width and depth. It safely transfers data across two unrelated clock domains (wclk and rclk). Write operations occur in the write clock domain when winc is asserted and the FIFO is not full. Read operations occur in the read clock domain when rinc is asserted and the FIFO is not empty. Cross-domain pointer synchronization must be handled via Gray code conversion and two-stage flip-flop synchronizers to prevent metastability. The memory array depth is determined by $2^{\text{ADDR\_WIDTH}}$.

Timing and Reset Rules

  • Clock edge: Both wclk and rclk trigger on the positive edge.
  • Reset type: wrst_n and rrst_n are asynchronous and active-low.
  • Reset behaviour: On reset, internal pointers clear. wfull goes to 0, rempty goes to 1, and rdata goes to 0.
  • Read behaviour: This is a standard FIFO, not First-Word Fall-Through (FWFT). rdata updates on the clock edge where rinc is high.
  • Priority: If winc is asserted while wfull is high, the write must be ignored. If rinc is asserted while rempty is high, the read must be ignored.

Worked Trace

Cycle 1: wrst_n=0, rrst_n=0 • Pointers reset. wfull=0, rempty=1. Cycle 2: wrst_n=1, winc=1, wdata=0xAA • Data written to memory. Write pointer increments. Cycle 3: (Wait for synchronizers) • Write pointer converted to Gray code, crosses to rclk domain via 2-stage synchronizer. Cycle 4: rempty=0 • Read domain detects FIFO is no longer empty. Cycle 5: rinc=1 • Data read from memory. rdata=0xAA. Read pointer increments. Cycle 6: (Wait for synchronizers) • Read pointer converted to Gray code, crosses to wclk domain via 2-stage synchronizer.

Timing Diagram

{ "signal": [
  { "name": "wclk",      "wave": "p........" },
  { "name": "winc",      "wave": "010......" },
  { "name": "wdata",     "wave": "x4x......", "data": ["D0"] },
  { "name": "wptr_gray", "wave": "34.......", "data": ["0", "1"] },
  {},
  { "name": "rclk",      "wave": "p........" },
  { "name": "rq1_wptr",  "wave": "3..4.....", "data": ["0", "1"] },
  { "name": "rq2_wptr",  "wave": "3...4....", "data": ["0", "1"] },
  { "name": "rempty",    "wave": "1...0...." }
], "head": { "text": "Write pointer synchronization leading to rempty de-assertion." } }

Port Table

| Signal | Direction | Width | Description | |--------|-----------|-------|-------------| | wclk | input | 1 | Write domain positive-edge clock | | wrst_n | input | 1 | Write domain asynchronous active-low reset | | winc | input | 1 | Write increment enable | | wdata | input | DATA_WIDTH | Write data input | | wfull | output | 1 | Write domain full flag; 1 when FIFO is full | | rclk | input | 1 | Read domain positive-edge clock | | rrst_n | input | 1 | Read domain asynchronous active-low reset | | rinc | input | 1 | Read increment enable | | rempty | output | 1 | Read domain empty flag; 1 when FIFO is empty | | rdata | output | DATA_WIDTH | Read data output |

*Module Parameters:* • DATA_WIDTH: Default 8. Width of the data bus. • ADDR_WIDTH: Default 4. Determines FIFO depth as $2^{\text{ADDR\_WIDTH}}$.

Constraints

  • You must use Gray code for crossing pointers between clock domains. Binary pointer crossing will fail timing in real hardware.
  • You must use two-stage flip-flop synchronizers for all cross-domain signals.
  • The full condition must be strictly evaluated in the wclk domain to prevent false negatives.
  • The empty condition must be strictly evaluated in the rclk domain to prevent false negatives.
  • The FIFO depth is strictly $2^{\text{ADDR\_WIDTH}}$. Memory bounds checking must use this parameter.
  • Writes to a full FIFO and reads from an empty FIFO must not corrupt pointers.

Topics

FIFOMemoryClock Domain CrossingSynchronization

Solve this problem

Write the module in Verilog, SystemVerilog or VHDL. Your submission is compiled and simulated against a real testbench — you get the waveform back, not a stored answer.

This problem is part of Codiode Pro. The statement above is free to read.

Sign in to solveSee what Pro unlocks

The circuit builder and code editor need a desktop screen. On a phone, read the problem here and open it on a laptop to solve.

Related problems

  • Basic D Flip FlopEasy
  • Debug: Missing Edge in Sensitivity ListMedium
  • Read After Write Hazard DetectionEasy
  • Parameterized Interface with ModportsHard
  • Struct Array PipelineHard
  • T Flip Flop from D Flip Flop TemplateEasy
  • Recursive Generate Reduction TreeHard
  • Four Stage Shift RegisterEasy

Browse all problems · Learning tracks