The two-flop synchronizer and what it cannot carry

Know what one two-flop synchronizer carries safely, see a multi-bit count torn by four of them, and carry the count with a Gray code instead.

Lesson
R6.2
Time
30 min
Runs on
Your browser
Check
5 questions

A 4-bit count crossed into another clock domain through four two-flop synchronizers, one per bit. When the count went from 0111 to 1000, the receiver read 1100 for one cycle, a value the counter never held. Every synchronizer worked; the bus did not.

A binary count read through four synchronizersEdges 9 to 14 of the lab bench, sampled on clk. x is the bus as the sender drives it; rx is the count after the synchronizers.
012345clkx01100111100010011010rx5671289
  1. Cycle 2

    Edge 11: the sender has just gone from 0111 to 1000. All four bits changed at once.

  2. Cycle 3

    Edge 12: the receiver reads 12, binary 1100. Bits 0, 1 and 3 arrived in time for edge 11; bit 2 did not.

  3. Cycle 4

    Edge 13: a cycle later every bit agrees again, and the receiver reads 8.

Measured on the lab bench. The receiver lags the sender by its two synchronizer flops, so a sample taken at edge 11 shows at edge 12.

Why the bits land in different cycles

In silicon, the four bit wires never arrive at exactly the same instant: wires differ in length and load. The lab bench models that by giving each bit wire its own delay, 0.4, 1.2, 2.8 and 2.0 ns. No metastability model is needed. Simulator note: zero-delay RTL never shows metastability, and the four wire delays are a model of skew, not measured silicon.

One synchronizer per bitArrows show the bit wires from the sender's flops to the receiver. Each synchronizer decides alone in which clk cycle its bit changes.
One synchronizer per bitArrows show the bit wires from the sender's flops to the receiver. Each synchronizer decides alone in which clk cycle its bit changes. clk_a count_tx 0111 to 1000 at112.75 ns clk bit 0 sync arrives 113.15 ns clk bit 1 sync arrives 113.95 ns clk bit 3 sync arrives 114.75 ns clk bit 2 sync arrives 115.55 ns clk count_q reads 1100 at edge12 0.4 ns1.2 ns2.0 ns2.8 ns
  1. Three bits make edge 11

    Edge 11 of clk is at 115 ns. Bits 0, 1 and 3 have their new values by then.

  2. Bit 2 misses it

    Bit 2 arrives at 115.55 ns, after the edge, so its synchronizer still holds the old 1.

  3. A torn value

    The receiver sees new 1, 0, 0 for bits 3, 1, 0 and old 1 for bit 2: 1100. The count never held 12 here.

Read the diagram as text
  • count_tx: 0111 to 1000 at 112.75 ns
  • bit 0 sync: arrives 113.15 ns
  • bit 1 sync: arrives 113.95 ns
  • bit 3 sync: arrives 114.75 ns
  • bit 2 sync: arrives 115.55 ns
  • count_q: reads 1100 at edge 12
  • count_tx → bit 0 sync: 0.4 ns
  • count_tx → bit 1 sync: 1.2 ns
  • count_tx → bit 3 sync: 2.0 ns
  • count_tx → bit 2 sync: 2.8 ns
  • bit 0 sync → count_q
  • bit 1 sync → count_q
  • bit 3 sync → count_q
  • bit 2 sync → count_q

Times from the lab bench. With metastability added, the same thing can happen even with perfectly matched wires: each first flop settles on its own.

A two-flop synchronizer gives a bit a clean value and a known timing in the new clock. It cannot say in which receiving cycle the change lands. For one bit that does not matter. For several bits it means each may land in a different cycle, and the receiver reads a mixture of old and new: a torn value.

What may cross a two-flop synchronizer

SignalThrough one two-flop synchronizer per bit?Why
One bit that holds each value for at least two receiving cyclesyesthe job it was built for
A multi-bit binary count or busnobits that change together can land in different cycles
A Gray-coded count that steps by oneyesonly one bit changes per step, so a read is the old or the new value
A bus held still, with a synchronized valid flagonly the flagthe data bits never change while they are read (the multi-cycle path scheme)
A pulse shorter than the receiving periodnoan edge may never see it (R6.3)

