Draft preview. Lessons are being written and reviewed.

Digital Design / Module 10: Clock domain crossing

56. Multi-bit transfers and handshakes

See why synchronizing each bit breaks a bus.

Lecture coming soon

Lab: Clock crossing lab

The idea

Suppose you put a two-flop on each bit of a bus. Each bit can settle independently, so when several bits change together, some may show up a cycle later than others. For a moment, the destination holds a value that the source never sent.

For example, a bus changing from 0111 to 1000 flips all four bits. If some arrive early, the destination may briefly read 1111, or 0000, or anything else in between. That's why this approach is wrong for general data.

Solution 1: request and acknowledge

Don't synchronize the data. Hold it stable, and synchronize a single control signal.

  • Source places data on the bus, then raises req.
  • Destination synchronizes req and sees it high. The data has been stable for cycles now, so it's safe to capture.
  • Destination raises ack.
  • Source synchronizes ack, drops req, and can change the data on a later cycle.
// Source side
always_ff @(posedge clk_a)
  if (!rst_a_n) req <= 1'b0;
  else if (send && !req && !ack_sync) begin
    data_hold <= data;
    req <= 1'b1;
  end else if (ack_sync) begin
    req <= 1'b0;
  end

This is safe at any clock ratio, but each transfer takes many cycles.

Solution 2: Gray code

If the value only ever steps by one, like a counter, then Gray encoding guarantees that one bit changes at a time. See the next lesson.

Solution 3: an async FIFO

This is the right answer when you need continuous data at speed. Its memory holds stable data while the Gray-coded pointers do the crossing.

How to choose

SituationUse
Single level signalTwo-flop synchronizer
Single pulseToggle synchronizer
Occasional multi-bit valueRequest and acknowledge
Counter or pointer
Streams of dataAsync

Common mistake

Believing that matching wire lengths would make all the bits arrive together. Even with perfect routing, each synchronizer's first flop resolves independently, which can create the of a whole cycle. Static timing can't save you here.

Key ideas

  • Bits may settle in different destination cycles.
  • Hold data stable and synchronize a single control signal.
  • Request-acknowledge round trips add latency.

Your exercise

Design a req/ack transfer of a 16-bit bus.

Interview questions

coreCan you put a 2-flop sync on each bus bit?

basicGive the usual crossing method for each case: a single level, a single pulse, an occasional multi-bit value, a counter, and a continuous data stream.

basicIn a request and acknowledge transfer, why does the data bus not need its own synchronizers?

coreSoftware writes a 32-bit configuration register in clk_a, and the value is used in clk_b. It changes rarely. How would you cross it?

coreA request and acknowledge handshake takes about 11 source cycles per word on a 100 MHz source clock. The application needs 50 million words per second. Is the handshake suitable?

coreA colleague says the per-bit synchronizer problem on a bus can be fixed by matching every wire length exactly. Is that right?

coreReview the source side of this handshake. What can go wrong if send stays high for several cycles?

always @(posedge clk_a)
  if (!rst_a_n) req <= 1'b0;
  else begin
    if (send) data_hold <= data;            // loads whenever send is high
    if (send && !req && !ack_sync) req <= 1'b1;
    else if (ack_sync)             req <= 1'b0;
  end

advancedA four-phase handshake needs request up, acknowledge up, request down and acknowledge down for every word. How can you make it faster without changing the safety argument?

advancedIn a hold-and-signal transfer, which timing assumption about the data wires must still hold, and how is it usually checked?

Practise these 9 questions with animations