UVM入门实验2

一、验证组件和层次构建

首先将各个package中的SV组件替换为UVM组件

实现组件对应原则:

  • SV的transaction类对应uvm_sequence_item
  • SV的driver类对应uvm_driver
  • SV的generator类对应uvm_sequence + uvm_sequencer
  • SV的monitor对应uvm_monitor
  • SV的agent对应uvm_agent
  • SV的env对应uvm_env
  • SV的checker对应uvm_scoreboard
  • SV的reference modelcoverage model均对应uvm_component
  • SV的test对应uvm_test

在遵循上面的对应原则的过程中,在进行类的转换时,需要注意:

  • SV的上述类均需要继承于其对应的UVM类
  • 在类定义过程中,一定需要使用'uvm_component_utils()或者'uvm_object_utils()完成类的注册。
  • 在使用上述工厂注册宏的时候,会伴随着“域声明自动化”,一般而言,将sequence item类定义时,应当伴随着域声明,即利用'uvm_object_utils_begin'uvm_object_utils_end完成。这是由于对于sequence item对象的拷贝、比较和打印等操作比较多,因此在定义sequence item类时,最好需要完成域的自动化声明。
  • 一定要注意构建函数new()的声明方式,uvm_component的构建函数有两个参数new(string name, uvm_component parent),而uvm_object的构建函数只有一个参数new(string name)
  • 在组件之间的层次关系构建中,依然按照之前SV组件的层次关系,只需要在不同的phase阶段完成组件的例化和连接。

UVM代码实现

chnl.pkg.sv

chnl_trans

相比于SV验证模块代码,成员变量没有发生什么变化,但是省略掉了clone()sprint()这两个方法,因为UVM做了类的注册以及域的自动化的声明,可以使用UVM核心基类的克隆、打印、比较等一些常见方法。

  // channel sequence item
  class chnl_trans extends uvm_sequence_item;
    rand bit[31:0] data[];
    rand int ch_id;
    rand int pkt_id;
    rand int data_nidles;
    rand int pkt_nidles;
    bit rsp;

    constraint cstr{
      soft data.size inside {[4:32]};
      foreach(data[i]) data[i] == 'hC000_0000 + (this.ch_id<<24) + (this.pkt_id<<8) + i;
      soft ch_id == 0;
      soft pkt_id == 0;
      soft data_nidles inside {[0:2]};
      soft pkt_nidles inside {[1:10]};
    };

    `uvm_object_utils_begin(chnl_trans)
      `uvm_field_array_int(data, UVM_ALL_ON)
      `uvm_field_int(ch_id, UVM_ALL_ON)
      `uvm_field_int(pkt_id, UVM_ALL_ON)
      `uvm_field_int(data_nidles, UVM_ALL_ON)
      `uvm_field_int(pkt_nidles, UVM_ALL_ON)
      `uvm_field_int(rsp, UVM_ALL_ON)
    `uvm_object_utils_end

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

