RTL Problems / Level 7 · Advanced RTL & Protocol Design
What You Will Learn
By solving Dual-Clock Asynchronous FIFO with Gray Pointer CDC Synchronization, you will acquire core industry skills:
- Synthesizable hardware modeling in standard Verilog-2001 / SystemVerilog.
- Correct handling of interface port contracts, edge-triggered clocks, and active-low/high resets.
- Simulation waveform interpretation and debugging using timing diagrams (VCD traces).
- Architectural avoidance of timing violations, glitching hazards, and synthesis warnings.
Problem Statement & Requirements
Design a dual-clock asynchronous FIFO with Gray-coded pointers, 2-flop CDC synchronizers, and safe full/empty flag generation.
Hardware design teams at leading semiconductor firms evaluate these exact port contracts, race-condition assertions, and corner cases during technical design reviews and ASIC engineer interviews.
Prerequisites: Asynchronous FIFO design principles (Cummings paper), Gray coding, 2-flop synchronizer, MTBF considerations.
Port List & Interface Timing
The hidden testbench instantiates top_module. Module port names and bit-widths must match the interface contract exactly:
| Port | Dir | Width | Description |
|---|---|---|---|
wr_clk, wr_rst_n | input | 1 | Write domain clock and reset |
wr_en, wr_data[7:0] | input | 1, 8 | Write domain push handshake |
rd_clk, rd_rst_n | input | 1 | Read domain clock and reset |
rd_en, rd_data[7:0] | in/out | 1, 8 | Read domain pop handshake |
full | output | 1 | Full flag synchronous to wr_clk |
empty | output | 1 | Empty flag synchronous to rd_clk |
Timing Constraints & Synthesizable RTL Rules
Timing Contract: Completely independent wr_clk and rd_clk. Note: RTL simulation checks functional equivalence; physical CDC safety requires ASYNC_REG synthesis attributes and set_max_delay timing constraints.
Synthesis Constraints: No multi-bit CDC crossing in binary. Use Gray code. Synthesizable Verilog-2001.
Architectural Deep Dive & Implementation Walkthrough
Classic Dual-Clock FIFO Architecture
Asynchronous FIFOs are ubiquitous in SoCs whenever data passes between independent oscillator domains (e.g. PCIe PHY, DDR controller, CPU core).
Why Gray Code is Mandatory
If binary pointers were synchronized across clock domains, a multi-bit change (e.g. 3'b011 -> 3'b100 where all 3 bits change simultaneously) would suffer from physical routing delay skew. The receiving synchronizer could sample intermediate corrupt codes (e.g. 3'b000 or 3'b111), causing false full or empty flags.
Gray code guarantees that exactly ONE bit changes between any two consecutive values ($000 o 001 o 011 o 010$). Even if the synchronizer samples during a metastable transition, it can only ever resolve to either the old value or the new value, never an erroneous random code.
Synthesis Note: Real silicon implementations require the(* ASYNC_REG = "TRUE" *)attribute on synchronizer registers and aset_max_delay -datapath_onlyconstraint in the SDC timing script.
Common RTL Designer Pitfalls
Checking full in the read clock domain or empty in the write clock domain. Full must be checked in wr_clk, empty in rd_clk.
Silicon & Hardware Engineering Notes
Async FIFO depth is restricted to powers of 2 ($2^N$) so that the Gray code sequence wraps around with a single-bit transition.
Starter RTL Code
module top_module #(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 3 // Depth = 8
)(
input wire wr_clk,
input wire wr_rst_n,
input wire wr_en,
input wire [DATA_WIDTH-1:0] wr_data,
input wire rd_clk,
input wire rd_rst_n,
input wire rd_en,
output wire [DATA_WIDTH-1:0] rd_data,
output wire full,
output wire empty
);
// Implement Dual-Clock Async FIFO:
// 1. Dual-port RAM addressed by binary wr_ptr[2:0] and rd_ptr[2:0].
Verified Reference Solution
🔒 Reveal Verified Reference RTL Solution
module top_module #(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 3 // Depth = 8
)(
input wire wr_clk,
input wire wr_rst_n,
input wire wr_en,
input wire [DATA_WIDTH-1:0] wr_data,
input wire rd_clk,
input wire rd_rst_n,
input wire rd_en,
output wire [DATA_WIDTH-1:0] rd_data,
output wire full,
output wire empty
);
localparam DEPTH = 1 << ADDR_WIDTH;
reg [DATA_WIDTH-1:0] mem [0:DEPTH-1];
// Binary and Gray pointers (ADDR_WIDTH+1 bits)
reg [ADDR_WIDTH:0] wr_bin, wr_gray;
reg [ADDR_WIDTH:0] rd_bin, rd_gray;
// 2-Flop CDC synchronizers
reg [ADDR_WIDTH:0] wr_gray_s1, wr_gray_s2; // into rd_clk domain
reg [ADDR_WIDTH:0] rd_gray_s1, rd_gray_s2; // into wr_clk domain
// Write operation
wire write_valid = wr_en && !full;
always @(posedge wr_clk) begin
if (write_valid) begin
mem[wr_bin[ADDR_WIDTH-1:0]] <= wr_data;
end
end
always @(posedge wr_clk or negedge wr_rst_n) begin
if (!wr_rst_n) begin
wr_bin <= 0;
wr_gray <= 0;
end else if (write_valid) begin
wr_bin <= wr_bin + 1'b1;
wr_gray <= (wr_bin + 1'b1) ^ ((wr_bin + 1'b1) >> 1);
end
end
// Synchronize rd_gray into wr_clk domain
always @(posedge wr_clk or negedge wr_rst_n) begin
if (!wr_rst_n) begin
rd_gray_s1 <= 0;
rd_gray_s2 <= 0;
end else begin
rd_gray_s1 <= rd_gray;
rd_gray_s2 <= rd_gray_s1;
end
end
// Full detection in wr_clk domain: MSB and 2nd MSB inverted, remaining bits equal
assign full = (wr_gray == {~rd_gray_s2[ADDR_WIDTH:ADDR_WIDTH-1], rd_gray_s2[ADDR_WIDTH-2:0]});
// Read operation
wire read_valid = rd_en && !empty;
assign rd_data = mem[rd_bin[ADDR_WIDTH-1:0]];
always @(posedge rd_clk or negedge rd_rst_n) begin
if (!rd_rst_n) begin
rd_bin <= 0;
rd_gray <= 0;
end else if (read_valid) begin
rd_bin <= rd_bin + 1'b1;
rd_gray <= (rd_bin + 1'b1) ^ ((rd_bin + 1'b1) >> 1);
end
end
// Synchronize wr_gray into rd_clk domain
always @(posedge rd_clk or negedge rd_rst_n) begin
if (!rd_rst_n) begin
wr_gray_s1 <= 0;
wr_gray_s2 <= 0;
end else begin
wr_gray_s1 <= wr_gray;
wr_gray_s2 <= wr_gray_s1;
end
end
// Empty detection in rd_clk domain
assign empty = (rd_gray == wr_gray_s2);
endmodule
Related RTL Problems in Level 7
- Async-Assert / Sync-Deassert Reset — Assert reset the instant it goes active, but only release it once it has been clean for 2 clock edges — the standard reset architecture for any real design.
- 3-Flop CDC Synchronizer — A deeper 3-stage synchronizer for signals that need extra MTBF margin beyond the standard 2-flop chain.
- Synchronized Rising-Edge Detector — Safely synchronize an asynchronous level signal, then produce a clean single-cycle pulse the moment it rises in the local clock domain.
Connected EcrioniX Learning Resources
Accelerate your VLSI engineering knowledge with these curated learning materials:
Progress through step-by-step verified modules from fundamentals to advanced silicon design.
Benchmark your hardware knowledge across 10 semiconductor domains and receive study roadmaps.
Explore foundational circuit theory, waveform timing, and physical hardware implementation.
Frequently Asked Questions
What does Dual-Clock Asynchronous FIFO with Gray Pointer CDC Synchronization require?
Design a dual-clock asynchronous FIFO with Gray-coded pointers, 2-flop CDC synchronizers, and safe full/empty flag generation. Implement it as Verilog module top_module matching the listed ports.
Is this design combinational or sequential?
Tags: advanced-rtl, cdc-and-rdc, systemverilog, asic, fpga. If a clock signal is present in the port table, implement it as sequential logic; otherwise, use continuous assignments or combinational blocks.
How is my solution graded?
A hidden SystemVerilog testbench simulates your module in the cloud judge. You pass when all directed corner cases match the golden model and the VCD timing waveform matches the contract.
How does this challenge help with semiconductor interviews?
Technical interviewers at NVIDIA, Qualcomm, Apple, and Intel routinely ask candidates to code, debug, and explain the timing waveforms of Dual-Clock Asynchronous FIFO with Gray Pointer CDC Synchronization to assess real-world synthesizability and race-condition awareness.