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
- 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.
- 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.
- 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.
- In the low-frequency domain, when the synchronized req signal (req_r0) goes low, deassert the ack signal.
Handshake Process Explanation
- The sender prepares data and notifies the receiver by asserting the req signal.
- The receiver samples the data when req is asserted.
- After receiving the data, the receiver asserts the ack signal to confirm successful reception.
- Upon receiving the ack signal, the sender deasserts the req signal, indicating data transfer completion.
- 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