基于UVM平台对异步FIFO的验证(2.建立sequence)

继上一章,仅使用了driver对DUT进行驱动,这一章将加入interface、env、agent、sequencer、sequence、driver等,如下图所示。

1.top.sv 需要包含所有子文件,并对DUT进行例化,interface的输入,以及在顶层通过config_db设置好interface,以便在下面的组件中获取interface

`include "./rtl/defines.v"
 `include "./rtl/dual_port_dram.v"
`include "./rtl/ASFIFO.v"
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "agent_config.sv"
`include "env_config.sv"
`include "dut_interface.sv"
`include "my_transaction.sv"
`include "my_sequence.sv"
`include "my_sequencer.sv"
`include "my_driver.sv"
`include "my_monitor.sv"
`include "master_agent.sv"
`include "my_env.sv"
`include "my_test.sv"
 
module top_tb;
  parameter WIDTH = 16;
  parameter PTR   = 4 ;


bit wrclk_sys,rdclk_sys;
dut_interface inf(wrclk_sys,rdclk_sys);
  
ASFIFO 
  uASFIFO
  (
  .wrclk(inf.wrclk),
  .rdclk(inf.rdclk),
  .rd_rst_n(inf.rd_rst_n),
  .wr_rst_n(inf.wr_rst_n),
  .wr_en(inf.wr_en),
  .rd_en(inf.rd_en),
  .wr_data(inf.wr_data),
  .rd_data(inf.rd_data),
  .wr_full(inf.wr_full),
  .rd_empty(inf.rd_empty)
  ); 
  
  initial begin
    wrclk_sys = 0;
	forever begin
	  #2 wrclk_sys= ~wrclk_sys;
	end
  end
  
  initial begin
   rdclk_sys = 0;
	forever begin
	  #4 rdclk_sys = ~rdclk_sys;
	end
  end
  

initial begin
 uvm_config_db#(virtual dut_interface)::set(null,"uvm_test_top","top_if",inf);
run_test("my_test");
end

initial begin

end

endmodule

top的下一层为testcase

2.my_test.sv代码如下,需要在其中例化env和env的配置参数。在build_phase 中创建、获取并配置相关资源:获取top层中的interface到m_env_cfg.m_agent_cfg.m_vif中,使用了default sequence方法调用sequence

//testcase的两个作用:
//testcase实例化和配置env
//指定需要启动的sequence
class my_test extends uvm_test;

	`uvm_component_utils(my_test)
	
	my_env m_env;
        env_config m_env_cfg;
	
	function new(string name = "my_test", uvm_component parent = null);
		super.new(name,parent);
                m_env_cfg=new("m_env_cfg");
	endfunction

	virtual function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		m_env = my_env::type_id::create("m_env",this);
		uvm_config_db#(uvm_object_wrapper)::set(this,
						"*.m_seqr.run_phase",
						"default_sequence",
						my_sequence::get_type());
		//使用uvm_config机制配置agent_sequencer的default_sequence
	uvm_config_db#(int)::set(this,"*.m_seqr","item_num",100);
        m_env_cfg.is_coverage=0;
        m_env_cfg.is_check=0;
        m_env_cfg.m_agent_cfg.is_active=UVM_ACTIVE;

        if(!uvm_config_db#(virtual dut_interface)::get(this,"","top_if",m_env_cfg.m_agent_cfg.m_vif)) begin
`uvm_fatal("CONFIG_EFFOR","test can not get the interface")
end

    uvm_config_db#(env_config)::set(this,"m_env","env_cfg",m_env_cfg);
endfunction

	virtual function void start_of_simulation_phase(uvm_phase phase);
		super.start_of_simulation_phase(phase);
		uvm_top.print_topology(uvm_default_tree_printer);
		//在start_of_simulation_phase中打印出本平台的结构
	endfunction

endclass

3.my_env.sv  

env中需要例化agent,以及env的配置参数,其中env中的配置参数中也包含了agent的配置参数,在factory中注册。在build_phase 中获取、配置相关资源

typedef class env_config;
class my_env extends uvm_env;
  `uvm_component_utils(my_env)
  master_agent m_agent;
  env_config m_env_cfg;


 function new (string name="" ,uvm_component parent);
  super.new(name,parent);

endfunction

 virtual function void build_phase(uvm_phase phase);
  super.build_phase(phase);

  if(!uvm_config_db#(env_config)::get(this,"","env_cfg",m_env_cfg)) begin
`uvm_fatal("CONFIG_FATAL","ENV can not get the configuration")
end

  uvm_config_db#(agent_config)::set(this,"m_agent","m_agent_cfg",m_env_cfg.m_agent_cfg);
  
  if(m_env_cfg.is_coverage) begin
`uvm_info("COVERAGE_ENABLE","The function coverage is enable for this testcase",UVM_MEDIUM)
end

  if(m_env_cfg.is_check) begin
`uvm_info("CHECK_ENABLE","The check function is enable for this testcase",UVM_MEDIUM)

end

  m_agent=master_agent::type_id::create("m_agent",this);
 

endfunction

endclass

4.agent的配置资源,包含了interface 和is_active ,is_active用与判断agent是passive模式还是active模式,在passive模式中只包含monitor

class agent_config extends uvm_object;
uvm_active_passive_enum is_active=UVM_ACTIVE;
virtual dut_interface m_vif;

`uvm_object_utils_begin(agent_config)
 `uvm_field_enum(uvm_active_passive_enum,is_active,UVM_ALL_ON)
`uvm_object_utils_end

function new(string name="agent_config");
 super.new(name);
endfunction
endclass



5.env的配置资源,agent的配置资源包含在env中

class env_config extends uvm_object;
 int is_coverage=0;
 int is_check=0;

 agent_config m_agent_cfg;

`uvm_object_utils_begin(env_config)
 `uvm_field_int(is_check,UVM_ALL_ON)
 `uvm_field_int(is_coverage,UVM_ALL_ON)
 `uvm_field_object(m_agent_cfg,UVM_ALL_ON)
`uvm_object_utils_end

function new(string name="env_config");
 super.new(name);
m_agent_cfg=new("m_agent_cfg");
endfunction
endclass

6.interface.sv

设置了时钟blocking ,分为了读driver,读monitor,写driver,写monitor

interface dut_interface(input bit wrclk,input rdclk);

//write interface
logic wr_rst_n;
logic [`WIDTH-1:0] wr_data;
logic wr_en;
logic wr_full;

//read interface

logic rd_rst_n;
logic rd_en;
logic [`WIDTH-1:0] rd_data;
logic rd_empty;

   //write clocking
    clocking drv_wr_cb@(posedge wrclk);
       default input #1 output #0;
       output wr_data;
       output wr_en;
       output wr_rst_n;
       input wr_full;
    endclocking

    clocking mon_wr_cb@(posedge wrclk);
       default input #1 output #0;
       input wr_data;
       input wr_en;
       input wr_full;
    endclocking

   //read clocking
    clocking drv_rd_cb@(posedge rdclk);
       default input #1 output #0;
       input rd_data;
       input rd_empty;
       output rd_en;
    endclocking

    clocking mon_rd_cb@(posedge rdclk);
       default input #1 output #0;
       input rd_data;
       input rd_empty;
       input rd_en;
    endclocking


  reg init_done;
 initial begin
    wr_rst_n = 1;
	rd_rst_n = 1;
	wr_en    = 0;
	rd_en    = 0;
	wr_data  = 'b0;
	init_done= 0;
	
	#3 wr_rst_n = 0;
	    rd_rst_n = 0;
	#3 wr_rst_n = 1;
	    rd_rst_n = 1;
	
	#3 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
 
endinterface

7.master_agent

例化了sequencer,driver,monitor以及相关配置文件。在build_phase中获取配置资源,以及interface,用factory机制创建相关组件。并在agent环境中连接sequencer与driver。事务在他们之间传递。

在sequencer中内建了一个export(实际是import) seq_item_export,在driver中内建了seq_item_port。在master中连接m_driv.seq_item_port.connect(seq_item_export)

typedef class agent_config;
class master_agent extends uvm_agent;
   `uvm_component_utils(master_agent)

   my_sequencer m_seqr;
   my_driver    m_driv;
   my_monitor   m_moni;
   agent_config m_agent_cfg;


 function new(string name="",uvm_component parent);
  super.new(name,parent);

endfunction

 virtual function void build_phase(uvm_phase phase);
 super.build_phase(phase);


 if(!uvm_config_db#(agent_config)::get(this,"","m_agent_cfg",m_agent_cfg)) begin
`uvm_fatal("CONFIG_FATAL","master_agent can not get the configuration")
end

is_active=m_agent_cfg.is_active;

uvm_config_db#(virtual dut_interface)::set(this,"m_driv","vif",m_agent_cfg.m_vif);
uvm_config_db#(virtual dut_interface)::set(this,"m_moni","vif",m_agent_cfg.m_vif);
 if(is_active==UVM_ACTIVE) begin
    m_seqr=my_sequencer::type_id::create("m_seqr",this);
    m_driv=my_driver::type_id::create("m_driv",this);
end
m_moni=my_monitor::type_id::create("m_moni",this);
endfunction

virtual function void connect_phase(uvm_phase phase);
  if(is_active==UVM_ACTIVE)
    m_driv.seq_item_port.connect(m_seqr.seq_item_export);
endfunction
endclass

8.my_driver.sv

例化interface,获取interface,driver一直向sequencer获取数据,采用seq_item_port.get_next_item(req)。 在m_vif.init_done=1后开始赋值,因为init_done是代表着初始化完毕可以开始赋值的标志,因为我们FIFO的地址是自动增加的,所以需要在此之后才能进行地址自增。在非满,且能写的情况下对interface中的wr_data进行赋值,赋的值为req.data,这个是在事务transaction中定义的,req相当于一个数据包。

class my_driver extends uvm_driver#(my_transaction);
 `uvm_component_utils(my_driver)
virtual dut_interface m_vif;


function new (string name="my_driver",uvm_component parent);
super.new(name,parent);
endfunction

virtual function void build_phase(uvm_phase phase);
   super.build_phase(phase);
  if(!uvm_config_db#(virtual dut_interface)::get(this,"","vif",m_vif)) begin
`uvm_fatal("CONFIG_FATAL","driver can not get the interface")
end


endfunction



virtual task configure_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("DRV_CONFIGURE_PHASE", "Now driver config the DUT...", UVM_MEDIUM)
phase.drop_objection(this);
endtask


virtual task run_phase(uvm_phase phase);
#10;
forever begin
 seq_item_port.get_next_item(req);//transaction request,blocking until get
`uvm_info("DRV_RUN_PHASE", {"\n",req.sprint()}, UVM_MEDIUM)


//send data
      if(m_vif.init_done == 0)begin
	  m_vif.wr_data <= 'b0;
	end

	else begin
	    if(m_vif.wr_full == 0 && m_vif.wr_en==1)begin
	      m_vif.wr_data <= req.data;
	      `uvm_info("my_driver",$sformatf("%0d is driverd at %0t",m_vif.wr_data,$time),UVM_LOW)
	    end
	    else begin
	      m_vif.wr_data <= m_vif.wr_data;
	      `uvm_info("my_driver","fifo is full",UVM_LOW)
	    end
	  end
	

  `uvm_info("fifo_driver","drive is finished",UVM_LOW)
$display("wr_data=%0d",m_vif.wr_data );
seq_item_port.item_done();//return done to sqr
@(m_vif.drv_wr_cb);

end
endtask
endclass

9.my_transaction.sv

在这里只进行data的随机化,只定义随机数data,使用uvm_field机制进行注册,赋予值打印、复制等属性。可以用constraint对随机数进行约束

class my_transaction extends uvm_sequence_item; 
        
	rand bit [`WIDTH-1:0] data;//
	
	`uvm_object_utils_begin(my_transaction)
		`uvm_field_int(data,UVM_ALL_ON)
	`uvm_object_utils_end

	constraint Limit{
		data inside {[0:65535]};
		
	}

	function new(string name = "my_transaction");
		super.new(name);
	endfunction

endclass

10.my_sequence.sv

task phase都需要raise_obiection() 和drop_obiection(), 同一个phase会运行相同的时间,运行结束后才运行下一个phase,具体可参考平台组件phase的运行图。sequence的作用就是发送事务,使用uvm_do宏发送事务。

class my_sequence extends uvm_sequence #(my_transaction);
`uvm_object_utils(my_sequence)

int item_num=3;
function new (string name ="my_sequence");
super.new(name);
endfunction

function void pre_randomize();
 uvm_config_db#(int)::get(m_sequencer,"","item_num",item_num);
endfunction



virtual task body();
 if(starting_phase !=null)
    starting_phase.raise_objection(this);
$display("item_num=%0d",item_num);
repeat(item_num) begin

`uvm_do(req)// generate/wait for finish,uvm_do_with/uvm_do_on,req=handle
end

#500;
if(starting_phase !=null)
  starting_phase.drop_objection(this);
endtask
endclass

11.my_sequencer.sv

之间从uvm_sequencer继承就行,transaction相当于子弹,sequence相当于弹夹,sequencer相当于枪。seuqnce与外界的通信都需通过sequencer进行。

typedef uvm_sequencer #(my_transaction) my_sequencer;

12.my_monitor.sv

一直发送检测到的data值就行。

class my_monitor extends uvm_monitor;
`uvm_component_utils(my_monitor)
virtual dut_interface m_vif;
function new (string name="my_monitor",uvm_component parent);
super.new(name,parent);
endfunction

virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual dut_interface)::get(this,"","vif",m_vif)) begin
`uvm_fatal("Monitor","Monitor don't get the interface")
end
endfunction

virtual task run_phase (uvm_phase phase);
forever begin
`uvm_info("Monitor",{"\n",$sformatf("monitor get the data %0d",m_vif.wr_data)},UVM_MEDIUM)
#10;
end
endtask

endclass

结果如下:UVM平台如下

phase机制

结果波形图

monitor输出

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
UVM验证是一种基于SystemVerilog的硬件验证方法,广泛应用于半导体行业。它提供了一套强大的验证框架,可以帮助工程师有效地进行验证工作。 异步FIFO验证UVM验证中的一个重要部分,用于验证异步First-In-First-Out(FIFO)电路。异步FIFO电路在大多数存储器系统中被广泛使用,因此验证其功能和性能非常重要。 在异步FIFO验证中,需要编写UVM验证环境和测试用例,以验证FIFO电路的功能和时序。首先,可以使用UVM的类来建立一个FIFO模型,包括读写指针、写入和读出逻辑等。然后,可以编写Constrained Random测试用例,以测试FIFO的各种情况,例如空FIFO、满FIFO和部分写入/读出的情况。验证环境会控制测试用例的生成和执行,同时收集和分析验证结果。 在异步FIFO验证中,还需处理时序相关的问题。由于读写指针的异步性质,可能会出现读指针追赶写指针或写指针追赶读指针的情况。为了验证这些情况,可以引入时序检查和约束,以确保FIFO电路在各种时序条件下都能正常工作。 总结来说,UVM验证是一种基于SystemVerilog的硬件验证方法,可以帮助工程师进行有效的验证工作。在异步FIFO验证中,需要建立UVM验证环境和编写测试用例,以验证异步FIFO电路的功能和时序。该验证过程涉及到建模、生成测试用例和进行时序验证等步骤,以确保FIFO电路能够正常工作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值