← RTL Design Interviews

Interview Question · Code Writing

A Four-Phase Request and Acknowledge Handshake

L2 Mid-Levelcdchandshakessynchronizers

The Question

The interviewer asks

Write a request and acknowledge handshake that carries an 8-bit word from one clock domain to an unrelated one.

Reveal the Model Answer

The sender puts the word on data, holds it, and raises req. The receiver synchronizes req, takes data when the synchronized req rises, and raises ack. The sender synchronizes ack, then drops req. The receiver sees req low and drops ack. The data needs no synchronizer, because it is stable long before the receiver reads it.

Four Phases, Two SynchronizersArrows show signals. req crosses into clk, ack crosses back into clk_f, each through two flops. data_x crosses with no synchronizer.
Four phases, two synchronizersArrows show signals. req crosses into clk, ack crosses back into clk_f, each through two flops. data_x crosses with no synchronizer. clk_f hs_tx clk_f; holds data_x clk r1, r2 req into clk clk hs_rx clk; takes data_x clk_f a1, a2 ack into clk_f reqr2acka2
  1. 1. req rises

    The sender raises req with the data on data_x, and holds both.

    Follow the calls, values and waits
    1. data rise: hs_tx → r1, r2
    2. data rise: r1, r2 → hs_rx
  2. 2. ack rises

    The receiver sees r2, takes data_x, which has been still for two clk edges, and raises ack.

    Follow the calls, values and waits
    1. data rise: hs_rx → a1, a2
    2. data rise: a1, a2 → hs_tx
  3. 3. req falls

    The sender sees a2 and drops req.

    Follow the calls, values and waits
    1. data fall: hs_tx → r1, r2
    2. data fall: r1, r2 → hs_rx
  4. 4. ack falls

    The receiver drops ack. When a2 falls, the sender is free for the next transfer.

    Follow the calls, values and waits
    1. data fall: hs_rx → a1, a2
    2. data fall: a1, a2 → hs_tx
Read the diagram as text
  • hs_tx: clk_f; holds data_x
  • r1, r2: req into clk
  • hs_rx: clk; takes data_x
  • a1, a2: ack into clk_f
  • hs_tx → r1, r2: req
  • r1, r2 → hs_rx: r2
  • hs_rx → a1, a2: ack
  • a1, a2 → hs_tx: a2

Every phase waits for the other side, so no clock ratio can lose an event. The cost is four synchronizer crossings per transfer.

One Transfer, Sampled on ClkThe handshake below on a bench like the lab's: clk 10 ns, clk_f 3 ns, the sender sends 5a, 3c and 7e, each when busy is 0.
01234567clkreqr2ackpulse_sdata_s005abusy
  1. Cycle 1

    Edge 5: req has risen in clk_f.

  2. Cycle 3

    Edge 7: r2 was 1 at this edge, so the receiver took 5a and raised ack.

  3. Cycle 4

    Edge 8: the sender saw ack through a1 and a2, and dropped req.

  4. Cycle 6

    Edge 10: ack is down. busy falls in clk_f at 109.75 ns, after a2 drops; the next req shows at edge 11.

Measured: req rose at clk_f edge 16 and the sender was free at clk_f edge 36, 20 fast cycles. The three transfers started 22 and 23 fast cycles apart, and all three arrived.

  1. Send

    The sender drives data and raises req, then holds both.

  2. Take

    Two flops later, the receiver sees req, registers data and raises ack.

  3. Release

    Two flops later, the sender sees ack and drops req.

  4. Reset

    The receiver sees req low and drops ack. When the sender sees ack low, the next word may start.

A 10 ns sender and a 14 ns receiver move three words.

RUNIcarus Verilog · in your browser
Edit the code, then run it
Expected: receiver took a1 b2 c3 by t=329 ns PASS: data held until ack, three words in order

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Mid-Level)

A good mid-level answer lists the four phases in order and says which signal each side synchronizes. It keeps data stable until ack comes back.

Cross-Questions

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

