The four-phase handshake is the default recommendation for occasional multi-bit transfers across clocks: configuration writes, commands, status words. It is slow but easy to reason about, easy to verify, and every step is a level that a synchroniser can carry. Signoff reviewers treat it as the reference against which faster variants must justify themselves.
Source puts the word on the data bus and raises req. The data stays stable from now on.
Destination sees req through its synchroniser, captures the data, and raises ack.
Source sees ack through its synchroniser and lowers req. Only now may the data change.
Destination sees req low and lowers ack. Both sides are back where they started.
Only single-bit levels cross a synchroniser: req one way, ack the other. The data bus never touches a synchroniser, which is legal precisely because the protocol guarantees stability: data is driven before req rises and held until ack has been seen. Metastability can delay any single crossing by a cycle, but it cannot corrupt the word, because no sampling of data happens near a data change.
The waveform below is simulation output for the wave source of this topic: one word carried source to destination and both sides returned to zero. One slot is one cycle of clk_s.
How to read it: req rises in slot 4 while the data bus is already stable. req_sync rises in slot 7, after the destination edges in slots 5 and 7. ack rises together with the capture in slot 9, and out_valid stays high across slots 9 and 10: one full destination period. ack_sync is high in slot 10, one slot after ack, even though its two-flop synchroniser needed two source-clock edges. The timing explains it: the lanes are sampled 2 ns after each 10 ns source edge, ack changes at about 90 ns (just after the destination edge) and is first visible in slot 9, and the two synchroniser edges fall at 95 ns and 105 ns. The first of them lands inside slot 9's window, so the second flop's output, ack_sync, shows up one slot later, in slot 10. A change that happens just before a source edge can therefore appear on a two-flop path only one slot after the raw signal; it does not mean a single flop was enough. On the way down, req falls in slot 11, req_sync in slot 15, ack in slot 17 and ack_sync in slot 18, by the same timing.
Each word pays two crossings in each direction: req there and back to zero, ack there and back to zero. With N destination cycles per synchroniser and M source cycles per synchroniser, one word costs about 2N destination cycles plus 2M source cycles (counted exactly in the latency lesson). That is the price of certainty, and the reason streams use FIFOs instead.
The source holds data from before req rises until after it sees ack, and the destination only raises ack after capturing. So the capture instant always falls inside the stability window, however many synchroniser cycles req took to arrive.