基于UVM平台对异步FIFO的验证(3.建立读写agent)

上一章已经建立了一个完整的agent,后续会添加scoreboard,那么scoreboard的对比数据来源何处? 答案是来源于读agent中monitor的输出  和 写agent中的monitor输出,对比这两个数是否相同。

那么还需不需要再建立一个新的agent呢,答案是否定的,我们可以直接例化同一个agent但是取不同名字就行了,这也是UVM的一个非常方便的功能。

这里借用一下这篇文章中的平台图,write_agent read_agent都是从同一个agent扩展出来的

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

那么如果是从同一个agent扩展出来的,他又如何判断是读还是写呢?

首先在不同的agent中会配置一个cmd参数,cmd=1就是写,cmd=0就是读,其次我们会在agent所有组件中都加入判断语句,cmd=1的话......  ;cmd=0的话执行另一条语句。两个agent并行执行。

1.env的配置文件中也例化了两个agent的配置参数


class my_env extends uvm_env;
  `uvm_component_utils(my_env)
  master_agent m_agent;
  master_agent r_agent;
    asyn_fifo_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);
  m_agent=master_agent::type_id::create("m_agent",this);
  r_agent=master_agent::type_id::create("r_agent",this);
  if(!uvm_config_db#(asyn_fifo_env_config)::get(this,"","asyn_fifo_env_config",m_env_cfg)) begin
`uvm_fatal("CONFIG_FATAL","ENV can not get the configuration")
end

    uvm_config_db#(fifo_agent_config)::set(this,"m_agent*","fifo_agent_config",m_env_cfg.m_wf_cfg);
    uvm_config_db#(fifo_agent_config)::set(this,"r_agent*","fifo_agent_config",m_env_cfg.m_rf_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*/



endfunction

//virtual function void connect_phase(uvm_phase phase)

endclass

2.agent的配置参数中还是例化了interface,在class中例化interface需要加virtual,在module中才可以不加。可参考这篇文章中提到的interface的知识

interface、virtual interface 与 config_db之间的关系?_interface和virtual interface-CSDN博客


class master_agent extends uvm_agent;
   `uvm_component_utils(master_agent)

   my_sequencer m_seqr;
   my_driver    m_driv;
   my_monitor   m_moni;
    fifo_agent_config m_cfg;
   uvm_analysis_port  #(my_transaction) ap;


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

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


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


uvm_config_db#(virtual dut_interface)::set(this,"m_driv","vif",m_cfg.m_vif);
uvm_config_db#(virtual dut_interface)::set(this,"m_moni","vif",m_cfg.m_vif);
 if(m_cfg.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);
    ap=m_moni.ap;
endfunction
endclass

3.top层包含了所有用到的子文件,并启动test,在这里我们还通过sequence配置了读写时钟,分别通过读写agent中的m_sequencer 启动相应的时钟配置sequence,来对顶层文件进行配置。

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

`include "fifo_agent_config.sv"
`include "asyn_fifo_env_config.sv"
`include "my_transaction.sv"
`include "asyn_fifo_seq_base.sv"
`include "rclk_config_seq.sv"
`include "wclk_config_seq.sv"
`include "wrstn_seq.sv"
`include "rdstn_seq.sv"
`include "read_seq.sv"
`include "write_seq.sv"
`include "dut_interface.sv"
`include "my_sequencer.sv"
//`include "my_sequence.sv"
`include "asyn_fifo_vseq_base.sv"
`include "write_fast_vseq.sv"
`include "write_with_reset_vseq.sv"
`include "my_driver.sv"
`include "my_monitor.sv"
`include "master_agent.sv"
`include "my_env.sv"
`include "asyn_fifo_test_base.sv"
`include "write_fast_test.sv"
`include "write_with_reset_test.sv"

