Forum Discussion
How do I synchronize gray code counts safely across asynchronous clock domains?
I have a design for a generic asynchronous FIFO that I have used for many years. In this FIFO, I use gray code counters for the read and write pointers to the core memory. These multi-bit pointers must be synchronized to the opposite clock domains to compute full and empty flags (e.g. rdptr is synchronized to wclk to compare against the wptr to determine the full flag.) I am using 2-stage flops in the synchronizer to reduce the metastability.
The problem that I am seeing has to do with the placement (Quartus Fit) of the original gray code pointer in domain 1 and the the first set of flops in the synchronizer in domain 2. For clarity: reg [w:0] rdptr_rclk; // rdptr in the rclk domain always @ (posedge rclk) begin rdptr_rclk <= nx_rdptr_rclk; end reg [w:0] rdptr_wclk_s1; // first stage synchronize of rdptr into the wclk domain reg [w:0] rdptr_wclk; // second (final) stage synchronize of rdptr into the wclk domain always @ (posedge wclk) begin rdptr_wclk_s1 <= rdptr_rclk; rdptr_wclk <= rdptr_wclk_s1; end In the sdc file, I have set_false_path between rclk and wclk. Ideally, all 3 of these synchronizer stages (rdptr_rclk, rdptr_wclk_s1, and rdptr_wclk) would be placed by the fitter as closely together as possible. However, the fitter wants to place the rdptr_rclk flops on one side of the fifo, close to where the empty flag is generated and used, and it wants to place the rdptr_wclk flops on the opposite side of the fifo, close to where the full flag is generated and used. The other register, rdptr_wclk_s1, usually will get placed right next to the rdptr_wclk. The problem occurs when some of the bits of rdptr_wclk_s1 are placed close to their rdptr_rclk counterpart, while other bits are placed far apart, especially when the skew between bits approaches or exceeds the period of the 2 clocks. In this case, the rdptr_wclk_s1 can see a transition on one bit before it sees the earlier transition on a different bit. For example: Correct rdptr_rclk sequence:- 0C:001100
- 0D:001101
- 0F:001111 (bit 1 transitions)
- 0E:001110 (bit 0 transitions)
- 0A:001010 (bit 2 transitions)
- 0C:001100
- 0D:001101
- 0C:001100 (bit 0 transitions)
- 0A:001010 (bit 1 and 2 transition)
- 0A:001010 (no transitions)
18 Replies
- Altera_Forum
Honored Contributor
I suggest you try set_max_delay on the paths.
By the way just curious why not use alter dc fifo? - Altera_Forum
Honored Contributor
Thank you for this suggestion kaz.
I have tried this in the past, with no success. The problem with set_max_delay is that the Quartus computation takes into account the clock insertion for both the source and the destination flops, which I don’t care about, and which can yield inaccurate results. So, for this path, the Required time is: + 1.000ns (max_delay) + 2.514ns (pll output through clock tree to receiving flop) – 0.140ns clock uncertainty + 0.228ns Tsu = 3.602ns The Arrival time is: + 3.016ns (input clock pin through clock tree to sending flop) + 0.140ns (flop output) + 3.854ns (chip route) + 0.273ns (cell route) = 7.247ns The slack is 3.602-7.247=-3.645ns. The problem is that I want to exclude the clock tree components from both halves of this computation, so that the slack would be computed only based on the actual chip route from flop-to-flop (3.854ns in this example.) In this particular example, the delta between the 2 clock delays is only 0.5ns. In other parts of the design (or build-to-build variation), the delta can be much larger. It can also have the opposite relationship (clock delay for Required is larger than clock delay for Arrival), meaning that the computed slack is too optimistic. As for using the Altera DC fifo -- I have found that it doesn't work when I compile my design using the tools from a different FPGA vendor :). I like to be able to use this one fifo in a block of shared IP that can be compiled either in Quartus or in a different vendor. Also, it is easy to change the dimensions of my fifo, without having to regenerate using the MegaWizard. - Altera_Forum
Honored Contributor
Well in that case try set_max_skew
This is something I haven't tried but reading through its description it may be just what you want as it can exclude clocks - Altera_Forum
Honored Contributor
Unfortunately, set_max_skew pays attention to the set_false_path. Because of the set_false_path, Timequest will say there is nothing to report in the "Report Max Skew Summary". The set_max_delay would be correct, but there is no way to say "datapath_only" and have it ignore the clock trees.
- Altera_Forum
Honored Contributor
--- Quote Start --- Unfortunately, set_max_skew pays attention to the set_false_path. Because of the set_false_path, Timequest will say there is nothing to report in the "Report Max Skew Summary". The set_max_delay would be correct, but there is no way to say "datapath_only" and have it ignore the clock trees. --- Quote End --- First you may remove set_false_path and ignore any reported violations or set to some high multicycle. second, this is an altera example that involves data path only: # Create a max skew constraint that includes only data path arrival set_max_skew -from [get_keepers inst1|*] -to [get_keepers inst2|*] 0.200 -exclude { from_clock to_clock clock_uncertainty } - Altera_Forum
Honored Contributor
Well, that was interesting.
I first tried using a "set_multicycle_path" AND and "set_false_path -hold" with the "set_max_skew". The false_path here, again, caused the set_max_skew to ignore the paths completely. Next I tried removing the "set_false_path -hold" but keeping the "set_multicycle_path". Now the design misses timing on the max_skew specification. Looking at the worst case path, I see that the fitter has added 13.658ns (!) of routing delay between these 2 flops (which are in the same LAB), presumably in order to meet the hold time requirement between these flops. The best case path (different fifo, different clock, but all lumped together with the single "set_max_skew") only had 8.069ns of routing delay added. I also tried to use the "-exclude" with set_max_delay, but apparently that is not supported. - Altera_Forum
Honored Contributor
you need to set mc path as say 3 for setup, 2 for hold otherwise you run into hold issues.
- Altera_Forum
Honored Contributor
Using 3 for setup and 2 for hold still didn't make it pass. However, this time, the fitter only added 5.2ns of delay between the flops.
I may be running into a different problem with the set_max_skew. I am using a single "set_max_skew" constraint that covers all of the synchronizers in this design. This means that the skew will include the wptr sync as well as the rptr sync. Plus, it will include the wptr and rptr for all of the FIFOs, not just a single one. It might work better if I can provide a constraint that is specific for each bus. How would I generically do that? - Altera_Forum
Honored Contributor
I still have not had any success with this. Are there any more ideas?
- Altera_Forum
Honored Contributor
I am not familiar with skew command. I hope somebody will help here.
I suggest you also try multicycle of 2(setup)/1 hold as well or even 1/0 (i.e. default, if you don't set false path) then ignore violations and see what sort of delay you get.