电子说
上一篇介绍了利用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下一个数据的时候同时来了pop。如下图所示
那么这个时候要push的D1是不能往SRAM里写的,一旦写进去要读出来还得多花一个周期,所以这个时候也是要bypass SRAM。
所以更加严谨的条件是
至此,我们利用一级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时序要求的缺陷呢?大家可以自己先思考一下,老李下篇再带来更深入的讲解。
全部0条评论
快来发表一下你的评论吧 !