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


    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

    手撕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;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    最后说明:

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

  • 相关阅读:
    DA14531在三星手机手写笔的应用让我打开眼镜
    备战蓝桥杯 Day9(最长公共子序列LCS模型)
    博客园众包平台:50w+驱动开发大单,全园找人接单
    【云原生之kubernetes实战】在k8s环境下部署Heimdall导航页
    封装axios
    配置 websocket 持续时间
    20. 机器学习——PCA 与 LDA
    linux新版本io框架 io_uring
    Python 数据库——链表
    【Spring Boot】实战:实现Session共享
  • 原文地址:https://blog.csdn.net/gzy0506/article/details/126257839