A synchronizer settles one bit. Everything else is about making sure only one bit is uncertain at a time.

  1. One bit per synchronizer

    Never put two bits that change together through separate synchronizers and use them as one value.

  2. No logic between the two flops

    Logic there eats the settle time of the first flop, which is the whole point of the second.

  3. Send from a flop

    Drive the crossing wire straight from a flop in its own domain. Logic there can glitch, and every glitch is another chance to be caught mid-change.

  4. Name the synchronizer

    Put the flops in one module, such as sync2, so reviewers and CDC tools can find every crossing.

Gray code: one bit per step

A Gray code orders the numbers so that each step changes exactly one bit. Binary to Gray is g = b ^ (b >> 1). Gray back to binary takes, for each bit i, the XOR of Gray bits i and above.

Binary on the wires

assign count_x = cnt;

The step from 0111 to 1000 changes four bits; the bench reads 12 once.

Gray on the wires

logic [3:0] cnt, cnt_d, gray;
assign cnt_d = cnt + 4'd1;
always_ff @(posedge clk_a or negedge rst_n)
  if (!rst_n) begin cnt <= 4'd0; gray <= 4'd0; end
  else        begin cnt <= cnt_d; gray <= cnt_d ^ (cnt_d >> 1); end
assign count_x = gray;               // one bit changes per step

The same step, 0100 to 1100, changes one bit; the bench reads no torn value.

The Gray value is computed from cnt_d, the next count, and registered, so the wires change once per step from a flop. The receiver converts back after the synchronizers.

The receiver's decode, in place of assign count_q = s2;.
always_comb                          // Gray to binary: bit i is the XOR of bits 3..i
  for (int i = 0; i < 4; i++) count_q[i] = ^(s2 >> i);
MeasuredBinaryGray
Torn values in 24 edges on the lab bench1 (12 at edge 12)0
Synthesis of count_cdc on this flow18 cells, 12 flops, 3 levels of logic29 cells, 15 flops, 3 levels of logic

The Gray sender has 7 flops, not 8: the top Gray bit equals the top count bit, and the synthesizer merged the two flops.

Gray works for a count that steps by one through a power-of-two range. A count that jumps, or wraps at another value, changes several bits at once again.

Design brief
PortDirectionWidthMeaning
clk_ainput1the sender's clock
clkinput1the receiver's clock
rst_ninput1reset, asynchronous, active-low, both domains
count_qoutput[3:0]count_q shows the sender's count, two or three clk cycles late, and never a value the count did not hold
Clock
clk_a 13 ns for the sender, clk 10 ns for the receiver, unrelated. Crossing: one two-flop synchronizer per bit; the wires are driven from flops
Corner cases
steps that change several binary bits: 0111 to 1000, and 1111 to 0000

The lab ships with binary on the wires. Run it and find the TORN line. Then replace the body of count_tx with the Gray version, and assign count_q = s2; with the decode. Run again: torn=0.

Asked in interviews
  1. Can you synchronize a bus bit by bit?

    Only if at most one bit changes at a time, as in a Gray count. Otherwise each synchronizer may take its change in a different cycle and the receiver reads a torn mixture of old and new bits.

  2. What may cross a two-flop synchronizer?

    One bit that changes slowly compared with the receiving clock: a level held for at least two receiving cycles. Multi-bit values need Gray coding, a synchronized valid flag over held data, a handshake or a FIFO.

  3. Why no logic between the synchronizer flops?

    The first flop may be metastable, and the second flop exists to give it a whole period to settle. Logic between them takes part of that period and shrinks the MTBF exponentially.

Try it

Each line is printed 1 ns after an edge of clk. rx steps by one or repeats, because clk is faster than clk_a, except at edge 12. The 15 to 0 step at edge 23 also changed four bits, but on this bench no edge fell between their arrivals.

With the Gray sender and the decode, the wires show 0100, 1100, 1101 around the same edges, and the last line reads torn=0. Synthesize both: 18 cells and 12 flops, then 29 cells and 15 flops.

RTLIcarus Verilog · in your browser · Lint: Verilator service · Synthesize: Yosys service
Edit the design, then Run, Lint or Synthesize
Expected: e0 x=0000 rx=0 e1 x=0000 rx=0 e2 x=0001 rx=0 e3 x=0010 rx=1 e4 x=0010 rx=1 e5 x=0011 rx=2 e6 x=0100 rx=3 e7 x=0101 rx=4 e8 x=0101 rx=5 e9 x=0110 rx=5 e10 x=0111 rx=6 e11 x=1000 rx=7 e12 x=1001 rx=12 TORN e13 x=1001 rx=8 e14 x=1010 rx=9 e15 x=1011 rx=10 e16 x=1100 rx=11 e17 x=1100 rx=11 e18 x=1101 rx=12 e19 x=1110 rx=13 e20 x=1111 rx=14 e21 x=1111 rx=15 e22 x=0000 rx=15 e23 x=0001 rx=0 e24 x=0010 rx=1 e25 x=0011 rx=2 torn=1