chnl_driver

  • 继承于uvm_driver是一个参数类,所以得uvm_driver #(chnl_trans)。而SV中的run()转换成了UVM中run_phase(uvm_phase phase)

  • do_driver()方法里面void'($cast(rsp, req.clone())),是因为UVM核心基类的克隆方法返回的是uvm_object类型,所以需要把父类的句柄转换为子类的句柄,而req.clone()这个父类句柄指向的是一个子类的对象,所以能转换成功。

  • chnl_write()方法中把SV中的$display替换成了UVM中的'uvm_info()

  // channel driver
  class chnl_driver extends uvm_driver #(chnl_trans);
    local virtual chnl_intf intf;
    mailbox #(chnl_trans) req_mb;
    mailbox #(chnl_trans) rsp_mb;

    `uvm_component_utils(chnl_driver)
  
    function new (string name = "chnl_driver", uvm_component parent);
      super.new(name, parent);
    endfunction
  
    function void set_interface(virtual chnl_intf intf);
      if(intf == null)
        $error("interface handle is NULL, please check if target interface has been intantiated");
      else
        this.intf = intf;
    endfunction

    task run_phase(uvm_phase phase);
      fork
       this.do_drive();
       this.do_reset();
      join
    endtask

    task do_reset();
      forever begin
        @(negedge intf.rstn);
        intf.ch_valid <= 0;
        intf.ch_data <= 0;
      end
    endtask

    task do_drive();
      chnl_trans req, rsp;
      @(posedge intf.rstn);
      forever begin
        this.req_mb.get(req);
        this.chnl_write(req);
        void'($cast(rsp, req.clone()));
        rsp.rsp = 1;
        this.rsp_mb.put(rsp);
      end
    endtask
  
    task chnl_write(input chnl_trans t);
      foreach(t.data[i]) begin
        @(posedge intf.clk);
        intf.drv_ck.ch_valid <= 1;
        intf.drv_ck.ch_data <= t.data[i];
        @(negedge intf.clk);
        wait(intf.ch_ready === 'b1);
        `uvm_info(get_type_name(), $sformatf("sent data 'h%8x", t.data[i]), UVM_HIGH)
        repeat(t.data_nidles) chnl_idle();
      end
      repeat(t.pkt_nidles) chnl_idle();
    endtask
    
    task chnl_idle();
      @(posedge intf.clk);
      intf.drv_ck.ch_valid <= 0;
      intf.drv_ck.ch_data <= 0;
    endtask
  endclass: chnl_driver

