Try   HackMD

Clock domain crossing

< 老李>

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

怎麼解決?

1. 打兩拍 ( 2-flop)

使用double flop來同步,有個最基本的“3個沿”要求,就是source data必須保證穩定不變至少碰見destination clock 3個連續的沿,這個沿可以是上升沿也可以是下降沿,持續3個沿之後才能變,否則就有可能在destination clock domain根本看不到這個data的變化。 例如下圖所示:adata第一次變高,只碰到了bclk的2個沿,就可能導致bdata根本沒有看到這個pulse,而第二次adata變高,持續了3個沿,這樣bdata就能夠確保也可以變高了。

always_ff @(posedge clk) begin if (rst) {rptr_t2,rptr_t1} <= 0; else {rptr_t2,rptr_t1} <= {rptr_t1,rptr}; end

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  1. 利用double flop,bdata發生變化可能是在adata翻轉之後1個週期,也可能是2個週期,這是由於第一級flop的metastable可能會resolve在不同的值。 如果第一級flop 穩定在和adata相同的值,那麼就只需要1個週期就能看到bdata翻轉。 而如果第一級flop 穩定在和adata相反的值,那麼則需要再多一個週期。 所以在設計和模擬驗證中,不能假定bdata一定會在2個周期之後發生變化,而是將這個因素隨機在模擬中,有的時候真的會暴露出設計中的問題。
  2. 我們說的單bit信號,有人可能會說,組合邏輯的輸出可不可以用double flop呢? 比如一個AND的輸出,不也是單bit嗎? 答案很簡單,不可以。 原因就是組合邏輯的輸出可能會有毛刺,這些毛刺會增大第一級flop產生metastable的概率,進而影響整個synchronizer的MTBF。 所以對於任何單bit信號,在跨時鐘域之前一定要先寄存(flop),只有flop的輸出才能經過synchronizer. 下面的圖就是不flop的情形。
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

在這個例子中,adata在aclk域一個週期就翻轉了,其實是aclk域的一個pulse信號。 對於pulse信號,我們其實不應該用double flop來同步,原因很簡單,就是上面所示的可能丟失pulse。

2. Syn pulse

For 單bit信號(eg. enable)

pulse信號在設計當中很常見,通常在某個時鐘沿變高,在下一個時鐘沿變低。 我們上期舉了下面的例子,可見當aclk頻率比bclk頻率高的時候,adata變高一個週期,那麼有可能bclk的時鐘沿根本看不到這個變化,或者有時候即使能被bclk采到一次,也可能無法滿足3個沿的要求,導致無法用常見的2flop synchronizer來去sync。

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

那我們應該要怎麼解呢?

  1. 將Aclk的pluse訊號轉為一個level信號
  2. 2-flop synchronizer
  3. 在Bclk中將同步過來的level信號轉為pulse
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →

    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →
module sync_pulse ( input aclk, arst, input bclk, brst, input Ain, output logic Bout ); logic Aintog, syn_Aintog_tmp, syn_Aintop, reg_syn_Aintop; assign Bout = syn_Aintop ^ reg_syn_Aintop; // q 1T pulse //toggle flop always_ff @(posedge aclk or posedge arst) begin if (arst) Aintog <= 1'd0; else Aintog <= Aintog ^ Ain; end //sync 2ff always_ff @(posedge bclk or posedge brst) begin if (brst) syn_Aintog_tmp <= 1'd0; else syn_Aintog_tmp <= Aintog; end always_ff @(posedge bclk or posedge brst) begin if (brst) syn_Aintop <= 1'd0; else syn_Aintop <= syn_Aintog_tmp; end //reg syn_Aintog always_ff @(posedge bclk or posedge brst) begin if (brst) reg_syn_Aintop <= 1'd0; else reg_syn_Aintop <= syn_Aintop; end endmodule

既然這個pulse synchronizer中間利用了2flop,那麼2flop的3edge要求就必須要滿足,換句話說,我們轉化成為的level的信號Tq要足夠長。 如果Tq不滿足bclk的3edge要求,那麼這個level信號我們就無法同步過去,也就無法產生bclk的pulse了。 而Tq每次變化是由於aclk來了一個新的pulse,這也就是要求aclk的連續兩個pulse之間的間隔要足夠大,要滿足bclk的3edge要求。

aclk時鐘域最接近的兩個pulse能靠多近呢,顯然就是兩個pulse中間只有一個aclk週期,這其實就是將aclk進行了2分頻。 那麼相應的,Tq就是對aclk進行了4分頻,每個Tq的level持續時間是2個aclk cycle,這2個cycle需要滿足bclk的3edge要求,如果滿足,那麼就可以保證bclk域也可以產生每個cycle的pulse的要求。

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

