← RTL Design Interviews

Interview Question · Design

Inside an Asynchronous FIFO

L2 Mid-LevelL3 Seniorfifocdcgray code
  • Asked at AMD

The Question

The interviewer asks

Draw an asynchronous FIFO between two unrelated clocks. What crosses between the domains, and how are full and empty computed?

Reveal the Model Answer

The memory is written in the write domain and read in the read domain. Only the two pointers cross, each Gray-coded and through two flops. The write side compares its pointer with the synchronized read pointer to compute full. The read side compares the other way to compute empty. Each side sees the other pointer late, so a flag can stay on too long, but it is never wrongly off.

Two Domains, One Memory, Two CrossingsArrows show signals. Everything left of the memory runs on wclk, everything right of it on rclk.
Two domains, one memory, two crossingsArrows show signals. Everything left of the memory runs on `wclk`, everything right of it on `rclk`. wclk Write side wbin_q, wptr_q, full wclk wq1, wq2 read pointer, two wclkflops queue Memory 8 words of 8 bits rclk rq1, rq2 write pointer, tworclk flops rclk Read side rbin_q, rptr_q, empty write wordread wordwptr_qseenrptr_qseen
  1. Write

    The write side stores the word and steps its pointer in the same edge.

    Follow the calls, values and waits
    1. data word 16: Write side → Memory
  2. The write pointer crosses

    The pointer goes through two read-clock flops. The read side compares it with its own pointer to compute empty.

    Follow the calls, values and waits
    1. data wptr 0000: Write side → rq1, rq2
    2. data wptr 0000: rq1, rq2 → Read side
  3. Read

    The word itself never goes through a synchronizer. By the time the read side sees the pointer, the word has sat still in memory for two cycles.

    Follow the calls, values and waits
    1. data word 16: Memory → Read side
  4. The read pointer crosses back

    The write side computes full from its own pointer and its copy of the read pointer.

    Follow the calls, values and waits
    1. data rptr: Read side → wq1, wq2
    2. data rptr: wq1, wq2 → Write side
Read the diagram as text
  • Write side: wbin_q, wptr_q, full
  • wq1, wq2: read pointer, two wclk flops
  • Memory: 8 words of 8 bits
  • rq1, rq2: write pointer, two rclk flops
  • Read side: rbin_q, rptr_q, empty
  • Write side → Memory: write word
  • Memory → Read side: read word
  • Write side → rq1, rq2: wptr_q
  • rq1, rq2 → Read side: seen
  • Read side → wq1, wq2: rptr_q
  • wq1, wq2 → Write side: seen

Each flag is computed in its own domain, from one local pointer and one synchronized copy.

  1. Write domain

    Binary write pointer for the RAM address, Gray copy registered for the crossing.

  2. Crossing

    Gray write pointer through two read-clock flops; Gray read pointer through two write-clock flops.

  3. Read domain

    Empty when the read Gray pointer equals the synchronized write pointer.

  4. Write domain

    Full when the write Gray pointer equals the synchronized read pointer with its top two bits inverted.

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Mid-Level)

A good mid-level answer draws both clock domains and shows that only the pointers cross. It explains why the flags can be late but are never unsafe.

Cross-Questions

Where the interviewer goes next. Open a node to practise that follow-up.

