异步FIFO

理论参考:
异步FIFO—Verilog实现
异步FIFO——结构、Verilog代码实现与仿真
FPGA逻辑设计回顾(6)多比特信号的CDC处理方式之异步FIFO有空满判断详细说明
引用红色闪电007的评论:
异步FIFO使用格雷码的唯一目的就是"即使在亚稳态进行读写指针抽样也能进行正确的空满状态判断"。 在异步的FIFO中,采用格雷码进行计数,相邻的数据仅仅只有1bit变化,这样在两个时钟域同步的时候仅仅可能只有1bit产生亚稳态,通过同步以后,亚稳态可以消除,最坏的情况是这1bit采错,但是即使是采错地址也只是相差1个,这对判断空满标志不会产生影响。 如果是采用10十进制进行编码,则相邻的数据可能有很多位同时进行变化,那么如果多位同时产生亚稳态而且同时采错数据,会对寄存器的空满标志做出严重错误的判断,会丢失数据或者读出无用的数据,使系统出错。 结论:个人认为格雷码的作用不能表述为消除亚稳态。
引用wulsong的评论:
【个人的一点理解】所谓的“虚满”、“虚空”概念并不那么重要。以“虚满”为例:写速率大于读速率时,会产生“满”标志,它是由读指针read_ptr转换为格雷码后同步到写时钟域进行判断生成的,相当于慢时钟同步到快时钟(empty也是,这是异步fifo能够正常工作的潜在原因)。慢时钟的读指针经过两级延时(wr_clk)同步到写时钟域,之间延时是写时钟的两个周期。又因为这种情况下读指针大于写指针,因此full可能会提前1~2个周期(写时钟),这对所有的读写频率差都是如此。所以,所谓的“虚满”只是在快时钟域的2级延时,属于正常现象,对FIFO本身没有影响。

module asys_fifo#(
    parameter   DATA_WIDTH  =   16,
    parameter   DATA_DEPTH  =   256,
    parameter   ADDR_WIDTH  =   8
    )
(
    input                           rst,
    input                           wr_clk,
    input                           wr_en,
    input       [DATA_WIDTH-1:0]    din,
    input                           rd_clk,
    input                           rd_en,
    output  reg                     valid,
    output  reg [DATA_WIDTH-1:0]    dout,
    output                          full,
    output                          empty
    );
    
    reg     [ADDR_WIDTH:0]      wr_addr_ptr;//地址指针,比地址多一位
    reg     [ADDR_WIDTH:0]      rd_addr_ptr;
    wire    [ADDR_WIDTH-1:0]    wr_addr;
    wire    [ADDR_WIDTH-1:0]    rd_addr;
    
    wire    [ADDR_WIDTH:0]      wr_addr_gray;
    reg     [ADDR_WIDTH:0]      wr_addr_gray_d1;
    reg     [ADDR_WIDTH:0]      wr_addr_gray_d2; 
    wire    [ADDR_WIDTH:0]      rd_addr_gray; 
    reg     [ADDR_WIDTH:0]      rd_addr_gray_d1;
    reg     [ADDR_WIDTH:0]      rd_addr_gray_d2; 
    
    reg     [DATA_WIDTH-1:0]    fifo_ram[DATA_DEPTH-1:0];
    
    genvar i;
    generate
        for(i = 0; i < DATA_DEPTH; i = i + 1) begin:fifo_init
            always@(posedge wr_clk or negedge rst) begin
                if(rst)
                    fifo_ram[i] <= 'h0;
                else if(wr_en && ~full)
                    fifo_ram[wr_addr] <= din;
                else
                    fifo_ram[wr_addr] <= fifo_ram[wr_addr];
            end
        end
    endgenerate
    
    always@(posedge rd_clk or negedge rst) begin
        if(rst) begin
            dout <= 'h0;
            valid <= 0;
        end
        else if(rd_en && !empty) begin
            dout <= fifo_ram[rd_addr];
            valid <= 1'b1;
        end
        else begin
           dout <= 'h0;
           valid <= 0; 
        end
    end
    
    assign wr_addr = wr_addr_ptr[ADDR_WIDTH-1:0];
    assign rd_addr = rd_addr_ptr[ADDR_WIDTH-1:0];
    
    always@(posedge wr_clk) begin
        rd_addr_gray_d1 <= rd_addr_gray;
        rd_addr_gray_d2 <= rd_addr_gray_d1;
    end
    
    always@(posedge wr_clk or negedge rst) begin
        if(rst)
            wr_addr_ptr <= 'h0;
        else if (wr_en && !full)
            wr_addr_ptr <= wr_addr_ptr + 1;
        else
            wr_addr_ptr <= wr_addr_ptr;
    end
    
    always@(posedge rd_clk) begin
        wr_addr_gray_d1 <= wr_addr_gray;
        wr_addr_gray_d2 <= wr_addr_gray_d1;
    end
    
    always@(posedge rd_clk or negedge rst) begin
        if(rst)
            rd_addr_ptr <= 'h0;
        else if(rd_en && !empty)
            rd_addr_ptr <= rd_addr_ptr + 1;
        else
            rd_addr_ptr <= rd_addr_ptr;
    end
    
    assign wr_addr_gray = (wr_addr_ptr>>1) ^ wr_addr_ptr;
    assign rd_addr_gray = (rd_addr_ptr>>1) ^ rd_addr_ptr;
    
    assign full = (wr_addr_gray == {~rd_addr_gray_d2[ADDR_WIDTH:ADDR_WIDTH-1],rd_addr_gray_d2[ADDR_WIDTH-2:0]});
    assign empty = (rd_addr_gray == wr_addr_gray_d2);
