← RTL Design Interviews

Interview Question · Concept

Carrying a Multi-Bit Value Across Clock Domains

L1 JuniorL2 Mid-Levelcdcmulti bitsynchronizers
  • Likely at Micron

The Question

The interviewer asks

A 32-bit value and its valid must cross into another clock domain. What are your options, and why not put two flops on every bit?

Reveal the Model Answer

Each bit's synchronizer can resolve on a different destination edge, so the destination can see a mix of old and new bits. Instead, hold the data stable and synchronize only a control signal. That can be a valid level, a toggle, or a request and acknowledge. For counters that step by one, Gray-code the value. For a stream of words, use an asynchronous FIFO.

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.

SchemeBest forCost
Held data plus a synchronized enableAn occasional wordThe source must hold the data for several destination cycles
Request and acknowledgeWords whose arrival the sender must knowA round trip of synchronizer delays per word
Gray-coded valueCounters and pointers that step by oneOnly works for steps of one
Asynchronous FIFOA stream of wordsMemory plus Gray pointers

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Junior)

A good junior answer explains why the bits can land on different destination edges. It then offers a fix: hold the data and synchronize only an enable.

Cross-Questions

Where the interviewer goes next. Open a node to practise that follow-up.

WhyWhat IfProve ItCompare

  1. Carrying a Multi-Bit Value Across Clock Domains
  2. WhyWhy Bits Changed Together Can Arrive ApartL1 Junior

    Why can two bits that changed at the same source edge be seen on different destination edges?

    Reveal the Model Answer

    The bits reach their flops at slightly different times, because wires and gates differ. If a destination edge falls between those times, one flop sees the new value and another the old. A flop that goes metastable can also resolve either way. Nothing ties one bit's synchronizer to another's.

    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.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "Bits launched by one flop bank always arrive together."

    They leave together, but each path has its own delay. The destination clock is unrelated, so an edge can fall between them.

    Source Mehta, ASIC/SoC Functional Design Verification (2018) ch. 8.3.4 Multi-bit synchronization

  3. WhyWhy One Flop Is Not EnoughL0 FresherL1 Junior

    Why is a single flop not enough to synchronize an asynchronous input?

    Reveal the Model Answer

    With one flop, logic reads its output in the same cycle that it might be metastable. Only the slack left after that logic is available for settling. Different gates can even read the same hanging value as different levels. The second flop isolates that settling from all other logic, and gives it about a full period.

    The Model's Bad Cycle, One FlopEdges 11 to 17 of a simulator bench with a metastability model, sampled on clk. One value per edge, printed 1 ns after it.
    0123456clkstatuss1idle_qbusy_q
    1. Cycle 2

      Edge 13: s1 has been 1 since edge 12, and the machine is in BUSY.

    2. Cycle 3

      Edge 14: status fell 0.70 ns before the edge. The model makes s1 metastable; it settles to the old 1, after 3 ns.

    3. Cycle 4

      Edge 15: 3 ns is more than the 2 ns the state flops can wait. Each reads its own bit, and both read 0: the bench prints ILLEGAL.

    4. Cycle 5

      Edge 16: s1 is a clean 0 again and the machine recovers into IDLE.

    Over 2000 edges of this bench, s1 went metastable 90 times. The state flops read it unsettled 22 times, and the machine was in an illegal state for 12 edges.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "One flop is enough if the clock is slow."

    A slower clock helps MTBF. But logic after a single flop still reads a possibly unsettled value. The exponent depends on the time left after that logic.

  4. What IfWhen One Period Is Not Enough Settling TimeL1 JuniorL2 Mid-Level

    What if the clock is so fast that one period is not enough settling time? What do you change?

    Reveal the Model Answer

    MTBF grows exponentially with settling time, so a third flop adds a full period and multiplies MTBF. Compute MTBF from the flop's library data at the worst corner. Add stages until it is far beyond the product lifetime. Each stage also adds one cycle of latency to the crossing.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "A third flop is always safer, so use three everywhere."

    Every stage adds a cycle of latency to each crossing. Add it only where the MTBF calculation needs it.

  5. CompareHeld Data with an Enable vs an Asynchronous FIFOL2 Mid-Level

    Compare a held-data crossing with an asynchronous FIFO. When do you pick each?

    Reveal the Model Answer

    With held data, the source holds the word while an enable crosses, so one word takes several destination cycles. It is small and suits configuration or occasional events. An asynchronous FIFO writes a word every source cycle and crosses only Gray-coded pointers. It suits streams, at the cost of memory and pointer logic.

    Self-Check

    Tick each key point you covered in your answer.

    Wrong Altitude

    "An asynchronous FIFO is always the safe choice."

    It is safe, but for one control word it costs memory, two pointer synchronizers and full and empty logic. A held word with an enable is enough.

  6. Prove ItSolve R6.6.1: The Asynchronous FIFOSimulator-graded

    Show it, don't just say it: this Silicon Lab problem is graded by the simulator.

    Open Problem R6.6.1

  7. CompareGray-Code Pointers vs a Handshake for Multi-Bit CrossingsL2 Mid-Level

    Compare a Gray-coded value with a request/acknowledge handshake for carrying a multi-bit value across clock domains.

    Reveal the Model Answer

    Gray code changes one bit per step, so a value caught mid-change is either the old or the new one. It suits counters that move by one, such as FIFO pointers. A handshake holds any data stable while a synchronized request crosses and an acknowledge comes back. It carries any value, but takes several cycles per transfer. Choose Gray for streaming pointers, and a handshake or FIFO for occasional wide data.

    The Same Edges with Gray PointersAn asynchronous FIFO with Gray-coded pointers, sampled at the read clock clk.
    012345clkwptr10011000000000010011seen10111001100000000001read_levelholds232empty
    1. Cycle 1

      Edge 21: empty is 1 while the FIFO holds two words. The read side has not seen those writes yet.

    2. Cycle 2

      Edge 22: the pointer steps from 1000 to 0000, one bit, the Gray wrap from 15 to 0.

    3. Cycle 4

      Edge 24: read_level is 1 with two words held. Low is safe; the read side just waits a cycle.

    Edges 20 to 25 of the run: the level the read side sees never rises above the words held.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Gray-code any multi-bit bus and it crosses safely."

    Gray only helps when consecutive values differ by one step. Arbitrary data jumps change many bits at once.

    Source Cummings, CDC Design and Verification Techniques (SNUG Boston 2008) §5.7.2-5.7.4; Mehta, ASIC/SoC Functional Design Verification ch. 8.3.4 (p155-156)

  8. Prove ItSolve R6.4.2: The Torn PointerSimulator-graded

    Show it, don't just say it: this Silicon Lab problem is graded by the simulator.

    Open Problem R6.4.2

  9. What IfWhen the Two Clocks Are RelatedL2 Mid-LevelL3 Senior

    Asked if they say the two clocks come from one PLL

    What if both clocks come from the same PLL, for example 100 MHz and 50 MHz with fixed phase?

    Reveal the Model Answer

    Then the crossing is synchronous, and STA can time it, because the edges have a known relation. No synchronizer is needed if the paths meet timing on the tightest edge pair. Declare the clocks as related, never as asynchronous groups. If the relation is not guaranteed, for example after a clock switch, treat it as asynchronous.

    Self-Check

    Tick each key point you covered in your answer.

    Tool as Truth

    Puts the related clocks in asynchronous clock groups "to be safe".

    That removes timing between them. A real synchronous path then goes unchecked, and it has no synchronizer either.

    Source Lesson L2.6, measured with OpenSTA 3.1.0: a FIFO path between related 10 ns and 7 ns clocks is timed; set_clock_groups -asynchronous removes timing between the groups

  10. Prove ItSolve R6.4.2: The Torn PointerSimulator-graded

    Show it, don't just say it: this Silicon Lab problem is graded by the simulator.

    Open Problem R6.4.2