WhyWhat IfProve ItBreak ItCompareScale It

  1. Inside an Asynchronous FIFO
  2. WhyWhy Async FIFO Flags Are Late but SafeL2 Mid-Level

    Why is it acceptable that the write side sees the read pointer a few cycles late?

    Reveal the Model Answer

    A late read pointer looks like fewer reads than really happened. So the write side thinks the FIFO is fuller than it is, and full may stay high a little too long. It never thinks there is space that is not there. The same reasoning makes empty late but safe on the read side.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "The delay can make the FIFO overflow."

    A stale read pointer can only make full assert early or release late. Overflow would need the write side to see reads that have not happened.

    Source Mehta, ASIC/SoC Functional Design Verification (2018) ch. 8.3.5 Design of an asynchronous FIFO using Gray code counters

  3. What IfSetting an Almost-Full ThresholdL2 Mid-LevelL3 Senior

    What if the source needs a warning before full, because it takes 3 cycles to stop?

    Reveal the Model Answer

    Raise almost-full when the free space drops to the number of words the source can still send after the warning. Here that is 3, plus any cycles the flag itself takes to reach the source. In an asynchronous FIFO, the flag is already pessimistic, which adds a little margin at no extra cost.

    Self-Check

    Tick each key point you covered in your answer.

    Delivery

    "Almost-full at half depth is a good default."

    The threshold must come from the source's stopping delay. Half depth either wastes space or, for a slow-to-stop source, is not enough.

  4. CompareGray-Code Pointers vs a Handshake for Multi-Bit CrossingsL2 Mid-Level

    Compare a Gray-coded value with a request/acknowledge handshake for carrying a multi-bit value across clock domains.

    Reveal the Model Answer

    Gray code changes one bit per step, so a value caught mid-change is either the old or the new one. It suits counters that move by one, such as FIFO pointers. A handshake holds any data stable while a synchronized request crosses and an acknowledge comes back. It carries any value, but takes several cycles per transfer. Choose Gray for streaming pointers, and a handshake or FIFO for occasional wide data.

    The Same Edges with Gray PointersAn asynchronous FIFO with Gray-coded pointers, sampled at the read clock clk.
    012345clkwptr10011000000000010011seen10111001100000000001read_levelholds232empty
    1. Cycle 1

      Edge 21: empty is 1 while the FIFO holds two words. The read side has not seen those writes yet.

    2. Cycle 2

      Edge 22: the pointer steps from 1000 to 0000, one bit, the Gray wrap from 15 to 0.

    3. Cycle 4

      Edge 24: read_level is 1 with two words held. Low is safe; the read side just waits a cycle.

    Edges 20 to 25 of the run: the level the read side sees never rises above the words held.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Gray-code any multi-bit bus and it crosses safely."

    Gray only helps when consecutive values differ by one step. Arbitrary data jumps change many bits at once.

    Source Cummings, CDC Design and Verification Techniques (SNUG Boston 2008) §5.7.2-5.7.4; Mehta, ASIC/SoC Functional Design Verification ch. 8.3.4 (p155-156)

  5. Prove ItSolve R6.4.2: The Torn PointerSimulator-graded

    Show it, don't just say it: this Silicon Lab problem is graded by the simulator.

    Open Problem R6.4.2

  6. Break ItBreaking a Gray Pointer CrossingL3 Senior

    The FIFO uses Gray pointers and two-flop synchronizers. How could it still fail in silicon?

    Reveal the Model Answer

    Gray code assumes the bits arrive close together. If the wires of one pointer differ by more than about a clock period, two steps can mix. Logic between the Gray register and the synchronizer can glitch. And a Gray pointer that is computed but not registered can show several bits changing.

    Self-Check

    Tick each key point you covered in your answer.

    Missed Corner

    "Put a false path on the pointer crossing; it is synchronized."

    A false path lets the bits of one pointer be routed with any skew. The crossing then needs a maximum-delay or skew constraint instead.

    Source Lesson L8.1, Constrain the asynchronous FIFO (set_max_delay on the pointer crossing, false path for hold only)

  7. Scale ItCDC Sign-Off on a Many-Clock SoCL3 SeniorL4 Staff

    How do you sign off clock domain crossings on an SoC with dozens of clocks?

    Reveal the Model Answer

    Start with structural analysis: define the clocks and their relationships. Then let the tool find every crossing and check that each has the right synchronizer for its type. Next comes protocol checking: each scheme's rules, such as hold times and Gray steps, become assertions in formal and simulation. Add reconvergence checks and reviewed waivers. Use metastability injection in simulation to expose designs that assume exact timing.

    Self-Check

    Tick each key point you covered in your answer.

    Tool as Truth

    "Our regressions pass with all clocks running, so CDC is covered."

    RTL simulation never goes metastable and rarely hits the bad clock phase. CDC needs structural and protocol checks, not only passing tests.

    Source Mehta, ASIC/SoC Functional Design Verification ch. 8.5 (PDF p186-188); Sutherland, RTL Modeling with SystemVerilog ch. 8.1.6

    Wrong Altitude

    A staff-level answer that stops at "run the CDC tool".

    The interviewer wants the flow: constraints, structure, protocol, reconvergence, waiver review and who owns each step.

  8. Prove ItSolve R6.6.1: The Asynchronous FIFOSimulator-graded

    Show it, don't just say it: this Silicon Lab problem is graded by the simulator.

    Open Problem R6.6.1

