cxhy 发表于 2014-7-25 13:54:31

手把手教你写异步FIFO

本帖最后由 cxhy 于 2014-7-25 14:01 编辑

         最近在上海实习,BOSS让我做一个异步FIFO。研究一阵子之后以后,代码已经逐渐完成。现在把设计思路一点一点写出来,当我把所有的代码调试完成以后,一定会开源的。
发这个帖子的主要目的纯粹是为了帮(shui)助(dian)新(mo)人(yuan)。楼主毕竟刚学FPGA不过半年左右,自己水平有限,只能分享一点自己的经验。如果有理解不到位的地方,请诸位一定要指出来。我一(fan)定(zheng)虚(lian)心(pi)学(hen)习(hou)。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
废话不说了,先从什么是FIFO说起吧。所谓的FIFO,就是first_in_first_out,即先入先出的数据缓存器。而一个FIFO的载体就是一块RAM,但是和普通的RAM又不太一样。主要的区别在于:FIFO没有内部地址线,它只能按顺序的读数据,按顺序写数据。数据的地址由内部指针自动加一可以得到。这样做的理由在于:当数据需要在两个不同的时钟域下面传输的时候,读写速率并不匹配,直接读写会造成数据的丢失。而且对于双方来说数据是直接读取,并不需要对地址进行额外的操作。所以在这种情况下,解决时钟不匹配的问题,FIFO就是一个很好的解决方案。

然后就是整个模块的接口,在顶层模块中,有复位信号(reset)输入时钟信号(输入input_clk) 输出时钟信号(output_clk输出,对你没有看错,这个输出时钟信号对于fifo模块来说就是一个输入信号,因为fifo必须对输出时钟信号进行匹配)输入数据(输入信号input_data)输出信号(输出信号output_data)以及对外输出的空标志位(empty)和满标志位(full)

再说一下整体结构,,,,,看图


一个RAM,一个写指针模块,一个读指针模块,最后一个比较模块。
写指针模块作用是控制写入数据的数据指针,并且接收来自比较模块的满信号决定是否继续把写地址指针+1。
读指针模块作用是控制读出数据的数据指针,并且接收来自比较模块的空信号决定是否继续把读地址指针+1。
比较模块比较输入到RAM内部的读指针和写指针的大小,再判断是空还是满以决定反馈到顶层的模块,进而决定两个指针的变化情况。
RAM就是RAM了,,,


未完,待续.........

bias 发表于 2014-7-25 14:54:34

收藏先,跟着学习

cxhy 发表于 2014-7-25 16:09:43

在详细介绍每一个模块之前,我先来说一下什么是亚稳态。亚稳态是一种在异步时钟域里面非常容易遇到的问题,而且亚稳态是无法被消除的,只能尽可能的减少亚稳态的持续时间,与可能存在亚稳态的端口数目。当在两个不同的时钟域下的时候,由于两个数据采集到的沿并未对齐。所谓只有在使用格雷码的情况下可以最小化亚稳态的影响

cxhy 发表于 2014-7-25 16:15:21

本帖最后由 cxhy 于 2014-7-25 16:18 编辑

首先说一说读指针模块,分为三个部分1.格雷码转换2.如果接收到空使能信号,则读指针停止计数(不变) 3.否则连续加1