What People Get Wrong

Common wrong answers and why they fail. Run It shows the wrong belief failing on a real simulator.

Misconception

"Send the data through two flops per bit, with the valid through two flops too. They take the same path, so they stay aligned."

Each flop samples its own wire, and wires settle at different times. The twin catches one bit early and three late, and takes 1111 for 1000.

Run It

Bit 3 settles 0.2 ns after a change and bits 2 to 0 after 1.0 ns; the change lands 0.5 ns before a destination edge.

RUNIcarus Verilog · in your browser
Edit the code, then run it
Expected: source sent 1000, destination took 1111 FAIL: two flops per bit do not keep a bus aligned; bits caught on different edges made a word never sent
See the Fix
`timescale 1ns/1ps
module tb;
  logic dclk = 0;
  always #5 dclk = ~dclk;                       // destination edges at 5, 15, 25, ...
  logic [3:0] tx_data = 4'b0111;
  logic tx_valid = 0;
  // Wires from the source flops: bit 3 arrives 0.2 ns after a change, bits 2..0 after 1.0 ns.
  logic [3:0] wire_data;
  logic wire_valid;
  assign #0.2 wire_data[3]   = tx_data[3];
  assign #1.0 wire_data[2:0] = tx_data[2:0];
  assign #0.2 wire_valid     = tx_valid;
  logic s1_v = 0, s2_v = 0, s3_v = 0;
  always_ff @(posedge dclk) begin s1_v <= wire_valid; s2_v <= s1_v; s3_v <= s2_v; end
  logic [3:0] got;
  // The fix: only valid is synchronized. Data is held stable by the source and
  // read directly when the synchronized valid rises.
  always_ff @(posedge dclk) if (s2_v && !s3_v) got <= wire_data;
  initial begin
    #14.5 tx_data = 4'b1000; tx_valid = 1;      // 0.5 ns before a destination edge
    #50 $display("source sent 1000, destination took %b", got);
    if (got == 4'b1000) $display("PASS: data held stable while the enable crossed, so every bit was settled");
    $finish;
  end
endmodule

Prints: source sent 1000, destination took 1000 PASS: data held stable while the enable crossed, so every bit was settled

Half-Truth

"Match the wire lengths and the skew goes away."

Even equal wires leave each flop to resolve metastability on its own. One bit can still land a cycle later than the others.

Source Mehta, ASIC/SoC Functional Design Verification (2018) ch. 8.3.4 Multi-bit synchronization

Half-Truth

"Gray-code the 32-bit value."

Gray code helps only when each new value differs from the last by one step. Arbitrary data changes many bits at once.

Source Golson, Synchronization and Metastability (SNUG Silicon Valley 2014) §5.2 Gray code

Learn It, Then Prove It