本节主要介绍多bit信号处理和FIFO, 重点为FIFO的结构与深度计算
来自中国大学MOOC上西南交通大学的慕课《芯动力-硬件加速设计方法》,这里放的是笔者的一些学习笔记,示例代码修正等,以下是他们的课程大纲,有兴趣的朋友也可以看看
数据流和指示信号不同:
解决方法:
FIFO 是一种先进先出的储存结构
与普通存储器的区别:
缺点
在IC设计中,模块与模块之间的通信设计中,多时钟的情况已经不可避免;数据在不同时钟域之间的传输很容易引起亚稳态;异步FIFO就是一种简单、快捷的解决方案。

FIFO的功能类似于一个调节上下游水量的一个蓄水池。FIFO的上游结点是FIFO的数据输入端,在写信号有效时,数据将被写入FIFO,由FIFO内部的写指针控制,并且在FIFO内部,写指针递增一个单元,同时FIFO的满信号(FIFO full Signal)将控制上游结点是否发送数据;FIFO的下游节点是FIFO的数据输出端,当读信号有效时,FIFO中的数据将被读出,由FIFO内部的读指针控制,并且在FIFO内部读指针递增一个单元,同时FIFO空信号(FIFO empty Signal))将控制下游节点是否读出数据。如果FIFO内部的空间已经被写满,则实时生成满信号,以反压上游节点,上游节点停止写新的数据进来,否则就会把已经写好的数据冲掉。如果FIFO内部的数据全部被读,则实时生成空信号,控制下游节点不再进行数据读操作。否则,下游节点就会将读过的数据重新再读一遍。从这里也可以看出,空信号和满信号对于FIFO的控制非常重要。
用异步FIFO读写分别采用相互异步的不同时钟
在现代集成电路芯片中,随着设计规模的不断扩大,一个系统中往往含有数个时钟多时钟域带来的一个问题就是,如何设计异步时钟之间的接口电路。异步FIFO是这个问题的一种简便、快捷的解决方案,使用异步FIFO可以在两个不同时钟系统之间快速而方便地传输实时数据。
对于不同宽度的数据接口也可以用FIFO
例如单片机位8位数据输出,而DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的。
FIFO的设计对调节上下游的吞吐量平衡具有非常重要的作用
结构如下:

设计关键:
读写指针相等 会触发满还是空,得看读写的指针方向是怎样的
当读写指针相等时,表明FIFO为空,这种情况发生在复位操作时或者当读指针读出FIFO中最后一个字后,追赶上了写指针;

当读写地址相等时,说明已经写入的数据,已经全部被读走,此时,FIFO还尚未有新的数据写入,说明FIFO为空,这种情况发生在复位操作时,或者当读地址读出FIFO中最后一个字后,追赶上了写地址。如果两个指针的MSB相同,则说明两个指针折回的次数相等。其余位相等,说明FIFO为空

满状态。当写地址超过读地址,写到最高地址后,重新从0开始写,再次追上了读地址。此时,读地址已经读过的地址空间,再一次被写地址写入。而读地址到最高地址之间的数据,还尚未被读。说明此时FIFO处于满的状态。如果两个指针的MSB不同,说明写指针比读指针多折回了一次;如raddr=0000,而w_addr=1000,为满。

在上一节中说过,多bit信号是不能直接通过这种打两拍进行同步的,所以我们需要采用格雷码进行传输。传输读写指针之前,需要将其先由二进制转为格雷码,然后再发送到对方的时钟域下。

引用格雷码之后,相邻值只有1位发生翻转,1位翻转所引起的亚稳态的概率远远要小于几位同时翻转所引起的概率;因此,格雷码能很好的亚稳态出现的概率。


所以在gray码上判断为满必须同时满足以下三条:
当FIFO深度不为2的次幂时,格雷码的翻转变化就不是1 bit了
假设FIFO深度为8,则读写指针可采用格雷码进行编码;

假设FIFO深度为6,如果读写指针继续采用格雷码,那么当前首尾指针的所有比特位都不相同,此时,如果从尾部返回首部,则无法实现消除亚稳态的目的。

此时,首地址是000,尾地址为111,没办法消除亚稳态。解决方法:
面试题++
假设FIFO的写时钟为100 MHz,读时钟为80 MHz。在FIFO输入侧,每100个写时钟,写入80个数据;读数据侧,假定每个时钟读走一个数据。
请问FIFO深度设置多少可以保证FIFO不会上溢出和下溢出?
对图,先找到最重载的情况:

最长burst写入时间为:
T
i
m
e
=
b
u
r
s
t
_
c
y
c
l
e
×
t
w
c
l
k
=
b
u
r
s
t
_
c
y
c
l
e
f
w
c
l
k
=
160
100
(
u
s
)
Time = burst\_cycle \times t_{wclk} = \frac{burst\_cycle}{f_{wclk}} = \frac{160}{100}(us)
Time=burst_cycle×twclk=fwclkburst_cycle=100160(us)
这段时间能最小读走的数据量:
n
u
m
r
e
a
d
=
T
i
m
e
t
r
=
T
i
m
e
×
f
r
=
160
100
×
80
=
128
num_{read} = \frac{Time}{t_r} = Time \times f_r = \frac{160}{100}\times 80 =128
numread=trTime=Time×fr=100160×80=128
这样就可以得到在 最差情况下留在FIFO中的数据:
d
e
p
t
h
=
b
u
r
s
t
−
n
u
m
r
e
a
d
=
160
−
128
=
32
depth = burst - num_{read} = 160-128 = 32
depth=burst−numread=160−128=32
抽象一下:
写时钟频率为WCLK:读时钟频率为RCLK:写时每B个时钟周期内会有A个数据写入FIFO,说明写入效率是A/B,读时每Y个时钟周期内会有X个数据读出FIFO,说明读出效率是X/Y。这种情况下,FIFO的最小深度是多少?
(burst_length/WCLK)表示这个burst的持续时间,持续时间乘以读时钟频率,可知道读数据在效率100%的时候的读出数据个数,然后再乘以读数据效率X/Y)则可知道在bust时间段内读出了多少数据。burst length与这个读出数据的差值,FIFO中残留的数据,这个也就是理论上的FIFO的最小深度。
d
e
p
t
h
=
b
u
r
s
t
_
l
e
n
g
t
h
−
b
u
r
s
t
_
c
y
c
l
e
f
w
c
l
k
×
(
f
r
c
l
k
×
X
Y
)
depth = burst\_length - \frac{burst\_cycle}{f_{wclk}} \times (f_{rclk} \times \frac{X}{Y})
depth=burst_length−fwclkburst_cycle×(frclk×YX)
这里会结合上一节对异步FIFO进行分析:
M T B F = e t M E T / C 2 C 1 f C L K f D A T A MTBF = \frac{e^{t_{MET}/C_2}}{C_1 f_{CLK} f_{DATA}} MTBF=C1fCLKfDATAetMET/C2
如果指针为格雷码,失效的后果?
格雷码一次只有一位数据发生变化,这样在进行地址同步的时候,只有两种情况:

也就是地址没有跳变,但是用这个错误的写地址去做空判断不会出错,最多是让空标志在FIFO不是真正空的时候产生,而不会出现空读的情形。
所以,gray码保证的是同步后的读写地址即使在出错的情形下依然能够保证FIFO功能的正确性,当然同步后的读写地址出错总是存在的。
需要注意gray码只是在相邻两次跳变之间才会出现只有1位数据不一致的情形超过两个周期则不一定,所以,地址总线bus skew一定不能超过一个周期,否则可能出现gray码多位数据跳变的情况,这个时候gray码就失去了作用,因为这时候同步后的地址已经不能保证只有1位跳变了。
将地址总线打两拍 -> 为了避免亚稳态传播
但是并不能消除亚稳态:
在低频情况下:
STA不需要分析这里的异步时序,因为寄存器都可以在一拍内将亚稳态消除,恢复到正常0/1态。
在高频情况下:
不一定,尤其在 28nm工艺以下,需要检查两级触发器的延迟,保证延迟低,提高系统MTBF。
对多拍同步,能够进一步将亚稳态出现的概率降低
对打两拍后,读写指针的动作为:
这个时候看满的时候并不是真满,因为此时又都走了两个数据

结论:
频率相差较大时,同步时间可以
模块划分:

在低频情况:
在高频情况下:
在布局布线(P&R)时
系统学习,还是得看书本和课程,靠几个 博客, 真不行的
笔者会集中在第二到第五章,也就是具体的设计部分,后续也会陆续更新别的关于IC设计相关的东西。