异步fifo的UVM验证

dirver的实现

关于UVM验证的项目与资料有点少,由于刚入门,想找一个简单的项目来做。决定从零开始搭建一个异步fifo验证环境,参考了很多大佬的资料(发现对着大佬的代码写下来环境跑不起来),用绿皮书+UVM实战两本书决定自己一步一步做一个吧。会按照自己做的顺序来更新组件,水平有限,有问题的地方还请指出,感谢。

1.异步fifo的设计:

异步FIFO的设计代码有很多,在这里不做赘述,直接将DUT+TB的代码贴出:数据16位,fifo存储深度4位:

DRAM模块:

`timescale 1ns/1ns
module DPRAM #
 (
  parameter WIDTH = 16,
  parameter DEPTH = 16,
  parameter ADDR  = 4 
 )
(
  input wrclk,
  input rdclk,
  input rd_rst_n,
  input wr_en,
  input rd_en,
  input [WIDTH-1:0]wr_data,
  input [ADDR-1:0]wr_addr,
  input [ADDR-1:0]rd_addr,
  output reg [WIDTH-1:0]rd_data  
);


  reg[WIDTH-1:0]DPRAM[DEPTH-1:0];
  
always@(posedge wrclk)begin
  if(wr_en)
    DPRAM[wr_addr] <= wr_data; 
end

always@(posedge rdclk or negedge rd_rst_n)begin
  if(!rd_rst_n)
    rd_data <= 'b0;
  else if(rd_en)
    rd_data <= DPRAM[rd_addr];
end
endmodule

FIFO模块:

`timescale 1ns/1ns
`include "DPRAM.v"
module ASFIFO #
(
  parameter WIDTH = 16, //数据总线宽度 
  parameter PTR  = 4   //fifo存储深度
)
(
  input wrclk,
  input rdclk,
  input wr_rst_n,
  input rd_rst_n,
  input wr_en,
  input rd_en,
  input [WIDTH-1:0]wr_data,
  output [WIDTH-1:0]rd_data,
  output reg wr_full,
  output reg rd_empty
);

//***********写时钟信号定义***************//
reg[PTR:0] wr_bin;
reg[PTR:0] wr_gray;
reg[PTR:0] rd_gray_ff1;
reg[PTR:0] rd_gray_ff2;
reg[PTR:0] rd_bin_wr;

//***********读时钟信号定义***************//
reg[PTR:0] rd_bin;
reg[PTR:0] rd_gray;
reg[PTR:0] wr_gray_ff1;
reg[PTR:0] wr_gray_ff2;
reg[PTR:0] wr_bin_rd;

integer i,j;

//**********DPRAM控制信号************//
wire dpram_wr_en;
wire [PTR-1:0] dpram_wr_addr;
wire [WIDTH-1:0] dpram_wr_data;
wire dpram_rd_en;
wire [PTR-1:0] dpram_rd_addr;
wire [WIDTH-1:0] dpram_rd_data;

//*********************写时钟域*******************//
//二进制写地址递增
always@(posedge wrclk or posedge wr_rst_n)begin
  if(!wr_rst_n)begin
    wr_bin <= 'b0;
  end  
  else if(wr_en == 1'b1 && wr_full == 1'b0)begin
    wr_bin <= wr_bin + 1'b1;
  end
  else begin
    wr_bin <= wr_bin;
  end
end
//*********二进制转换格雷码***********//
//********写地址**********************//

always@(posedge wrclk or posedge wr_rst_n)begin
  if(!wr_rst_n)begin
    wr_gray <= 'b0;
  end
  else begin
    wr_gray <= {wr_bin[PTR],wr_bin[PTR:1]^wr_bin[PTR-1:0]};
  end
end

//***********r2w********************//
//*************使用多个中间值打两拍gray>ff1>ff2最终两拍后取ff2***************//
always@(posedge 
 or posedge wr_rst_n)begin
  if(!wr_rst_n)begin
    rd_gray_ff1 <= 'b0;
	rd_gray_ff2 <= 'b0;
  end
  else begin
    rd_gray_ff1 <= rd_gray;
	rd_gray_ff2 <= rd_gray_ff1;
  end
end
//**********rd_addr_bin 2 wr_addr_bin****************// 
always@(*)begin
  rd_bin_wr[PTR] = rd_gray_ff2[PTR];
  for(i=PTR-1;i>=0;i=i-1)
    rd_bin_wr[i] = rd_bin_wr[i+1]^rd_gray_ff2[i];
end

//**********写满*****************//

always@(*)begin
  if(wr_bin[PTR]!=rd_bin_wr[PTR]&&(wr_bin[PTR-1:0]==rd_bin_wr[PTR-1:0]))
    wr_full = 1'b1;
  else
    wr_full = 1'b0;
end

//************读时钟域********************//
always@(posedge rdclk or posedge rd_rst_n)begin
  if(!rd_rst_n)begin
    rd_bin <= 'b0;
  end
  else if(rd_en == 1'b1 && rd_empty == 1'b0)begin
    rd_bin <= rd_bin + 1'b1;
  end
  else begin
    rd_bin <= rd_bin;
  end
end
//***********读地址**************//

always@(posedge rdclk or posedge rd_rst_n)begin
  if(!rd_rst_n)begin
    rd_gray <= 'b0;
  end
  else begin
    rd_gray <= {rd_bin[PTR],rd_bin[PTR:1]^rd_bin[PTR-1:0]};
  end
end

always@(posedge rdclk or posedge rd_rst_n)begin
  if(!rd_rst_n)begin
    wr_gray_ff1 <= 'b0;
	wr_gray_ff2 <= 'b0;
  end
  else begin
    wr_gray_ff1 <= wr_gray;
	wr_gray_ff2 <= wr_gray_ff1;
  end
end

always@(*)begin
  wr_bin_rd[PTR] = wr_gray_ff2[PTR];
  for(j=PTR-1;j>=0;j=j-1)
    wr_bin_rd[j] = wr_bin_rd[j+1]^wr_gray_ff2[j];
end

always@(*)begin
  if(rd_bin == wr_bin_rd)
    rd_empty = 1'b1;
  else
    rd_empty = 1'b0;  
end

DPRAM
  #(.WIDTH(16),.DEPTH(16),.ADDR(4))
  U_DPRAM
  (
  .wrclk(wrclk),
  .rdclk(rdclk),
  .rd_rst_n(rd_rst_n),
  .wr_en(dpram_wr_en),
  .rd_en(dpram_rd_en),
  .wr_data(dpram_wr_data),
  .rd_data(dpram_rd_data),
  .wr_addr(dpram_wr_addr),
  .rd_addr(dpram_rd_addr)
  );
  
  assign dpram_wr_en = (wr_en == 1'b1 && wr_full == 1'b0)? 1'b1:1'b0;
  assign dpram_rd_en = (rd_en == 1'b1 && rd_empty == 1'b0)? 1'b1:1'b0;
  
  assign dpram_wr_data = wr_data;
  assign rd_data = dpram_rd_data;
  
  assign dpram_wr_addr = wr_bin[PTR-1:0];
  assign dpram_rd_addr = rd_bin[PTR-1:0];
endmodule

TB:

`timescale 1ns/1ns

