FPGA跨時鍾域處理方法


        跨時鍾域的信號分為兩類,一類是單比特的信號,一類是多比特的信號。這兩類信號無論是快時鍾域到慢時鍾域還是慢時鍾域到快時鍾域,無論是流數據還是控制信號,都可以使用異步FIFO進行同步。因此下文分類的不同情景,每一種情景都可以使用異步FIFO進行同步,后文就不作介紹。但需要注意的是,快時鍾域到慢時鍾域的同步,在使用異步FIFO時,快時鍾域平均流量是不能大於慢時鍾域的處理速度的,否則數據會丟失,這其實與是否使用FIFO來進行同步無關。因為FIFO的作用本就是在某一段持續時間內,發送方發送的數據大於接收方的處理能力時,暫時作為緩存用的。若發送方的平均流量大於接受方的處理能力,那么除非FIFO無限大,否則隨着時間的增加,就會丟失數據。

1、單比特信號

         大部分文章介紹單比特信號的跨時鍾域處理時,都是默認該單比特信號為控制信號或者說變化相對較慢的單比特脈沖信號。但實際上單比特信號也存在是流數據的可能,可能實際工程上單比特的流數據信號較少,但是起碼理論上存在這種可能,如果不加以區分討論,初學者往往容易混淆。

1.1 單比特脈沖信號

1.1.1 慢時鍾域到快時鍾域

        慢時鍾域的單比特脈沖信號同步至快時鍾域,可以采用多級寄存器的方法,也就是將單比特脈沖信號在快時鍾域打多拍。一般情況下只需要采用兩級寄存器即可,因為更多級的寄存器對性能提升並不明顯。前提條件要求從慢時鍾域到快時鍾域,原因是只有慢時鍾域到快時鍾域,才能保證慢時鍾域的脈沖信號能被快時鍾域采樣到。

        需要注意的是,多級寄存器的主要作用是避免亞穩態的傳播(不能完全消除亞穩態,但可以使亞穩態出現的概率大大降低),並不能保證數據穩定后,是正確的值,而是隨機的0或1。但是由於慢時鍾域的脈沖信號持續時間大於快時鍾域的一個周期,因此在快時鍾域的下一個上升沿到來時,慢時鍾域的脈沖信號仍然持續,此時快時鍾域可以采到正確的值。也就是說,出現亞穩態時,快時鍾域實際上需要已經對慢時鍾域的信號進行第二次采樣了。顯然地,第二次采樣時,需要滿足建立時間與保持時間,否則可能會再次出現亞穩態。因此可以看出快時鍾域與慢時鍾域的關系並不是任意的,兩者並不能接近到無法滿足第二次采樣的建立時間和保持時間。快時鍾域域慢時鍾域需要滿足下面的條件:Tslow>Tfast+Thold+Tsetup,其中Tslow為慢時鍾域的時鍾周期,Tfast為快時鍾域的時鍾周期,Thold與Tsetup分別為快時鍾域寄存器的保持時間和建立時間。通過上面的討論我們可以發現,當使用多級寄存器時,如果出現了亞穩態,快時鍾域 能夠 采到慢時鍾域信號所需的時間 比沒有出現亞穩態時 可能會多一個周期。如果有彼此關聯的兩個多比特信號,比如說地址信號,它們從慢時鍾域同步至快時鍾域時,可能到達快時鍾域的時間時不一樣的,那么得到的地址就是錯誤的,這就是多比特信號即使是從慢時鍾域到快時鍾域,也不能夠使用多級寄存器同步的原因。但如果多比特信號彼此無關,從慢時鍾域到快時鍾域時,是可以使用多級寄存器同步的。

代碼如下:

  1.  
    module syn(
  2.  
    input rsta_n,
  3.  
    input clka,
  4.  
    input dataa,
  5.  
    input rstb_n,
  6.  
    input clkb,
  7.  
    output datab
  8.  
    );
  9.  
     
  10.  
    reg syn1;
  11.  
    reg syn2;
  12.  
     
  13.  
    always@(posedge clkb or negedge rstb_n) begin
  14.  
    if(!rstb_n) begin
  15.  
    syn1<=1'b0;
  16.  
    syn2<=1'b0;
  17.  
    end
  18.  
    else begin
  19.  
    syn1<=level;
  20.  
    syn2<=syn1;
  21.  
    end
  22.  
    end
  23.  
     
  24.  
    assign datab=syn2;
  25.  
     
  26.  
    endmodule

2、快時鍾域到慢時鍾域

(1)使用握手信號

        快時鍾但慢時鍾域的單比特信號同步可以使用握手信號。握手信號的使用相對來說耗時較長,如果快時鍾域的信號變化較快,是無法使用握手信號來進行同步的,否則慢時鍾域可能會漏采快時鍾域的信號。(下圖輸出的不是脈沖信號而是電平信號,與下文的代碼有點區別)

 

