Panel For Example Panel For Example Panel For Example

Single-Bit Clock-Domain Synchronization via Handshake

Author : Adrian September 11, 2025

Introduction

In digital circuits, clock-domain crossing is a broad topic that will be covered as a series. This article begins with handling single-bit signal synchronization across clock domains.

01. Pulse Loss When Crossing from Fast to Slow Clock

Handshake is an effective method for transferring single-bit signals across clock domains. When transferring from a faster clock domain to a slower one, a fast input pulse may disappear before the slower domain samples it, causing a missed-sample condition.

Missed-sample demonstration

In the figure above, because of the frequency difference between the two clocks, a pulse from the fast domain disappears before the slow domain reaches its sampling edge, resulting in a missed sample.

To ensure reliable transfer, one approach is to widen the pulse and have the destination detect and acknowledge it back to the source after decoding the pulse, indicating receipt and completion. This process is called a handshake.

02. Basic Handshake Protocol

Below is a basic handshake circuit. src_clk and dst_clk are the source and destination clocks respectively; src_pulse is the input pulse; dst_pulse is the pulse synchronized to the destination. After the pulse is synchronized at the destination, the destination immediately sends an acknowledgement back to the source. Only after the acknowledgement is synchronized back to the source can the source issue the next pulse. The Verilog description of this circuit is shown below.

module Sync_Pulse ( input wire src_clk, input wire dst_clk, input wire rst_n, input wire src_pulse, output wire dst_pulse ); reg req_state_dly1, req_state_dly2,dst_req_state,src_sync_req; reg ack_state_dly1,src_sync_ack; wire dst_sync_ack; always @ (posedge src_clk or negedge rst_n) begin if (rst_n == 1'b0) src_sync_req <= 1'b0; else if (src_pulse) src_sync_req <= 1'b1; else if (src_sync_ack) src_sync_req <= 1'b0; else; end always @ (posedge dst_clk or negedge rst_n) begin if (rst_n == 1'b0) begin req_state_dly1 <= 1'b0; req_state_dly2 <= 1'b0; dst_req_state <= 1'b0; end else begin req_state_dly1 <= src_sync_req; req_state_dly2 <= req_state_dly1; dst_req_state <= req_state_dly2; end end assign dst_sync_ack = req_state_dly2; always @ (posedge src_clk or negedge rst_n) begin if (rst_n == 1'b0) begin ack_state_dly1 <= 1'b0; src_sync_ack <= 1'b0; end else begin ack_state_dly1 <= dst_sync_ack; src_sync_ack <= ack_state_dly1; end end assign dst_pulse = dst_req_state & (~req_state_dly2); endmodule

03. Improved Handshake with Failure Indication

Although the previous circuit can synchronize pulses correctly under normal conditions, if a new input pulse arrives while a prior pulse is still being synchronized, the new pulse will be lost. Worse, the basic circuit provides no feedback indicating synchronization failure, which can mislead users into believing the pulse was synchronized. To address this, the circuit can be improved to assert a src_sync_fail signal when synchronization fails. The following Verilog implements this improvement. This code is sourced from the internet.

module handshake_pulse_sync ( src_clk , //source clock src_rst_n , //source clock reset (0: reset) src_pulse , //source clock pulse in src_sync_fail , //source clock sync state: 1 clock pulse if sync fail. dst_clk , //destination clock dst_rst_n , //destination clock reset (0:reset) dst_pulse //destination pulse out ); //PARA DECLARATION //INPUT DECLARATION input src_clk ; //source clock input src_rst_n ; //source clock reset (0: reset) input src_pulse ; //source clock pulse in input dst_clk ; //destination clock input dst_rst_n ; //destination clock reset (0:reset) //OUTPUT DECLARATION output src_sync_fail ; //source clock sync state: 1 clock pulse if sync fail. output dst_pulse ; //destination pulse out //INTER DECLARATION wire dst_pulse ; wire src_sync_idle ; reg src_sync_fail ; reg src_sync_req ; reg src_sync_ack ; reg ack_state_dly1 ; reg ack_state_dly2 ; reg req_state_dly1 ; reg req_state_dly2 ; reg dst_req_state ; reg dst_sync_ack ; //--========================MODULE SOURCE CODE==========================-- //--=========================================-- // DST Clock : // 1. generate src_sync_fail; // 2. generate sync req // 3. sync dst_sync_ack //--=========================================-- assign src_sync_idle = ~(src_sync_req | src_sync_ack ); //report an error if src_pulse when sync busy ; always @(posedge src_clk or negedge src_rst_n) begin if(src_rst_n == 1'b0) src_sync_fail <= 1'b0 ; else if (src_pulse & (~src_sync_idle)) src_sync_fail <= 1'b1 ; else src_sync_fail <= 1'b0 ; end //set sync req if src_pulse when sync idle ; always @(posedge src_clk or negedge src_rst_n) begin if(src_rst_n == 1'b0) src_sync_req <= 1'b0 ; else if (src_pulse & src_sync_idle) src_sync_req <= 1'b1 ; else if (src_sync_ack) src_sync_req <= 1'b0 ; end always @(posedge src_clk or negedge src_rst_n) begin if(src_rst_n == 1'b0) begin ack_state_dly1 <= 1'b0 ; ack_state_dly2 <= 1'b0 ; src_sync_ack <= 1'b0 ; end else begin ack_state_dly1 <= dst_sync_ack ; ack_state_dly2 <= ack_state_dly1 ; src_sync_ack <= ack_state_dly2 ; end end //--=========================================-- // DST Clock : // 1. sync src sync req // 2. generate dst pulse // 3. generate sync ack //--=========================================-- always @(posedge dst_clk or negedge dst_rst_n) begin if(dst_rst_n == 1'b0) begin req_state_dly1 <= 1'b0 ; req_state_dly2 <= 1'b0 ; dst_req_state <= 1'b0 ; end else begin req_state_dly1 <= src_sync_req ; req_state_dly2 <= req_state_dly1 ; dst_req_state <= req_state_dly2 ; end end //Rising Edge of dst_state generate a dst_pulse; assign dst_pulse = (~dst_req_state) & req_state_dly2 ; //set sync ack when src_req = 1 , clear it when src_req = 0 ; always @(posedge dst_clk or negedge dst_rst_n) begin if(dst_rst_n == 1'b0) dst_sync_ack <= 1'b0; else if (req_state_dly2) dst_sync_ack <= 1'b1; else dst_sync_ack <= 1'b0; end endmodule

From the code, src_sync_idle is pulled low while a synchronization is in progress; if another input pulse arrives during this time, src_sync_fail will be asserted to indicate synchronization failure.

Circuit simulation result

The simulation shows that this design meets the intended synchronization behavior.