如果無法滿足,那麼我們就要另想辦法,如果還是要使用pulse synchronizer這個電路,我們繼續祭出反饋大法。

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

本質思路就是我們不能讓aclk域的pulse產生得很快,而是要等到pulse同步到bclk之後才能繼續產生下一個pulse,於是我們要將b_p重新synchronize回到aclk域,作為放行下一個pulse的條件。

可是這個反饋的辦法要求每個pulse產生都要同步回來,這樣做的效率不高,有沒有更快的辦法呢? 我們想像有一個細長的管子,管子的一頭我們往裡面塞玻璃球,每塞進去個玻璃球對應一個aclk時鐘域的pulse,管子的另一頭我們往外彈玻璃球,每彈出一個玻璃球對應一個bclk時鐘域的pulse。 (哎,是不是嗅到了一點FIFO的味道了? )那麼除非假定管子無限長,那麼我們往管子里塞進球的平均速率不能永遠大於從管子里彈出球的速率,我們可以允許某一段時間塞進球的速率高,管子作為一個緩衝區,來容納之前塞入但是還沒有彈出的球,但是當管子塞滿的時候,我們必須得停下塞進球的動作,等待彈出球的那邊彈出球之後才能騰出空間來繼續塞。 也就是說,當管子不是無限長時,儘管兩邊的時鐘速率不同,能夠保持一一對應的條件是兩邊塞球彈球的平均速率是一樣的。 這就是利用FIFO來解決的思路。 如果FIFO告訴aclk說FIFO滿了,那麼aclk域就得停止產生pulse,如果不滿,就可以繼續產生pulse,最後我們把FIFO裡面的元素完全彈完,就可以做到一一對應了。

For 多bit信號 (eg. Data, counter)

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

注意adata從2'b00變到2'b11,一段時間之後再變為2'b00,但是因為2flop synchronizer的delay有隨機性,可能是一個周期之後就同步過去了,也可能需要兩個週期。 這樣我們就可能在bdata上看到一個週期的2'b01,之後也可能看到一個週期的2'b10,這兩個值都是adata沒有出現過的,也就是說bdata出現了錯誤的值。

How to slove ???

  1. for load signal
    1. 在aclk時鐘域產生一個load_aclk信號,load_aclk為1'b1時代表多bit data信號穩定
    2. load_aclk信號本身利用double flop同步到bclk時鐘域得到load_bclk
    3. bclk時鐘域可以直接利用flop來load bus信號
      Image Not Showing Possible Reasons
      • The image was uploaded to a note which you don't have access to
      • The note which the image was originally uploaded to has been deleted
      Learn More →

第一,要有專門的邏輯保證aload為高的時候data_aclk不變。

第二,在aload為1'b1的時候,data_bclk會持續load data_aclk, aload從0—>1是ok的,但是1—>0會發生錯誤,因為data_aclk是不穩定的!

第三,aclk時鐘域怎麼知道data_aclk已經被成功傳到bclk時鐘域,從而可以更新下一組data了呢?

How to solve???

我們首先看如何解決第二個問題。 我們其實需要的是:當load_aclk變高的時候,把data_aclk當前的值同步過去之後就行了,並不需要持續load。 這個時候我們上一篇講的pulse synchronizer就派上用場了,我們讓load_aclk是一個pulse,然後把這個pulse同步過去,這樣data_bclk只會load一次

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

可是這個還是沒有辦法解決第三個問題,要解決它,我們只能繼續引入反饋大法:把信號從bclk時鐘域反饋回來,告訴aclk時鐘域load成功,可以更新下一個數據了。 如下圖所示,aclk時鐘域的load_aclk是由一個valid/ready的握手邏輯產生。 我們可以把load_bclk再利用pulse synchronizer同步回去,從而讓ready_aclk為1,這樣我們就知道data_aclk肯定已經被同步到了bclk時鐘域,可以更新下一個data了。

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

適用情況
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  1. 無法化簡為單bit信號跨時鐘域傳遞
  2. 適用於非高速傳輸的場合,即在source 時鐘域的多bit信號可以保持穩定一段時間,而不是時刻都在變化,可以有一個明確的load視窗
  3. load信號為高時必須保證多bit信號穩定不變
  4. 如果沒有連續同步數據的要求,可以適當使用不帶反饋的
  1. For no load signal
    儘管我們看到了2'b01和2'b10這兩個錯誤的值,但是這兩個值中間可是2'b11是正確的值啊,而且2'b11至少持續了3個周期,那麼我們其實可以設計一個比較邏輯,利用2flop synchronizer同步到bclk時鐘域之後,再用兩級flop把bdata打兩拍,然後比較這3級的值,如果這三級flop的值是相同的, 那不就證明2flop synchronizer同步到的值是穩定的嗎? 我們可以用三級flop的值相等作為一個update信號,來updata最後輸出級的flop(輸出級沒有畫)。
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →

    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →
