async_fifo实现与注意要点

  1. 目录

    async_fifo结构说明

    为什么使用格雷码

    空满判断标志

    假空假满

    注意事项


    async_fifo结构说明

和同步fifo一样,中间是双口的ram,左右两个模块分别是读写地址的产生和判断空满信号。比如在FIFO wptr &waddr模块中产生ram的地址信号waddr,然后将waddr这个二进制编码的地址信号转换为格雷码来进行跨时钟域处理(注意,二进制编码转换格雷码是组合逻辑,组合逻辑的输出不能直接的进行跨时钟域,因为毛刺会影响跨时钟域的结果,需要将组合逻辑的输出放到寄存器中,之后再进行跨时钟域的处理)。在读时钟域下的rq2_wptr信号与原来的写时钟域的下的rptr信号来进行空状态的判断,以上是从写时钟域到读时钟域下的操作,从读时钟域到写时钟域同理。

为什么使用格雷码

使用格雷码的根本原因就是就算跨时钟域的时候,出现了亚稳态,整个fifo的功能也不会出现错误。比如用二进制的waddr来进行跨时钟域,比如waddr从7到8的变化(0111->1000),raddr此时处于6的位置,跨时钟域的时候,寄存器会随机的取到中间状态的一个值,所以在rq2_wptr处得到的结果可能是2,此时raddr是6,相当于fifo存就存到了2,读却读到了6,这样会使fifo的功能错误。如果使用格雷码,waddr从7到8的变化(0100->1100),中间就涉及到了一个值的变化,就算出现亚稳态,不是1100就是0100,是1100的话,正好功能正确,如果是0100的话,不影响fifo的读,最多可能出现fifo的假空判断,不会出现fifo功能的错误,即读大于写(读指针指向当前的值,写指针指向下一时刻的值)

空满判断标志

在格雷码的基础上,扩展一位最高位,这个最高位可以方便判断空满,当写指针转了一圈又回来,说明把fifo写满了,此时读指针没有动的话,说明fifo是满的,此时扩展后的读指针和扩展后的写指针最高位不同,其他位置都一样。如果fifo是空的话,二者全相等。空标志在读时钟域判断,满标志在写时钟域判断

假空假满

假空假满根本原因是因为打两拍的原因,比如写指针打两拍到了读时钟域,此时写指针已经大于了同步的写指针,如果此时判断为空的话,实际上,写指针在同步的时间里已经存了值进去,这样就是假空。假满同理,但是这个对fifo功能没有影响,比如fifo的深度是6,在1的时候空标志位拉高,只能说fifo实际的深度只有5,这是一种保守设计,对功能没影响。

关于异步FIFO设计,这7点你必须要搞清楚_孤独的单刀的博客-CSDN博客_异步fifo为什么要打两拍

这篇文章从快慢时钟详细的分析了空满的判断已经对fifo功能影响

注意事项

二进制编码转换格雷码是组合逻辑,组合逻辑的输出不能直接的进行跨时钟域,因为毛刺会影响跨时钟域的结果,需要将组合逻辑的输出放到寄存器中,之后再进行跨时钟域的处理

async_fifo的设计代码(欢迎大家挑错)

module async_fifo(wclk,wr,din,full,rclk,rd,dout,empty,rst_n);
parameter data_width = 16;
parameter data_depth = 8;
parameter ram_depth = 256;

input rst_n;
input wclk;
input wr;
input [data_width-1:0] din;
output full;

input rclk;
input rd;
output [data_width-1:0] dout;
output empty;

reg [data_width-1:0] dout;
wire empty;
wire [data_depth-1:0] wr_addr;
wire [data_depth-1:0] rd_addr;

reg [data_depth:0] wr_addr_ptr;
reg [data_depth:0] rd_addr_ptr;

reg [data_depth:0] wr_addr_gray_rdy;
wire [data_depth:0] wr_addr_gray;
reg [data_depth:0] wr_addr_gray1;
reg [data_depth:0] wr_addr_gray2;

reg [data_depth:0] rd_addr_gray_rdy;
wire [data_depth:0] rd_addr_gray;
reg [data_depth:0] rd_addr_gray1;
reg [data_depth:0] rd_addr_gray2;

assign wr_addr = wr_addr_ptr[data_depth-1:0];
assign rd_addr = rd_addr_ptr[data_depth-1:0];

assign wr_addr_gray = (wr_addr_ptr >> 1) ^ wr_addr_ptr;
assign rd_addr_gray = (rd_addr_ptr >> 1) ^ rd_addr_ptr;

reg [data_width-1 : 0] ram_fifo [ram_depth-1:0];

integer i;
always@(posedge wclk or negedge rst_n)
if(!rst_n)begin
    for(i=0;i<ram_depth;i=i+1)
    ram_fifo[i] <= 0;
end
else if(wr && (!full))
ram_fifo[wr_addr] <= din;

always @(posedge rclk or negedge rst_n) begin
    if(!rst_n)
    dout <= 0;
    else if(rd && (!empty))
    dout <= ram_fifo[rd_addr];  
end

