手撕IP核系列——Xilinx FIFO IP核-异步FIFO

前言
以前从来没有这么细扣过,认识比较肤浅,通过几天对Xilinx IP核的仿制,对异步FIFO有了更深刻的认识。一开始,我是希望做到时序完全一模一样的,在某些间隔读和间隔写的场景中,也确实做到了输出时序完全一模一样,但发现在有些读和写同时进行的场景,有些输出就是与异步FIFO IP核会有差距。尽管最终我还是放弃追求完全和IP核的时序保持一致了,但通过这几天的手写和研究,也能写出功能正确的异步FIFO出来

下面,将描述实现的一些过程,我们以一个IP核的设置作为参考

IP核设置
设置卡片1

设置卡片2

设置卡片3

设置卡片4

设置卡片5

总结卡片

引脚图示:

IP核标志信号研究
我们先研究下IP输出的几个标志信号
FIFO写入阶段分析,(读使能保持为0)

1、 Wr_data_count
连续写模式

间隔写模式

Wr_data_count计数器相对于wr_en延迟了一个CLK

2、 Full almostfull
连续写模式:

间隔写模式:

Wr_data_count 最大数只会到3f,不会出现同步FIFO data_count的00。

full 会提前拉高,这里是3e处且wr_en为高时下一个时钟full拉高
almostfull 会提前拉高,这里是3d处且wr_en为高时下一个时钟almostfull拉高

rd_data_count

连续写模式:

间隔写模式

两种模式同样的规律都是:
Empty 只要rd_data_count 不为0了,立刻拉低
Almost_Empty 只要rd_data_count 不为1了,立刻拉低

上面是FIFO写入阶段,下面FIFO读出阶段。
1、 Wr_data_count、fill、almostfull

Wr_data_count从63变化到62时候,full立刻拉低
Wr_data_count从62变化到61时候,almostfull立刻拉低

2、rd_data_count empty almost empty

empty 会提前拉高,这里是01处且rd_en为高时下一个时钟empty拉高
almost empty 会提前拉高,这里是02处且rd_en为高时下一个时钟almostempty拉高

可以发现,这几个读写信号有种对称的规律。得好好体会。

此外,还有其他规律,
1、 FIFO最多只能容纳63个数据,因为会提前报full
2、 读写指针格雷码交互时钟域大致在3-4个clk时间,这个多设置几个时钟组合来摸索一下规律

知道了这些规律,就可以开干了

代码要点
1、异步FIFO设计最重要的地方在于读写指针的时钟域转化,需要借助于格雷码
2、处于读时钟域的读指针通过格雷码跨越到写时钟域,参与wr_data_count的计算
3、处于写时钟域的写指针通过格雷码跨越到读时钟域,参与rd_data_count的计算
4、wr_data_count用于产生满标识,rd_data_count用于产生空标识

用一下条件分别进行仿真,看会不会丢数

always @(posedge wr_clk)
r_cnt <= r_cnt + 1;

always @(posedge rd_clk)
r_cnt1 <= r_cnt1 + 1;

assign i_wr_en = r_cnt > 1000 ? ~fifo_full: 0;

always @(posedge wr_clk)
if(i_wr_en == 1)
begin
i_din <= i_din +1;
end

assign rd_en = r_cnt1 > 1000 ? ~fifo_empty : 0;

assign i_wr_en1 = r_cnt > 1000 ? ~fifo_full1: 0;

always @(posedge wr_clk)
if(i_wr_en1 == 1)
begin
i_din1 <= i_din1 +1;
end

assign rd_en1 = r_cnt1 > 1000 ? ~fifo_empty1: 0;

r_cnt > 1000 是因为IP核在一开始有一段busy的时间,空满都会拉高

1、读快写慢,写满则停,不满则写,非空则读,空了就不读

always #5 wr_clk = ~wr_clk;
always #4 rd_clk = ~rd_clk;

IP核和手写FIFO对比:写慢,所以输入是连续的,读快,所以读会存在不连续的情况,但数据是没有丢失的,只是比IP核的提前了一个时钟周期,而,rd_data_cnt 和 wr_data_cnt 的统计方式就有一点差别了,但这个不重要,只要数据没有丢就行了

2、读慢写块,写满则停,不满则写,非空则读,空了就不读
always #4 wr_clk = ~wr_clk;
always #5 rd_clk = ~rd_clk;

IP核和手写FIFO对比:读慢,所以输出是连续的,写快,所以写会存在不连续的情况,但同样输入输出的数据是没有丢失的

3、外部不进行空满判断,随机读和写,验证与IP核输出的数据是否一致。间隔写和间隔读

时序基本是一致的,有几个有1个clk的错位

(一开始我还想时序也完全一致,但折腾了好久,臣妾实在做不到呀)
所谓的时序一致就是希望

dout
empty
full
wr_data_count
rd_data_count

这几个输出信号,能和IP核时序完全一摸一样,但我实在时没有猜透IP里面的count empty full。某些场景能够弄成一致,结果换个场景又不行,所以我后面还是按照自己的想法来,只要功能对了就行吧。手撕一半吧姑且叫做,哈哈

最后说明:

资源备份:百度网盘-Xilinx设计-Xilinx异步FIFO
执行srcs中tb_async_fifo.tcl 文件即可直接仿真

最新文章

最新文章