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個的輸出經過邏輯閘來判斷跳變沿