Showing results for 
Show  only  | Search instead for 
Did you mean: 

Constraining Asynchronous Clocks

Xilinx Employee
Xilinx Employee
8 1 25.8K

For asynchronous clocks, there are four ways to write the constraints.


This post provides the pros and cons for each method:


a)      set_false_path:

This was the “original” method, used during the early days of PrimeTime.

There are two main disadvantages:

    1. You have to specify it both ways: clocks1 to clock2; and then clock2 to clock1.
    2. The path is not timed, so theoretically, the tool can have as much routing delay as it wants.
      Effectively, the two flops involved in the crossing could be sitting at two different corners of the device, because there is nothing to tell them that they should meet any specific requirement.


b)      set_clock_groups:

This has been introduced relatively recently in SDC.

There are three main advantages, compared to a):

    1. You have to specify it only once between the pair, saving on the length of your XDC/SDC files.
    2. The path is not timed, the same as when using set_false_path. However, it considers the signal-integrity aspects, while set_false_path does not. 
    3. In the world of FPGA based designs, this is immaterial, as signal integrity aspects have been considered while designing the FPGA device.
    4. This one is a little philosophical.
    5. When you use set_false_path, you are just specifying that the path is false. There is no “implicit” understanding as to why the path is false. 
    6. For example, take asynchronous clocks. In reality, the path is not false. It exists. Signals will travel across this path. set_false_path is a mechanism used to convey to the tool, that it should not time this path. 


However, in the case of set_clock_groups, you are specifying the exact reason, which is that the clock groups are different. 

The fact that they should not be timed is a consequence of what you have stated to be the intent.
So, this also acts as a note as to why a path should not be timed, and makes it easier to understand the cause of the specific constraint.


Usually, (b) is preferred to (a), mainly because of points (ii) and (iii). Point (i) just acts as an additional convenience.


c)       set_multicycle_path:

Around the mid-2000s, the set_false_path users started migrating to set_multicycle_path based constraints.

(a)   and (b) do NOT time the path at all – allowing for any amount of delay in the path.

This method provides at least some kind of upper-bound to the time-of-flight across the crossing.


d)      set_max_delay:

This option can achieve the same effect as (c).

Use of the –datapath_only option is specific to Vivado. This makes it easier for the user who does not have to worry about the clock skews involved.

Also, set_max_delay once again better represents the intent compared to set_multicycle_path.


If I had to choose, I would go in the order:

(d), (b), (c), (a).

Sometimes, I might give higher preference to (b).


For example, if I am working on an Out-Of-Context design-portion, and that design portion can be used with different applications, I will not know what kind of peripheral or device that portion will be implemented on.

In this case, I might not know what would be a good max_delay value to specify, so I might play it safe by going for option (b), rather than (d).


If using Xilinx tools, Vivado allows access to the “period” at which my piece would be operating. In this case, I might specify the delay value in terms of the period, so I would be more likely to use option (d).


However, others might have a different order of preference, based on personal choice/need/familiarity etc.


Best regards,


1 Comment

I agree with the vast majority of what Sanjay says here, but I have to add some additional material to this topic.


Clock crossing can be one of the most complex aspects of a modern digital design. The main reason is that the paradigm of "synchronous design" breaks down at these boundaries. The vast majority of RTL design that we do is based on describing a "cycle accurate" description of the functionality that we want to implement, and letting the tools generate the required hardware based on this cycle accurate description. This works so well because the ultimate hardware implementation (generated by the tools) will match the cycle accurate RTL description as long as the setup and hold checks on the synchronous paths meet timing. For a single clock domain, this is (generally) as simple as describing the clock driving the system to the static timing engine and letting it do its work (i.e. using create_clock and/or create_generated_clock).


At clock crossing boundaries, though, these no longer apply. The actual timing requirements on these paths are not covered by the "normal" synchronous setup and hold checks, but require "something else". The "something else" is not trivial, and is highly dependent on a number of factors

   - what kind of data is crossing the boundary (single bit vs. bus, and for a bus, the type of encoding)

   - how fast the source and destination clocks are

   - how often data must cross the boundary

   - (and probably others)


Based on these criteria, an appropriate clock domain crossing (CDC) circuit  must be designed. There is no "one size fits all" CDC circuits - there are many techniques that have evolved over time for different situations, and the appropriate one must be chosen for each case.


By definition, the paths between the different domains in a CDC circuit are not accurately described by the default synchronous relationship between clocks - an exception is needed. The question is "which exception"?


Where I disagree with Sanjay is the idea that this is a "choice". Like using the proper CDC circuit, you must use the correct exceptions for the CDC. Sometimes more than one type of exception is "good enough", but in other cases, you must use the right one. Using the wrong one can result in the CDC being underconstrained and hence causing system failure. As an example, see message 4 of this forum post - this example shows why a set_clock_groups or set_false_path constraint on a Gray code CDC can cause system failure. Only a set_max_delay -datapath_only is a correct constraint for this clock crosser.


It is also important to understand that there is an inherent priority for constraints. The set_clock_groups and set_false_path commands have inherently higher priority than other exceptions (notably set_max_delay -datapath_only). So, if you do a set_clock_groups or a set_false_path between clocks there is no way to add any other constraints on paths between these domains. This will even do nasty things like override the (corrrect) clock crossing constraints put in place by Xilinx clock crossing FIFO IP. For a more in-depth analysis of the impact of this, take a look at message 3 of this post.