“When do you choose an async FIFO, and why Gray pointers?” The interviewer is testing the multi-bit crossing argument, not the FIFO interface.
The short answer: Gray pointers cross domains while full and empty stay synchronised. The answer below says why each half is needed.
A FIFO is the structure for streams between unrelated clocks: data waits in memory, and only the pointers cross. Binary pointers fail because several bits change at once, so the destination samples a mix of old and new.
Read each panel at the dashed sample line: every bit reads its level at that instant.
Binary: bit 2 has already risen, bits 1 and 0 have not yet fallen, so the destination reads 7, a value the counter never held.
Gray: only bit 2 moves. Whichever side of the edge the sample lands on, the result is 3 or 4.
Old or new is safe for a FIFO flag: a stale pointer only delays full or empty by a cycle, as the table below explains.
Registered Gray changes one bit per adjacent source step. For an isolated transition the synchronized sample can reflect old or new; a slow destination may skip intermediate pointer values. This requires per-bit synchronizers plus implemented bus-skew/arrival bounds that prevent successive source transitions from overlapping at destination inputs. Derive bounds from the fastest permitted source step across clock modes and audit exception precedence (physical constraints).
The bottom row is the Gray image of the counter above it. Each step flips exactly one bit.
Slot 0: bin 0, gray 0. The crossing is idle.
Slot 1: bin 1, gray 1. Bit 0 flips.
Slot 2: bin 2, gray 3. Bit 1 flips.
Slot 3: bin 3, gray 2. Bit 0 flips back.
Slot 4: bin 4, gray 6. Bit 2 flips.
Status | Binary meaning | Gray comparison |
|---|---|---|
Empty | Write pointer equals read pointer | Gray values equal. |
Full | Same address, opposite lap bit | Top two Gray bits inverted, rest equal. |
Under those crossing/reset assumptions and correct local next-pointer logic, stale remote pointers conservatively delay permission: full may persist after a read and empty may persist after a write. See almost flags.
A FIFO with 2^AW entries uses AW + 1 bit pointers. The lower bits address memory and the top bit counts laps, so full (one lap ahead) differs from empty (equal).
No. A jump breaks the single-bit rule. Values that can jump need a handshake, not a Gray crossing.
In the Gray domain: the next write Gray pointer equals the synchronised read Gray pointer with its top two bits inverted (pointer lesson).
“Binary pointers with a 2FF each are fine.” Multi-bit changes land in the window and the full and empty flags corrupt.
“Full means the pointers are equal.” Equal pointers mean empty. Full is one lap apart.
A Gray pointer with an exhaustive testbench: one-bit steps including the wrap, plus exact round trip for all 16 values.
// Gray-coded FIFO pointer: binary count with a registered Gray image for the crossing.
module gray_ptr #(parameter int W = 4) (
input logic clk,
input logic rst_n,
input logic inc,
output logic [W-1:0] bin,
output logic [W-1:0] gray
);
logic [W-1:0] bin_next;
assign bin_next = bin + 1'b1;
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) begin bin <= '0; gray <= '0; end
else if (inc) begin
bin <= bin_next;
gray <= (bin_next >> 1) ^ bin_next; // convert before the register: no glitch crosses
end
endmodule
Gray pointer testbench Run with Icarus Verilog 13.0 (2026-10-16): prints PASS. It checks one-bit steps including the wrap and the exact round trip for all 16 values. It checks conversion only, not bus routing, metastability or a whole FIFO.