endmodule

module tb_asys_fifo();
 parameter   DATA_WIDTH  =   16;
 parameter   DATA_DEPTH  =   16;
 parameter   ADDR_WIDTH  =   4;
 
 reg wr_clk, rd_clk ;
 reg [DATA_WIDTH-1:0]    din ; 
 reg rd_en ;
 reg rst ;
 reg wr_en ; 
 
 
wire [DATA_WIDTH-1:0]    dout;
wire empty ;
wire full ; 
wire valid;
initial begin
    rd_clk=0;
    wr_clk=0;
    rst=1;
    rd_en=0;
    wr_en=0;
    din=0;

    #40
    rst = 0 ;
    #35
    wr_en=1;
    #400
    rd_en=1;
    #400
    wr_en=0;
    #400
    wr_en = 1;
    #400
    rd_en = 0;
    #400
    $stop;
end
//写得比读的快,容易写满,产生full信号
always #20 din<=din+1;

always #15 rd_clk=~rd_clk;
always #10 wr_clk=~wr_clk;
//读得比写的快,容易读空,产生empty信号
//always #30 din<=din+1;

//always #10 rd_clk=~rd_clk;
//always #15 wr_clk=~wr_clk;

asys_fifo #(
    DATA_WIDTH,
    DATA_DEPTH,
    ADDR_WIDTH)
u_asyc_fifo (
    .rst(rst),
    .wr_clk(wr_clk),
    .wr_en(wr_en),
    .din(din),
    .rd_clk(rd_clk),
    .rd_en(rd_en),
    .valid(valid),
    .dout(dout),
    .full(full),
    .empty(empty)
    );

endmodule

结果:
1. 当读时钟频率 > 写时钟频率,容易读空,产生empty信号;
当读空后,同时读写,产生空标志,读出的数据不连续,写入数据不影响。
在这里插入图片描述
读出数据与fifo_ram存储数据,看出读出数据不连续:
在这里插入图片描述
产生空标志,读出的数据不连续,写入数据不影响
在这里插入图片描述
2. 当写时钟频率 > 读时钟频率,容易写满,产生full信号;
当写满后,同时读写,产生满标志,写入的数据不连续,读出数据不影响。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
面试问题
芯动科技:
1.一般设计FIFO深度为2的幂次方,现在设计一个深度为6的FIFO要怎么设计?
答:还是采用格雷码,只是找到连续6个相邻变化只有1位的格雷码组
在这里插入图片描述
可以采用1-6的格雷码
2.两边时钟差异很大时,比如写时钟500M,读时钟5M,读时钟采写时钟域地址时,相比上一次采样,数据都变了,要怎么设计(大致这个意思)
答:不变 ,读时钟采数据的时候稳定了

全志:
双口ROM同时读写时读写冲突怎么办?

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vivado是一款由赛灵思(Xilinx)公司开发的集成电路设计工具。它提供了丰富的功能和工具,用于设计、验证和实现FPGA(现场可编程门阵列)和SoC(片上系统)。异步FIFO(First In First Out)是FIFO的一种类型,用于跨时钟域信号传递。异步FIFO与同步FIFO相比,不要求读写时钟频率相同,因此更加灵活。 在Vivado中,可以使用IP核(Intellectual Property)库中提供的现成的异步FIFO IP来设计异步FIFO。这个IP核包含了异步FIFO的功能模块,可以直接在设计中使用。可以通过Vivado的可视化界面添加异步FIFO IP核,并根据设计需求进行配置。配置完成后,可以生成对应的硬件描述语言(HDL)代码,通过这些代码可以实例化异步FIFO模块。 除了使用Vivado提供的异步FIFO IP核外,您也可以自己编写异步FIFO模块。根据您提供的引用,您可以自编异步FIFO,并根据具体需求选择normal模式或show-ahead模式。在设计完成后,您可以使用Vivado提供的testbench功能对异步FIFO进行仿真,以验证其功能和性能。根据您的描述,测试结果与Xilinx IP一致,这表明您的自编异步FIFO在功能上与Xilinx IP相似。 总结起来,您可以在Vivado中使用现成的异步FIFO IP核,也可以自己编写异步FIFO模块。通过Vivado的可视化界面或者HDL代码进行配置和实例化。最后,使用Vivado的testbench功能对异步FIFO进行仿真,以验证其功能和性能。这样,您就可以在Vivado中设计和实现异步FIFO了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值