Unlike Xilinx which provides their users with a set of convenient xpm_cdc_* modules, Lattice does not seem to have “the standard” way for clock domain crossing. So I have no choice other than write my own synchronizer.

In the Xilinx's world I'd use the ASYNC_REG and KEEP_HIERARCHY attributes to prevent the synthesizer from making destructive optimizations, and write some set_false_path constraint alongside.

It looks like /* synthesis syn_hier="hard" */ is the same thing as KEEP_HIERARCHY, but, unfortunately, I found no mention of ASYNC_REG in the Lattice docs.

What attribute (or combination of attributes) should I use instead?

  • \$\begingroup\$ You can't just run it through two flip-flop registers? Or is this on a different level than I am used to working with? I don't even know what these xpm_cdc_ modules you that you are referring to when you cross clock domains. \$\endgroup\$
    – DKNguyen
    Feb 14, 2022 at 21:44
  • \$\begingroup\$ What is your signal? Is it serial or parallel? Can’t you just put 1-bit shift registers when you cross domains. Usually you set the depth to 2. That’s what I’ve used and the synthesizer doesn’t touch them. Isn’t ASYNC_REG for simulation anyway? \$\endgroup\$
    – user110971
    Feb 14, 2022 at 21:52
  • 1
    \$\begingroup\$ @user110971: It's not for simulation only. Citing the UG912: “With the ASYNC_REG property, the registers are grouped so that they are placed as closely together as possible.” \$\endgroup\$ Feb 14, 2022 at 21:58
  • \$\begingroup\$ @dknguyen, the 2-DFF thing is fine for passing levels between domains but what about passing 1-CLK pulses into a slower clock domain (e.g. 100 MHz pulse to 6 MHz domain), or passing a 32-bit data bus without misalignment (some bits 1-CLK delayed) or metastability or wasting FIFO RAM on it. I've written my own to do these with full handshaking. Xilinx offer similar stuff, though I've not looked at them in donkey's years. \$\endgroup\$
    – TonyM
    Feb 14, 2022 at 22:47
  • 1
    \$\begingroup\$ @DKNguyen, you're forgetting that we're talking about domain crossing, not within a single domain... (I agree - go to chat if you're still not getting this, we'll get through it swiftly enough). \$\endgroup\$
    – TonyM
    Feb 14, 2022 at 23:41

1 Answer 1


Write the flop synchronizer for CDC in plain RTL.

In Xilinx, ASYNC_REG attribute makes sure that the flops in the synchronizer chain are packed and placed as close as possible for the best MTBF, by placing most probably into the same slice.

Not familiar with Lattice, so I am not sure whether it has an equivalent attribute as in Xilinx, but would like to throw some light here.

Lattice seems to support SDC constraints. So what you can do is somehow force the synthesiser to place these flops together by enforcing a tight timing requirement between flop datapaths; by using set_max_delay constraint between their Q and D pins, i.e, on their datapath.

For e.g., for a two-flop synchronizer (F1-F2), it looks something like this (syntax may vary across tools):

set_max_delay 2 -from [get_pins {F1/Q}] -to [get_pins {F2/D}]

This is recommended in Xilinx as well, but it recommends its own flavor of SDC (XDC) which supports custom flag -datapath_only to ignore skews while analyzing these timing paths.

For an n-flop synchronizer, you would have to use n-1 constraints like above.

Also I would recommend to use the syn_keep attribute for the registers used in the synchronizer chain to avoid any possible optimizations.


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.