Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Reading Paths
  • Concept Map
  • How to Read the Diagrams
  • Part 0: What Is a Protocol?10
  • Part 1: Timing and Clocking11
  • Part 2: Encoding and Framing12
  • Part 3: Handshakes13
    • Valid-Only Streams
    • Wait States and Stretching
    • The Valid/Ready Handshake
    • The Valid/Ready Rules
    • Request/Acknowledge: Two-Phase and Four-Phase
    • Push and Pull
    • Global Stall or Elastic Pipeline
    • Register Slices
    • Skid Buffers
    • Bubbles and Half Throughput
    • Latency-Insensitive Design
    • Protocols Compared: Handshakes
    • Drills: Handshakes
  • Part 4: Flow Control17
  • Part 5: Transactions19
  • Part 6: Concurrency and Performance17
  • Part 7: Ordering13
  • Part 8: Sharing and Arbitration12
  • Part 9: Deadlock and Liveness13
  • Part 10: Reliability and Recovery20
  • Part 11: Bring-Up and Link Management10
  • Part 12: Power8
  • Part 13: Shared State and Coherence9
  • Part 14: Security and Isolation7
  • Part 15: Specifying and Verifying11
  • Part 16: Protocols Dissected17
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 3: Handshakes
  1. Topics
  2. First Principles of Hardware Protocols
  3. Part 3: Handshakes
  4. Request/Acknowledge: Two-Phase and Four-Phase

Part 3 · Handshakes · Working (W)

Request/Acknowledge: Two-Phase and Four-Phase

Request/acknowledge handshakes in four phases (levels) and two phases (transitions): what each costs per word, and why they are the standard way to cross a clock domain.