`include "FIFO.v"

module ASFIFO_tb;
  parameter WIDTH = 16;
  parameter PTR   = 4 ;
  
  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;
  reg[3:0]  cnt;
  initial begin
    wr_rst_n = 1;
	rd_rst_n = 1;
	wrclk    = 0;
	rdclk    = 0;
	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;
	
	#30 init_done = 1;
  end
  
  always
    #2 wrclk = ~wrclk;
  
  always
	#4 rdclk = ~rdclk;
	  
  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
  
  always@(posedge wrclk)begin
    if(init_done)begin
	  if(wr_full == 1'b0)begin 
	  wr_data <= wr_data + 1;
	  end
	  else begin
	  wr_data <= wr_data;
	  end
	end
	else 
	wr_data <= 'b0;
  end
  
  ASFIFO 
    #(.WIDTH(16),.PTR(4))
  ASFIFO
  (
  .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)
  );  
endmodule 

2.搭建UVM验证环境

(1)top.tb的搭建:

这里的写法其实类似于tb,主要是我们将发送数据更改到了driver中,多个initial块并行执行,执行到$finish后跳出,这里我们将dirver例化,然后调用main_phase,执行发送随机data的task

`timescale 1ns/1ns
`include "uvm_macros.svh"

import uvm_pkg::*;
`include "fifo_driver.sv"