module top_tb;
  parameter WIDTH = 16;
  parameter PTR   = 4 ;
 int     wclk_half_period;
    int      rclk_half_period;

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;
#10
if(uvm_config_db#(int)::get(uvm_root::get(),"uvm_test_top.m_env.m_agent.m_seqr","wclk_half_period",wclk_half_period))
`uvm_info("WCLK",$sformatf("Configure the wclk_half_period = [%0d]",wclk_half_period),UVM_NONE)
else begin
            `uvm_info("WCLK","Can't configure wclk with config_db correctly,will use default value:10",UVM_MEDIUM)
            wclk_half_period = 10;
        end
	forever begin
	  #wclk_half_period wrclk_sys= ~wrclk_sys;
	end
  end
  
  initial begin
   rdclk_sys = 0;
 #10
        if(uvm_config_db#(int)::get(uvm_root::get(),"uvm_test_top.m_env.r_agent.m_seqr","rclk_half_period",rclk_half_period))
            `uvm_info("RCLK",$sformatf("Configure the rclk_half_period = [%0d]",rclk_half_period),UVM_NONE)
        else begin
            `uvm_info("RCLK","Can't configure rclk with config_db correctly,will use default value:10",UVM_MEDIUM)
            rclk_half_period = 10;
        end
	forever begin
	  #rclk_half_period rdclk_sys = ~rdclk_sys;
	end
  end
  

initial begin
 uvm_config_db#(virtual dut_interface)::set(null,"uvm_test_top","w_top_if",inf);
 uvm_config_db#(virtual dut_interface)::set(null,"uvm_test_top","r_top_if",inf);
run_test("write_fast_test");
end

initial begin
#5000;
$finish;
end

endmodule

4.时钟的配置文件如下:分别为写时钟和读时钟。分别在body任务中配置了半周期的数值,通过sequencer来传递。那么如何启动这些sequence,又如何配置到相关agent中呢。我们在面对多高sequence并且需要配置到不同agent时,需要用到virtual sequence机制,他来控制目标agent和启动不同sequence的先后顺序。

该图出自B站的UVM基础视频

 在virtual sequence中启动方式如下,使用了`uvm_do_on_with语句,on指定哪一个agent的sequencer,with指定了参数的约束。在这里我们使用了virtual sequence中自定义的时钟周期,就没有用读写时钟sequence中的rand值,如果想产生随机时钟的同学,可以把with部分的参数删掉。

class wclk_config_seq extends uvm_sequence;
    `uvm_object_utils(wclk_config_seq)
    rand int wclk_half_period;
    extern function new(string name = "wclk_config_seq");
    extern task body();

endclass : wclk_config_seq

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

task wclk_config_seq::body();
    uvm_config_db#(int)::set(m_sequencer,"","wclk_half_period",wclk_half_period);
    `uvm_info("WCLK",$sformatf("Configure the wclk half period = [%0d]",wclk_half_period),UVM_MEDIUM)
endtask : body
class rclk_config_seq extends uvm_sequence;
    `uvm_object_utils(rclk_config_seq)
    rand int rclk_half_period;
    extern function new(string name = "rclk_config_seq");
    extern task body();

endclass : rclk_config_seq

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

task rclk_config_seq::body();
    uvm_config_db#(int)::set(m_sequencer,"","rclk_half_period",rclk_half_period);
    `uvm_info("rclk",$sformatf("Configure the rclk half period = [%0d]",rclk_half_period),UVM_MEDIUM)
endtask : body

5. 接下来就是testcase,他负责启动virtual sequence,在这里我没有采用之前default sequence的自动启动方法啊,而是采用的手动启动sequence,启动的是virtual sequence。


`ifndef WRITE_FAST_TEST_SV
`define WRITE_FAST_TEST_SV
class write_fast_test extends asyn_fifo_test_base;
    `uvm_component_utils(write_fast_test)
    extern function new(string name = "write_fast_test",uvm_component parent = null);
    extern task run_phase(uvm_phase phase);
    extern function void build_phase(uvm_phase phase);
endclass : write_fast_test

function write_fast_test::new(string name = "write_fast_test",uvm_component parent = null);
    super.new(name,parent);
endfunction : new

function void write_fast_test::build_phase(uvm_phase phase);
    super.build_phase(phase);
endfunction : build_phase

task write_fast_test::run_phase(uvm_phase phase);
    write_fast_vseq vseq = write_fast_vseq::type_id::create("vseq");
    phase.raise_objection(this);
    init_vseq(vseq);//asyn_fifo_test_base
    vseq.start(null);
    phase.drop_objection(this);
endtask : run_phase
`endif // write_fast_test_SV

6.my_driver是本代码的核心,负责把transaction中的值赋给DUT能收到的PIN级信号

class my_driver extends uvm_driver#(my_transaction);
 `uvm_component_utils(my_driver)
virtual dut_interface m_vif;
   fifo_agent_config cfg;
    bit  has_trans;
    bit  rd_has_reset,wr_has_reset;
    bit  req_status;
    bit [`WIDTH-1:0] data_t;
//int unsigned pad_cycles;

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);
//cfg=fifo_agent_config::type_id::create("cfg");
  if(!uvm_config_db#(virtual dut_interface)::get(this,"","vif",m_vif)) begin
`uvm_fatal("CONFIG_FATAL","driver can not get the interface")
end
 if(!uvm_config_db#(fifo_agent_config)::get(this,"","fifo_agent_config",cfg))
        `uvm_fatal(get_type_name(),"Can't get the fifo_agent_config!")




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

//`uvm_register_cb(my_driver,driver_base_callback)

virtual task reset_wr_rd();
forever begin:reset
             if(cfg.cmd) begin//write
               @(negedge m_vif.wr_rst_n);
               m_vif.drv_wr_cb.wr_en<=0;        
               m_vif.drv_wr_cb.wr_data<=0;    
               wr_has_reset=0;
              // `uvm_info("wr_has_reset",$sformatf("wr_has_reset=%0d",wr_has_reset),UVM_NONE)
             end
             else begin
             @(negedge m_vif.rd_rst_n);
              m_vif.rd_en<=0; 
              rd_has_reset=0;
             end
end
endtask

virtual task reset_flag();
forever begin:reset_flag
             if(cfg.cmd) begin//write
               @(posedge m_vif.wr_rst_n);
               wr_has_reset=1;
              // $display("wr_has_reset=%0d",wr_has_reset);
             end
             else begin
             @(posedge m_vif.rd_rst_n);
              rd_has_reset=1;
               //`uvm_info("RESET_FLAG",$sformatf("rd_has_reset=%0d",rd_has_reset),UVM_NONE)
             end
end
endtask

virtual task check_req();
forever begin
if(cfg.cmd) begin
@(negedge m_vif.wrclk);
if(req_status==0) begin
@(posedge m_vif.wrclk);
#1;
 m_vif.drv_wr_cb.wr_en  <= (req_status==0)?0:1;
end
end
else begin
@(negedge m_vif.rdclk);
if(req_status==0) begin
@(posedge m_vif.rdclk);
#1;
 m_vif.drv_rd_cb.rd_en  <= (req_status==0)?0:1;
end
end


end
endtask

virtual task get_and_drive();
seq_item_port.get_next_item(req);//transaction request,blocking until get
`uvm_info("DRV_RUN_PHASE", {"\n",req.sprint()}, UVM_MEDIUM)
req_status=1;
if(cfg.cmd) begin

        if(!m_vif.wr_full && wr_has_reset &&req_status)   begin //if write
            @(posedge m_vif.wrclk);
           m_vif.drv_wr_cb.wr_en  <= 1;
           m_vif.drv_wr_cb.wr_data <= req.data;
          end
@(m_vif.drv_wr_cb);
      /* else if(!m_vif.wr_full && !wr_has_reset) begin //if write
            @(posedge m_vif.wrclk);
           m_vif.drv_wr_cb.wr_en  <= 0;
           m_vif.drv_wr_cb.wr_data <= 0;
               $display("wr_has_reset=%0d",wr_has_reset);
            end*/
end
else  begin

if(!m_vif.rd_empty&& rd_has_reset &&req_status) begin
        @(posedge m_vif.rdclk);
        m_vif.drv_rd_cb.rd_en  <= 1;
      `uvm_info("RESET_FLAG",$sformatf("rd_has_reset=%0d",rd_has_reset),UVM_NONE)

end
@(m_vif.drv_rd_cb);
end
req_status=0;
seq_item_port.item_done();

endtask : get_and_drive

virtual task write_one_pkt(my_transaction req);

        @(posedge m_vif.wrclk);
           m_vif.drv_wr_cb.wr_en  <= 1;
           m_vif.drv_wr_cb.wr_data <= req.data;
        
    repeat(req.pkt_idles)
        drive_idle(req.cmd);

endtask : write_one_pkt

virtual task read_one_pkt(my_transaction req);

        @(posedge m_vif.rdclk);

        m_vif.drv_rd_cb.rd_en  <= 1;
        
        
    repeat(req.pkt_idles)
        drive_idle(req.cmd);

endtask : read_one_pkt

virtual task run_phase(uvm_phase phase);
    my_transaction req;
    m_vif.wr_data <= '0;
    m_vif.wr_en  <=  0;
    m_vif.rd_en  <=  0;
fork
        reset_wr_rd();
        reset_flag();
//repeat(300)  begin
        check_req();
//end
forever  begin 
            get_and_drive();
end

            //check_req();

join_none
endtask : run_phase

virtual task write_dile();
    forever begin
        @(posedge m_vif.wrclk);
        if(has_trans & cfg.cmd) m_vif.drv_wr_cb.wr_en <= 0;
    end
endtask : write_dile

virtual task read_dile();
    forever begin
        @(posedge m_vif.rdclk);
        if(!has_trans & (!cfg.cmd)) m_vif.drv_rd_cb.rd_en <= 0;
    end
endtask : read_dile


virtual task drive_idle(bit cmd);
    if(cmd) begin
        @(m_vif.drv_wr_cb);
        m_vif.wr_data <= '0;
        m_vif.wr_en  <=  0;
        end
    else begin
        @(m_vif.drv_rd_cb);
        m_vif.rd_en <=  0;
        end
endtask : drive_idle

endclass

7.写sequence,cmd=1即可


typedef class my_transaction;
class write_seq extends uvm_sequence #(my_transaction);
     `uvm_object_utils(write_seq)
rand int pkt_idles;

constraint cstr{
    soft pkt_idles==0;
}

function new (string name ="write_seq");
super.new(name);
endfunction:new
task body();
my_transaction req;
`uvm_do_with(req,{cmd == 1;pkt_idles == local::pkt_idles;})
endtask:body
endclass:write_seq







 8.读sequence

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

function new (string name ="write_seq");
super.new(name);
endfunction
virtual task body();
my_transaction req;
`uvm_do_with(req,{cmd == 0;})
endtask
endclass

7.virtual sequence的配置,其中包含了时钟配置的启动和读写sequence的启动,他们共同构成了这个virtual sequence 

// ---------------------------------------------------------------------------------
//                 Copyright (c) 2022 
//                 ALL RIGHTS RESERVED
// ---------------------------------------------------------------------------------
// Filename      : write_fast_vseq.sv
// Author        : AiF
// Created On    : 2022-05-19 11:14
// Last Modified : 2022-05-19 16:53
// ---------------------------------------------------------------------------------
// Description   : 
//                  
//                  
// ---------------------------------------------------------------------------------
`ifndef WRITE_FAST_VSEQ_SV
`define WRITE_FAST_VSEQ_SV
typedef class write_seq;
class write_fast_vseq extends asyn_fifo_vseq_base;
    `uvm_object_utils(write_fast_vseq)
    int wclk_half_period = 10;
    int factor = 2;            
    extern function new(string name = "write_fast_vseq");
    extern task body();
endclass : write_fast_vseq

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

task write_fast_vseq::body();
    write_seq        wr_sq;
    read_seq         rd_sq;
   // wrstn_config_seq wrstn_cfg_seq;
   // rrstn_config_seq rrstn_cfg_seq;
    wclk_config_seq  wclk_cfg_seq;
    rclk_config_seq  rclk_cfg_seq;
   // my_sequence      m_seq;
    wrstn_seq        wrreset_seq;
    rdstn_seq        rdreset_seq;
    fork
       // `uvm_do_on(wrstn_cfg_seq,wf_sqr)
      //  `uvm_do_on(rrstn_cfg_seq,rf_sqr)
        //`uvm_do_on(m_seq,wf_sqr)
       // `uvm_do_on(m_seq,rf_sqr)
        `uvm_do_on_with(wclk_cfg_seq,wf_sqr,{wclk_half_period == local::wclk_half_period;})
        `uvm_do_on_with(rclk_cfg_seq,rf_sqr,{rclk_half_period == local::wclk_half_period*factor; })
        `uvm_do_on(wrreset_seq,wf_sqr)
        `uvm_do_on(rdreset_seq,rf_sqr)
    join_none
    

    repeat(5)
        `uvm_do_on(wr_sq,wf_sqr)
   #50
  repeat(5)
        `uvm_do_on(rd_sq,rf_sqr)
   #100    
    fork
             repeat(100)
            `uvm_do_on_with(wr_sq,wf_sqr,{pkt_idles == 0;})
       repeat(100)
            `uvm_do_on(rd_sq,rf_sqr)
    join    
    #500;
endtask : body
`endif // write_fast_VSEQ_SV 

 时钟配置

 结构树,可以看见有两个从masteragent扩展出来的agent

 monitor和sequencer、sequence等等不变

 这也是我做验证的中间产物,我都是一步一步添加功能的,可能这里面在功能上有一些bug,但是在最后一版中我会对功能做检查,确保没有问题

  • 18
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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电路能够正常工作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值