握手(handshake)是用來處理單bit信號的跨時鐘域傳遞的一個有效的方法。在從快時鐘向慢時鐘傳遞時,因爲輸入信號變化較快,輸出一側可能跟不上輸入的變化,從而致使「漏採「現象。php
圖1 「漏採」現象演示web
在圖1中,因爲兩個時鐘的速度差距,來自快時鐘域的脈衝信號還未到達慢時鐘的採樣邊沿便消失了,致使了「漏採」。微信
在這種狀況下,如何讓脈衝信號準確無誤地傳遞過去呢?一個方法是將脈衝信號展寬,待輸出一側檢測到信號並將其解析爲脈衝信號後,再向輸入一側發送應答信號,代表接收到信號而且傳輸完成。這個過程被稱之爲「握手」。網絡
先來看一個最基本的握手協議,下邊是它的電路圖。 less
圖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
上述電路雖然能夠完整的同步信號,可是若在同步一個脈衝的過程當中,輸入端又接收到一個輸入進來的脈衝,那麼此時剛剛輸入進來的脈衝將會同步失敗。更糟糕的是該電路沒有同步失敗的反饋,致使使用者誤覺得正確同步了信號。鑑於此,將上述電路進行改進,當同步失敗後將輸出src_sync_fail信號來指示同步失敗。如下爲該電路的verilog描述(此代碼來源於網絡):flex
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; endendmodule
從代碼能夠得知,在同步信號的過程當中,src_sync_idle會拉低,此時若再有輸入脈衝,則會輸出同步失敗信號。url
仿真結果以下spa
圖3 電路仿真結果
從仿真結果能夠看到,該設計達到了同步信號要求。
對該部分有興趣的,或者有問題想要探討,歡迎點擊「閱讀原文」進行留言,也能夠直接向公衆號發送消息,歡迎你們的來信!
最後,創做不易,若是對你有幫助的話,點個「在看」再走唄!
本文分享自微信公衆號 - 數字積木(ggreat-top)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。