On this page

    Note

    Level: Working (W) · Answers: Is the receiver ready?

    Before this: The Valid/Ready Rules

    A configuration word must move from a block on one clock to a block on another, unrelated clock. Valid/ready cannot be used directly: each side samples the other's signal with its own clock, so a single-cycle valid might be missed, or seen for a different number of cycles than it lasted. Every CDC course teaches the same answer, and it predates valid/ready by decades.

    How do two sides agree that a word moved when they do not even share a clock, and what does that agreement cost?

    The obvious answer, and why it breaks

    The obvious answer is valid/ready with synchronisers on both signals. But valid/ready's rule is "a transfer happens on an edge where both are high", and there is no shared edge across two clocks. One side sees the other's signal a few cycles late, and a pulse shorter than the receiver's sampling period may be missed entirely. A handshake across clocks must use signals that stay put until the other side has clearly seen them, and must never depend on a shared edge.

    The idea: request, acknowledge, and nothing changes until it is seen

    In a request/acknowledge handshake, each side changes its signal only in answer to a change it has seen from the other. Because each signal holds until answered, it does not matter how many cycles the other side takes to notice. There are two variants:

    Variant

    What means "a new word"

    Steps per word

    Trade-off

    Four-phase (return-to-zero)

    req rising

    req up, ack up, req down, ack down

    Simple to reason about (levels); twice the round trips

    Two-phase (transition)

    any change of req

    req toggles, ack toggles

    Half the round trips; each side must remember the last level it saw

    Four-phase spends two round trips per word, two-phase spends one. Neither depends on how quickly the other side sees a change.

    The data never crosses through a synchroniser. The sender sets the data before raising (or toggling) req and holds it until it sees ack, so the receiver always samples a settled bus. Only the single-bit req and ack signals need synchronisers, which is why this handshake is the standard way to move occasional words between clocks.

    Build it: the cost per word

    The testbench runs both variants on one clock, with each side reacting one cycle after it sees a change, as if one register separated them. Real crossings add a synchroniser's two or three cycles to each step, but the ratio between the variants stays the same.

    tb.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    // Four-phase and two-phase request/acknowledge handshakes carrying four words each,
    // side by side on one clock. Each side reacts one cycle after it sees the other's
    // change (as if a one-flop delay separated them), so the cycle counts show the
    // protocols' cost per word. Four-phase: req up, ack up, req down, ack down per word.
    // Two-phase: every toggle of req is a word, every toggle of ack answers it.
    module tb;
      logic clk = 0;
      always #5 clk = ~clk;
      logic rst_n = 0;
      initial begin
        $dumpfile("wave.vcd"); $dumpvars(0, tb);
        #12 rst_n = 1;
      end
      localparam int N = 4;
    
      // ---- Four-phase ----
      logic req4 = 0, ack4 = 0;
      int   sent4 = 0, got4 = 0, done4_at = -1, cyc = 0;
      always @(posedge clk) if (rst_n) cyc <= cyc + 1;
      always @(posedge clk) if (rst_n) begin                  // sender
        if (!req4 && !ack4 && sent4 < N) begin req4 <= 1; sent4 <= sent4 + 1; end
        else if (req4 && ack4) req4 <= 0;
      end
      always @(posedge clk) if (rst_n) begin                  // receiver
        if (req4 && !ack4) begin ack4 <= 1; got4 <= got4 + 1; end
        else if (!req4 && ack4) begin
          ack4 <= 0;
          if (got4 == N) done4_at <= cyc + 1;
        end
      end
    
      // ---- Two-phase ----
      logic req2 = 0, ack2 = 0;
      int   sent2 = 0, got2 = 0, done2_at = -1;
      always @(posedge clk) if (rst_n) begin                  // sender: toggle when ack has caught up
        if (req2 == ack2 && sent2 < N) begin req2 <= ~req2; sent2 <= sent2 + 1; end
      end
      always @(posedge clk) if (rst_n) begin                  // receiver: answer each toggle
        if (req2 != ack2) begin
          ack2 <= req2; got2 <= got2 + 1;
          if (got2 + 1 == N) done2_at <= cyc + 1;
        end
      end
    
      initial begin
        #300;
        if (got4 == N && got2 == N && done4_at == 2 * done2_at)
          $display("PASS: four-phase took %0d cycles for %0d words, two-phase %0d: half the round trips", done4_at, N, done2_at);
        else $display("FAIL: four-phase %0d words in %0d cycles, two-phase %0d words in %0d cycles", got4, done4_at, got2, done2_at);
        $finish;
      end
    endmodule

    Watch it run

    How to read it, slot by slot:

    • Four-phase, slots 1 to 4: req4 rises (slot 1), ack4 answers (slot 2), req4 falls (slot 3), ack4 falls (slot 4). Four steps, four cycles, one word. The next word starts in slot 5.

    • Two-phase, slots 1 to 2: req2 toggles (slot 1) and ack2 toggles to match (slot 2): one word in two cycles. The next toggle comes in slot 3.

    • By slot 8: two-phase has moved all four words; four-phase has moved two. Four-phase finishes its fourth word at slot 16.

    Worked numbers: a real crossing

    Across a clock boundary each step waits for a synchroniser. With illustrative numbers, two-flop synchronisers, a 100 MHz sender and a 50 MHz receiver:

    Diagram
    Each step = the other side's synchroniser (2 of its cycles) + 1 cycle to react
      step seen by receiver: 2 x 20 ns + 20 ns = 60 ns
      step seen by sender:   2 x 10 ns + 10 ns = 30 ns
    
    Four-phase: 2 steps each way per word = 2 x 60 + 2 x 30 = 180 ns per word (about 5.6 M words/s)
    Two-phase:  1 step each way per word  =     60 +     30 =  90 ns per word (about 11 M words/s)

    Fine for configuration writes and status words; far too slow for a data stream, which is why streams cross clocks through an async FIFO instead (CDC course).

    The design space

    Option

    What it costs

    When it wins

    Four-phase req/ack

    Two round trips per word

    Occasional words across clocks; easiest to verify

    Two-phase req/ack

    One round trip per word; edge-detection logic on each side

    Higher event rates across clocks

    Async FIFO

    Memory, Gray-coded pointers

    Streams across clocks

    valid/ready

    Requires a shared clock edge

    Within one clock domain

    How real designs use it

    Where

    What is used

    Configuration and status between clock domains

    Four-phase or two-phase request/acknowledge, with synchronisers on req and ack and the data bus held stable across the handshake.

    Self-timed (clockless) links

    The same four-phase protocol with no clock at all (lesson 1.8).

    Single events across clocks (pulses, interrupts)

    A toggle synchroniser, which is a two-phase signal without the acknowledge.

    What breaks

    A sender updates its data bus on the same cycle it raises req, from a register that also changes on the next cycle (illustrative). The receiver captures when its synchronised copy of req arrives, two cycles later, by which time the bus holds the following value. The fix is the rule the protocol depends on: data changes only before req rises (or toggles), and holds until ack is seen. A handshake carries the timing; the data must obey it.

    Why this matters

    Senior lens. Request/acknowledge is the most robust handshake there is, because it assumes nothing about relative speed. That robustness is exactly why it is slow. When you see it on a path, check two things: that the data is truly held from before the request until after the acknowledge (the classic bug), and that the traffic rate fits its cost per word. If the rate grows, move to an async FIFO rather than squeezing the handshake.

    Interview

    Asked: "Why use two-phase signalling across a CDC?"

    Weak answer: It is faster.

    Strong answer: Across clocks each step of a handshake waits for a synchroniser, so the number of steps per word sets the throughput. Four-phase needs req up, ack up, req down, ack down, two round trips per word. Two-phase treats every toggle as an event, one round trip per word, so it roughly doubles the rate. The cost is that each side must remember the last level it saw to detect toggles, and it's a little harder to debug. In both, only req and ack are synchronised; the data is held stable across the handshake.

    Follow-ups: (1) Why does the data bus not need a synchroniser? (2) When would you use an async FIFO instead?

    Check yourself In the four-phase handshake, when may the sender change the data for the next word?

    After it has seen ack high and lowered req. Changing it earlier risks the receiver capturing while the bus is changing.

    Check yourself In the simulation, why does two-phase finish four words in half the cycles?

    Each two-phase word needs one req change and one ack change; each four-phase word needs two of each (up and back down).

    Check yourself Why can req/ack work without any shared clock edge?

    Each signal holds its value until the other side answers, so it does not matter when, or in which clock, the other side samples it.

    Key takeaways

    • Request/acknowledge changes each signal only in answer to the other, so it works whatever the relative speeds or clocks.

    • Four-phase costs two round trips per word, two-phase one; across clocks each step waits for a synchroniser.

    • Only req and ack are synchronised; the data is held stable from before req until ack is seen.

    Keep going

    Next: Lesson 3.6, Push and Pull. Who starts a transfer: the producer pushing, or the consumer pulling? It changes where the buffering goes.

    Related topics

    • Part 3 overview: Handshakes

    • CDC & RDC course: Handshake Protocols Across Domains

    • RTL course: CDC Protocols

    Previous

    42 / 234

    Next: Push and Pull

    Up next

    Up nextPush and Pull

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Valid-Only Streams
    • Wait States and Stretching
    • The Valid/Ready Handshake
    clkreq4ack4req2ack2
    Simulation output: four words by four-phase handshake (top pair) and two-phase handshake (bottom pair). One slot is one cycle.