基于UVM平台对异步FIFO的验证(1.基本的FIFO知识)

一、异步FIFO verilog代码

异步FIFO的verilog代码,以及testbench出自这篇文章:

【原创】异步FIFO设计原理详解 (含RTL代码和Testbench代码)_异步fifo testbench-CSDN博客

对其做简要概括:异步FIOFO主要由一个双口RAM 和两个地址控制器(读、写)组成。

双口RAM有:写使能、写数据、写地址、读使能、读数据、读数据、读写时钟和复位信号组成,其中写使能由使能和满标志共同决定,读使能由空标准和读使能共同决定。这部分的代码很简单,主要难点是在读写地址的跨时钟域传输及判断。

读写地址判断器有:使能、地址、复位和空满判断等组成,存在跨时钟域的判断。跨时钟判断的意义是:在写时钟域中需要得到读指针的位置,才能判断是否写满,就不能再写入了,RAM的写使能为0;在读时钟域中需要得到写指针的位置,才能判断是否读空,就不能再读出了,RAM的读使能为0。

1.异步FIFO的地址规则

异步FIFO不能写入指定地址,该代码默认从地址1开始存数据,写入一个数据地址就加一,但是地址的加一并不是通过是否写入数据来判断,而是在时钟上升沿,无复位,无满信号下自动加一。那么这种情况在:写入几个数据后延迟一定时间写入,会出现问题,会出现没有写入新数据但是,地址在增加。针对这种情况后续会在UVM验证平台解决。并且还要在最高位加一位判断位,来判断是否已经多跑了一圈

其次还有一个小问题:在写入数据的同一时钟内地址加一,还是后一个时钟再加一??