verilog代碼如下:

  1.  
    module syn(
  2.  
    input clka,
  3.  
    input rsta_n,
  4.  
    input bit_in,
  5.  
    input clkb,
  6.  
    input rstb_n,
  7.  
    output bit_out
  8.  
    );
  9.  
    reg req;
  10.  
    reg req_f;
  11.  
    reg req_ff;
  12.  
    reg req_fff;
  13.  
     
  14.  
    wire ack;
  15.  
    reg ack_f;
  16.  
    reg ack_ff;
  17.  
     
  18.  
    //使用req信號對a時鍾域數據進行保持
  19.  
    always@(posedge clka or negedge rsta_n) begin
  20.  
    if(!rsta_n)
  21.  
    req<=1'b0;
  22.  
    else if(ack_ff)//ack信號為高時,不接收新的數據
  23.  
    req<= 1'b0;
  24.  
    else if(bit_in)
  25.  
    req<=1'b1;
  26.  
    else
  27.  
    req<=req;
  28.  
    end
  29.  
     
  30.  
    //將a時鍾域的req信號同步至b時鍾域
  31.  
    always@(posedge clkb or negedge rstb_n) begin
  32.  
    if(!rstb_n) begin
  33.  
    req_f <=1'b0;
  34.  
    req_ff<=1'b0;
  35.  
    end
  36.  
    else begin
  37.  
    req_f <=req;
  38.  
    req_ff<=req_f;
  39.  
    end
  40.  
    end
  41.  
    //在b時鍾域產生單個數據脈沖
  42.  
    always@(posedge clkb or negedge rstb_n) begin
  43.  
    if(!rstb_n)
  44.  
    req_fff<=1'b0;
  45.  
    else
  46.  
    req_fff<=req_ff;
  47.  
    end
  48.  
    assign bit_out=~req_fff&req_ff;
  49.  
    //將b時鍾域的ack信號同步至a時鍾域
  50.  
    assign ack=req_ff;
  51.  
    always@(posedge clka or negedge rsta_n) begin
  52.  
    if(!rsta_n) begin
  53.  
    ack_f <= 1'b0;
  54.  
    ack_ff <= 1'b0;
  55.  
    end
  56.  
    else begin
  57.  
    ack_f <= ack;
  58.  
    ack_ff <= ack_f;
  59.  
    end
  60.  
    end
  61.  
     
  62.  
    endmodule

