上一篇介紹了利用Flip Flop來作為存儲單元的同步FIFO設計。這一篇咱們來看如何利用2 Port SRAM來作為存儲單元設計同步FIFO。
開始往下讀之前,老李先問一個問題,假如現在讓你設計一個深度為N的基于2port SRAM的同步FIFO,請問至少需要多大的SRAM? 假設SRAM的位寬就是你的數據寬度,那么問題就是問你需要的SRAM的行數至少是多少?如果你覺得答案是顯而易見的N,那么你值得讀完這一篇。
首先來說為什么要用SRAM設計FIFO,很簡單為了省面積。我們說存1bit的數據,SRAM里一個bit cell經典結構是6個晶體管,而一個flip flop需要的晶體管在20個左右,那么從面積上來說,肯定是SRAM小對吧?但是這里面有個平衡點,對于一塊SRAM,算面積的時候不能僅僅算里面存儲單元bit cell的面積,還要算外圍的decode邏輯的面積。當SRAM的row size x column size比較小的時候,外圍的decode邏輯占的比重比較大,反而這個時候SRAM的總面積不如row x column個flop的面積小。至于這個轉折點要看工藝和memory compiler的data sheet,具體面積要拿SRAM的data sheet去比較。老李自己總結的一個經驗,不保證完全準確,在5nm工藝下,這個轉折點大概在2k bit,低于2k bit,比如16x32的大小,那還是Flop劃算,如果遠大于2k bit,那么就用SRAM,差不多在2-3k bit量級的時候要根據memory datasheet里的area number來比較。
再來說說什么是2 port SRAM,2 port 通常也被稱作dual port,一個端口為寫端口,一個端口為讀端口。這兩個端口可以同時工作,同一個周期內既可以讀,也可以寫,簡化的框圖如下圖所示
WCEN:Write Chip Enable Neg,當這個信號為0的時候,要寫入數據
WDATA: 要寫入的數據
WADDR:要寫入的地址
RCEN:Read Chip Enable Neg,當這個 信號為0的時候,要讀出數據
RDATA: 讀出的數據
RADDR: 讀出的地址
這里要注意,要寫入的數據是指WCEN為0那個周期的WDATA,而讀出的數據并不是RCEN為0那個周期的RDATA,而是下一個周期的RDATA。
時序圖如下圖所示
可以看到,cycle 1進行寫操作,在地址A0寫入數據D0。在cycle 2進行讀操作,要在cycle 3才能讀出D0。那你一定好奇如果給同一個地址在同一個周期又讀又寫怎么辦呢?這個不同的foundary的sram可以有不同的實現,這里表示的是一個較為常見的實現,看cycle 7,讀寫同時發生并且在同一個地址,在cycle 8讀出的還是之前寫入在這個地址的數據D0,而不是cycle 7寫入的新數據D1。只有再讀一次A0,你才能看到D1。
下面我們來考慮如何設計基于2port SRAM的同步FIFO。既然FIFO有push和pop端,也有wdata和rdata,那剛好對應SRAM的write port和read port,看起來我們大概只需要直接把SRAM包起來,用兩個計數器來分別計算write pointer和read pointer就可以了,如下圖所示
真的這么簡單嗎?我們來思考一個問題,用上面的FIFO來存第一個數D0,利用上面的結構,q能夠在push的下一個周期變為D0嗎?
回顧一下我們想要的FIFO的時序
在cycle1的時候我們push D0,我們期望Q在cycle 2的時候就可以輸出FIFO最頭上的數D0,而且注意,這個時候我們并沒有進行pop操作。
可是從上面SRAM的時序圖我們可以看到,要想讓RDATA輸出D0,我們至少要讓RCEN為0一個周期,而且RCEN為0必須得在WCEN為0之后,讓RDATA拿到D0最快也到了WCEN之后的2個周期,因而不滿足在PUSH之后下一個周期Q就輸出D0。
另外還有個問題,當我們push進去一個數之后,如果這個數寫入了SRAM,那么我們必須要有一次讀SRAM的操作,才能把數讀出來,這在上面的框圖里也就是要執行一次pop。這也和FIFO的工作相背離,因為FIFO可能并不是需要立刻pop。
那么我們怎么做才能讓Q在push的下一個周期輸出D0呢?環顧四周,好像除了利用Flip Flop也沒有別的辦法了。也就是說 ,第一個數我們把數不寫進SRAM,而是寫到一個Flop里,然后讓Q從Flop輸出 。
等等,說好了用SRAM來存數據的,怎么又把數據存到Flop里面去了呢?
別急,這里確實是沒有辦法的辦法,如果我們有可以和Flop時序一樣的SRAM,那么也可以不用這種辦法,但是在接受SRAM的時序是這樣的情況下,我們必須借助Flop來實現第一個數在push之后下一個周期就能夠出現在Q上。
當然,我們不是說所有的數都要用Flop來存,接下來push的數據我們還是要放在SRAM里面的,我們把上面的結構改一改,可以先設計出下面的FIFO結構。
在這個結構中,我們從D可以直接把數據存到輸出級的這個Flop中,相當于bypass了SRAM,然后我們的想法是:當第二次push的時候,我們再把數據存到SRAM里,同時,當我們把第一個數據pop出來之后,我們就把數據從SRAM里拿到這個輸出級的Flop中,這樣是不是就對了呢?
不好意思,其實還是有問題,我們假設FIFO里面已經存了2個數據,D0存在輸出級的Flop里,D1存在SRAM里,如下圖所示
那么當我們要pop一次之后,我們期望的FIFO的時序是:在pop的下一個周期,Q就應該是D1了,因為D0被彈出,FIFO最頭上的數是D1了。如下圖所示
但是如果在pop為1的那個周期去讀SRAM,則要在下一個周期RDATA才能讀出D1,再還需要一個周期才能把D1存到輸出級的Flop上,也就是pop之后兩個周期才能看到D1, 這樣就不滿足FIFO的時序了。
那怎么辦呢?我們只好省去把D1從RDATA存到Flop上的那一步,而是把RDATA當做Q來直接輸出。電路結構變成了下面
我們的設計思路就變成了
- 往FIFO里push的第一個數要bypass SRAM,把數據直接存到輸出級Flop去。
- 如果SRAM里面存了數,那么pop一次,就要把RDATA直接輸出到Q端。
看起來沒有問題了吧?其實還有一個特殊情況沒有考慮到,也就是當FIFO里只有一個數據,而在push下一個數據的時候同時來了pop。如下圖所示
那么這個時候要push的D1是不能往SRAM里寫的,一旦寫進去要讀出來還得多花一個周期,所以這個時候也是要bypass SRAM。
所以更加嚴謹的條件是
- 往FIFO里push的第一個數要bypass SRAM, 或者FIFO里只有1個數據,而且在push新數據的同時pop ,那么把數據直接存到輸出級Flop去。
- 當FIFO里只有一個數據的時候,Q端來自于輸出級Flop
- 如果SRAM里面存了數,那么pop一次,就要把RDATA直接輸出到Q端。
至此,我們利用一級flip flop來實現了省去一個讀SRAM的周期,可以實現FIFO的時序。
現在可以回答文章開頭的問題了,因為有了輸出級Flop來存第一個數據,那么SRAM其實并不需要存N個數據,只需要存N-1個數據就好了。但是在實際工作中,Width x N 和Width x (N-1)的面積其實沒有差很多,你用Width xN的SRAM完全沒有問題。當然要注意,這個時候memory 的address pointer和我們上一講里的wr_ptr, rd_ptr就不是完全一樣了,因為第一個數據并不是存在SRAM里。
寫到這里,是不是就完全搞定問題了呢?我們來分析一下,我們這樣設計可行其實是有一個前提,即SRAM的RDATA的timing是下面的。這里把開頭的圖再看一遍
我們說FIFO的Q在不pop的時候是穩定不變的,是利用了這里SRAM的特性,即讀完一次之后,RDATA的值會保持不變,一直到下一次讀操作(圖中的cycle 3-7),這樣我們就可以直接把RDATA輸出到Q。
但是并不是所有廠家的SRAM的時序是這樣的,大家要看廠家的memory的datasheet來確認時序(TSMC家的是這樣的, 但是可能別的廠家并不保證)如果SRAM時序是下面這樣,那么我們的設計就不能滿足FIFO的要求了
而且上面的設計可能還有一個STA上的問題,因為Q來自于RDATA,那么SRAM內部的clock-to-rdata的timing可能比較大,這樣給后面FIFO輸出的Q后面所留有的空間就比較小了。如果Q之后還要做邏輯運算,或者再下一級Flop距離比較遠,那么修timing的時候就比較挑戰。
要解決上面兩個問題,一個必然的思路是不能直接輸出RDATA到Q,還是要把RDATA給flop住,這樣一方面使得輸出Q可以保持穩定,另一方面,這一級flop依然在FIFO內部,從Flop直接輸出對于FIFO后級的timing有幫助。你看,兜兜轉轉,似乎還是要回到我們前面要斃掉的方案。那么我們要怎么設計,才能解決前面那個方案不滿足FIFO時序要求的缺陷呢?大家可以自己先思考一下,老李下篇再帶來更深入的講解。
-
數據
+關注
關注
8文章
7085瀏覽量
89204 -
sram
+關注
關注
6文章
768瀏覽量
114734 -
fifo
+關注
關注
3文章
389瀏覽量
43745
發布評論請先 登錄
相關推薦
評論