Cross-Clock Domain Handshake Mechanisms

When transferring data between different clock domains, handshake synchronization mechanisms provide a reliable solution. These mechanisms are categorized into half-handshake and full-handshake approachse. The half-handshake method is suitable for transferring data from a lower frequency clock domain to a higher frequency domain, as the receiver can complete operations more quickly. Conversely, when transferring data from a higher frequency clock to a lower frequency domain, a full-handshake mechanism is required.

Handshake Synchronization Process

  1. In the high-frequency clock domain, prepare the data to be trensmitted and assert the request (req) signal. In the low-frequency clock domain, synchronize this req signal, resulting in synchronized signals req_r0 and req_r1.
  2. In the low-frequency domain, detect rising edges of req_r0 and req_r1 using edge detection, generating pos_req_r0 and pos_req_r1. Sample the data when pos_req_r0 is asserted and generate an acknowledgment (ack) signal when pos_req_r1 is asserted.
  3. In the high-frequency domain, synchronize the ack signal, resulting in ack_r0 and ack_r1. Detect the rising edge of the ack signal (pos_ack_r0). When pos_ack_r0 is high, deassert the req signal.
  4. In the low-frequency domain, when the synchronized req signal (req_r0) goes low, deassert the ack signal.

Handshake Process Explanation

  1. The sender prepares data and notifies the receiver by asserting the req signal.
  2. The receiver samples the data when req is asserted.
  3. After receiving the data, the receiver asserts the ack signal to confirm successful reception.
  4. Upon receiving the ack signal, the sender deasserts the req signal, indicating data transfer completion.
  5. The receievr, detecting the deasserted req signal, also deasserts the ack signal.

Implementation

module cross_domain_handshake(
    input                                fast_clk,          // Fast clock domain
    input                                slow_clk,          // Slow clock domain
    input                                reset_n,           // Active low reset
    input                                data_valid,        // Single cycle pulse
    input            [7:0]                data_input,        // Input data
    output reg        [7:0]                data_output        // Output data
);

// Internal signals for fast clock domain
reg                                        data_valid_sync0;
reg                                        data_valid_sync1;
wire                                    data_falling_edge;

reg                                        request_signal;
reg                                        ack_signal;

reg                                        ack_sync_fast0;
reg                                        ack_sync_fast1;
wire                                    ack_rising_edge;

// Internal signals for slow clock domain
reg                                        req_sync_slow0;
reg                                        req_sync_slow1;
reg                                        req_sync_slow2;
wire                                    req_rising_edge0;
wire                                    req_rising_edge1;

// Synchronize data_valid signal in fast clock domain
always @(posedge fast_clk or negedge reset_n) begin
    if(!reset_n) begin
        data_valid_sync0 <= 1'b0;
        data_valid_sync1 <= 1'b0;
    end
    else begin
        data_valid_sync0 <= data_valid;
        data_valid_sync1 <= data_valid_sync0;
    end
end

// Detect falling edge of data_valid
assign    data_falling_edge = (data_valid_sync1 == 1'b1) && (data_valid_sync0 == 1'b0);

// Synchronize ack signal to fast clock domain
always @(posedge fast_clk or negedge reset_n) begin
    if(!reset_n) begin
        ack_sync_fast0 <= 1'b0;
        ack_sync_fast1 <= 1'b0;
    end
    else begin
        ack_sync_fast0 <= ack_signal;
        ack_sync_fast1 <= ack_sync_fast0;
    end
end

// Detect rising edge of ack in fast domain
assign ack_rising_edge = (ack_sync_fast1 == 1'b0) && (ack_sync_fast0 == 1'b1);

// Request signal generation in fast domain
always @(posedge fast_clk or negedge reset_n) begin
    if(!reset_n)
        request_signal <= 1'b0;
    else if(data_falling_edge == 1'b1)
        request_signal <= 1'b1;
    else if(ack_rising_edge == 1'b1)
        request_signal <= 1'b0;
    else
        request_signal <= request_signal;
end

// Synchronize request signal to slow clock domain
always @(posedge slow_clk or negedge reset_n) begin
    if(!reset_n) begin
        req_sync_slow0 <= 1'b0;
        req_sync_slow1 <= 1'b0;
        req_sync_slow2 <= 1'b0;
    end
    else begin
        req_sync_slow0 <= request_signal;
        req_sync_slow1 <= req_sync_slow0;
        req_sync_slow2 <= req_sync_slow1;
    end
end

// Detect rising edges of request signal in slow domain
assign req_rising_edge0 = (req_sync_slow1 == 1'b0) && (req_sync_slow0 == 1'b1);
assign req_rising_edge1 = (req_sync_slow2 == 1'b0) && (req_sync_slow1 == 1'b1);

// Data capture in slow domain
always @(posedge slow_clk or negedge reset_n) begin
    if(!reset_n)
        data_output <= 8'd0;
    else if(req_rising_edge0 == 1'b1)
        data_output <= data_input;
    else
        data_output <= data_output;
end

// Ack signal generation in slow domain
always @(posedge slow_clk or negedge reset_n) begin
    if(!reset_n)
        ack_signal <= 1'b0;
    else if(req_rising_edge1 == 1'b1)
        ack_signal <= 1'b1;
    else if(req_sync_slow0 == 1'b0)
        ack_signal <= 1'b0;
    else
        ack_signal <= ack_signal;
end

endmodule

Testbench

`timescale 1ns/1ps

module handshake_tb;
    reg                                fast_clk;          // Fast clock
    reg                                slow_clk;          // Slow clock
    reg                                reset_n;           // Active low reset
    reg                                data_valid;        // Data valid pulse
    reg            [7:0]                data_input;        // Input data
    wire        [7:0]                data_output;       // Output data
    
    reg            [3:0]                cycle_counter;

// Instantiate the handshake module
cross_domain_handshake uut (
    .fast_clk(fast_clk),
    .slow_clk(slow_clk),
    .reset_n(reset_n),
    .data_valid(data_valid),
    .data_input(data_input),
    .data_output(data_output)
);

// Generate VCD file for waveform viewing
initial
    begin
        $dumpfile("handshake_wave.vcd");
        $dumpvars(0,handshake_tb);
    end
    
// Fast clock generation (period = 8ns)
initial
    fast_clk = 1'b0;
    always #4 fast_clk = ~fast_clk;
    
// Slow clock generation (period = 20ns)
initial
    slow_clk = 1'b0;
    always #10 slow_clk = ~slow_clk;
    
// Reset sequence
initial
    begin
        #1;
        reset_n = 1'b0;
        #51;
        reset_n = 1'b1;
    end
    
// Cycle counter for test pattern generation
always @(posedge fast_clk or negedge reset_n) begin
    if(!reset_n)
        cycle_counter <= 4'd0;
    else
        cycle_counter <= cycle_counter + 1'b1;
end

// Generate data_valid pulse
always @(posedge fast_clk or negedge reset_n) begin
    if(!reset_n)
        data_valid <= 1'b0;
    else if(&cycle_counter == 1'b1)
        data_valid <= 1'b1;
    else
        data_valid <= 1'b0;
end

// Generate test data pattern
always @(posedge fast_clk or negedge reset_n) begin
    if(!reset_n)
        data_input <= 8'd0;
    else if(&cycle_counter == 1'b1)
        data_input <= data_input + 1'b1;
    else
        data_input <= data_input;
end

endmodule

Tags: clock domain crossing handshake protocol FPGA design Verilog Synchronization

Posted on Wed, 20 May 2026 05:53:58 +0000 by doremi