always_ff @(posedge clk2) begin if(rst2) begin wtocnt1 <= 32'b0; wtocnt2 <= 32'b0; wtocnt3 <= 32'b0; end else begin wtocnt1 <= wtocnt; wtocnt2 <= wtocnt1; wtocnt3 <= wtocnt2; end end

需要data變化的頻率不高的狀況

3. Asynchronous FIFO 異步

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

同步FIFO(Syn)就是說進口(寫入端)和出口(讀出端)是同一個時鐘域。 FIFO一般深度多於1,就需要兩個指標: write pointer和read pointer(可用2 flip-flop來做)。
image

異步 FIFO = push 和 pop 在不同的時域
對pop來說FIFO是不是空的是重點,對push來說就是滿不滿才是重點
Gray code
連結 : https://www.youtube.com/watch?v=4FMmDabL4wY

image

  1. 每相鄰的兩個編碼之間有且只有一位不同
  2. 當第N位從0變到1的時候,之後的數的N-1位會關於前半段軸對稱,而比N位高的位是相同的。

老李所說的軸對稱是什麼意思呢? 請看Gray code前兩個數4'b0000, 4'b0001,它們倆之間可以畫一條對稱軸,第1-3位都是相同的。 再看前4個數,在4'b0001和4'b0011之間畫一條對稱軸,第2、3位是相同的,第0位則是軸對稱的。

bnary to Gray g(3) = b(3) ^ 0 g(2) = b(3) ^ b(2) g(1) = b(2) ^ b(1) g(0) = b(1) ^ b(0) g(n) = b(n+1) ^ b(n) Gray to Binary b(3) = g(3) b(2) = g(3) ^ g(2) b(1) = g(3) ^ g(2) ^ g(1) b(0) = g(3) ^ g(2) ^ g(1) ^ g(0)

關鍵就在於它的第一個特點:相鄰兩個編碼之間有且只有1位不同。 我們說multi-bit如果在一個時鐘沿有多個bit同時翻轉,在另外一個時鐘域采到的時候由於2flop 穩定需要1個或2個週期,所以可能會出現錯誤的值。 Gray code這種編碼,從根本上就沒有這個問題,因為以Gray code編碼作為計數器,每個時鐘沿來的時候只會有1個bit發生了翻轉,其餘所有bit都是穩定的! 這樣即使這一個bit在用2flop synchronizer同步到另外一個時鐘域時,可能需要1個周期發生變化,或者2個週期,在發生變化前,另一個域的值就是之前的穩定值,變化后就是新的值,而不會出現其他不該出現的值。

我們需要做的就是在同步pointer的時候把binary pointer 轉化為gray code pointer,然後用2flop synchronizer同步到對面時鐘域之後,再來判斷空滿。

假設FIFO一開始一直寫,不讀,寫滿8個entry後write pointer 的binary變成4'b1000, gray code是4'b1100, 而read pointer的gray code是4'b0000,可以看到高兩位是相反的,之後的低位是相同的。 再舉個例子,假設write pointer 的gray code到了4'b1011, 而這個時候read pointer如果是4'b0111,那麼也是8個entry滿了。
HINT : 差八格 = Gray code中兩個bits不相同 or 前二不同,後二相同
image

assign full = (write_ptr_gray[N:N-1] == ~read_ptr_gray[N:N-1]) &&(write_ptr_gray[N-2:0] == read_ptr_gray[N-2:0]);

如果兩個Pointer低位全等,最高位不等,那就是「滿」;

如果兩個Pointer低位全等,最高位也相等,那就是“空”。

那真空假空真滿假滿的問題呢?
https://zhuanlan.zhihu.com/p/24439677

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

假滿空
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

assign Empty = (WR_Pointer_syn[4:0]== RD_Pointer[4:0]); assign Full = (WR_Ponter[4] != RD_Pointer_syn[4]) && (WR_Ponter[3:0] == RD_Pointer_syn[3:0]);

真滿空

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

之前我們是在寫時鐘域判斷「滿」信號,在讀時鐘域判斷「空」信號,得到了「假」滿空。

如果我們在寫時鐘域判斷「空」信號,在讀時鐘域判斷「滿」信號,得到的就是「真」滿空!

assign Empty_Real = (WR_Pointer[4:0] == RD_Pointer_syn[4:0]); assign Full_Real = (RD_Pointer[4] != WR_Pointer_syn[4]) && (RD_Pointer[3:0] == WR_Pointer_syn[3:0]);

