cdc跨時鍾域處理-結繩握手法


參考文檔

https://blog.csdn.net/u011412586/article/details/10009761

前言

對於信號需要跨時鍾域處理而言,最重要的就是確保數據能穩定的傳送到采樣時鍾域。

普通的cdc處理方法需要關注時鍾域速度的異同,即分慢時鍾域到快時鍾域、快時鍾域到慢時鍾域、相位關系等問題,會讓人瞬間爆炸。

那么,是否有一種相對穩定,又無需關注傳送時鍾域和接收時鍾域兩者時鍾速度、相位關系的數據傳遞方式呢?

這里仿真驗證握手結繩法,自己想的,不知道跟結繩法是不是一致的。。。。。。

思考方式為:要絕對的保證數據被接收,自然是握手應答機制。

 

流程

首先考慮亞穩態說明:對於異步信號的采集,不可避免的會有亞穩態,但亞穩態的恢復時間一般為1個時鍾或者兩個時鍾,同樣其不會是0、1的中間值,而是一個開發者無法確定的定值,同時你的時鍾頻率越高,亞穩態發生的概率就越大。

網上感覺都說的不明就里,對於簡單的使用多級觸發器,然后使用最高的2bit進行邊沿檢測,目測是不行的。

可以理解為仿真中的x值,但數字電路的本質上是有值的,要么0,要么1。

所以如果是檢測一個信號的上升沿,即使采到亞穩態,那就分兩種情況:

(1)邊沿比較陡峭,時鍾只采到1次亞穩態,那么邊沿寄存器的值就可能為:00、01。檢測到00,下一次就會采到01。如果是檢測到01,就是想要的沿。

(2)邊沿一點都不陡峭,導致時鍾采到兩次甚至更多次的亞問題,那這就沒得玩了。因為你不知道當前是00\01\10\11,這就可能導致你的后續邏輯完蛋了。

當然這里第二種情況不予考慮,FPGA內部不太可能出現這么糟糕的上升沿,除非你的時鍾過快,在時鍾沿到來前,亞穩態還沒有恢復過來。

極端的處理方式是(本質上是濾波):用更多位的寄存器,比如8bit,當檢測到為8'b0000_1111,那就是上升沿啦,雖然這闊以絕對可靠的檢測到邊沿,但檢測出沿的所用的時間更長,同時需要保證前級信號的高低電平長度,否則就被濾波了。。。。。。

所以,信號的邊沿陡峭性是很重要的,專業術語叫做:上升時間、下降時間。

言歸正傳,那么握手結繩法是咋樣的操作呢?

如下圖所示:有點復雜

如圖所示,則操作流程為:上述方法應該是支持多bit數據跨時鍾域的,這里的多bit說的是FPGA內部數據,pulse_clk1表示發送數據使能,clk1檢測到使能則拉高pulse_delay_clk1開始結繩。在clk2中,檢測到clk1中的結繩信號的上升沿則產生pulse_clk2,clk2檢測到pulse_clk2則啟動clk2中的結繩pulse_delay_clk2,在clk1中檢測到clk2中的結繩信號為高則解掉clk1的結繩。在clk2中檢測到clk1的結繩拉低,則解繩clk2中的結繩信號pulse_delay_clk2,完成傳輸。

待傳送數據在clk2的結繩上升沿被采樣。

busy信號為clk1中的信號,在clk1的發送數據使能到來則拉高,在pulse_delay_clk2的下降沿則拉低。

可以看到,這種方式只適合少量的慢速的數據傳輸。

 

源代碼

 

`timescale 1ns/1ps
module cdc_test (
    input               i_clk1               ,
    input               i_clk2               ,
    // input               i_rst_n              ,
    input               i_en_clk1            ,
    input     [15:0]    i_data_clk1          ,
    output              o_busy_clk1          ,
    output              o_valid_clk2         ,
    output    [15:0]    o_data_clk2             
);
////clk1
////clk1的發送使能邊沿檢測
reg [1:0] r_en_clk1_edge = 2'b00;
always @(posedge i_clk1)
begin
    r_en_clk1_edge <= {r_en_clk1_edge[0],i_en_clk1};
end
reg r_en_clk2 = 1'b0;////clk2的使能邊沿檢測
reg [1:0] r_en_clk2_edge = 2'b00;
always @(posedge i_clk1)
begin
    r_en_clk2_edge <= {r_en_clk2_edge[0],r_en_clk2};
end
reg r_en_expand = 1'b0; ////clk1的使能延長
always @(posedge i_clk1)
begin
    if (r_en_clk1_edge == 2'b01) 
        r_en_expand <= 1'b1;
    else if (r_en_clk2_edge == 2'b01)
        r_en_expand <= 1'b0;
end
reg r_busy_clk1 = 1'b0; ////傳輸忙碌指示,忙碌的時候clk1的數據不可發生改變
always @(posedge i_clk1)
begin
    if (r_en_clk1_edge == 2'b01)
        r_busy_clk1 <= 1'b1;
    else if (r_en_clk2_edge == 2'b10)
        r_busy_clk1 <= 1'b0;
end

////clk2
reg [1:0] r_en_expand_edge = 2'b00; ////clk1的擴展后的使能邊沿檢測
always @(posedge i_clk2)
begin
    r_en_expand_edge <= {r_en_expand_edge[0],r_en_expand};
end

always @(posedge i_clk2) ////clk2中的使能
begin
    if (r_en_expand_edge == 2'b01)
        r_en_clk2 <= 1'b1;
    else if (r_en_expand_edge == 2'b10)
        r_en_clk2 <= 1'b0;
end

reg r_valid_clk2 = 1'b0;
always @(posedge i_clk2)
begin
    if (r_en_expand_edge == 2'b01) ////上升沿表示數據有效
        r_valid_clk2 <= 1'b1;
    else 
        r_valid_clk2 <= 1'b0;
end
reg [15:0] r_data_clk2 = 16'd0;
always @(posedge i_clk2)
begin
    if (r_en_expand_edge == 2'b01) ////上升沿刷新數據
        r_data_clk2 <= i_data_clk1;
end

////信號輸出

assign o_busy_clk1  = r_busy_clk1;
assign o_valid_clk2 = r_valid_clk2;
assign o_data_clk2  = r_data_clk2;


endmodule // end the cdc_test model

 

仿真看看。

可以看到數據正確傳輸,不管是快到慢還是慢到快。

以后再板級測試啦。

以上。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM