1. 程式人生 > >FPGA跨時鐘域的處理方法

FPGA跨時鐘域的處理方法

一、時鐘域
如果一個設計全域性只使用了一個時鐘,那麼此設計有一個時鐘域。如果在一個設計中有兩個時鐘去控制不同的介面,那麼就稱這個設計中有兩個時鐘域。如下圖所示雙時鐘域圖
當時鐘不匹配時,就要進行同步化,否則就可能出現亞穩態,從而造成整個設計不穩定。
二、亞穩態
亞穩態其實就是資料的轉變沒有符合時鐘取樣所需要的setup/hold時間,在時鐘的上升沿或下降沿到來時正好採到資料的變化狀態。此時,由於資料並沒有穩定,所以會導致採到的資料不停變化,而不是邏輯0或邏輯1.此時採到的資料會一直抖動,直至隔一段時間穩定。產生亞穩態的時序圖如下:亞穩態產生原理
處理亞穩態的方法:
1、相位控制
2、多級暫存器
3、非同步FIFO快取
多級觸發暫存器處理
如果訊號來自同一時鐘域,則不需要多級觸發器處理,如果來自兩個時鐘域,那麼分為兩種情況:
1、從快到慢
2、從慢到快
此處的快慢指時鐘的頻率,首先說從快到慢的處理方法:
其實此處也分訊號所來的時間長短與慢的時鐘的頻率關係。
有以下兩種可能這裡寫圖片描述


從快到慢
右圖中的pulse_a訊號是不會被clk_b採集到的,因為pulse_a訊號的保持時間小於clk_b的時鐘週期,所以要從clk_a的時鐘域下將此訊號採集過來就必須對其進行展寬,否則極有可能採集不到。

 module  Sync_Pulse(
                                         input              clk_a,
                                         input              clk_b,
                                         input              rst_n,
                                         input              pulse_a_in,
                                         output           pulse_b_out,
                                         output           b_out
                                          );
  /**************************************************************************************/
reg signal_a; reg signal_b; reg signal_b_s; reg signal_b_ss; reg signal_b_a1; reg signal_b_a2; //在時鐘域clk_a下,生成展寬訊號signal_a always @ (posedge clk_a or
negedge rst_n) begin if (rst_n == 1'b0) signal_a <= 1'b0; else if (pulse_a_in) //檢測到到輸入訊號pulse_a_in被拉高,則拉高signal_a signal_a <= 1'b1; else if (signal_b_a2) //檢測到signal_b1_a2被拉高,則拉低signal_a signal_a <= 1'b0; else; end //在時鐘域clk_b下,採集signal_a生成signal_b [email protected](posedge clk_b or negedge rst_n)begin if(rst_n == 1'b0)begin signal_b <= 0; end else begin signal_b <= signal_a; end end //多級觸發器將clk_b抓到的signal_b訊號打兩拍輸出 [email protected](posedge clk_b or negedge rst_n)begin if(rst_n == 1'b0)begin signal_b_s <= 1'b1; signal_b_ss <= 1'b1; end else begin signal_b_s <= signal_b; signal_b_ss <= signal_b_s; end end //在時鐘域clk_a下,採集signal_b_s,用於反饋來拉低展寬訊號signal_a [email protected](posedge clk_a or negedge rst_n)begin if(rst_n == 1'b0)begin signal_b_a1 <= 1'b0; signal_b_a2 <= 1'b0; end else begin //對signal_b_s打兩拍,因此處涉及到跨時鐘域 signal_b_a1 <= signal_b_s; signal_b_a2 <= signal_b_a1; end end assign pulse_b_out = signal_b_s & (~signal_b_ss); assign b_out = signal_b_s; endmodule

從慢到快
這裡寫圖片描述
在clk_a時鐘看來,pulse_b訊號是一個非常寬的訊號,那麼在clk_b時鐘域下的pulse_b必然能夠被clk_a採到。如果pulse_b是clk_b時鐘域下的組合邏輯所產生的訊號,那麼就得先用DFF在clk_b時鐘抓一拍,之後再用DFF抓兩次或者更高次向clk_a時鐘域傳遞。

module  sys_clk(
                  input    clk_a,
                  input    clk_b,
                  input    pules_in,
                  input    rst_n,
                  output   pules_out 
                 );
reg         pules_b;
reg         pules_a1;
reg         pules_a2;
reg         pules_a3;
wire        pules_a_pos;
wire        pules_a_neg;
[email protected](posedge clk_b or negedge rst_n)begin
if(rst_n == 1'b0)begin
   pules_b <= 1'b0;
end
else  begin
   pules_b <= 1'b1;
end
end
[email protected](posedge clk_a or negedge rst_n)begin
if(rst_n == 1'b0)begin
  pules_a1 <= 1'b0;
  pules_a2 <= 1'b0;
  pules_a3 <= 1'b0;
end
else begin
  pules_a1 <= pules_b;
  pules_a2 <= pules_a1;
  pules_a3 <= pules_a2;
end
end
assign  pules_out = pules_a2;
assign pules_a_pos = pules_a2 & (~pules_a3); //輸出訊號的上升沿檢測
assign pules_a_neg = pules_a3 & (~pules_a2); //下降沿檢測

endmodule

需要強調的是,此時的pules_必須為clk_b時鐘域下的訊號**

在設計中可以簡單的牢記以下五條原則:
1. 再全域性時鐘的跳變沿最可靠。
2. 來自非同步時鐘域的輸入需要寄存一次以同步化,再寄存一次以減少亞穩態帶來的影響。
3. 不需要用到跳變沿的來自同一時鐘域的輸入,沒有必要對訊號進行寄存。
4. 需要用到跳變沿的來自同一時鐘域的輸入,寄存一次即可。
5. 需要用到跳變沿的來自不同時鐘域的輸入,需要用到3個觸發器,前兩個用以同步,第3個觸發器的輸出和第2個的輸出經過邏輯閘來判斷跳變沿