如上图所示,他们的区别在于wr_en 在时钟上升沿变为1时,地址在写入后下一个时钟加一;如果wr_en在时钟上升沿之前变为1,地址在写入同时钟加一。他们的区别不大。(原因是在clocking中为避免竞争冒险加入了default延迟,input#1 表示在上升沿前一个时间单位判断)

2.地址转为格雷码的原因,如何转?

由于异步FIFO是工作在两个不同的时钟域中,如果读地址在某一时刻从0111->1000转变 ,恰好写时钟要在这时刻采样读地址,那么得到的值有可能是000-1111中的任一值,这个不确定的读地址值会导致空满状态判断错误。

相邻格雷码之间变化只有一位进行翻转,假如出现错误0000->0001,也是读时钟读到了0000,也就是地址没有跳变,但是用这个错误的写地址去做空判断不会出错(写满还写,读空还读),最多是让空标志在FIFO不是真正空的时候产生,而不会出现空读的情形。

格雷码保证了地址传输过程中出现错误的最小代价:传输的地址只能是实际地址或者上一位地址。写的地址小了>>假空,读的地址小了>>假满,不会产生数据的丢失。

对于如何转的问题有两种方法:第一种就是原文中的:保留自然二进制码的最高位作为格雷码的最高位,而次高位格雷码为二进制码的高位与次高位相异或,而格雷码其余各位与次高位的求法相类似

第二种就是二进制数与右移一位的二进制数进行异或,这种更简单一点。

二进制转为格雷码之后就开始跨时钟域传输了。

3.格雷码的跨时钟域传输

如何进行跨时钟域传输? 打两拍。

这一招对于快时钟到慢时钟奏效,不会漏数据,但是慢时钟到快时钟会出问题,导致读出不连续的数据。如下图,wr_bin_rd为读到写时钟域,rd_bin_wr为写到读时钟域,出现了数据断档,但是传的都是实时的地址(写时钟比读时钟快。)

快时钟到慢时钟的数据丢失会给空满标志产生影响吗?不会,因为他不是采的慢,是漏采,会及时更新现在的指针地址位置。

4.格雷码的解码为二进制(也可不进行解码)

保留格雷码的最高位作为自然二进制码的最高位,而次高位自然二进制码为高位自然二进制码与次高位格雷码相异或,而自然二进制码的其余各位与次高位自然二进制码的求法相类似。

解不解码是关系着后面空满标志的判断。

5.空满标志的判断

解码后位二进制数,空标志为读写地址完全相同,满标志为最高一位不同,其余相同。

如果未解码,直接用格雷码比较的话,写满:最高位和次高位不同,其余相同;读空:完全相同。

最后重温一下开题文章作者在最后的讨论:打两拍是否会对判断产生影响。以“写满”为例,写地址为本地地址,读地址实际经过了:一个读时钟(在本地将二进制读地址转为格雷码)->最多两个写时钟(跨时钟)。才传到了写地址,此时如果判断为满的话,实际上传到时又读了一个多数,所以现在是假满。假空一样的思路。如图所示

完整代码在开头的那篇文章中有。

二、先使用sequence机制发送随机数

这个验证思路来自:

异步FIFO的UVM验证(VCS+Verdi 附源代码)_异步fifo的验证代码-CSDN博客

异步fifo的UVM验证_uvm 异步fifo-CSDN博客

整个验证环境从零开始搭建是很困难的,在这里推荐B站的一个视频《UVM基础》讲得很好,看完之后就对整个平台框架有很好的理解

验证文件夹包含:rtl 、 uvm1.1d 、uvm_tb。

uvm1.1d下载方式如下:

Linux中UVM环境搭建_uvm下载-CSDN博客

将ASFIFO 、DPRAM 、defines 放入rtl 文件夹。defines包含了FIFO的深度和位宽。

这是UVM的结构图,首先需要搭建top层,top层中要包含DUT(即rtl代码,testcase,env,scoreboard,write-master,read-monitor,driver,sequencer,以及事务transaction等等)。

先从一个简单的top+driver结构开始

fifo_driver.sv

`ifndef MY_DRIVER__SV
`define MY_DRIVER__SV
  
class fifo_driver extends uvm_driver;
  function new(string name = "fifo_driver",uvm_component parent = null);
    super.new(name,parent);
  endfunction
  extern virtual task main_phase(uvm_phase phase);
endclass
 
task fifo_driver::main_phase(uvm_phase phase);
  fork
  
    //forever begin
      @(top_tb.init_done == 0)begin
	  top_tb.wr_data <= 'b0;
	end
	
	 
	@(top_tb.init_done == 1) begin
	
      for(int i = 0;i < 100;i++) begin
        @(posedge top_tb.wrclk); 
	    if(top_tb.wr_full == 0)begin
	      top_tb.wr_data <= $urandom_range(0,255);
	      `uvm_info("fifo_driver",$sformatf("%0d is driverd at %0t",top_tb.wr_data,$time),UVM_LOW)
	    end
	    else begin
	      top_tb.wr_data <= top_tb.wr_data;
	      `uvm_info("fifo_driver","fifo is full",UVM_LOW)
	    end
	  end
    end
	
  join
  `uvm_info("fifo_driver","drive is finished",UVM_LOW)
endtask
`endif

top.sv

由于只输入了data,对其他参数还是需要自定义的输入,之后会把所有参数都封装起来。

`include "./rtl/defines.v"

 `include "./rtl/dual_port_dram.v"
`include "./rtl/ASFIFO.v"
import uvm_pkg::*;
`include "uvm_macros.svh"

`include "fifo_driver.sv"
 
module top_tb;
  reg wrclk;
  reg wr_rst_n;
  reg[`WIDTH-1:0] wr_data;
  reg wr_en;
  wire wr_full;
  
  reg rdclk;
  reg rd_rst_n; 
  wire[`WIDTH-1:0] rd_data;
  reg rd_en;
  wire rd_empty;
  
  reg init_done;
  
  ASFIFO 
  uASFIFO
  (
  .wrclk(wrclk),
  .rdclk(rdclk),
  .rd_rst_n(rd_rst_n),
  .wr_rst_n(wr_rst_n),
  .wr_en(wr_en),
  .rd_en(rd_en),
  .wr_data(wr_data),
  .rd_data(rd_data),
  .wr_full(wr_full),
  .rd_empty(rd_empty)
  ); 
  
  initial begin
    wrclk = 0;
	forever begin
	  #2 wrclk = ~wrclk;
	end
  end
  
  initial begin
    rdclk = 0;
	forever begin
	  #4 rdclk = ~rdclk;
	end
  end
  
  initial begin
    wr_rst_n = 1;
	rd_rst_n = 1;
	wr_en    = 0;
	rd_en    = 0;
	wr_data  = 'b0;
	init_done= 0;
	
	#30 wr_rst_n = 0;
	    rd_rst_n = 0;
	#30 wr_rst_n = 1;
	    rd_rst_n = 1;
	
	#29 init_done = 1;
  end
  always@(*)begin
	if(init_done)begin
	  if(wr_full) wr_en = 0;
      else        wr_en = 1;  
	end
  end
  
  always@(*)begin
    if(init_done)begin
	  if(rd_empty) rd_en = 0;
	  else		   rd_en = 1;
	end
  end
  initial begin
    fifo_driver drv;
	drv = new("drv",null);
	drv.main_phase(null);
	$finish();
  end
endmodule

在终端输入命令 vcs -full64 -sverilog -debug_all +v2k -R -nc -debug_pp -LDFLAGS -rdynamic -P ${NOVAS_HOME}/share/PLI/VCS/LINUX64/novas.tab ${NOVAS_HOME}/share/PLI/VCS/LINUX64/pli.a -cpp g++-4.8 -cc gcc-4.8 -LDFLAGS -Wl,--no-as-needed  +define+UVM_NO_DPI -ntb_opts uvm  uvm-1.1d/src/uvm_pkg.sv  top.sv

结果如图:完成了对异步FIFO的随机注入,且读出数据为正确。

  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: FIFO验证是在UVM框架中非常常见的一种验证方式。FIFO是First-In-First-Out的缩写,它是一种特殊的数据结构,它可以按照进入队列的顺序进行数据的读写操作。 在UVM框架中,FIFO验证主要用于验证存储和传输数据的模块,比如FIFO模块和存储器模块。通过使用UVM中提供的各种类和功能,我们可以轻松地进行FIFO验证。 首先,我们需要创建一个FIFO模型,并定义相应的输入和输出接口。然后,我们可以使用UVM中提供的sequencer和driver来生成和驱动随机的输入数据。通过监视器(monitor)和分析器(analyzer),我们可以对输出结果进行检查,并与预期结果进行比较,以判断验证是否通过。 在验证过程中,我们可以使用UVM中提供的各种工具和方法来验证FIFO的各种功能和性能特性,比如空闲状态、写入和读取操作、数据丢失和溢出等等。同时,我们还可以使用UVM提供的各种约束来验证特定的属性和规则,确保FIFO模块的正确性和稳定性。 总之,通过使用UVM框架中提供的各种类和方法,我们可以方便地进行FIFO验证的设计和实现。这种验证方式可以有效地验证FIFO模块的各种功能和性能特性,提高验证的可靠性和效率,为系统级设计的开发和调试提供有力的支持。 ### 回答2: FIFO(First In First Out)是一种数据结构,在UVM(Universal Verification Methodology)框架中,可以使用FIFO验证设计中的数据传输和存储。 在UVM中,我们可以通过使用uvm_fifomem类来实现FIFO验证。首先,我们需要定义FIFO的深度(即可以存储的元素数量),并在测试环境中实例化uvm_fifomem类。然后,在测试环境的构造函数中,我们可以通过调用write()函数向FIFO中写入元素,并使用read()函数从FIFO中读取元素。 在进行FIFO验证时,我们可以编写不同的测试用例,来验证FIFO是否能够正确地处理数据。例如,我们可以编写一个测试用例,向FIFO中按顺序写入一些元素,然后再依次读取这些元素,并验证读取的元素与写入的元素是否一致。此外,我们还可以编写其他的测试用例,验证FIFO在各种情况下的正确性,例如在写满的情况下是否能够正确处理数据。 FIFO验证的重点是确认FIFO能够按照预期的顺序读取和写入数据。我们可以使用UVM提供的assert()函数来进行验证。在写入数据时,我们可以使用assert()函数来验证写入数据的完整性和一致性;在读取数据时,我们可以使用assert()函数来验证读取的数据是否与预期一致。此外,我们还可以使用UVM提供的randomize()函数来生成随机的数据,以测试FIFO是否能够正确地处理各种不同的数据。 总结来说,FIFO验证是使用UVM框架来验证FIFO数据结构的正确性。通过使用uvm_fifomem类,我们可以实现FIFO的功能,并编写相应的测试用例来验证FIFO在各种情况下的正确性。使用assert()函数和randomize()函数可以帮助我们进行验证并生成随机的数据,以确保FIFO的正确性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值