在數字電路中,跨時鐘域處理是個很龐大的問題,因此將會作為一個專題來陸續分享。今天先來從處理單bit跨時鐘域信號同步問題來入手。
01
握手(handshake)是用來處理單bit信號的跨時鐘域傳遞的一個有效的方法。在從快時鐘向慢時鐘傳遞時,由于輸入信號變化較快,輸出一側可能跟不上輸入的變化,從而導致“漏采“現象。
圖1 “漏采”現象演示
在圖1中,由于兩個時鐘的速度差距,來自快時鐘域的脈沖信號還未到達慢時鐘的采樣邊沿便消失了,導致了“漏采”。
在這種情況下,如何讓脈沖信號準確無誤地傳遞過去呢?一個方法是將脈沖信號展寬,待輸出一側檢測到信號并將其解析為脈沖信號后,再向輸入一側發送應答信號,表明接收到信號并且傳輸完成。這個過程被稱之為“握手”。
02
先來看一個最基本的握手協議,下邊是它的電路圖。
圖2 “握手”電路演示
在上圖中 src_clk和dst_clk分別為輸入和輸出側的時鐘,src_pulse為輸入的脈沖信號,dst_pulse為同步到輸出端的脈沖信號??梢钥吹?,脈沖信號被同步到輸出端后,輸出端便立即向輸入端發送了應答信號,表示收到信號。而當輸出端的應答信號同步到輸入端后,輸入端才可以同步下一個信號。以下為這個電路的verilog描述。
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
上述電路雖然可以完整的同步信號,但是若在同步一個脈沖的過程中,輸入端又接收到一個輸入進來的脈沖,那么此時剛剛輸入進來的脈沖將會同步失敗。更糟糕的是該電路沒有同步失敗的反饋,導致使用者誤以為正確同步了信號。鑒于此,將上述電路進行改進,當同步失敗后將輸出src_sync_fail信號來指示同步失敗。以下為該電路的verilog描述(此代碼來源于網絡):
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