rgnext <= (rbnext >> 1)^rbnext   //格雷码转换
rbnext = (empty == 1'b0) ? (rbnext = rbnext + 1) : (rbnext);

cxhy 发表于 2014-7-25 16:21:42

其次是写指针模块,写指针控制略微麻烦一点,分为以下几步。1.判满,判断满标志位是否为满。 2.只要不为满,则持续计数 3.否则停止计数 4.转换为格雷码作为写地址的格雷码输出。写法大致和上面是类似的

cxhy 发表于 2014-7-25 17:06:56

接着就是判断位 。接收来自以上两个模块的指针并作判断状态为满还是空,进而发出不同的标志位。这一部分就比较复杂了。

这里我才用的是网上流传的方向标志位判断法。通过这个direction,告诉fifo里面数据的朝向,进而判断当读写指针相等时刻系统处于空还是满。方向的满空标志位计算基于这个标志所指向的方向,如果指向为满,则当两指针相等时候,fifo为满,当指向空的时候,当量指针相同时,fifo为空。在这里,我们可以选择75%和25%作为门限,这样做的话不需要比较每一位,只需要比较前两位就可以判断direction了。

公式如下:先有两个标志位,set和clr,以及标志位dir
assign dirset = (w_ptr ^ r_ptr) & (~(w_ptr ^ r_ptr));
assign dirclr = (~(w_ptr ^ r_ptr)) & (w_ptr ^ r_ptr);

最后
assign direction = ~((~(dirset | direction)) | dirclr | (~rst_n));


真值表如下
w_ptr      w_ptr
r_ptr       r_ptr

00          01          11          10
00          00          00          00

00          01          11          10
01          01          01          01

00          01          11          10
11          11          11          11         

00          01          11          10
10          10          10          10         

而所对应的set变量和clr变量如下
set0          0          0          1
clr   0          1          0          0

set1          0          0          0
clr   0          0          1          0

set0          1          0          0
clr   0          0          0          1

set0          0          1          0
clr   1          0          0          0

这时候,如果dir变量处于0时:

其对应的真值表为
0          0          0          1
1          0          0          0
0          1          0          0
0          0          1          0

当dir为1时,对应的真值表为

1          0          1          1
1          1          0          1
1          1          1          0
0          1          1          1

所以可以看到,dir在一系列的变化中有着很强的方向性
只要使判空变量与dir进行and操作即可获得一个可靠的判断。

cxhy 发表于 2014-7-25 17:09:50

本帖最后由 cxhy 于 2014-7-25 17:27 编辑

最后上一份参考文档,说的比我详细多了

yuloong 发表于 2014-7-25 20:05:59

好的,认真学习中

cxhy 发表于 2014-7-26 20:35:11

算了,下次还是发博客吧

linjpxt 发表于 2014-7-26 20:53:08

为什么不直接用 fifo dc 的 ip

cxhy 发表于 2014-7-27 14:12:38

linjpxt 发表于 2014-7-26 20:53
为什么不直接用 fifo dc 的 ip

定制的fifo不能自由修改RAM的大小啊。如果编译过了的代码,工程量很大的话,编译要很久,但是如果用参数化设计,把大小设置为常量,就可以直接修改工程文件而不用重新编译来获得更大的RAM

linjpxt 发表于 2014-7-27 15:01:16

cxhy 发表于 2014-7-27 14:12
定制的fifo不能自由修改RAM的大小啊。如果编译过了的代码,工程量很大的话,编译要很久,但是如果用参数 ...

可以的啊,用的是m9k或m18k,一最多能有100来k到几百k,取决fpga的容量,应该是够用的

cxhy 发表于 2014-7-27 16:45:36

linjpxt 发表于 2014-7-27 15:01
可以的啊,用的是m9k或m18k,一最多能有100来k到几百k,取决fpga的容量,应该是够用的 ...

可能我没有表达清楚,我的意思是,在定制一个FIFO的IP之后,就无法对这个IP的容量进行修改了,尤其是编译之后更加麻烦,但是如果在设计FIFO中使用参数化设计,可以避免修改FIFO的大小而重新编译工程的繁琐。不知道我这一次说清楚没有

baoya1 发表于 2014-7-27 18:34:38

其实也就是这个概念可以通用到各类MCU里?

cxhy 发表于 2014-7-28 09:42:32

baoya1 发表于 2014-7-27 18:34
其实也就是这个概念可以通用到各类MCU里?

当然                           

coolamber1 发表于 2014-7-29 22:43:17

嗯。。。。。。。。

hy2515131 发表于 2014-7-29 23:07:25

越详细越好啊!

hy2515131 发表于 2014-7-30 23:54:56

cxhy 发表于 2014-7-25 16:15
首先说一说读指针模块,分为三个部分1.格雷码转换2.如果接收到空使能信号,则读指针停止计数(不变) 3. ...

LZ普及一下格雷码的转换吧,不明白啊!

strongking 发表于 2014-7-31 13:28:24

不错啊,先mark下

pengchhui 发表于 2014-7-31 13:44:54

这个感觉很不错,有助于大家学习

cxhy 发表于 2014-8-1 00:30:03

hy2515131 发表于 2014-7-30 23:54
LZ普及一下格雷码的转换吧,不明白啊!

移位再异或。没了

hy2515131 发表于 2014-8-2 09:17:37

cxhy 发表于 2014-7-25 16:15
首先说一说读指针模块,分为三个部分1.格雷码转换2.如果接收到空使能信号,则读指针停止计数(不变) 3. ...

rgnext rbnext 定义是?

cxhy 发表于 2014-8-2 20:51:58

hy2515131 发表于 2014-8-2 09:17
rgnext rbnext 定义是?

g代表的是格雷码,b代表的是二进制。

hy2515131 发表于 2014-8-3 13:34:00

cxhy 发表于 2014-8-2 20:51
g代表的是格雷码,b代表的是二进制。

格雷码和二进制转换啊

honeybear 发表于 2014-8-8 14:57:38

很认真的,写得很详细

cxhy 发表于 2014-8-8 16:37:17

刚才花了100莫元,先挣一点回来

carollim 发表于 2014-8-8 17:05:12

不错啊,先mark下
页: [1]
查看完整版本: 手把手教你写异步FIFO