Sign in on the Premium hub to save lesson progress. This lesson is open to read so the page is a complete article, not an empty login wall.
Lesson 3.3

Convert then register — the XOR-order trap

Gray code only helps if the CDC net is a registered Gray bus. If Gray is combinational from a binary counter that is also transitioning, multiple Gray bits can glitch in one window — you are back to the binary problem.

1

Correct pattern

registered gray
wire [PTR_W-1:0] bin_next  = bin + wr_fire;
wire [PTR_W-1:0] gray_next = bin_next ^ (bin_next >> 1);

always @(posedge wr_clk or negedge wr_rst_n) begin
  if (!wr_rst_n) begin
    bin  <= {PTR_W{1'b0}};
    gray <= {PTR_W{1'b0}};
  end else begin
    bin  <= bin_next;
    gray <= gray_next;   // flop the Gray word, then 2FF-sync gray
  end
end

The net that leaves the write domain is gray, a flop output. Between wr_clk edges it is stable (ignoring clock-to-q). At most one Gray bit differs from the previous registered value.

2

Broken pattern — XOR after the fact, combinational CDC

do not ship
always @(posedge wr_clk) bin <= bin + wr_fire;
wire [PTR_W-1:0] gray_combo = bin ^ (bin >> 1);
// then 2FF-sync gray_combo  ← CDC on combinational XOR

When bin bits have different clock-to-q, the XOR cloud produces intermediate Gray values with two or more bits flipping during the settling window. Sampling that cloud is the same class of bug as sampling binary.

A related silicon bug: registering binary, then registering Gray from the old binary in a way that two Gray bits update on one cycle at a specific count (for example 7→8) because the XOR was applied to a mis-aligned copy of the counter. Check with $onehot0(gray ^ $past(gray)) on the exact net you synchronize.

3

Waveform — glitch vs clean

Synthesis

Mark the 2FF pair (* ASYNC_REG = "TRUE" *) (Xilinx) / equivalent dont_touch. Do not let retiming insert logic between the Gray flop and the first synchronizer flop.

4

Constraints on the Gray bus

Set a false path or a max-delay path from the Gray source flop to the first synchronizer flop. A false path says timing does not have to close at the destination period — correct because you accept metastability on FF1. Some teams prefer a set_max_delay of one destination period minus setup to keep the bus from being absurdly routed. Do not false-path the whole FIFO; RAM and local pointer math must still meet their clocks.

5

Skew between Gray bits

Even registered Gray can have bit-to-bit routing skew on a wide pointer. Because only one bit is meant to toggle, skew just moves when that one bit arrives. If two bits toggled, skew would recreate reconvergence. Unit-distance is what makes moderate skew safe. Still keep the Gray bus compact in the floorplan: source flops next to the synchronizer bank.

Q

Frequently asked questions

What is the XOR-order trap?

If you CDC a combinational Gray XOR of a binary counter, bits glitch while the counter’s clock-to-q skews. Sample that cloud and you can capture multi-bit changes. Convert bin_next to gray_next, then register Gray, then 2FF the flop output.

Which net gets ASYNC_REG?

The first synchronizer flop in the destination domain, and usually the second. Do not retime logic between the source Gray flop and FF1.

What SVA catches a bad Gray register?

$onehot0(gray ^ $past(gray)) on the exact source register that feeds the CDC, every source clock, excluding reset.

Can you register binary and Gray in different always blocks?

Yes if both use the same next-state. Prefer one block so they cannot diverge. Never Gray-encode the *old* binary while incrementing a separate copy.

Key takeaways

  • Compute gray_next from bin_next, register both.
  • Synchronize the flopped Gray, never the XOR cloud.
  • Assert unit-distance on the CDC source register.