module top_tb;
  parameter WIDTH = 16;
  parameter PTR   = 4 ;
  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 
    #(.WIDTH(16),.PTR(4))
  ASFIFO
  (
  .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;
	
	#30 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

(2)fifo_driver的搭建

首先定义一个driver,比较简单,只需要一个new函数与main_phase(run_phase 中的一个phase)

这里考虑到要让线程同步进行,选用fork join可以解决这个问题,之前调试了半天,有一个bug就是init_done拉高前,wr_en拉高已经开始有wrdata了,但是rddata读取数据是从init_done拉高后一个cycle开始读的,用了fork join后完美解决。用forever 会导致这个sim无法finish 一直卡在第一个线程中。使用for循环来控制我们要发送多少个随机数据,然后把每一个发送的数据与发送时间打印出来,如果fifo已经满了 显示full,等全部发送完后,打印finished。逻辑比较简单,其实跟tb的区别也就是可以发送随机的数据。

`ifndef MY_DRIVER__SV
`define MY_DRIVER__SV

import uvm_pkg::*;
  `include "uvm_macros.svh"
  
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
	  //@(posedge top_tb.wrclk);
      for(int i = 0;i < 32;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

最后给各位附上波形图:

可见在90ns init_done拉高的下一个wrclk 开始写入随机数据

然后就先写这么多,后面会根据自己做的进度来慢慢更新的。实在是太菜了,调个driver调一天

  • 7
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
异步FIFO是一种常见的硬件设计模块,用于在不同的时钟域之间传输数据。UVM(Universal Verification Methodology)是一种用于验证硬件设计的方法学。在UVM验证中,验证工程师可以使用UVM中提供的各种类和方法来验证异步FIFO的功能和正确性。 下面是一个简单的UVM验证异步FIFO的示例: 首先,我们需要创建一个Transaction类来表示FIFO中的数据项。这个类可以包含需要验证的数据字段。 然后,我们创建一个Agent类来模拟FIFO的发送和接收端。这个Agent类可以包含两个接口,一个用于发送数据到FIFO,另一个用于从FIFO接收数据。Agent类还可以包含一个Monitor来监视FIFO的状态,并将收到的数据转换为Transaction对象。 接下来,我们创建一个Sequencer类来生成数据项并将其发送到FIFO的发送端口。Sequencer类可以使用UVM提供的随机化机制来生成不同的数据项。 然后,我们创建一个Driver类来驱动Sequencer生成的数据项,并将其发送到FIFO的发送端口。 最后,我们可以创建一个Test类来实例化和连接上述组件,并编写测试用例来验证异步FIFO的功能和正确性。 在验证过程中,我们可以使用UVM提供的各种断言和功能覆盖率工具来验证异步FIFO的正确性。通过生成不同的测试用例和使用各种场景和边界条件,我们可以尽可能地覆盖所有可能的情况,并验证异步FIFO的正确性。 需要注意的是,上述只是一个简单的UVM验证异步FIFO的示例,实际的验证过程可能更为复杂,需要根据具体的设计和需求进行调整和扩展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值