UVM学习记录2

验证组件和层次构建

实现从SV组件到UVM组件的替换:

  • transaction -> uvm_sequence_item
  • driver -> uvm_driver
  • generator -> uvm_sequence + uvm_sequencer
  • monitor -> uvm_monitor
  • agent ->uvm_agent
  • env -> uvm_env
  • checker -> uvm_scoreboard
  • refmod/coveragee model -> uvm_component
  • test -> uvm_test

进行类的转换时需注意:

  • 上述类均需派生自对应的UVM类
  • 定义类时使用 'uvm_component_utils()'uvm_object_utils() 宏实现factory的注册。
  • 使用factory注册宏的时候,引入field automation机制,一般定义uvm_sequence_item类时,使用 'uvm_object_utils_begin()'uvm_object_utils_end实现factory注册,并使用 'uvm_field注册所有字段,即可调用copy、compare、print等函数。
  • 注意构建函数new()的声明方式,uvm_component相关类的new函数有string name(实例名字)和 uvm_component parent两个参数,而uvm_object相关类的的new函数只有string name参数。
  • 在组件之间的层次关系构建中,依然按照之前SV组件的层次关系,在不同的phase完成组件的例化和连接。

以chnl_pkg为例:

package chnl_pkg;
  import uvm_pkg::*;
  `include "uvm_macros.svh"

  // channel sequence item
  class chnl_trans extends uvm_sequence_item;
	...
    `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

    // field automation机制已实现clone()和sprint()方法
    // function chnl_trans clone();
    // function string sprint();

  endclass: chnl_trans
  
  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
    ...
    task run_phase(uvm_phase phase);    // 执行SV中的run()
      fork
       this.do_drive();
       this.do_reset();
      join
    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
	...	
  endclass: chnl_driver
  
  // channel generator and to be replaced by sequence + sequencer later
  class chnl_generator extends uvm_component;
	...
    `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
	...
    function string sprint();
      string s;
      s = {s, $sformatf("=======================================\n")};
      s = {s, $sformatf("chnl_generator object content is as below: \n")};
      s = {s, super.sprint()};  // 调用父类,field automation机制已实现
      return s;
    endfunction
	...
  endclass: chnl_generator

  class chnl_monitor extends uvm_monitor;
	...
  
  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
	...
    task run_phase(uvm_phase phase);
      // 不需要再手动调用子一级的run_phase(),交给uvm_root自动调用。
    endtask
  endclass: chnl_agent

endpackage

测试的开始和结束

在tb.sv中已注释了关于SV各test声明、例化、外部参数传递、执行选择过程及开始测试的过程,并用UVM实现测试的开始、环境构建的过程、连接以及结束的控制。

  • 通过uvm_config_db完成了各个接口从TB(硬件一侧)到验证环境mcdf_env(软件一侧)的传递。理论上可以移除所有的set_interface()函数,完全使用uvm_config_db的set和get方法,从而使mcdf_env与其各个子组件之间相互独立。
  • 调用run_test() 函数完成test的选择、例化和开始测试。
  • 可在代码中指定UVM test,或通过 +UVM_TESTNAME=mytest在仿真中传参指定。
  • 在run_test()执行中会初始化objection机制,使用phase.raise_objection() 来挂起仿真,避免仿真退出;而在仿真需要结束时,使用phase.drop_objection() 来允许仿真退出。
  • 创建uvm_test组件,及其以下的各层组件群
  • 调用phase控制方法,按照所有phase顺序执行。
  • 在UVM中,将对象的例化放置在build_phase中,而将对象的连接放置在connect_phase中。
  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
    run_test("mcdf_data_consistence_basic_test");
  end

TLM通信

TLM单向通信和多向通信

在之前monitor与checker,和checker与refmod之间的通信,都是通过mailbox以及在上层进行句柄传递实现的。这次使用TLM端口进行通信,做逐步的通信元素和方法的替换。相较于mailbox句柄连接,更加容易定制,同时提高组件独立性。

  • TLM通信结构更新方案
  1. 将monitor中用来与checker中mailbox通信的mon_mb句柄替换为对应的uvm_blocking_put_port类型。
	uvm_blocking_put_port #(mon_data_t) mon_bp_port;
  1. 在checker中声明与monitor通信的import端口类型,以及reference model通信的import端口类型,由于checker与多个monitor以及reference model通信,是典型的多方向通信类型,因此需要使用多端口通信的宏声明方法(防止通信方法名冲突)。在使用了宏声明端口类型之后,再在checker中声明其句柄,并且完成例化。
  2. 根据声明的import端口类型,实现其对应的方法。
  `uvm_blocking_put_imp_decl(_chnl0)  // 宏定义端口类型
    ...
    uvm_blocking_put_imp_chnl0 #(mon_data_t, mcdf_checker)   chnl0_bp_imp;  // 声明句柄
    ...
    chnl0_bp_imp = new("chnl0_bp_imp", this);  // 例化
    ...
    task put_chnl0(mon_data_t t);  // 实现定义的方法
      chnl_mbs[0].put(t);
    endtask
  1. 在mcdf_refmod中声明用来与mcdf_checker中的import连接的端口,并且完成例化。完成声明和例化后,使用TLM端口呼叫方法。
  2. 在env的connect_phase()阶段,完成monitor、refmod的TLM port与checker的TLM import的连接。
    refmod.in_bgpk_ports[0].connect(chnl0_bgpk_imp);
    ...
    refmod.reg_bg_port.connect(reg_bg_imp);

