[TOC]
FIFO详解
同步FIFO
同步fifo需要解决的主要问题是如何判断空和满
空满判断逻辑
解决方法是多用一位来充当空满判断位,如果地址位数为3(存储器中有8个存储单元),则地址位扩展后为4位,具体思想来自Clifford E. Cummings的论文
例如,刚开始的写地址为0000,当写满存储器8个单元后的写地址为1000,此时的读地址为0000,可以发现写地址和读地址的最高位相反,其余位相同,此时就是代表存储器满!同理,当读写地址所有位都相同时代表存储器空,因此可以得到存储器空满的判断逻辑代码:
1 | // 空和满判断只能用组合逻辑,否则会滞后一拍 |
此时的方法只适用于存储器单元数为2的倍数,如果不是2的倍数需要采取其他方法,如使用一个多余的计数器来计数。
双端口存储器
双端口存储器的一个设计要点是有两个时钟,一个是写时钟,另一个是读时钟,使用两个不同的端口,具体代码为:
1 | //////////////////////////////////////////////////////////////////////////////// |
同步FIFO模块的编写
同步FIFO比较简单,需要注意的事项为传给双端口存储器的写使能信号和读使能信号要与空满信号相联系,否则会读出错误的值
1 | //////////////////////////////////////////////////////////////////////////////// |
tb和仿真波形图
1 |
|
异步FIFO
异步FIFO的设计难点有两个:
- 时钟偏差
- 空满逻辑判断
- 亚稳态问题
异步fifo的模块图如下图所示(来自Clifford E. Cummings的论文)
亚稳态
由于是异步的时钟,不能直接使用同步FIFO中所用的判断方法,因为是异步时钟,当一个信号跨越某个时钟域时会产生亚稳态问题,因为读写地址指针是来自不同时钟域,直接比较会产生亚稳态问题
亚稳态是指触发器无法在某个规定时间段内达到一个可确认的状态
解决方法就是引入时钟同步机制,一种简单方法是双触发器法,即上图中的两个同步模块:sync_r2w和sync_w2r
格雷码
如果直接用读写地址的二进制进行比较可能会出现问题,在采集同步信号时,由于时钟偏差的干扰,可能会采集到错误的值,如从3->4时(011->100)所有二进制位都发送了跳变,可能会产生8种中间态(000->111)
时钟偏差,Clock Skew,是指同一个时钟域内的时钟信号到达数字电路各个部分(一般是指寄存器)所用时间的差异
以下图为例,1号寄存器的时钟先到达,此时会产生111(7)这个中间态,如果wr_clk先于其余两个寄存器的rd_clk到达,此时同步寄存器会采集到错误的值7,而不是正确的值4
图源:https://cloud.tencent.com/developer/article/2115100
因此需要引入格雷码,格雷码的特点是相邻值的跳变只翻转一位,因此不会产生很多次态,同步端可能会采集到变化之前的值,不会采集到其他值,此时的时钟偏差带来的影响会减少到最小,如3->4(010->110)
格雷码的转换代码如下,本身与右移一位的结果异或得到格雷码
1 | assign wr_addr_g = wr_addr ^ (wr_addr >> 1); |
空满判断逻辑
此处使用格雷码来进行判断,仍然是将地址位扩展一位(原因和同步FIFO中一样),然后取其格雷码进行比较
假设存储器地址单元为8个,则地址位为3位,扩展1位到4位。如果读地址指针为0(0000),写地址指针为8(1000),此时代表存储器满。而0和8的格雷码是0000和1100,可以发现只有最高两位相反,其余位相同,此时可以得到空满判断逻辑
1 | // 判断空满逻辑 |
注意到,代码中比较的是下一地址的格雷码和同步过来的信号,因为此处使用了时序逻辑,在下一时钟才更新空满信号的状态
或者可以使用组合逻辑,则此时的空满信号是通过现态的格雷码和同步的格雷码比较产生,代码修改如下
1 | // 空满判断逻辑 |
异步FIFO所有代码
双端口存储器使用的是同步FIFO中的
1 | //////////////////////////////////////////////////////////////////////////////// |
testbench,代码来自知乎:行走的BUG永动机
1 |
|
波形图