Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
11 years ago

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:

  1. 0C:001100

  2. 0D:001101

  3. 0F:001111 (bit 1 transitions)

  4. 0E:001110 (bit 0 transitions)

  5. 0A:001010 (bit 2 transitions)

Sequence seen by rdptr_wclk_s1:

  1. 0C:001100

  2. 0D:001101

  3. 0C:001100 (bit 0 transitions)

  4. 0A:001010 (bit 1 and 2 transition)

  5. 0A:001010 (no transitions)

The sequence (2)0D to (3)0C at rdptr_sclk_s1 may look correct (only 1 bit changed), but this actually is a -1 step of the code, rather than a +1 step.

Note that this is NOT a metastability problem. The problem occurs because the fitter has placed rdptr_wclk_s1[1] far from rdptr_rclk[1] while placing rdptr_wclk_s1[0] right next to rdptr_rclk[0]. Also, this problem is build dependent. One build may have the problem, but it may disappear with the build the next day. And the same build may work on one board (slightly faster FPGA, available to me on my test floor) but have errors on a different one (slow FPGA on the customer's system).

In the tools for a different FPGA vendor, I am able to specify a DATAPATHONLY requirement of 1ns on the nets going into rdptr_wclk_s1, telling the placement tool to place the rdptr_wclk_s1 flops no further than 1ns away from the rdptr_rclk flops. But I have not found any way to do this with the Quartus tools.

The best that I am able to do is to create a logic-lock region around my fifo (or just my synchronizer), but this is an afterthought process, and can be forgotten when new fifos are added to a design. I would really like something that I can put into my code or into my constraints that will handle this for any of my fifos in my design.

Is there a different way of constraining this to force the fitter to place these 3 sets of flops near each other?

Or is there a different way of coding this to be more tolerant of the placement?

18 Replies