TLM通信管道

TLM通信的优点:

  • 通信函数可以定制化,例如可定制put()/get()/peek()的内容和参数,比mailbox的通信更加灵活。
  • 将组件实现了完全的隔离,通过层次化的TLM端口连接,可以很好地避免直接将不同层次的数据缓存对象的句柄进行“空中传递”。

使用uvm_tlm_fifo类可以不自己实现put()/get()/peek()等方法,同时享受到TLM的好处。

  1. 将原本在mcdf_refmod中的out_mb替换成uvm_tlm_fifo类型,并且完成例化以及对应的变量名替换。
	uvm_tlm_fifo #(fmt_trans) out_tlm_fifos[3];
	...
	foreach(out_tlm_fifos[i]) out_tlm_fifos[i] = new($sformatf("out_tlm_fifos[%0d]", i), this);
	...
	this.out_tlm_fifos[id].put(ot);
  1. 将原本在mcdf_checker中的exp_mbs[3]的mailbox句柄数组,替换为uvm_blocking_get_port类型句柄数组,并做相应例化及变量名替换。
  2. 在mcdf_checker中,完成mcdf_checker中的TLM port端口到mcdf_refmod中的uvm_tlm_fifo自带的blocking_get_export端口的连接。
    foreach(exp_bg_ports[i]) begin
      exp_bg_ports[i].connect(refmod.out_tlm_fifos[i].blocking_get_export);
    end

UVM回调类

类的复用除了可以使用继承,还可以使用回调函数。将原有的mcdf_data_consistence_basic_test和mcdf_full_random_test的类库实现方式(即类继承方式)改为回调函数的实现方式。(以实现do_formatter() 为例)

  1. 在uvm_callback类中,预先定义需要的虚方法。
  class cb_mcdf_base extends uvm_callback;
    `uvm_object_utils(cb_mcdf_base)
    ... 
    virtual task cb_do_formatter();  // 1.定义
    endtask
  1. 使用callback对应的宏,完成目标uvm_test类型与目标uvm_callback类型的关联。
  class mcdf_base_test extends uvm_test;
    ...
    `uvm_register_cb(mcdf_base_test, cb_mcdf_base)  // 2.绑定
  1. 在目标uvm_test类型指定的方法中,完成uvm_callback的方法回调指定。
    virtual task do_formatter();
      `uvm_do_callbacks(mcdf_base_test, cb_mcdf_base, cb_do_formatter())  // 3.插入
    endtask
  1. 实现uvm_callback和对应test类的定义。
  class cb_mcdf_data_consistence_basic extends cb_mcdf_base;
    ...
    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      cb = cb_mcdf_data_consistence_basic::type_id::create("cb");  // 创建
      uvm_callbacks#(mcdf_base_test)::add(this, cb);  // 4.添加 (关联父类)
    endfunction
	...
	
  class cb_mcdf_data_consistence_basic extends cb_mcdf_base;
    ...
    task cb_do_formatter();  // uvm_callback可直接继承使用
      super.cb_do_formatter();
      void'(test.fmt_gen.randomize() with {fifo == LONG_FIFO; bandwidth == HIGH_WIDTH;});
      test.fmt_gen.start();
    endtask

UVM仿真控制方法

在test类中添加end_of_elaboration_phase() 函数,并添加一些uvm_root类的方法。

  • 利用uvm_root类来将信息冗余度设置为UVM_HIGH,以允许更多低级别的信息打印出来。
  • 使用uvm_root::get().set_report_max_quit_count()函数设置当uvm_error数量超过设定值时仿真退出。默认数值为-1,表示仿真不会随uvm_error退出。
  • 使用uvm_root::get().set_timeout()设置仿真的最大时间长度,替代原定义的do_watchdog()方法。
    function void end_of_elaboration_phase(uvm_phase phase);
      super.end_of_elaboration_phase(phase);
      uvm_root::get().set_report_verbosity_level_hier(UVM_HIGH);
      uvm_root::get().set_report_max_quit_count(1);
      uvm_root::get().set_timeout(10ms);
    endfunction
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小破同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值