若是FIFO 深度不等於2的冪次呢?

eg. depth = 7?
image
比如從4'd15到4'd0,也只有1位不同。 但是如果不是2的冪次,比如DEPTH=7,那我們怎麼樣來利用Gray code呢? 直接從4'b0000到4'b0101肯定是不行的,因為4'b0101變到4'b0000有兩個bit發生了變化,這樣我們就沒法利用2flop synchronizer來同步了。 解決這個辦法的訣竅其實就是老李上一篇提到的gray code的第二個性質:gray code每一位是有個對稱軸的。 我們可以這樣編碼,addr==0的時候gray code不從4'b0000開始,而是從4'b0001開始,直到4'b1001來wrap around,這樣從4'b1001->4'b0001依然只有一個bit翻轉。 同理,如果是depth=6,那麼我們繼續往裡收縮1位,只利用gray code關於對稱軸兩側的部分編碼,從4'b0011到4'b1011,我們可以看到,這樣的編碼依然可以保證相鄰兩個碼之間只會有1位變化。
image
image

FIFO Extra Bits

一般情況下深度為N=2^n的FIFO其位址的位寬為n,其讀寫位址的位寬也為n。 共有N個儲存單元,若數據位寬為W則該FIFO的容量即為N*W bit。

現在在指標中添加額外的位(extra bit,即位址的MSB)變為n+1bit,該extra bit用於指示寫指標是否遞增並越過最後一個FIFO位址,若越過則將該MSB加1,其它位清零。 對讀指標也進行同樣的操作。 如對於深度為8的FIFO,需要採用3+1bit是計數器:0000-1000、1001-1111,MSB作為折回標誌,而低3位作為位址指標。

那麼判斷機制為:

***如果兩個指標的MSB不同,就說明寫指標比讀指標多折回一次:如r_addr=0000,且w_addr=1000,為滿;

如果兩個指標的MSB相同,就說明兩個指標折回次數相等。 再者其餘位相等(則說明FIFO為空。*
image

Asyn reset vs syn reset

同步reset需要時鐘,而異步reset不需要時鐘。 如果說你的模組需要在沒有時鐘的時候複位,那只有異步reset能夠做到,這也是絕大多數晶元的上電複位信號(PowerOn Reset)以及一些PHY比如USB的內部需要異步reset的原因。 而在一些IP中,如果你可以等到時鐘開始翻轉之後再複位,時鐘開始翻轉之前內部即使沒有複位也沒有關係的話,那麼就可以用同步reset。

syn reset
image

always_ff @(posedge clk) begin if (!reset_n) begin q2 <= 1'b0; end else begin // q2 <= ... end end

Asyn reset
image

always_ff @(posedge clk or negedge reset_n) begin if(!reset_n) begin q2 <= 1'b0; end else begin //q2 <= ... end end

從綜合出來的邏輯可以看出,異步reset由於對寄存器之間的datapath沒有貢獻,所以在timing上面能夠略微比同步reset好一些,特別是reset信號作為一個負載很大的信號,如果reset tree做得不好可能使得reset path的combo delay變得很大,反而限制了performance的提高。 所以在對logic depth摳得很細的設計中,可以使用異步reset來避免引入更多的combo delay。

但是同步reset還有一個優勢,由於reset信號會最終起作用在寄存器的D輸入端,那麼通過reset的組合邏輯都會被STA所約束,也就是說reset信號和其他datapath的信號一起要滿足寄存器的setup time, hold time, min pulse等一系列check,在timing close的情況下我們可以拍著胸脯保證:寄存器不會因為reset信號的變化產生metastable。

如果使用異步reset,reset assertion是異步的,但是reset release一定要和時鐘同步!

因為對於reset assertion,reset active之後flop的值是穩定在reset value的,只要reset繼續active,來多少個clock,其他datapath上的信號怎麼變,flop的值都不會變化,所以reset 在什麼時候assertion都沒關係。 但是reset release就不一樣了,一旦reset從active變為in-active,那麼flop之後的值就得取決於其他信號輸入和時鐘沿的關係了,所以對於reset de-assertion,在STA里有兩個專門的參數來check,叫做recovery time和removal time。

recovery time指的是reset release之後要求距離下一個時鐘沿的最小間隔,可以類比於其他信號datapth上的setup time

removal time指的是reset release之後要求距離上一個時鐘沿的最小間隔,可以類比於其他信號datapth上的hold time

換句話說,reset release必須在recovery time和removal time加起來這個視窗之外,這樣才能保證寄存器不會產生metastable。

VSD HW

image