Showing results for 
Show  only  | Search instead for 
Did you mean: 
Registered: ‎11-23-2018

PhysOpt Produces CDC-1 Errors

I recently enabled phys_opt_design after place, and also after route.

I have seen the new designs complain about CDC-1 errors in CDC synchronisers. The registers have ASYNC_REG=TRUE attribute, and we have set_max_delay paths configured correctly in XDC files.

It looks like some of the phys_opt_design directives push logic between the meta-stability registers.

Good CDC syncGood CDC syncScreenshot from 2018-11-23 15-13-52.pngScreenshot from 2018-11-23 15-12-43.pngBad CDC syncBad CDC sync

Tags (2)
0 Kudos
6 Replies
Registered: ‎01-22-2015


Welcome to the Xilinx Forum!

As described in <this> post, the VHDL attribute statement did not reliably set ASYNC_REG=TRUE for a register when using older versions of Vivado (before about v2016.1).  A workaround is to use an XDC constraint like the following:   

set_property ASYNC_REG TRUE [get_cells <your_register_name>]

In versions of Vivado after v2016.2, I've found that both the VHDL attribute statement and the XDC constraint work properly. That is, for your synchronizer, both registers will be placed in the same FPGA slice and synthesis/implementation will be prevented from inserting combinational logic between the registers.

Also, when cascading reset-bridges (a synchronizer-like structure), Vivado sometimes gets confused with the ASYNC_REG property.  See <this> post for a solution.

Finally, when you look at "Cell Properties" for your synchronizer registers in the open-implemented design, do you see a check-mark beside ASYNC_REG as shown below?


0 Kudos
Registered: ‎11-23-2018

I am using Vivado 2018.2

Both registers still have ASYNC_REG=TRUE inth e final implemented design.

This module is not a cascading reset-bridge, so it shouldn't be confused. The source register in clk_A domain is a plain FF with no special attributes.

I believe the issue here is that some of the phys_opt directives ignore the ASYNC_REG attribute of FDREs during optimisation. I am still inth e process of trying to narrow down which directive it could be, and whether it happens during post-place or post-route.

Registered: ‎01-22-2015

     I believe the issue here is that some of the phys_opt directives ignore the ASYNC_REG attribute of FDREs during optimisation.

Sounds like you are on to something.  I look forward to hearing about what you find.

You might also try setting DONT_TOUCH=TRUE for the FDREs - although this should have been done automatically when you set ASYNC_REG=TRUE.


0 Kudos
Registered: ‎01-22-2015


     I believe the issue here is that some of the phys_opt directives ignore the ASYNC_REG attribute of FDREs during optimization.

Using Vivado v2018.2, I created a small project (runs through implementation in a few minutes) that contains a synchronizer. Using more than a dozen combinations of the directives for Post-Place Phys Opt Design and Post-Route Phys Opt Design, I am unable to duplicate the error you are seeing.

As you know, broken synchronizers are a big problem. Especially since many of us don’t faithfully run  report_cdc and see the CDC-1 error. 

I hope you’ll get back to us if you find what’s causing the problem.


0 Kudos
Xilinx Employee
Xilinx Employee
Registered: ‎11-28-2007


PhysOpt adding LUT1s is normally a sign that you have severe hold time (worse than -1ns WHS) failures.

Can you analyse the hold time on these paths on the post-place DCP? Does the timing report show any exceptions that apply to this path?

How does the clock interaction report look like? Do you have unsafe CDC clocks?

Have you reviewed the methodology report? What TIMING methodology messages do you have?

In general I typically recommend to set max-delay datapath-only constraints between asynchronous clocks because asynchronous clocks groups are the same as false paths and they override any max-delay constraints on specific paths like in FIFO IPs and CDC XPMs.


Having said that: the philosophy behind ASYNC_REG is that it guarantees optimal placement and implementation of 1-bit synchronizers.

Now...if you have hold failures, then even if you wouldn't have this LUT1 inserted, route_design would have added a very long net to fix the hold time, so you still would have a non-optimal synchronizer circuit.

Therefore, I think it's essential to understand why the LUT1 was inserted. Fixing physopt by not adding LUT1 on ASYNC_REG marked FFs wouldn't have solved the root-cause, so although in essence I agree you have a point, it wouldn't bring us much further.

At this time without more information, I'm not sure if we really need to fix physopt.



Best regards


Please mark the Answer as "Accept as solution" if the information provided is helpful.

Give Kudos to a post which you think is helpful and reply oriented by clicking the star next to the post.
Registered: ‎01-23-2009

I agree with @driesd - this extra LUT and long routing are consistent with the tool attempting to fix a bad hold time violation. In your original post you blame it on phys_opt_design - are you sure it's phys_opt_design and not route_design that is doing this (since hold times are fixed by route_design).

In any case, the question is why the tool thinks there is a hold time violation here. The two flip-flops are clocked by the same net and the net is driven by a BUFG_GT, so there should be no significant clock skew between these two flip-flops - in fact since they are in the same CLB there should be absolutely no clock skew between them. So the hold time violation can't be caused by clock skew.

So what else can cause hold time problems?

The most likely is a constraint problem. Normally a clock net carries only a single clock. However, it is possible through the constraint system for a net to carry multiple clocks. Under this circumstance, any path that is clocked by this net will really end up with four static timing paths. Let's say the path starts at src_ff and ends at dst_ff, and that these two FFs are connected to the same net (from a clock buffer), but the net carries two clocks; clkA and clkB - in this case there are 4 paths:

  1. src_ff@clkA to dst_ff@clkA
  2. src_ff@clkB to dst_ff@clkB
  3. src_ff@clkA to dst_ff@clkB
  4. src_ff@clkB to dst_ff@clkA

Clearly paths 1 and 2 cannot have problems - we already know there is no clock skew on them. However, if the definition of clkA and clkB have a frequency or phase difference between them such that path 3 or 4 have a hold violation, then the tool will try and fix it - in fact, it would have to fix it - in spite of the ASYNC_REG property.

So we need to see if this is the case. You can probably see if this is true using the "report_clocks" command. Or you can ask the tool directly using the command

get_clocks -of_objects [get_nets <clock_net_driving_the_two_ffs>

If there is more than one clock here, this is almost certainly your problem. In which case you have to figure out where the two clocks are coming from.

Since this is a BUFG_GT, I assume the source of this clock is a GTx. In UltraScale (but not in the 7 series) the tool will automatically generate a clock on the CLKRXOUT and CLKTXOUT based on a create_clock applied to the REFCLK pin. Since this was not done in 7 series designs, it was normal to do a create_clock on the CLKRXOUT and CLKTXOUT pins of the GTX/GTH. However, in UltraScale if you have a create_clock on both the REFCLK and on the CLKRXOUT/CLKTXOUT (and one or both of them use the -add option in the create_clock command), then you will end up with two clocks on this net - one that has no propagation delay (the create_clock on CLKRXOUT/CLKTXOUT) and one that has propagation delay through the GT (the automatically generated clock created by the tool based on the create_clock on the REFCLK). This would certainly explain the hold time fixing we are seeing here...