8
\$\begingroup\$

I'm trying to understand how clock crossing FIFOs are implemented, and the usual answer I see to convert the read/write address pointers to gray code and then pass through synchronizer circuits into each others clock domain to determine if there's data in contained in the FIFO. The idea of using gray code is so the other clock domain can detect metastability on the other end by the fact that only one bit should change per address increment...and it can safely ignore the address pointer until it looks valid.

What about the scenario where the write clock domain is much faster than the slow clock domain? If the write address pointer can increment many times per read clock cycle, at some point there will be more than 1 bit flipped in the gray code value, and that wouldn't necessarily be a mistake. How is this situation usually handled?

I know there's a similar question on SE, but the answer seems to show an example of the write clock being only 2x faster than the read clock, so maybe this situation is never experienced.

\$\endgroup\$

1 Answer 1

12
\$\begingroup\$

In an asynchronous FIFO, one clock domain is associated with the write port, and the "head" pointer (the next write address) is kept in that clock domain. Similarly, the other clock domain is associated with the read port, and the "tail" pointer is kept there.

The problem is that both clock domains need to be able to keep track of the number of words in the FIFO, and the way to do this is to subtract the value of the tail pointer from the value of the head pointer, modulo the size of the RAM. Therefore, each pointer is encoded as Gray code, transferred to the other clock domain and converted back to binary.

It doesn't matter if more than one write happens during a read clock period, or vice-versa. The point is, with Gray code encoding, only one bit changes between any pair of values. If one clock happens to catch a transition in the other clock domain, at most only one bit can be metastable, and the ambiguity is between two adjacent states of the counter.

In other words, each side sees a monotonically increasing sequence of values from the other side, even if some values are skipped — and more importantly, even if metastability occurs. Therefore, it isn't possible for either clock domain to calculate an erroneous value for the number of words in the FIFO — it simply becomes a question of whether it gets the update one clock sooner or later than it otherwise might have.

So, on the write side, the head pointer increments directly, increasing the depth of the FIFO, but the updated tail pointer coming from the other side may be delayed. This can only cause the write side to overestimate the number of words in the FIFO, and therefore, the FIFO will never overflow.

Similarly, on the read side, the tail pointer increments directly, decreasing the depth of the FIFO, but the updated head pointer may be delayed. This can only cause the read side to underestimate the number of words in the FIFO, and as a result, it will never underflow.

In fact, you can put any number of synchronizing stages in the path of the Gray code transfers, and the only effect this will have is to increase the latency through the FIFO. This is even a configurable parameter in the Xilinx dual-clock FIFO generator.

\$\endgroup\$
14
  • 4
    \$\begingroup\$ Okay, I think I get it. So multiple bits can change when the counter is crossed over to the slower clock domain, but only 1 bit will pose the risk of being metastable based on the fact that only 1 bit changes in the fast domain gray coding. All the other bits should have held steady for a longer amount of time, and thus the chance of violating the setup/hold time of the slow clock domain registers has reduced. Passing the gray code value through a double-register will clear up the single bit metastable error as it usually does, and you don't have to worry about multi-bit bus deskewing. \$\endgroup\$ Apr 12, 2019 at 13:01
  • \$\begingroup\$ You mentioned "with Gray code encoding, only one bit changes between any pair of values". How can this be true for the fast changing gray code counter being captured with the clock say 5 times slower. That means the capturing flop sees every 5-th value of the gray counter and this is not just one bit change. I agree with the explanation in context of slow to fast domain transition, but not the other way around. Can you clarify please? \$\endgroup\$
    – Khach
    Nov 10, 2021 at 5:27
  • 3
    \$\begingroup\$ @Khach: It's a subtle point, but the question is not whether the bits are changing from the previous captured value. It's a question of whether we're seeing a transition from one value of the counter to the next in the source clock domain, and with Gray code, there can only be one bit making such a transition at any given moment in time. \$\endgroup\$
    – Dave Tweed
    Nov 10, 2021 at 16:26
  • 1
    \$\begingroup\$ Why do some modules such as Xilinx's xpm_cdc_gray require the destination clock to register at least 2 times the source data? It doesn't seem necessary and this requirement kinda simulates a slower source clock (src_clk<=dst_clk/2)... I believe when the source clock is much faster than the destination clock, we might face issues in the SLA with setup/hold timings but as long as the difference is reasonable, we should be fine. \$\endgroup\$
    – None
    Feb 16, 2022 at 0:01
  • 2
    \$\begingroup\$ @Alexis: Honestly, I don't know. Which clock is faster should really make no difference at all. After all, when you're crossing between the domains of two unrelated clocks, there are no guarantees with respect to setup/hold anyway. That's why any solution must be designed to deal with the resulting opportunities for metastability, timing skew, etc. \$\endgroup\$
    – Dave Tweed
    Feb 16, 2022 at 3:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.