Digital FPGA & ASIC design - Clock domain crossing (CDC)

The principles of synchronous digital design state that we must have a clock, which will nicely mark the rhythm of the different signals traversing our device. We can think of this clock as a sort of orchestra director, which will make sure that everyone does its job exactly when it is required - not before, nor after.

However, complex digital designs will often have more than one clock domain. When this happens, it is like having a whole new orchestra, playing side by side to the old one. If we keep them away from each other, they will both play independently, each at its own pace - but what happens when we need to make them play together? Which director will the different instruments follow? What happens when one orchestra needs to “borrow” a player from the other one in the middle of the concert? This is the problem we face when dealing with clock domain crossings, or CDCs.

Why do we care about CDCs?

Timing rules for a flip-flop (FF) dictate that, for a signal to be correctly sampled, it must be valid in a certain time window centered in the rising edge of the operating clock.

The time window is defined by the setup (ts) and hold (th) times. If we can guarantee that the FF input signal (D) can remain stable both before (setup) and after (hold) the clock edge, then the signal will be captured and propagated to the Q pin, after some propagation time clk-to-q time (tp).

For signals coming from a different clock domain (and also from no clock domain at all, like some external signals), the destination clock domain is unknown and, therefore, the setup and hold constraints are not guaranteed to be met. 

The following example shows an extreme case of what can happen when a signal is not properly sampled. Undetermined effects such as ringing on the output “q” are prone to happen, since the output of the FF (sync1) is not guaranteed to be stable when the input setup and hold constraints are not met. 

These types of problems can be a real headache:

  • They can cause timing violations to be propagated across our otherwise-working synchronous logic.
  • They are rarely detected in RTL simulations (where setup and hold times are assumed to be zero)
  • They can cause undefined behavior in hardware testing.
  • They can affect timing closure of the rest of our logic.

And the list goes on! The problems can have even a bigger impact if we consider data buses (several bits transferred between the clock domains), or clocks with related frequencies, whose CDCs are sometimes assumed to be “safe” when they are not.

How can we handle CDCs?

Proper CDC handling is a very important skill for any digital designer starting to work on a project. And sadly, there isn't a one-size-fits-all solution - different factors that need to be accounted for will influence the choice of CDC technique.

Some of these factors are:

  • The frequency and phase relationship between the clocks (if any)
  • The dynamics of the source signal - does it change with every clock cycle? How fast is it compared to the destination clock?
  • The dimensionality of the signal being synchronized - is it a multi bit signal? Can the bits be treated as independent entities? Or do they convey information only when interpreted together?
  • Is the signal being synchronized in a latency-critical path in our application?
  • Is the signal being synchronized part of a communication protocol?

Therefore, designers must be really aware of what’s going on if they are to achieve a proper CDC.

Let’s start with some basic examples.

Bit synchronizer or 2 FF synchronizer

Basic assumptions for this technique:

  • The source signal has a single bit.
  • The source signal is a “level” - i.e., when it changes value, it stays unchanged for a long time.

This is one of the “classical” synchronization techniques, and probably the first one that we learn when we start to dip our toes in the CDC world.

The main concept was already presented above - the idea is to avoid any ringing or transient effects from being propagated to the rest of our design. For this, we add a second FF right after the one that captures the asynchronous signal.

While the first FF output may have a temporary undetermined behavior, this won’t trigger an update of the second FF, which will only update its output once the signal has stabilized on the first FF:

In this way, no undesired signal effects are propagated to the rest of the logic synchronous to clk2.

Limitations:

  • The latency introduced is unknown (though we can know the min-max latency range) This is an effect of the unknown phase relationship between the clocks.
  • The source signal must remain stable for several clock cycles of the destination clock, so that FF2 can stabilize its output and propagate the value to FF3.

Pulse synchronizer

Basic assumptions:

  • The source signal has a single bit.
  • The source signal lasts for a single cycle of the source clock, and we want a single cycle pulse in the destination clock.

Very often, we use pulses to signal that an event has happened - for example, the processing of a signal has been completed, an error has been detected, or a time counter has expired. 

Let’s first analyze what would happen if we tried to use a 2 FF synchronizer for a single clock pulse. Since the phase between the clocks is unknown, for a single-cycle pulse it is likely that we will miss the destination clock edge at some point, resulting in a setup violation in this clock domain. Therefore, the source signal will be lost, and no one will know about it…

The key point when synchronizing a single-cycle pulse is that the information lies in the rising edge of the pulse - and this is what we can take advantage of when synchronizing pulses.

This scheme can be used to synchronize a single-cycle pulse originating from clk1, generating a single-cycle pulse in clk2. We can see that, in the middle, we’re using the well-known 2 FF synchronizer, but with the addition of:

  • A pulse-to-level block is implemented on the input for clk1. Whenever a pulse is detected, the signal tgl1 will toggle its state.
  • A level synchronizer to synchronize the tgl1 signal to the other clock domain.
  • A toggle detector in clk2. Whenever a change in tgl2_r2 (output of the 2 FF synchronizer) is detected, a single-cycle pulse will be generated.

Conclusion

We’ve presented the basics of CDCs, but certainly there is a lot more to learn. In our next blog entries we will continue building with these blocks and introduce more techniques and code samples. See you next time!

Jose Quinteros del Castillo

Senior FPGA Design Engineer at Emtech S.A

Any Comments or questions, please feel free to contact us: info@emtech.com.ar