Sometimes, when crossing clock domains, synchronizers are just not enough.
Imagine sending data serially over a single line and receiving it on the other side from the output of a common synchronizer as shown bellow.
Assuming one clock cycle is enough to recover from metastability under the given operating conditions, what seems to be the main problem is not the integrity of the signal - i.e. making sure it is not propagating metastability through the rest of the circuit - but rather the correctness of the data.
Let’s observe the waveform below. The red vertical lines represent the sampling point of the incoming signal. We see from the waveform that since sometimes we sample during a transition - in effect violating the setup-hold window - the output of the first sampling flop (marked “x“) goes metastable. This metastability does not propagate further into the circuit, it is effectively blocked by the second flop, but since the result of recovery from metastability is not certain (see previous post) the outcome might be a corrupt data.
In this specific example we see that net x goes metastable after sampling the 3rd bit but recovers correctly. In a later sampling, for the 6th bit we see that the recovered outcome is not correct and as a result the output data is wrong.
Another interesting case is when both the send clock and the receive clock are frequency locked but their phase might drift in time or the clock signals might experience occasional jitter.
In that case, a bit might “stretch” or “shrink” and can be accidentally sampled twice or not sampled at all.
The waveform below demonstrates the problem. Notice how bit 2, was stretched and sampled twice.
To sum up, never use a simple synchronizer structure to transfer information serially between clock domains, even if they are frequency locked. You might be in more trouble than you initially thought.
On the next post we will discuss how to solve this problem with ring buffers (sometimes mistakenly called FIFOs).