chnl_generator

  • 相比于SV使用构建函数new()来创建对象, send_trans()方法中使用req = chnl_trans::type_id::create("req")来创建对象。
  • 打印消息使用了UVM中的'uvm_info()
  // channel generator and to be replaced by sequence + sequencer later
  class chnl_generator extends uvm_component;
    rand int pkt_id = 0;
    rand int ch_id = -1;
    rand int data_nidles = -1;
    rand int pkt_nidles = -1;
    rand int data_size = -1;
    rand int ntrans = 10;

    mailbox #(chnl_trans) req_mb;
    mailbox #(chnl_trans) rsp_mb;

    constraint cstr{
      soft ch_id == -1;
      soft pkt_id == 0;
      soft data_size == -1;
      soft data_nidles == -1;
      soft pkt_nidles == -1;
      soft ntrans == 10;
    }

    `uvm_component_utils_begin(chnl_generator)
      `uvm_field_int(pkt_id, UVM_ALL_ON)
      `uvm_field_int(ch_id, UVM_ALL_ON)
      `uvm_field_int(data_nidles, UVM_ALL_ON)
      `uvm_field_int(pkt_nidles, UVM_ALL_ON)
      `uvm_field_int(data_size, UVM_ALL_ON)
      `uvm_field_int(ntrans, UVM_ALL_ON)
    `uvm_component_utils_end

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

    task start();
      repeat(ntrans) send_trans();
    endtask

    task send_trans();
      chnl_trans req, rsp;
      req = chnl_trans::type_id::create("req");;
      assert(req.randomize with {local::ch_id >= 0 -> ch_id == local::ch_id; 
                                 local::pkt_id >= 0 -> pkt_id == local::pkt_id;
                                 local::data_nidles >= 0 -> data_nidles == local::data_nidles;
                                 local::pkt_nidles >= 0 -> pkt_nidles == local::pkt_nidles;
                                 local::data_size >0 -> data.size() == local::data_size; 
                               })
        else $fatal("[RNDFAIL] channel packet randomization failure!");
      this.pkt_id++;
      `uvm_info(get_type_name(), req.sprint(), UVM_HIGH)
      this.req_mb.put(req);
      this.rsp_mb.get(rsp);
      `uvm_info(get_type_name(), rsp.sprint(), UVM_HIGH)
      assert(rsp.rsp)
        else $error("[RSPERR] %0t error response received!", $time);
    endtask

    function string sprint();
      string s;
      s = {s, $sformatf("=======================================\n")};
      s = {s, $sformatf("chnl_generator object content is as below: \n")};
      s = {s, super.sprint()};
      // NOTE:: field automation already implemented clone() method
      // s = {s, $sformatf("ntrans = %0d: \n", this.ntrans)};
      // s = {s, $sformatf("ch_id = %0d: \n", this.ch_id)};
      // s = {s, $sformatf("pkt_id = %0d: \n", this.pkt_id)};
      // s = {s, $sformatf("data_nidles = %0d: \n", this.data_nidles)};
      // s = {s, $sformatf("pkt_nidles = %0d: \n", this.pkt_nidles)};
      // s = {s, $sformatf("data_size = %0d: \n", this.data_size)};
      s = {s, $sformatf("=======================================\n")};
      return s;
    endfunction

    function void post_randomize();
      string s;
      s = {"AFTER RANDOMIZATION \n", this.sprint()};
      `uvm_info(get_type_name(), s, UVM_HIGH)
    endfunction
  endclass: chnl_generator

chnl_monitor

将SV中的run()替换成了UVM中的
run_phase(uvm_phase phase)以及使用UVM中的'uvm_info()打印消息。

  // channel monitor
  class chnl_monitor extends uvm_monitor;
    local virtual chnl_intf intf;
    mailbox #(mon_data_t) mon_mb;

    `uvm_component_utils(chnl_monitor)

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

    function void set_interface(virtual chnl_intf intf);
      if(intf == null)
        $error("interface handle is NULL, please check if target interface has been intantiated");
      else
        this.intf = intf;
    endfunction

    task run_phase(uvm_phase phase);
      this.mon_trans();
    endtask

    task mon_trans();
      mon_data_t m;
      forever begin
        @(posedge intf.clk iff (intf.mon_ck.ch_valid==='b1 && intf.mon_ck.ch_ready==='b1));
        m.data = intf.mon_ck.ch_data;
        mon_mb.put(m);
        `uvm_info(get_type_name(), $sformatf("monitored channel data 'h%8x", m.data), UVM_HIGH)
      end
    endtask
  endclass: chnl_monitor

chnl_agent

  • SV验证结构中例化和连接都发生在构建函数new()里面,而UVM中例化是在build_phase()方法中,并且通过create()来例化创建对象。
  • SV验证结构中run()需要调用子一级的run()方法,而在UVM中不需要手动去调用子一级的run_phase(),因为run_phase是按照层次来执行的,是由uvm_root来安排的,会自动调用。
  // channel agent
  class chnl_agent extends uvm_agent;
    chnl_driver driver;
    chnl_monitor monitor;
    local virtual chnl_intf vif;

    `uvm_component_utils(chnl_agent)

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

    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      driver = chnl_driver::type_id::create("driver", this);
      monitor = chnl_monitor::type_id::create("monitor", this);
    endfunction

    function void set_interface(virtual chnl_intf vif);
      this.vif = vif;
      driver.set_interface(vif);
      monitor.set_interface(vif);
    endfunction

    task run_phase(uvm_phase phase);
      // NOTE:: No more needed to call run manually
      // fork
      //   driver.run();
      //   monitor.run();
      // join
    endtask
  endclass: chnl_agent

reg_pkg.sv文件、fmt_pkg.sv文件、mcdf_pkg.sv文件修改的地方相似。

二、测试的开始和结束

UVM验证环境测试的开始、环境构建的过程、连接以及结束的控制。

tb.sv