testbench:

  1.  
    `timescale 1ns / 1ps
  2.  
     
  3.  
    module tb(
  4.  
     
  5.  
    );
  6.  
     
  7.  
    reg clka,clkb;
  8.  
    reg bit_in;
  9.  
    reg rsta_n,rstb_n;
  10.  
    wire bit_out;
  11.  
     
  12.  
    syn test(
  13.  
    .clka(clka),
  14.  
    .rsta_n(rsta_n),
  15.  
    .bit_in(bit_in),
  16.  
    .clkb(clkb),
  17.  
    .rstb_n(rstb_n),
  18.  
    .bit_out(bit_out)
  19.  
    );
  20.  
     
  21.  
    initial begin
  22.  
    clka=1'b0;
  23.  
    clkb=1'b1;
  24.  
    rsta_n=1'b0;
  25.  
    rstb_n=1'b0;
  26.  
    bit_in=1'b0;
  27.  
    #28
  28.  
    rsta_n=1'b1;
  29.  
    rstb_n=1'b1;
  30.  
    end
  31.  
     
  32.  
    always #2 clka=~clka;
  33.  
    always #7 clkb=~clkb;
  34.  
     
  35.  
    initial begin
  36.  
    #98
  37.  
    bit_in =1'b1;
  38.  
    #4
  39.  
    bit_in =1'b0;
  40.  
    #60
  41.  
    bit_in =1'b1;
  42.  
    #4
  43.  
    bit_in =1'b0;
  44.  
    #8
  45.  
    bit_in =1'b1;
  46.  
    #4
  47.  
    bit_in =1'b0;
  48.  
    #300
  49.  
    bit_in =1'b1;
  50.  
    end
  51.  
     
  52.  
    endmodule

仿真時序圖:

從仿真圖可以看出,對於快時鍾域的兩個脈沖離得比較近的話,慢時鍾域是會漏采的,使用握手信號時,對此需要注意。

(2)T觸發器 + 多級觸發器

        對於單比特的脈沖信號,我們也可以使用T觸發器 + 多級觸發器的方法來進行同步,這種方法相較於使用握手信號所需時間較短,但沒有ack信號,無法判斷接受方是否接受到了脈沖信號。因此使用時一定要保證 滿足使用條件。

        T觸發器的真值表達式為 Qn+1 =T⊕Qn。總結來說的話,就是每來一個周期的高電平,輸出就翻轉一次。我們利用這個特性,可以將單比特的信號展寬。就是說在兩個脈沖之間的信號是保持不變的,不管保持的是0還是1並不重要,我們只要知道脈沖到來之時,T觸發器的輸出會翻轉就足夠了。只要信號發生了變化,我們在進行同步的時鍾域多打一拍,並與前一拍的信號進行異或就可以得到一個周期的脈沖,雖然b時鍾域采到的並不是脈沖,但是異或之后得到的就是一個脈沖。

        這種方法的本質實際上是將信號展寬,只不過展寬的信號可能是0也可能是1。但很顯然,a時鍾域的兩個脈沖間隔要足夠大,因為兩個脈沖的信號的間隔就是a時鍾域的信號持續的時間。如果這個時間太短,在b時鍾域是無法采到的。兩個脈沖之間的間隔要大於Tb+Thold+Tsetup,其中Tb為b時鍾域的時鍾周期,Thold與Tsetup分別為b時鍾域寄存器的保持時間和建立時間。

verilog代碼如下:

  1.  
    module syn(
  2.  
    input rsta_n,
  3.  
    input clka,
  4.  
    input plusea,
  5.  
    input rstb_n,
  6.  
    input clkb,
  7.  
    output pluseb
  8.  
    );
  9.  
     
  10.  
    reg level;
  11.  
    reg syn1;
  12.  
    reg syn2;
  13.  
    reg syn2_f;
  14.  
     
  15.  
    //將a時鍾域的脈沖信號轉為電平信號
  16.  
    always@(posedge clka or negedge rsta_n) begin
  17.  
    if(!rsta_n)
  18.  
    level<=1'b0;
  19.  
    else if(plusea)
  20.  
    level<=~level;
  21.  
    else
  22.  
    level<=level;
  23.  
    end
  24.  
     
  25.  
    //用兩級寄存器同步電平信號
  26.  
    always@(posedge clkb or negedge rstb_n) begin
  27.  
    if(!rstb_n) begin
  28.  
    syn1<=1'b0;
  29.  
    syn2<=1'b0;
  30.  
    end
  31.  
    else begin
  32.  
    syn1<=level;
  33.  
    syn2<=syn1;
  34.  
    end
  35.  
    end
  36.  
     
  37.  
    //在b時鍾域將同步過來的電平信號轉為脈沖信號
  38.  
    always@(posedge clkb or negedge rstb_n) begin
  39.  
    if(!rstb_n)
  40.  
    syn2_f<=1'b0;
  41.  
    else
  42.  
    syn2_f<=syn2;
  43.  
    end
  44.  
    assign pluseb=syn2^syn2_f;
  45.  
     
  46.  
    endmodule

1.2 單比特流數據

        對於單比特流數據而言,無論是快時鍾域到慢時鍾域,還是慢時鍾域到快時鍾域,如果不使用RAM或者FIFO這類存儲空間,想直接將數據通過流的方式進行同步,是無法做到的。這是因為兩個時鍾域的時鍾周期長度不一樣,隨着時間的積累,一定會發生數據的錯位。因此若想同步跨時鍾域的流數據,必須要借助存儲器空間,否則是無法同步流數據的。需要注意的是,快時鍾域到慢時鍾域的流數據,是不能一直持續的,否則就需要無限大的存儲空間,這在文章開頭已經提到了。

2、多比特信號

2.1 多比特單

(1)方法一:DUUX實現CDC

控制信號tx_sel經兩級寄存器同步后作為多路選擇器的sel信號,cdc_d為發送時鍾域多比特數據。tx_sel信號與cdc_d信號都需要持續一定的時間以保證能被接收時鍾域采到。 

2.2 多比特流數據

        分析方法同但比特的流數據。

---------------------------------------------------------------------------------------------------------------------------------

        最后需要說明的一點是,除了異步FIFO,當一個時鍾域的信號送入另一個時鍾域時,都需要另一個時鍾域使用兩級寄存器進行打拍,這是為了避免出現亞穩態的傳播。但需要注意的是,一個時鍾域的信號送入另一個時鍾域時,這個信號必須時寄存器輸出的。這是因為若不是寄存器輸出,輸入另一個時鍾域時,就可能產生毛刺,會加大出現亞穩態的概率。雖然說即使出現亞穩態,多級寄存器同步后大概率數據也是會穩定下來的,但是發生故障的概率會隨着亞穩態出現次數的增加而增加,系統的穩定性會受到影響。更為重要的是,毛刺產生的亞穩態會導致出現一個不想要出現的0或1。這我們之前討論的亞穩態有所不同,之前討論的出現了亞穩態,數據穩定下來后,最多也就是數據推遲一個周期來到,但這個數據我們還是需要的。但是因毛刺產生的亞穩態穩定之后,產生的0或1是我們不需要的,這個不需要的0或1如果出現在后續的電路中,且后續的電路有較強的因果關系時,整個系統都會出現錯誤,且難以排查。

參考鏈接:

1.你真的懂2-flop synchronizer嗎-- CDC的那些事(2)

2.常見數電面試題Pulse Synchronizer -- CDC的那些事(3)

3.多bit信號跨時鍾域怎么辦?


免責聲明!

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



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