always @(posedge wclk or negedge rst_n) begin
    if(!rst_n)
    wr_addr_ptr <= 0;
    else if(wr &&(!full))
    wr_addr_ptr <= wr_addr_ptr + 1;
end

always @(posedge rclk or negedge rst_n) begin
    if(!rst_n)
    rd_addr_ptr <= 0;
    else if(rd &&(!empty))
    rd_addr_ptr <= rd_addr_ptr + 1;
end

always@(posedge wclk or negedge rst_n) begin
    if(!rst_n)
    wr_addr_gray_rdy <= 0;
    else
    wr_addr_gray_rdy <= wr_addr_gray;
end

always@(posedge rclk or negedge rst_n) begin
    if(!rst_n)
    rd_addr_gray_rdy <= 0;
    else 
    rd_addr_gray_rdy <= rd_addr_gray;
end


always@(posedge wclk or negedge rst_n)begin
    if(!rst_n)begin
    rd_addr_gray1 <=0;
    rd_addr_gray2 <=0;
    end
    else begin
    rd_addr_gray1 <= rd_addr_gray_rdy;
    rd_addr_gray2 <= rd_addr_gray1;
    end
end

always@(posedge rclk or negedge rst_n)begin
    if(!rst_n)begin
    wr_addr_gray1 <=0;
    wr_addr_gray2 <=0;
    end
    else begin
    wr_addr_gray1 <= wr_addr_gray_rdy;
    wr_addr_gray2 <= wr_addr_gray1;
    end
end

assign empty = (wr_addr_gray2 == rd_addr_gray)? 1'b1:1'b0;
assign full = (wr_addr_gray[data_depth:data_depth-1'b1] != rd_addr_gray2[data_depth:data_depth-1'b1]) && (wr_addr_gray[data_depth-1:0] == rd_addr_gray2[data_depth-1:0]);
endmodule

仿真波形

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: async_fifo是一种用于数据传输的FIFO(先进先出)队列,其在UVM验证环境中的验证是非常重要的。 在进行async_fifo的UVM验证过程中,需要创建一个验证组件(agent),该组件主要由sequencer、driver和monitor组成。其中,sequencer负责向FIFO队列中发出数据请求,driver负责处理这些请求并将数据发送到FIFO队列中,monitor负责监测FIFO队列的状态和数据。 首先,需要编写驱动程序(driver),该驱动程序会从随机产生的数据生成交易,并将其发送到FIFO队列中。驱动程序还会处理来自环境的输入交易,并将它们传递给FIFO队列。通过监控程序(monitor)可以实时监测FIFO队列中的数据状态以及数据的正确性。 其次,需要编写一个产生随机交易的生成器(generator),用于测试FIFO队列的各种边界条件和异常情况。生成器可以随机生成一系列输入交易,并将其提供给驱动程序。 然后,需要编写一个转发器(sequencer),用于将生成器产生的交易传递给驱动程序,并处理来自驱动程序的数据请求。转发器可以控制交易的发送顺序以及产生的时序等。 最后,需要编写一个检查器(checker),以验证FIFO队列的输出数据是否正确。检查器可以对接收到的数据进行校验,并与预期结果进行比较来判断测试是否通过。 在整个验证过程中,需要编写相应的验证测试用例,覆盖不同的情况和边界条件,以确保async_fifo的正确性和稳定性。 综上所述,async_fifo的UVM验证主要涉及到创建验证组件、编写驱动程序、生成器、转发器和检查器,并通过编写相应的测试用例来验证FIFO队列的功能和性能。这样可以保证async_fifo的正确性,并提高其在实际应用中的可靠性。 ### 回答2: async_fifo是一种异步先进先出(FIFO)队列,在UVM验证环境中使用它可以有效地进行一些验证任务。在验证过程中,我们经常需要将一些数据交换或传递给不同的模块或组件。使用FIFO队列可以帮助我们管理输入和输出数据的顺序,以确保正确性和一致性。 在UVM中验证async_fifo,我们可以创建一个自定义的验证组件,该组件包含了FIFO队列。我们可以使用UVM提供的uvm_queue类来实现FIFO功能,该类提供了插入和提取数据项的方法,以及一些其他操作。 为了验证async_fifo,我们需要编写一些验证代码,以测试FIFO的正确性和性能。我们可以创建信号生成器来生成一系列随机的输入数据项,并将其插入到FIFO队列中。然后,我们可以创建一个监控器,用于检查FIFO队列的输出数据,并与预期结果进行比较。通过这种比较,我们可以验证async_fifo是否按照预期的顺序提取数据项。 此外,我们还可以使用UVM的事务和驱动机制来验证async_fifo的性能。我们可以创建一个驱动来向FIFO队列中连续插入数据项,并创建一个监控器来检查每个数据项的提取时间。通过分析提取时间,我们可以评估FIFO队列的性能和延迟,并确定是否满足设计要求。 总而言之,验证async_fifo是一个重要的任务,可以通过创建自定义的验证组件、使用uvm_queue类实现FIFO功能以及使用UVM的事务和驱动机制来完成。通过验证FIFO的正确性和性能,我们可以确保它在实际应用中的可靠性和稳定性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值