通过uvm_config_db完成了各个接口从TB(硬件一侧)到验证环境mcdf_env(软件一侧)的传递。实现了以往SV函数的剥离,即UVM不需要深入到目标组件一侧,调用其set_interface()即可完成传递。这种传递方式有赖于config_db的数据存储和层次传递特性。而在mcdf_env中,暂时保留了mcdf_envset_interface()以及各个子组件的set_interface()函数。仅修改了TBmcdf_env之间的接口传递,其实可以移除所有的set_interface()函数,完全使用uvm_config_dbsetget方法,从而使得mcdf_env与其各个子组件之间也实现“层次剥离”,这样也就进一步促进了组件之间的独立性。

    // do interface configuration from top tb (HW) to verification env (SW)
    uvm_config_db#(virtual chnl_intf)::set(uvm_root::get(), "uvm_test_top", "ch0_vif", chnl0_if);
    uvm_config_db#(virtual chnl_intf)::set(uvm_root::get(), "uvm_test_top", "ch1_vif", chnl1_if);
    uvm_config_db#(virtual chnl_intf)::set(uvm_root::get(), "uvm_test_top", "ch2_vif", chnl2_if);
    uvm_config_db#(virtual reg_intf)::set(uvm_root::get(), "uvm_test_top", "reg_vif", reg_if);
    uvm_config_db#(virtual arb_intf)::set(uvm_root::get(), "uvm_test_top", "arb_vif", arb_if);
    uvm_config_db#(virtual fmt_intf)::set(uvm_root::get(), "uvm_test_top", "fmt_vif", fmt_if);
    uvm_config_db#(virtual mcdf_intf)::set(uvm_root::get(), "uvm_test_top", "mcdf_vif", mcdf_if);

通过调用run_test()函数即完成了test的选择、例化和开始测试。可以在代码中指定UVM test,或者通过 +UVM_TESTNAME=mytest在仿真选项中灵活传递test名。在run_test()执行中,它会初始化objection机制,即查看objection有没有挂起的地方,因此在test或者generator中必须至少有一处地方使用phase.raise_objection()来挂起仿真,避免仿真退出,而在仿真需要结束时,使用phase.drop_objection()来允许仿真可以退出。同时run_test()可以创建uvm_test组件,及其以下的各层组件群,并且可以调用phase控制方法,按照所有phase顺序执行。在UVM中,将对象的例化放置在build_phase中,而将对象的连接放置在connect_phase中。

