精华内容
下载资源
问答
  • 现在开始对上一篇博文介绍的异步FIFO...对异步FIFO验证的平台如图1所示。 图1 异步FIFO验证平台 其中,clock为时钟生成器,asyn_fifo_if为产生异步FIFO读写命令的模块,asyn_fifo为异步FIFO设计模块。 验证顶...

     现在开始对上一篇博文介绍的异步FIFO进行功能验证,上一篇博文地址:http://blog.chinaaet.com/crazybird/p/5100000872 。对异步FIFO验证的平台如图1所示。

    测试平台.jpg

    图1  异步FIFO验证平台

        其中,clock为时钟生成器,asyn_fifo_if为产生异步FIFO读写命令的模块,asyn_fifo为异步FIFO设计模块。

        验证顶层模块testbench的代码如下所示:

    /*******************************版权申明********************************
    **                     电子技术应用网站, CrazyBird
    **     http://www.chinaaet.com, http://blog.chinaaet.com/crazybird
    **
    **------------------------------文件信息--------------------------------
    ** 文件名:          clock.v
    ** 创建者:          CrazyBird
    ** 创建日期:        2016-1-16
    ** 版本号:           v1.0
    ** 功能描述:        时钟生成器
    **                   
    ***********************************************************************/
    // synopsys translate_off
    `timescale 1 ns / 1 ps
    // synopsys translate_on
    module testbench;
        //******************************************************************
        //  变量定义
        //******************************************************************
        wire            wr_rst_n;
        wire            wr_clk;  
        wire            wr_en;   
        wire    [7:0]   wr_data; 
        wire            wr_full; 
        wire    [4:0]   wr_cnt;  
        wire            rd_rst_n;
        wire            rd_clk;  
        wire            rd_en;   
        wire    [7:0]   rd_data; 
        wire            rd_empty;
        wire    [4:0]   rd_cnt;  
        
        //******************************************************************
        //  时钟生成器例化
        //******************************************************************
        clock #(
            .C_CLK_FREQ(100.0)
        )
        u_clock_wr (
            .clk    (   wr_clk  )
        );
        
        clock #(
            .C_CLK_FREQ(70.0)
        )
        u_clock_rd (
            .clk    (   rd_clk  )
        );
        
        //******************************************************************
        //  异步FIFO读写指令产生模块例化
        //******************************************************************
        asyn_fifo_if #(
            .C_DATA_WIDTH(8)
        )
        u_asyn_fifo_if (
            .wr_rst_n   (   wr_rst_n    ),
            .wr_clk     (   wr_clk      ),
            .wr_en      (   wr_en       ),
            .wr_data    (   wr_data     ),
            .wr_full    (   wr_full     ),
            .rd_rst_n   (   rd_rst_n    ),
            .rd_clk     (   rd_clk      ),
            .rd_en      (   rd_en       ),
            .rd_empty   (   rd_empty    )
        );
        
        //******************************************************************
        //  异步FIFO模块例化
        //******************************************************************
        asyn_fifo #(
            .C_DATA_WIDTH(8),
            .C_FIFO_DEPTH_WIDTH (4)
        )
        u_asyn_fifo (
            .wr_rst_n   (   wr_rst_n    ),
            .wr_clk     (   wr_clk      ),
            .wr_en      (   wr_en       ),
            .wr_data    (   wr_data     ),
            .wr_full    (   wr_full     ),
            .wr_cnt     (   wr_cnt      ),
            .rd_rst_n   (   rd_rst_n    ),
            .rd_clk     (   rd_clk      ),
            .rd_en      (   rd_en       ),
            .rd_data    (   rd_data     ),
            .rd_empty   (   rd_empty    ),
            .rd_cnt     (   rd_cnt      )
        );
        
    endmodule

        时钟模块clock的代码实现如下所示(时钟的频率可设置):

    /*******************************版权申明********************************
    **                     电子技术应用网站, CrazyBird
    **     http://www.chinaaet.com, http://blog.chinaaet.com/crazybird
    **
    **------------------------------文件信息--------------------------------
    ** 文件名:          clock.v
    ** 创建者:          CrazyBird
    ** 创建日期:        2016-1-16
    ** 版本号:           v1.0
    ** 功能描述:        时钟生成器
    **                   
    ***********************************************************************/
    // synopsys translate_off
    `timescale 1 ns / 1 ps
    // synopsys translate_on
    module clock(
        clk
        );
        //******************************************************************
        //  参数定义
        //******************************************************************
        parameter   C_CLK_FREQ = 100.0;         //MHz
        localparam  C_CLK_CYCLE = 1000.0 / C_CLK_FREQ;
        
        //******************************************************************
        //  端口定义
        //******************************************************************
        output reg      clk;
        
        //******************************************************************
        //  时钟生成
        //******************************************************************
        initial
        begin
            clk = 0;
            forever #(C_CLK_CYCLE/2)
                clk = ~clk;
        end
        
    endmodule

        本次测试的步骤如下所示:

        (1)只写异步FIFO

        (2)只读异步FIFO

        (3)同时读写异步FIFO

        测试步骤的代码在asyn_fifo_if模块中,如下所示:

    /*******************************版权申明********************************
    **                     电子技术应用网站, CrazyBird
    **     http://www.chinaaet.com, http://blog.chinaaet.com/crazybird
    **
    **------------------------------文件信息--------------------------------
    ** 文件名:          asyn_fifo_if.v
    ** 创建者:          CrazyBird
    ** 创建日期:        2016-1-16
    ** 版本号:           v1.0
    ** 功能描述:        产生异步FIFO的读写命令
    **                   
    ***********************************************************************/
    // synopsys translate_off
    `timescale 1 ns / 1 ps
    // synopsys translate_on
    module asyn_fifo_if(
        wr_rst_n,
        wr_clk,
        wr_en,
        wr_data,
        wr_full,
        rd_rst_n,
        rd_clk,
        rd_en,
        rd_empty
        );
        //******************************************************************
        //  参数定义
        //******************************************************************
        parameter C_DATA_WIDTH = 8;
        
        //******************************************************************
        //  端口定义
        //******************************************************************
        output reg                      wr_rst_n;
        input                           wr_clk;
        output reg                      wr_en;
        output reg  [C_DATA_WIDTH-1:0]  wr_data;
        input                           wr_full;
        output reg                      rd_rst_n;
        input                           rd_clk;
        output reg                      rd_en;
        input                           rd_empty;
        
        //******************************************************************
        //  内部变量定义
        //******************************************************************
        reg             normal_wr;
        reg             normal_rd;
        
        
        //******************************************************************
        //  变量初始化
        //******************************************************************
        initial
        begin
            wr_rst_n  = 1'b0;
            rd_rst_n  = 1'b0;
            normal_wr = 1'b0;
            normal_rd = 1'b0;
            #492;
            wr_rst_n  = 1'b1;
            rd_rst_n  = 1'b1;
            #100;
            //只写FIFO
            normal_wr = 1'b1;
            repeat(20) @(negedge wr_clk);
            normal_wr = 1'b0;
            //只读FIFO
            normal_rd = 1'b1;
            repeat(20) @(negedge rd_clk);
            normal_rd = 1'b0;
            //同时读写FIFO
            normal_wr = 1'b1;
            normal_rd = 1'b1;
            repeat(100) @(negedge wr_clk);
            normal_wr = 1'b0;
            normal_rd = 1'b0;
            repeat(20) @(negedge rd_clk);
            $stop;
        end
        
        //******************************************************************
        //  写FIFO信号的产生
        //******************************************************************
        always @(negedge wr_clk or negedge wr_rst_n)
        begin
            if(wr_rst_n == 1'b0)
            begin
                wr_en   <= 1'b0;
                wr_data <= {(C_DATA_WIDTH){1'b0}};
            end
            else if(normal_wr == 1'b1)
            begin
                if(wr_full == 1'b0)
                begin
                    wr_en   <= 1'b1;
                    wr_data <= {$random%((1 << C_DATA_WIDTH)-1)};
                end
                else
                begin
                    wr_en   <= 1'b0;
                    wr_data <= {(C_DATA_WIDTH){1'b0}};
                end
            end
            else
            begin
                wr_en   <= 1'b0;
                wr_data <= {(C_DATA_WIDTH){1'b0}};
            end
        end
        
        //******************************************************************
        //  读FIFO信号的产生
        //******************************************************************
        always @(negedge wr_clk or negedge wr_rst_n)
        begin
            if(wr_rst_n == 1'b0)
                rd_en   <= 1'b0;
            else if(normal_rd == 1'b1)
            begin
                if(rd_empty == 1'b0)
                    rd_en   <= 1'b1;
                else
                    rd_en   <= 1'b0;
            end
            else
                rd_en   <= 1'b0;
        end
        
    endmodule

        modelsim仿真结果如图2~4所示(具体情况可自行分析)。

    只写异步FIFO.png

    图2  只写异步FIFO

    只读异步FIFO.png

    图3  只读异步FIFO

    同时读写异步FIFO.png

    图4  同时读写异步FIFO

        至此,异步FIFO的设计和验证都完成了。现在预告下一篇博文:参数化的优先级编码器设计。哈哈,不要以为很简单哦。记住,是“参数化”,即输入信号的位宽可设置。大家也可以先想想如何实现。也许,灵感一来很快就想出来了,否则,半个小时、1个小时、甚至很多。

    转载:http://blog.chinaaet.com/crazybird/p/5100000874

    转载于:https://www.cnblogs.com/chengqi521/p/8392164.html

    展开全文
  • FIFO根据输入输出时钟是否一致,分为同步FIFO与...本文以异步FIFO与同步FIFO的异同入手,在比较过程中逐步对异步FIFO进行分析,介绍异步FIFO相比于同步FIFO的额外处理,最终实现异步FIFO,并进行了仿真、调试、以及验证

    FIFO根据输入输出时钟是否一致,分为同步FIFO与异步FIFO。同步FIFO中,读写控制信号以及数据均处于同一时钟域,满足STA分析时一般不会出现亚稳态等不稳定情形;而对于异步FIFO,读写相关信号处于不同时钟域,信号的不同步可能会导致亚稳态,导致FIFO工作异常,设计较为复杂;在之前的记录中,我们对同步FIFO的设计进行了分析:

    Verilog实现FIFO专题(3-同步FIFO设计)

    此处我们不再对同步FIFO进行介绍而直接以异步FIFO与同步FIFO的异同为线索,逐步对异步FIFO进行分析,介绍异步FIFO相比于同步FIFO的额外处理,并进一步实现异步FIFO。

    目录

    一、异步FIFO与同步FIFO工作流程比较

    1、同步FIFO

    2、异步FIFO

    二、异步FIFO的空满检测

    1、同步FIFO的空满检测

    2、异步FIFO的空满检测

    计数检测空满:

    指针比较检测空满:

    扩展指针比较检测空满:

    格雷码指针比较检测空满:

    三、异步FIFO的同步处理

    1、同步方式

    2、延迟对FIFO设计的影响

    结论:

    FIFO满检测:

    FIFO空检测:

    四、异步FIFO设计

    1、端口设计

    外部端口

    内部信号

    2、功能描述

    3、实现代码

    4、仿真验证

    五、参考文献


    一、异步FIFO与同步FIFO工作流程比较

    1、同步FIFO

    同步FIFO的读写控制信号以及数据均处于同一时钟域,即:FIFO在同一时钟驱动下进行读写操作,读控制信号有效且FIFO不为空时,输出读指针对应地址的数据,随后读指针加1;写控制信号有效且FIFO不为满时,将输入数据存储到写指针对应地址处,随后写指针加1;

    2、异步FIFO

    异步FIFO的工作内容与同步FIFO类似:FIFO在时钟驱动下进行读写操作,读控制信号有效且FIFO不为空时,输出读指针对应地址的数据,随后读指针加1;写控制信号有效且FIFO不为满时,将输入数据存储到写指针对应地址处,随后写指针加1;

    但是异步FIFO的控制并不像同步FIFO那么简单,因为异步FIFO工作在不同的时钟域,这就带来了一些问题:

    (1)如何进行空满检测?还能像同步FIFO中通过计数值来判断吗?

    (2)需要同步电路

    二、异步FIFO的空满检测

    1、同步FIFO的空满检测

    同步FIFO的空满检测可以通过计数很简单的实现:

    读写逻辑是同一个时钟,因此可以在每次时钟来临时进行判断,如果不执行读写操作/同时读写,则计数值不变;只执行读操作,计数值减1;只执行写操作,计数值加1;

    如果计数值为0,则说明FIFO空,只能写不能读(直到写入一次数据,计数值加1,FIFO不再为空才能执行读操作);

    如果计数值为FIFO深度,则说明FIFO满,只能读不能写(直到读出一次数据,计数值减1,FIFO不再为满才能执行写操作);

    2、异步FIFO的空满检测

    计数检测空满:

    异步FIFO不能采用同步FIFO这种计数方式来实现空满检测,因为用两个时钟去控制同一个计数器的加剪很明显是不可取的。

    如果不明白为什么不能用两个时钟控制同一个计数器,可以查阅:Verilog中always@()语句双边沿触发(语法与综合的差异)

    指针比较检测空满:

    读写指针指向读写操作面向的FIFO地址空间,因此空满检测的另一个思路是比较读写指针。每次写操作执行,写指针加1;而每次读操作执行,读指针加1,因此:

    FIFO空发生在:读指针追上写指针时;

    FIFO满发生在:写指针追上读指针时;

    但是这种处理仍存在一个问题,就是读写指针相等时,难以区分FIFO是空还是满。

    扩展指针比较检测空满:

    如上分析,直接比较读写指针时存在一个特例:读写指针相等时,难以区分FIFO是空还是满。

    因此,有人提出,将指针进行高位扩展。即指针加宽一位,当写指针超出FIFO深度时,这额外的一位就会改变。FIFO的深度决定了指针扩展前(即除了最高位的其余位)的宽度,而这扩展的一位与FIFO深度无关,是为了标志指针多转了一圈,因此:

    当读写指针完全相同时,FIFO空;

    当读写指针高位不同,其余位完全相同时,FIFO满;

    经过指针扩展,可以明确的进行空满检测。但是这种处理仍然不够,因为异步FIFO读写时钟相互独立,分属不同时钟域,相邻二进制地址位变化时,不止一个地址位都要变化,这样指针在进行同步过程中很容易出错,比如写指针在从0111到1000跳变时4位同时改变,这样读时钟在进行写指针同步后得到的写指针可能是0000-1111的某个值,一共有2^4个可能的情况,而这些都是不可控制的,你并不能确定会出现哪个值,那出错的概率非常大。

    格雷码指针比较检测空满:

    如上分析,直接比较扩展读写指针时可能因多位改变导致错误。因此,进一步采用gray码形式的指针,利用格雷码每次只变化一位的特性,降低同步发生时错误的概率。如图,为一个深度为8FIFO的格雷码指针(绿色框中):

    0-7为真实的FIFO地址,而8-15是指针多转一圈以后的地址(8-0,9-1...)。应注意,此时指针按照格雷码方式进行编码,不能再用二级制指针的比较方式来判断空满。比如:位置6(0101)和位置9(1101),除最高位外其余位均相等。但是很明显,位置9实际对应位置1处的存储空间。

    因此,格雷码指针下的空满检测条件为:

    当最高位和次高位均相同,其余位相同:FIFO空

    当最高位和次高位均相反,其余位相同:FIFO满

    因此,最终的空满检测方式为:将二进制指针转换为格雷码,用于另一时钟域接收,随后按照检测条件进行检测。

    二进制指针转换为格雷码的详情见:Verilog实现二进制码与格雷码转换  此处不再展开。

    三、异步FIFO的同步处理

    1、同步方式

    判断FIFO空满状态时,需要在读FIFO时获取写时钟域的写指针,与读指针比较来判断FIFO是否为空;需要在写FIFO时获取读时钟域的读指针,与写指针比较来判断FIFO是否为满;

    也就是说,判断空满状态时牵扯到跨时钟域问题,需要进行同步;

    采用两级寄存器打两拍的方式进行同步,具体实现见:亚稳态专题

    2、延迟对FIFO设计的影响

    异步FIFO通过比较读写指针进行满空判断,但是读写指针属于不同的时钟域,所以在比较之前需要先将读写指针进行同步处理,将读写指针同步后再进行比较,判断FIFO空满状态。但是因为在同步指针时需要时间(如延迟两拍同步),而在这个同步的时间内有可能还会写入/读出新的数据,因此同步后的指针一定是小于或者等于当前实际的读/写指针,那么此时判断FIFO满空状态时是否会出错?是否会导致错误?

    结论:

    先说结论:异步逻辑进行同步时,不可避免需要额外的时钟开销,这会导致满空趋于保守,但是保守并不等于错误,这么写会稍微有性能损失,但是不会出错。

    FIFO满检测:

    FIFO满检测发生在写时钟域,将读指针同步到写时钟域后再和写指针比较,进行FIFO满状态判断。因为同步时间的存在,同步后的读指针一定是小于或者等于当前真正的读指针(同步时间内可能出现了读操作,导致读指针增加),所以此时判断FIFO为满不一定是真满,这样更保守(即:写指针=同步读指针<=真实读指针),这样可以保证FIFO的特性:FIFO空之后不能继续读取。

    FIFO空检测:

    FIFO空检测发生在读时钟域,将写指针同步到读时钟域后再和读指针比较,进行FIFO空状态判断。因为同步时间的存在,同步后的写指针一定是小于或者等于当前真正的写指针(同步时间内可能出现了写操作,导致写指针增加),所以此时判断FIFO为空不一定是真空,这样更保守(即:读指针=同步写指针<=真实写指针),这样可以保证FIFO的特性:FIFO满之后不能继续写入。

    四、异步FIFO设计

    1、端口设计

    外部端口

    1、读时钟信号clk_r,作为异步FIFO的读驱动信号

    2、写时钟信号clk_w,作为异步FIFO的写驱动信号

    3、异步复位信号rst_n

    // 写FIFO相关

    4、数据输入信号din[DW-1:0],作为FIFO数据写入端,DW数据为位宽

    5、写使能we

    // 读FIFO相关

    6、数据输出信号dout[DW-1:0],作为FIFO数据输出端

    7、读使能re

    // 标志相关

    8、FIFO满标志full,FIFO满时不能再写入数据

    9、FIFO空标志empty,FIFO空时不能再读出数据

    内部信号

    1、读指针wp,作为读地址(FIFO读写只能顺序进行,不能外部设置,因此为内部信号)

    2、格雷码读指针wp_g,供写时钟域同步后判断FIFO满使用

    3、写指针rp,作为写地址

    4、格雷码写指针wp_g,供读时钟域同步后判断FIFO空使用

    5、[DW-1:0]ram[0:Depth-1],数据存储空间,Depth为FIFO深度

    2、功能描述

    读逻辑:

    clk_r来临,re有效,并且FIFO非空(empty=0)时,将读指针rp对应地址处的数据读出至dout;

    // 读操作
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            dout <= {DW{1'bz}};
        else if(!empty & re)
            dout <= ram[rp[AW-1:0]];
        else
            dout <= dout;
    end
    

    读指针逻辑

    clk_r来临,re有效,并且FIFO非空(empty=0)时,进行一次读操作,rp加1(即顺序读出),并进行格雷码转换,生成对应rp_g;

    // 读指针
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            rp <= {AW{1'b0}};
        else if(!empty & re)
            rp <= rp+1'b1;
        else
            rp <= rp;
    end
    

    写逻辑:

    clk_w来临,we有效,并且FIFO不满(full=0)时,将din写入写指针wp对应地址处;

    //写操作
    always@(posedge clk_w)
    begin
        if(!full & we)
            ram[wp[AW-1:0]] <= din;
        else
            ram[wp[AW-1:0]] <= ram[wp[AW-1:0]];
    end
    

    写指针逻辑

    clk_w来临,we有效,并且FIFO不满(full=0)时,进行一次写操作,wp加1(即顺序存储),并进行格雷码转换,生成对应wp_g;

    //写指针
    always@(posedge clk_w or negedge rst_n)
    begin
        if(!rst_n)
            wp <= {AW{1'b0}};
        else if(!full & we)
            wp <= wp+1'b1;
        else
            wp <= wp;
    end
    

    格雷码指针生成逻辑:

    利用二进制与格雷码的转换关系,将二进制指针转换为格雷码指针,用于另一个时钟域的同步接收;

    // 二进制指针转换为格雷指针
    assign wp_g = (wp>>1) ^ wp;
    assign rp_g = (rp>>1) ^ rp;
    

    格雷码指针同步逻辑:

    读时钟域,同步写地址,用于空逻辑判断;写时钟域,同步读地址,用于满逻辑判断;

    // 读时钟域,写地址同步
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            begin
                wp_m <= {AW{1'b0}};
                wp_s <= {AW{1'b0}};       
            end
        else
            begin
                wp_m <= wp_g;
                wp_s <= wp_m;    
            end       
    end
    // 写时钟域,读地址同步
    always@(posedge clk_w or negedge rst_n)
    begin
        if(!rst_n)
            begin
                rp_m <= {AW{1'b0}};
                rp_s <= {AW{1'b0}};       
            end
        else
            begin
                rp_m <= rp_g;
                rp_s <= rp_m;    
            end       
    end
    

    空满检测逻辑:

    根据格雷码指针判断逻辑,进行空满检测,应注意:

    读时钟域,同步写地址。读格雷码指针同步后写格雷码指针比较,用于空逻辑判断;

    写时钟域,同步读地址,写格雷码指针同步后读格雷码指针比较,用于满逻辑判断;

    // 空满检测,使用同步后的格雷指针?
    assign empty = (wp_s == rp_g)?1'b1:1'b0;// 空检测,使用同步后的写格雷指针
    assign full = ( {~wp_g[AW:AW-1] , wp_g[AW-2:0]} == {rp_s[AW:AW-1] , rp_s[AW-2:0]} )?1'b1:1'b0;  // 满检测,使用同步后的读格雷指针
    

    3、实现代码

    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer: guoliang CLL
    // 
    // Create Date: 2020/03/24 20:51:06
    // Design Name: 
    // Module Name: afifo
    // Project Name: 
    // Target Devices: 
    // Tool Versions: 
    // Description: 
    // 
    // Dependencies: 
    // 
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    // 
    //
    
    
    module afifo
    #(parameter DW = 8,AW = 4)//默认数据宽度8,FIFO深度16
    (
        input clk_r,
        input clk_w,
        input rst_n,
        input we,
        input re,
        input [DW-1:0]din,
        output reg [DW-1:0]dout,
        output empty,
        output full
        );
    // internal signal
    parameter Depth = 1 << AW;//depth of FIFO 
    reg [DW-1:0]ram[0:Depth-1];
    reg [AW:0]wp;  //point
    reg [AW:0]rp;
    wire [AW:0]wp_g;//Gray point
    wire [AW:0]rp_g;
    reg [AW:0]wp_m;//mid_point for syn
    reg [AW:0]rp_m;
    reg [AW:0]wp_s;//point after syn
    reg [AW:0]rp_s;
    // FIFO declaration
    // 二进制指针转换为格雷指针
    assign wp_g = (wp>>1) ^ wp;
    assign rp_g = (rp>>1) ^ rp;
    // 空满检测,使用同步后的格雷指针?
    assign empty = (wp_s == rp_g)?1'b1:1'b0;// 空检测,使用同步后的写格雷指针
    assign full = ( {~wp_g[AW:AW-1] , wp_g[AW-2:0]} == {rp_s[AW:AW-1] , rp_s[AW-2:0]} )?1'b1:1'b0;  // 满检测,使用同步后的读格雷指针
    // 读指针
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            rp <= {AW{1'b0}};
        else if(!empty & re)
            rp <= rp+1'b1;
        else
            rp <= rp;
    end
    //写指针
    always@(posedge clk_w or negedge rst_n)
    begin
        if(!rst_n)
            wp <= {AW{1'b0}};
        else if(!full & we)
            wp <= wp+1'b1;
        else
            wp <= wp;
    end
    // 读操作
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            dout <= {DW{1'bz}};
        else if(!empty & re)
            dout <= ram[rp[AW-1:0]];
        else
            dout <= dout;
    end
    //写操作
    always@(posedge clk_w)
    begin
        if(!full & we)
            ram[wp[AW-1:0]] <= din;
        else
            ram[wp[AW-1:0]] <= ram[wp[AW-1:0]];
    end
    // 读时钟域,写地址同步
    always@(posedge clk_r or negedge rst_n)
    begin
        if(!rst_n)
            begin
                wp_m <= {AW{1'b0}};
                wp_s <= {AW{1'b0}};       
            end
        else
            begin
                wp_m <= wp_g;
                wp_s <= wp_m;    
            end       
    end
    // 写时钟域,读地址同步
    always@(posedge clk_w or negedge rst_n)
    begin
        if(!rst_n)
            begin
                rp_m <= {AW{1'b0}};
                rp_s <= {AW{1'b0}};       
            end
        else
            begin
                rp_m <= rp_g;
                rp_s <= rp_m;    
            end       
    end
    endmodule
    

    4、仿真验证

    测试文件如下:

    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer: CLL guoliang
    // 
    // Create Date: 2020/03/24 21:22:45
    // Design Name: 
    // Module Name: afifo_tsb
    // Project Name: 
    // Target Devices: 
    // Tool Versions: 
    // Description: 
    // 
    // Dependencies: 
    // 
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    // 
    //
    
    
    module afifo_tsb(
    
        );
    // port declaration
    reg clk_r;
    reg clk_w;
    reg rst_n;
    reg we;
    reg re;
    reg [7:0]din;
    wire  [7:0]dout;
    wire empty;
    wire full;
    //clk
    initial
    begin
        clk_r = 1'b0;
        forever #25 clk_r = ~clk_r;
    end
    initial
    begin
        clk_w = 1'b0;
        forever #10 clk_w = ~clk_w;
    end
    // 
    initial
    begin
        rst_n = 1'b1;
        din = 1'b0;
        re = 1'b0;
        we = 1'b0;
        #50 rst_n = 1'b0;
        #50 rst_n = 1'b1;
        // only write
        we = 1'b1;
        repeat(20) #20 din = din+1'b1;
        // only read 
        we = 1'b0;
        re = 1'b1;
        repeat(20) #50; 
        // read and write
    //    we = 1'b1;
    //    re = 1'b1;
    //    din = 1'b0;
    //    repeat(20) #20 din = din+1'b1;     
    end
    // inst
    afifo inst2(
        .clk_r(clk_r),
        .clk_w(clk_w),
        .rst_n(rst_n),
        .we(we),
        .re(re),
        .din(din),
        .dout(dout),
        .empty(empty),
        .full(full)
    );
    endmodule
    
    

    仿真结果如下:

    篇幅有限,仿真结果不再展开分析。提醒自己,应注意仿真测试是很必要的,通过功能仿真能暴露出设计上的不足、缺陷、以及实现过程中因粗心等导致的其余问题;

    因此,如何设计测试文件也具有重要意义。测试文件容易编写,但是如何使得测试文件能全面的对设计进行检测,高效准确的对设计进行测试,无疑是一门学问;

    我只简单记录一下,我调试时关注的部分

    1、写逻辑

    数据能否在写时钟驱动下,顺序写入FIFO中对应地址;FIFO满时,是否停止写入;

    2、读逻辑

    能否在读时钟驱动下,顺序读出FIFO中对应数据;FIFO空时,是否停止读出;

    3、满判断

    设计能否在写时钟驱动下,同步读指针,并且在适当位置产生满标志;

    3、空判断

    设计能否在读时钟驱动下,同步写指针,并且在适当位置产生空标志;

    RTL电路如下:

    五、参考文献

    Verilog实现FIFO专题(3-同步FIFO设计)

    异步FIFO的设计

    Verilog中always@()语句双边沿触发(语法与综合的差异)

    Verilog实现二进制码与格雷码转换

    亚稳态专题



     

    展开全文
  • 异步FIFO关键

    2020-02-10 14:44:23
    1:地址转化为格雷码编写,防止出现不需要的状态。 2:格雷码地址要用两级同步器同步。 3:空满信号,主要是追及问题。 3.1:读地址追上写地址,则读写地址完全一致,则为空。 3.2:写地址追上读地址,则读写地址,...

    1:地址转化为格雷码编写,防止出现不需要的状态。
    2:格雷码地址要用两级同步器同步。
    3:空满信号,主要是追及问题。
    3.1:读地址追上写地址,则读写地址完全一致,则为空。
    3.2:写地址追上读地址,则读写地址,除了最高位(overflow位)不同,其他位完全一致,则为满。

    展开全文
  • 基于SOC异步FIFO的设计与形式验证_张波.caj
  • 异步FIFO

    2021-06-11 13:50:01
    文章目录前言FIFO使用场景FIFO参数同步FIFO异步FIFO 前言 FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,,但缺点就是只能顺序写入数据,顺序的...

    👉注:代码链接在文末,有需要直接移至文末观看。
    👉注:更多精彩请看:面试常问的verilog代码汇总 一文


    简介

    异步FIFO 的读写是用不同的时钟来控制的,所以不能采用共享同一个计数器的方法来产生空满标志。那么我将通过引入问题的方式,逐步异步FIFO中重要的概念和机制,再来进行代码环节。

    注:同步FIFO也可以看同步FIFO

    指针该怎么设计?

    将读写指针向高位扩展一位,也就是说,深度为2n 的异步FIFO,读写指针的宽度应该为(n+1)位。当读写指针指向最FIFO中的最后一位时,指针最高为从0变为1,其他位变成0,指针再次折回到FIFO的首位。
    在这里插入图片描述

    空满标志产生的条件?

    因为读写指针向高位扩展一位,所以当使用二进制进行编码读写指针时,空满标志为:

    • 读写指针相同时,FIFO为空。只有在复位操作或者当读指针读出FIFO中的最后一个数据时,读指针追赶上了写指针。
    • 读写指针最高位(MSB)不同,其他位相同时,FIFO为满。这说明写指针比读指针已经多折回了一次,此时FIFO已满。

    为什么是格雷码?

    用上面二进制编码读写指针,这样问题就真的解决了吗?还是那个问题,读写指针是在不同的时钟域,要进行跨时钟域同步(不太了解的可以看跨时钟域传输),这势必要考虑传输时的亚稳态问题。采用二进制码进行读写指针的编码时,我们知道,其相邻点的递增可能有好几个二进制位同时发生变化。而在异步FIFO中,我们一般采用两级触发器同步打两拍的方式进行读写指针的同步。也就是说,如果使用二进制进行编码,就不能采用这种简单的同步两拍的方式,因为这种方式只能进行单bit信号跨时钟域传输。那为何解决这个问题?
    采用格雷码。格雷码是一种在0 ~ 2n -1之间循环变化的编码,其相邻两个数值之间只有一位二进制位发生变化,有效的降低了在指针同步的过程中产生亚稳态的概率。
    所以采用格雷码后,产生空满标志的条件又要变一变了。

    • 空标志:读写指针所有位均相同
    • 满标志:读写指针最高位(MSB)不同次高位不同其他位相同

    PS:这里我补充解释几个点:

    1.为什么要改变满标志判断条件?

    答:假如是如下图所示的4位格雷码,FIFO深度为8,读写指针从0000 ,…,0100(7)再到1100(8)。格雷码具有镜像对称的特点,这里的1100(8)的意义就相当于指针折回到FIFO的首位(0000)。所以假如此时要比较满时,要比较1100和0000,也就是要满足最高位和次高位不同,其他位相同。在这里插入图片描述

    2.是用格雷码需要注意什么?

    答:格雷码只有满足2n 个数,才可以形成循环,否则会导致相邻位不止一位发生变化。这也就是使用格雷码作为读写指针时,FIFO的深度必须为2n 。有的时候宁愿浪费一点空间,也要将深度设为2n
    例如:如果设置FIFO的深度为6,首位跳变时有多位发送变化。
    在这里插入图片描述

    3.格雷码同步时出错了,怎么办?

    答:格雷码每次只要1位二进制位发生变化,这样在进行地址同步时,如果地址同步出错,如下图:写地址跳变由000到001,同步时出错,将写指针由001同步成000。

    在这里插入图片描述
    尽管写地址跳变出错,在读时钟域判断空是否产生空标志时也不会出现严重错误,最坏的情况就是让空标志在FIFO不是为空的时钟产生空标志,也就是假空的情况。但是不会产生更加严重的空度情况。

    4.格雷码与二进制之间的转换?

    答:这个还是看我之前的博客:IC验证面试题(题2),描述的更详细。

    空满标志产生需要读写指针如何同步?

    首先我们要明白,读写指针受不同的时钟控制,假如你把写指针同步到读指针时钟域中去判断满,行不行?不行,假如加入此时你判断了为满(最高位和次高位不同,其他位相同),控制FIFO不要再写了。但你注意了,此时写指针在经过两级触发器同步到读时钟域的过程中,写指针它在写时钟域中还进行了至少两次的写操作(一般写时钟更快),这就造成了FIFO的溢出。
    那么该怎么办?

    • 满标志: 将读指针同步到写时钟域,告诉写信号“你TM别写了!,再写就溢了”
    • 空标志:将写指针同步到读时钟域,告诉写信号“你TM别读了!,再读就空了”

    那么这就有个问题了?上面说同步的过程中,FIFO实际还在写,所以会造成写满。那你现在把读指针同步到写时钟域来判断满,实际FIFO应该还未满,这不造成羞耻的浪费了嘛。其实这种设计称为保守设计,就是为了增加FIFO的安全性,防止写指针孬头吧唧地写满了。同理,也防止读指针孬头吧唧地产生空读,读出无效数据。

    FIFO的深度如何计算?

    Question1:写时钟频率为100MHz,读时钟频率为80MHz。每100个时钟周期写入80个数据,每一个周期读出一个数据。那么FIFO的深度应该是多少?
    首先我们要确定最极端的情况,也就是FIFO的Burst 会写了多少个数据。这里要考虑数据是“背靠背”传输的,如下:
    在这里插入图片描述
    这种情况是,前100个时钟周期,数据在后80个周期内发生了连续的写操作,在后100个时钟周期内,数据在前80个周期写入。这就造成了:

    • 数据突发长度为 :160
    • burst长度(160个数据)写入需要的时间:burst长度/写时钟频率 = 160/100
    • burst长度(160个数据)写入期间可以读出的数据量:160/100 ÷ 1/80 =128
    • FIFO的深度:160 - 128 = 32

    Question2:设计一个同步FIFO,每100个时钟周期可以写80个数据,每10个周期可以读8个数据,FIFO的深度为多少?
    和上面的情况一样,burst长度为160。所以:

    • burst长度(B)为:B =160
    • 写入B长度所需的时间:T = (1/fwr)*160
    • 从FIFO中读出一个数据所需的时间:t = (1/frd)*(8/10)
    • 在T时间读走的数据量:T/T= 160* (frd/fwr)(8/10)
    • FIFO的深度:160 - 160* (frd/fwr)(8/10)

    因为是同步FIFO,所以frd/fwr = 1,代入得 深度为32。
    由此我们可以推出FIFO的计算公式:
    在这里插入图片描述

    想了解FIFO深度的计算场景,还可以看一个大佬的FIFO深度计算

    FIFO的测试点?

    question1:(22届乐鑫提前批)一个异步FIFO,rdata和wdata均为8位,FIFO深度为16,当rst_n为低时,FIFO复位。当wclk的上升沿采样且wr为高时,数据被写入FIFO,当rclk的上升沿采样且rd为高时,FIFO输出数据。此外,当FIFO为空时,EMPTY为高,当FIFO为满时,FULL信号为高。尽可能列出你能想到的FIFO测试点。

    有两个一级测试点:1.FIFO的基本功能;2.异步处理

    1.FIFO的基本功能测试包括:

    • 检查端口时序。

      1.检查数据数据是否在wclk的上升沿并且wr为高时被写入。如,在wr为高或为低时写入的数据能不能被有效写入。

      2.检查数据数据是否在rclk的上升沿采样且rd为高时读出。

    • 数据一致性。先写后读,检查是否能按先入先出的顺序读出;

    • 空满信号能不能正确生成。先连续地写,再连续的读,检查空满信号是否准确生成;

    • 读写指针有没有做空满限制。当FIFO满时,外部有没有做保证写指针不会增加;当FIFO空时,读指针是否还在增加;

    • 检查复位信号,各个寄存器和信号是否能被正常复位,复位释放后信号的初始值是否正常;

    • 读写指针转换为格雷码的逻辑检查。

    2.异步处理:

    • 读写指针跨时钟域传输时,有没有做同步处理。
    • FIFO的读写时钟频率差速耐受能力。检查读快写慢、写慢读快一定周期后,FIFO是否还能保持稳定。

    异步FIFO的verilog代码

    异步FIFO的结构如图所示。由读时钟域、写时钟域、Mermory模块和跨时钟域同步组成。
    在这里插入图片描述

    异步FIFO代码

    本博客内容过长,不方便阅读。详细代码请看我的另一篇博客:异步FIFO代码(Verilog)

    展开全文
  • 摘要:异步FIFO(First-In-First-Out,先进先出队列)作为数字系统中的一种基本结构,被广泛应用于设计之中。其可靠性影响整个系统能否正常运行。提出一种折叠式的容错方法,用于提高异步FIFO的可靠性。该方法通过控制...
  • 异步fifo设计

    2020-11-30 21:22:32
    异步FIFO主要用于跨时钟域数据的传输,一些异步FIFO的错误设计也能实现90%的功能,错误较少的设计在99%的时间内也能正常工作,本文指明了一些异步FIFO设计中不能忽视的细节。 二、传输异步信号 FIFO设计的主要难点...
  • 异步FIFO总结

    2017-07-09 14:10:00
    异步FIFO总结 异步FIFO的基本概念 异步FIFO读写分别采用相互异步的不同时钟,使用异步FIFO可以在两个不同时钟系统之间快速而方便地传输实时数据 FIFO的常见参数 FIFO的宽度:即FIFO一次读写操作的数据位; FIFO...
  • 异步FIFO的VHDL实现

    2021-03-05 16:53:07
    --rtl 这是源代码 --sim 这是modelsim仿真...已经验证,可以实现异步FIFO 两级寄存器实现读写指针的同步,地址采用格雷码形式防止亚稳态。异步FIFO的源码,个人觉得不易理解,故上传本人最近写的源码,与大家一起分享
  • 异步FIFO控制器的设计

    2020-10-18 23:06:39
    通过分析异步FIFO设计中的难点,以降低电路中亚稳态出现的概率为主要目的,提出了一种格雷码计数器的技术,通过仿真验证,有效地实现了异步FIFO控制器的设计。该设计将大大提高工作频率和资源利用率。
  • 异步FIFO(一)

    2021-08-27 22:00:39
    异步FIFO是指读写时钟不一致,读写时钟是互相独立的,异步FIFO常用于跨时钟域交互。 重要参数 FIFO的深度:通俗地说,就是异步FIFO可以存多少个数据的意思! FIFO的宽度:上面说FIFO的深度是表示能存放...
  • 利用异步FIFO实现FPGA与DSP进行数据通信的方案。FPGA在写时钟的控制下将数据写入FIFO,再与DSP进行握手后,DSP...经验证,利用异步FIFO的方法,在FPGA与DSP通信中的应用,具有传输速度快、稳定可靠、实现方便的优点。
  • 异步FIFO的空、满、半满、将空、将满标志都有包含,代码通过modelsim验证
  • 异步FIFO设计与实现

    千次阅读 2017-06-22 20:07:02
     异步FIFO是一种先进先出电路,用在需要实时数据接口的部分,用来存储、缓冲在两个异步时钟之间的...异步FIFO与同步FIFO最大的不同在于异步FIFO读写时钟不同,通常异步FIFO用来做数据的时钟域转换,FIFO设计中难度最
  • 异步FIFO的设计

    2020-08-22 09:13:07
    本文首先对异步 FIFO 设计的重点难点进行分析 最后给出详细代码 一、FIFO简单讲解 FIFO的本质是RAM, 先进先出 ...异步FIFO可以解决跨时钟域的问题,在应用时需根据实际情况考虑好fifo深度即可...
  • 异步FIFO设计注意事项

    2019-08-25 22:21:05
    在我看来,异步FIFO的设计注意事项大体分为两: 亚稳态的处理 空满状态的判断 第一,来说一下亚稳态。亚稳态是指触发器无法在某个规定时间内达到一个可确认的状态。在FIFO的设计中,产生的原因主要由两:...
  • 异步fifo设计要点

    2020-04-17 22:04:19
    今天经过与HR的交谈意识到自己对于异步FIFO的理解还不深,因此回来之后又其结构进行了一些理解,于此分享。 异步FIFO的设计:http://blog.csdn.net/moon9999/article/details/77822606 疑问1:进行数据空满的对比时...
  • 本文提出一种新颖的异步FIFO设计方案,它通过先比较读写地址并结合象限检测法产生异步的空/满标志,再把异步的空/满标志同步到相应的时钟域。通过仿真验证,该方法是稳定有效的。
  • 同步FIFO和异步FIFO的Verilog实现
  • 简明 异步FIFO设计

    2018-01-22 17:04:10
    异步fifo的设计(FPGA) 原文摘自:http://www.cnblogs.com/aslmer/p/6114216.html 本文首先对异步 FIFO 设计的重点难点进行分析 最后给出详细代码 一、FIFO简单讲解 FIFO的本质是RAM, 先进先出 ...
  • 关于异步FIFO

    千次阅读 2018-08-10 23:18:59
    参考http://blog.163.com/jinyun_xie/blog/static/12839081120123692259923/ 异步FIFO 在产生写满读空信号的时候,可以有两种解决方式:一,格雷码两级同步触发器同步后地址比较  ...
  • 同步FIFO和异步FIFO

    万次阅读 多人点赞 2017-09-19 09:30:03
    1.定义 FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据, 其数据地址由内部...
  • 一开始是想既然是极简教程,就应该只给出FIFO的概念,没想到还是给出了同步以及异步FIFO的设计,要不然总感觉内容不完整,也好,自己设计的FIFO模块不用去担心因IP核跨平台不通用的缺陷!那我们开始吧。
  • 异步FIFO设计三、重难点四、仿真波形4.1 读写不同时进行的逻辑测试4.1.1 FIFO初始化功能验证4.1.2 FIFO空标志产生逻辑的验证4.1.4 FIFO写满保护功能的验证4.1.5 FIFO读空保护(不再读)功能的验证4.2 读写同时进行的...
  • 同步Fifo和异步fifo

    2013-10-11 21:52:11
    【转】同步FIFO和异步FIFO的Verilog实现2011-10-1010:59:26|分类:FPGA学习|标签:fifoverilogfpga|字号大中小订阅FIFO是英文FirstInFirstOut的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,649
精华内容 2,659
关键字:

异步fifo验证点