Asynchronous FIFOs and Gray-Coded Pointers: How Hardware Passes Data Between Clocks Without Losing a Word

2026-05-19

You've seen FIFOs as elastic buffers and Gray codes as glitch-free counters. Today: the place those two ideas fuse into the most-deployed clock-domain-crossing structure in modern chips — the asynchronous FIFO. Every USB controller, every PCIe endpoint, every audio codec interface has one.

The problem: a write-side clock (say 100 MHz) pushes data into a RAM. A read-side clock (say 75 MHz, totally unrelated phase) pulls it out. Each side needs to know the other side's pointer to compute "full" and "empty" — but reading a multi-bit binary counter across clock domains is fatal. If the write pointer transitions from 0111 → 1000 while the read side samples, you could latch any of 16 values. Full might look like empty.

The trick: convert each pointer to Gray code before crossing. Only one bit changes per increment, so even if the receiver catches the transition mid-flight, it samples either the old value or the new value — never a garbage in-between. Then a two-flop synchronizer on each bit handles metastability, and the receiver converts back to binary (or compares in Gray directly) to compute fill level.

The classic full-detection rule (Cummings 2002, the canonical paper): FIFO is full when the synchronized read pointer equals the write pointer with the MSB inverted and the second-MSB inverted. That's because for a depth-N FIFO you use an (N+1)-bit pointer — the extra bit distinguishes "full" from "empty" when low bits match. In Gray code, inverting the top two bits is equivalent to adding N in binary.

Latency rule of thumb: worst-case "full" detection lag = 2 write clocks (sync of read pointer) + 1 write clock (compare). So design your FIFO depth to absorb at least 3 × (write_clk / read_clk_ratio) extra words beyond your burst size, or you'll backpressure spuriously.

Real example: the FX3 USB 3.0 controller in a logic analyzer streams 400 MB/s from a 200 MHz capture domain to a 125 MHz USB PHY. Internal async FIFO is 1024 deep × 32 bits, Gray-coded pointers, two-flop synchronizers. Total cross-domain latency: ~30 ns. Without Gray coding, this exact structure would silently corrupt one packet per million transfers.

Key Takeaway: An async FIFO is a dual-port RAM whose pointers cross clock domains in Gray code so only one bit ever changes per transition, making metastable samples land on a valid value instead of garbage.

All newsletters