module tb;
  logic         clk;
  logic         rstn;

  mcdf dut(
     .clk_i       (clk                )
    ,.rstn_i      (rstn               )
    ,.cmd_i       (reg_if.cmd         ) 
    ,.cmd_addr_i  (reg_if.cmd_addr    ) 
    ,.cmd_data_i  (reg_if.cmd_data_m2s)  
    ,.cmd_data_o  (reg_if.cmd_data_s2m)  
    ,.ch0_data_i  (chnl0_if.ch_data   )
    ,.ch0_vld_i   (chnl0_if.ch_valid  )
    ,.ch0_ready_o (chnl0_if.ch_ready  )
    ,.ch1_data_i  (chnl1_if.ch_data   )
    ,.ch1_vld_i   (chnl1_if.ch_valid  )
    ,.ch1_ready_o (chnl1_if.ch_ready  )
    ,.ch2_data_i  (chnl2_if.ch_data   )
    ,.ch2_vld_i   (chnl2_if.ch_valid  )
    ,.ch2_ready_o (chnl2_if.ch_ready  )
    ,.fmt_grant_i (fmt_if.fmt_grant   ) 
    ,.fmt_chid_o  (fmt_if.fmt_chid    ) 
    ,.fmt_req_o   (fmt_if.fmt_req     ) 
    ,.fmt_length_o(fmt_if.fmt_length  )    
    ,.fmt_data_o  (fmt_if.fmt_data    )  
    ,.fmt_start_o (fmt_if.fmt_start   )  
    ,.fmt_end_o   (fmt_if.fmt_end     )  
  );
  
  // clock generation
  initial begin 
    clk <= 0;
    forever begin
      #5 clk <= !clk;
    end
  end
  
  // reset trigger
  initial begin 
    #10 rstn <= 0;
    repeat(10) @(posedge clk);
    rstn <= 1;
  end

  import uvm_pkg::*;
  `include "uvm_macros.svh"
  import chnl_pkg::*;
  import reg_pkg::*;
  import arb_pkg::*;
  import fmt_pkg::*;
  import mcdf_pkg::*;

  reg_intf  reg_if(.*);
  chnl_intf chnl0_if(.*);
  chnl_intf chnl1_if(.*);
  chnl_intf chnl2_if(.*);
  arb_intf  arb_if(.*);
  fmt_intf  fmt_if(.*);
  mcdf_intf mcdf_if(.*);

  // mcdf interface monitoring MCDF ports and signals
  assign mcdf_if.chnl_en[0] = tb.dut.ctrl_regs_inst.slv0_en_o;
  assign mcdf_if.chnl_en[1] = tb.dut.ctrl_regs_inst.slv1_en_o;
  assign mcdf_if.chnl_en[2] = tb.dut.ctrl_regs_inst.slv2_en_o;

  // arbiter interface monitoring arbiter ports
  assign arb_if.slv_prios[0] = tb.dut.arbiter_inst.slv0_prio_i;
  assign arb_if.slv_prios[1] = tb.dut.arbiter_inst.slv1_prio_i;
  assign arb_if.slv_prios[2] = tb.dut.arbiter_inst.slv2_prio_i;
  assign arb_if.slv_reqs[0] = tb.dut.arbiter_inst.slv0_req_i;
  assign arb_if.slv_reqs[1] = tb.dut.arbiter_inst.slv1_req_i;
  assign arb_if.slv_reqs[2] = tb.dut.arbiter_inst.slv2_req_i;
  assign arb_if.a2s_acks[0] = tb.dut.arbiter_inst.a2s0_ack_o;
  assign arb_if.a2s_acks[1] = tb.dut.arbiter_inst.a2s1_ack_o;
  assign arb_if.a2s_acks[2] = tb.dut.arbiter_inst.a2s2_ack_o;
  assign arb_if.f2a_id_req = tb.dut.arbiter_inst.f2a_id_req_i;

  // mcdf_data_consistence_basic_test t1;
  // mcdf_full_random_test t2;
  // mcdf_base_test tests[string];
  // string name;

  initial begin 
    // do interface configuration from top tb (HW) to verification env (SW)
    uvm_config_db#(virtual chnl_intf)::set(uvm_root::get(), "uvm_test_top", "ch0_vif", chnl0_if);
    uvm_config_db#(virtual chnl_intf)::set(uvm_root::get(), "uvm_test_top", "ch1_vif", chnl1_if);
    uvm_config_db#(virtual chnl_intf)::set(uvm_root::get(), "uvm_test_top", "ch2_vif", chnl2_if);
    uvm_config_db#(virtual reg_intf)::set(uvm_root::get(), "uvm_test_top", "reg_vif", reg_if);
    uvm_config_db#(virtual arb_intf)::set(uvm_root::get(), "uvm_test_top", "arb_vif", arb_if);
    uvm_config_db#(virtual fmt_intf)::set(uvm_root::get(), "uvm_test_top", "fmt_vif", fmt_if);
    uvm_config_db#(virtual mcdf_intf)::set(uvm_root::get(), "uvm_test_top", "mcdf_vif", mcdf_if);

    // If no external configured via +UVM_TESTNAME=my_test, the default test is
    // mcdf_data_consistence_basic_test
    run_test("mcdf_data_consistence_basic_test");

    // NOTE:: SV RUN test method is replaced by UVM run_test()
    // + UVM_TESTNAME=[my_test] solution
    // t1 = new();
    // t2 = new();
    // tests["mcdf_data_consistence_basic_test"] = t1;
    // tests["mcdf_full_random_test"] = t2;
    // if($value$plusargs("TESTNAME=%s", name)) begin
    //   if(tests.exists(name)) begin
    //     tests[name].set_interface(chnl0_if, chnl1_if, chnl2_if, reg_if, arb_if, fmt_if, mcdf_if);
    //     tests[name].run();
    //   end
    //   else begin
    //     $fatal($sformatf("[ERRTEST], test name %s is invalid, please specify a valid name!", name));
    //   end
    // end
    // else begin
    //   $display("NO runtime optiont +TESTNAME=xxx is configured, and run default test mcdf_data_consistence_basic_test");
    //   tests["mcdf_data_consistence_basic_test"].set_interface(chnl0_if, chnl1_if, chnl2_if, reg_if, arb_if, fmt_if, mcdf_if);
    //   tests["mcdf_data_consistence_basic_test"].run();
    // end
  end
endmodule

运行仿真:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值