What People Get Wrong

Common wrong answers and why they fail. Run It shows the wrong belief failing on a real simulator.

Misconception

"Send the binary pointer through two flops per bit."

Several bits change at once, and each can land on a different read edge. In the twin, the step from 7 to 8 is read as 0.

Run It

A 4-bit pointer crossing with different wire delays per bit, binary then Gray.

RUNIcarus Verilog · in your browser
Edit the code, then run it
Expected: first torn value: 7 -> 0 binary pointer: 215 changes seen, 32 jumped by more than one FAIL: a binary pointer with two flops per bit showed values that were neither the old nor the next pointer
See the Fix
`timescale 1ps/1ps
// A 4-bit write pointer crosses into a faster read clock. Each bit reaches the reader with its
// own wire delay (0, 700, 1400, 2100 ps). The reader samples at its clock and decodes the value.
module tb;
  logic wclk = 0, rclk = 0;
  logic [3:0] wcount = 0, tx = 0, wire_d = 0, s1 = 0, s2 = 0, now, prev = 0;
  int torn = 0, steps = 0;
  always #5000 wclk = ~wclk;                     // write clock, 10 ns
  always #3650 rclk = ~rclk;                     // read clock, 7.3 ns
  logic [3:0] next;
  assign next = wcount + 4'd1;
  always @(posedge wclk) begin
    wcount <= next;
    tx <= next ^ (next >> 1);   // the pointer as sent: Gray code, registered
  end
  for (genvar i = 0; i < 4; i++) begin : wires
    always @(tx[i]) wire_d[i] <= #(i * 700) tx[i];
  end
  always @(posedge rclk) begin
    s1 <= wire_d;                                // two-flop synchronizer
    s2 <= s1;
  end
  always @(s2) begin
    now = s2 ^ (s2 >> 1) ^ (s2 >> 2) ^ (s2 >> 3);   // Gray to binary
    steps++;
    if (4'(now - prev) > 1) begin
      if (torn == 0) $display("first torn value: %0d -> %0d", prev, now);
      torn++;
    end
    prev = now;
  end
  initial begin
    #2000000;
    $display("Gray pointer: %0d changes seen, %0d jumped by more than one", steps, torn);
    if (torn == 0) $display("PASS: the Gray pointer only ever showed the old or the next value");
    $finish;
  end
endmodule

Prints: Gray pointer: 199 changes seen, 0 jumped by more than one PASS: the Gray pointer only ever showed the old or the next value

Misconception

"Full and empty are exact in an asynchronous FIFO."

Each side sees the other pointer two or three cycles late. Full stays high a little after a read frees space. It is safe, and it costs some throughput.

Source Mehta, ASIC/SoC Functional Design Verification (2018) ch. 8.3.5 Design of an asynchronous FIFO using Gray code counters

Half-Truth

"Gray code makes any counter safe to cross."

Only if it steps by one between samples. The depth must be a power of two, so the wrap is also one step. Reset can also change many bits at once.

Source Golson, Synchronization and Metastability (SNUG Silicon Valley 2014) §5.2

Learn It, Then Prove It