Prove ItBreak ItCompareScale It

  1. A Four-Phase Request and Acknowledge Handshake
  2. CompareTwo-Phase vs Four-Phase HandshakesL2 Mid-LevelL3 Senior

    Compare a two-phase handshake, where each change of req is a request, with the four-phase one.

    Reveal the Model Answer

    In a two-phase handshake, every toggle of req is a new request and every toggle of ack answers it. So each word needs one round trip instead of two. The cost is edge detection on both sides, an XOR of two flops. Four-phase uses plain levels and is easier to read and check.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Two-phase is twice as fast in every case."

    It removes the return-to-zero trip, but each word still waits for synchronizer delays in both directions. The data must also stay held for that whole trip.

    Source Cummings, Clock Domain Crossing (CDC) Design & Verification Techniques Using SystemVerilog (SNUG Boston 2008) §5.6.1-5.6.3

  3. Scale ItWhen a Handshake Is Too SlowL3 Senior

    Scale it: the link must now move a word on every destination cycle. Does the handshake still work?

    Reveal the Model Answer

    No. Each word waits for two synchronizer delays in each direction, several cycles of each clock. In the run, three words took about 330 ns with 10 ns and 14 ns clocks. A stream needs an asynchronous FIFO. It crosses only the Gray-coded pointers, and words flow every cycle while it is neither full nor empty.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Pipeline the handshake: start the next request before the last acknowledge."

    Then several words are in flight across the boundary at once, which is what a FIFO does properly with storage and pointers.

    Source Mehta, ASIC/SoC Functional Design Verification (2018) ch. 8.3.5 Design of an asynchronous FIFO using Gray code counters

  4. Break ItA Handshake Where Only One Side ResetsL2 Mid-LevelL3 Senior

    Break it: what goes wrong if the receiver resets while the sender is in the middle of a transfer?

    Reveal the Model Answer

    The receiver clears ack and forgets the word, while the sender still holds req high. When the receiver leaves reset, it sees req high again and may take the same word twice. It may also miss the edge entirely. Each domain needs its own reset synchronizer, and a crossing needs a rule for what happens when one side is in reset.

    Two Events Sent into a Sleeping DomainThe lab design. Every value is sampled 1 ns after an edge of the sender's clock clk, edges 2 to 24. t2 is the receiver's synchronized copy of the toggle; got counts the events it has received.
    012345678910111213141516171819202122clkrst_a_nrst_b_ntog_at2gotB out of reset
    1. Cycle 1

      Edge 3: the sender's domain is out of reset, so ready_a is 1.

    2. Cycle 2

      Edge 4: event 1. The toggle goes to 1 while the receiver is still in reset.

    3. Cycle 10

      Edge 12: event 2. The toggle goes back to 0, still before the receiver wakes.

    4. Cycle 14

      Edge 16: the receiver is out of reset, and its flops read a toggle of 0, the same as their reset value. Events 1 and 2 left no trace.

    5. Cycle 22

      Edge 24: event 3, sent at edge 20, arrives. Three sent, one received.

    Measured in lesson R6.5 with a toggle crossing, not this handshake. Events sent while the receiver is in reset leave no trace.

    Self-Check

    Tick each key point you covered in your answer.

    Missed Corner

    "Both domains share one reset, so this cannot happen."

    Each domain releases reset on its own clock. For a few cycles one side is out of reset and the other is not. R6.5 measured an event lost that way.

    Source Lesson R6.5, measured on its lab bench: a receiver still in reset swallowed an event, and a ready flag from the receiver fixed it (3 sent, 3 received)

  5. Prove ItSolve R6.3.1: One Event, One ByteSimulator-graded

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

    Open Problem R6.3.1

What People Get Wrong

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

Misconception

"Once req is up, the receiver has the word, so the sender can load the next one."

req takes two receiver flops to arrive, and the receiver reads data only then. The twin changes data early, and the receiver takes the wrong words.

Run It

The same handshake, but the sender loads the next word one cycle after raising req.

RUNIcarus Verilog · in your browser
Edit the code, then run it
Expected: FAIL: the sender changed data before the acknowledge, so the receiver took the next word
See the Fix
`timescale 1ns/1ps
module hs_tx (input logic clk, rst_n, ack, output logic req, output logic [7:0] data);
  function automatic logic [7:0] word(input int i);
    case (i) 0: return 8'ha1; 1: return 8'hb2; 2: return 8'hc3; default: return 8'h00; endcase
  endfunction
  logic a1, ack_s;                               // ack synchronized into the sending domain
  int i;
  logic [1:0] st;                                // 0 idle, 1 wait for ack high, 2 wait for ack low
  always_ff @(posedge clk or negedge rst_n)
    if (!rst_n) begin a1 <= 0; ack_s <= 0; i <= 0; st <= 0; req <= 0; data <= 0; end
    else begin
      a1 <= ack; ack_s <= a1;
      case (st)
        0: if (i < 3) begin data <= word(i); req <= 1; st <= 1; end
        1: begin
             // hold data until the acknowledge comes back
             if (ack_s) begin req <= 0; st <= 2; end
           end
        2: if (!ack_s) begin i <= i + 1; st <= 0; end
      endcase
    end
endmodule

module hs_rx (input logic clk, rst_n, req, input logic [7:0] data, output logic ack, output logic took, output logic [7:0] word_q);
  logic r1, r2, r3;                              // req synchronized, plus one flop for the edge
  always_ff @(posedge clk or negedge rst_n)
    if (!rst_n) begin r1 <= 0; r2 <= 0; r3 <= 0; ack <= 0; took <= 0; word_q <= 0; end
    else begin
      r1 <= req; r2 <= r1; r3 <= r2;
      took <= 0;
      if (r2 && !r3) begin word_q <= data; took <= 1; ack <= 1; end   // data is stable by now
      if (!r2 && r3) ack <= 0;
    end
endmodule

module tb;
  logic tclk = 0, rclk = 0, rst_n = 0, req, ack, took;
  logic [7:0] data, word_q;
  string log = "";
  int n = 0, good = 0;
  always #5 tclk = ~tclk;                        // 10 ns sender
  always #7 rclk = ~rclk;                        // 14 ns receiver
  hs_tx tx (.clk(tclk), .rst_n(rst_n), .ack(ack), .req(req), .data(data));
  hs_rx rx (.clk(rclk), .rst_n(rst_n), .req(req), .data(data), .ack(ack), .took(took), .word_q(word_q));
  always @(posedge rclk) if (took) begin
    log = {log, $sformatf(" %h", word_q)};
    if (word_q == (n == 0 ? 8'ha1 : n == 1 ? 8'hb2 : 8'hc3)) good++;
    n++;
    if (n == 3) $display("receiver took%s by t=%0d ns", log, $time);
  end
  initial begin
    #23 rst_n = 1;                               // flops reset at the clock edges before this
    #600;
    if (good == 3) $display("PASS: data held until ack, three words in order");
    $finish;
  end
endmodule

Prints: PASS: data held until ack, three words in order

Misconception

"Each data bit needs its own synchronizer as well."

The data is held stable from before req rises until ack returns. By the time the receiver reads it, it has settled.

Source Sutherland, RTL Modeling with SystemVerilog (2017) ch. 8.1.6 Multiple clocks and clock domain crossing

Delivery

Starts writing code before asking about the rate.

A handshake moves one word per round trip. If the source makes a word every cycle, the right answer is a FIFO, not a handshake.

Learn It, Then Prove It