前面提到了簡單的雙電平鎖存器,下面是一些單bit同步電路。
一、慢時鐘域向快時鐘域
邊沿檢測同步器
將慢時鐘域的脈沖搬移并縮小為快時鐘域的脈沖。
既可以檢測上升沿,也可以檢測下降沿。
如上圖,慢時鐘下一個有效脈沖的最短周期為慢時鐘的一個周期,站在快時鐘的角度下,這個慢時鐘域的信號會在快時鐘域下持續很多個周期。實際上,這個脈沖在慢時鐘域只發生了一次,所以如果用快時鐘去檢查有效脈沖的翻轉邊沿是最準確的。邊沿上升與下降也只有一次。
適用條件:data_width>clk_fast+T_hold,最安全的就是兩個同步周期長度。這樣才能保證慢時鐘域的脈沖足夠保持到被快時鐘的同步器采樣到。
其verilog代碼如下:
二、快時鐘域向慢時鐘域
脈沖同步器
從快時鐘域的取出一個單時鐘寬度脈沖,在慢時鐘域建立新的單時鐘寬度脈沖。
?
圖中陰影部分為快時鐘域下的翻轉電路。
由于在慢時鐘域下直接采樣快時鐘域的信號,很大概率會采樣失敗。因此,我們采用翻轉電路對有效脈沖進行標定。
適用條件:輸入脈沖間隔>=2*clk_slow。輸入脈沖相隔過近,則慢時鐘域的新脈沖也緊密相鄰,結果是輸出脈沖比一個時鐘周期寬;如果太近,則無法檢測到每一個脈沖。
其verilog代碼如下:
針對此脈沖同步器出現的問題:源時鐘域中的第一個脈沖和第二個脈沖間隔過短,第一個脈沖未完成同步,第二脈沖又將狀態清空,導致最終脈沖同步丟失。我們引入握手機制進行解決,思路如下:
(1) 同步請求產生;當同步器處于空閑(即上一次已同步完成)時,源同步脈沖到達時產生同步請求信號sync_req;
(2) 同步請求信號sync_req同步到目的時鐘域,目的時鐘域產生脈沖信號并將產生應答信號sync_ack;
(3) 同步應答信號sync_ack同步到源時鐘域,源時鐘域檢測到同步應答信號sync_ack后,清除同步請求信號;
(4)?目的時鐘域檢測到sync_req撤銷后,清除sync_ack應答;源時鐘域將到sync_ack清除后,認為一次同步完成,可以同步下一個脈沖。
verilog代碼如下:
module HANDSHAKE_PULSE_SYNC(src_clk , //source clock src_rst_n , //source clock reset (0: reset)src_pulse , //source clock pulse insrc_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 ininput 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)
beginif(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)
beginif(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 ;
endalways @(posedge src_clk or negedge src_rst_n)
beginif(src_rst_n == 1'b0)beginack_state_dly1 <= 1'b0 ;ack_state_dly2 <= 1'b0 ;src_sync_ack <= 1'b0 ; endelsebeginack_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)
beginif(dst_rst_n == 1'b0)beginreq_state_dly1 <= 1'b0 ;req_state_dly2 <= 1'b0 ;dst_req_state <= 1'b0 ;endelsebeginreq_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)
beginif(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;
endendmodule