Original design · 18 cells · 12 flip-flops · 0 latches · 3 logic levels. Synthesis uses Yosys; counts depend on the toolchain.

Read included file: tb.sv
`timescale 1ns/1ps
module tb;
  logic clk = 0;
  always #5 clk = ~clk;                   // the sampling clock: rising edges at 5, 15, 25, ... ns
  logic clk_a = 0;                      // the second clock: rising edges at 8.75, 21.75, ... ns
  initial begin
    #8.75;
    forever begin clk_a = 1; #6.5; clk_a = 0; #6.5; end
  end
  logic rst_n = 1;                        // falls at 1 ns: a real edge, so asynchronous resets act before any clock
  int edge_n = -1;                        // the number of the last rising edge of clk
  always @(posedge clk) edge_n = edge_n + 1;
  logic [3:0] count_x;                    // the bus as count_tx drives it
  wire  [3:0] count_w;                    // the same bus at count_rx: each bit wire has its own delay
  logic [3:0] count_q;
  assign #0.4 count_w[0] = count_x[0];
  assign #1.2 count_w[1] = count_x[1];
  assign #2.8 count_w[2] = count_x[2];
  assign #2.0 count_w[3] = count_x[3];
  logic [3:0] last = 0;                   // the last value that was not torn
  int torn = 0;
  count_tx u_tx (.clk_a(clk_a), .rst_n(rst_n), .count_x(count_x));
  count_rx u_rx (.clk(clk), .rst_n(rst_n), .count_x(count_w), .count_q(count_q));
  // Advance to the next rising edge of clk, wait for the design to settle, then print.
  task automatic tick();
    @(posedge clk); #1;
    if (count_q != last && count_q != last + 4'd1) begin
      torn++;                             // a value the count never held at this point
      $display("e%0d x=%b rx=%0d TORN", edge_n, count_x, count_q);
    end
    else begin
      $display("e%0d x=%b rx=%0d", edge_n, count_x, count_q);
      last = count_q;
    end
  endtask
  initial begin
    #1 rst_n = 0; tick(); tick(); rst_n = 1;   // asserted at 1 ns, clk edges 0 and 1 in reset, released 1 ns after edge 1
    repeat (24) tick();
    $display("torn=%0d", torn);
    $finish(0);                             // quiet: no "$finish called" line
  end
endmodule

Check yourself

Predict the outputThe program prints the 3-bit Gray code of 0 to 7, in order, on one line. What does it print?
module tb;
  function automatic logic [2:0] gray(input logic [2:0] b);
    return b ^ (b >> 1);
  endfunction
  initial begin
    $display("%b %b %b %b %b %b %b %b", gray(0), gray(1), gray(2), gray(3), gray(4), gray(5), gray(6), gray(7));
    $finish(0);
  end
endmodule
Spot the bugEach option codes a 4-bit count before it crosses (enc, registered in the sender) and decodes it after two flops per bit (dec). The bench delays each bit wire differently and flags any torn value. Which option fails?
Read the waveformThis is the lab bench with binary on the wires, edges 10 to 13. What happened at edge 12?
Binary count, sampled on clk
0123clkx011110001001rx67128

One value per edge, printed 1 ns after it, from edge 10 on the left. rx lags x by the two synchronizer flops.

ConceptWhat does a two-flop synchronizer guarantee about one bit that changes from 0 to 1?
ConceptA 16-bit configuration value crosses into another domain. It changes rarely, and the sender can hold it still for many cycles. Which crossing is safe?

Remember

  • A two-flop synchronizer carries one bit that holds each value for at least two receiving cycles; the change lands in a cycle it cannot predict.
  • Bits of one value through separate synchronizers can land in different cycles and read as a torn mixture of old and new.
  • A Gray count changes one bit per step, so each read is the old or the new value; it needs steps of one through a power-of-two range.
  • One bit per synchronizer, no logic between its flops, the crossing wire driven from a flop, and the synchronizer in a named module.