[转]UVM入门实验3

一、TLM单向通信和多向通信

在之前的monitor到checker的通信,以及checker与reference model之间的通信,都是通过mailbox以及在上层进行其句柄的传递实现的。这次实验则使用TLM端口进行通信,做逐步的通信元素和方法的替换。

TLM通信结构框图
在这里插入图片描述
monitor中的用来与checker中的mailbox通信的mon_mb句柄替换为对应的uvm_blocking_put_port类型。

uvm_blocking_put_port #(mon_data_t) mon_bp_port;

checker中声明与monitor通信的import端口类型,以及reference model通信的import端口类型,由于checker与多个monitor以及reference model通信,是典型的多方向通信类型,因此需要使用多端口通信的宏声明方法。在使用了宏声明端口类型之后,再在checker中声明其句柄,并且完成例化。

  //宏声明端口
  `uvm_blocking_put_imp_decl(_chnl0)
  `uvm_blocking_put_imp_decl(_chnl1)
  `uvm_blocking_put_imp_decl(_chnl2)
  `uvm_blocking_put_imp_decl(_fmt)
  `uvm_blocking_put_imp_decl(_reg)

  `uvm_blocking_get_peek_imp_decl(_chnl0)
  `uvm_blocking_get_peek_imp_decl(_chnl1)
  `uvm_blocking_get_peek_imp_decl(_chnl2)

  `uvm_blocking_get_imp_decl(_reg)
	
	//声明句柄
    uvm_blocking_put_imp_chnl0 #(mon_data_t, mcdf_checker)   chnl0_bp_imp;
    uvm_blocking_put_imp_chnl1 #(mon_data_t, mcdf_checker)   chnl1_bp_imp;
    uvm_blocking_put_imp_chnl2 #(mon_data_t, mcdf_checker)   chnl2_bp_imp;
    uvm_blocking_put_imp_fmt   #(fmt_trans , mcdf_checker)   fmt_bp_imp  ;
    uvm_blocking_put_imp_reg   #(reg_trans , mcdf_checker)   reg_bp_imp  ;

    uvm_blocking_get_peek_imp_chnl0 #(mon_data_t, mcdf_checker)  chnl0_bgpk_imp;
    uvm_blocking_get_peek_imp_chnl1 #(mon_data_t, mcdf_checker)  chnl1_bgpk_imp;
    uvm_blocking_get_peek_imp_chnl2 #(mon_data_t, mcdf_checker)  chnl2_bgpk_imp;

    uvm_blocking_get_imp_reg    #(reg_trans , mcdf_checker)  reg_bg_imp  ;
	

	//例化
      chnl0_bp_imp = new("chnl0_bp_imp", this);
      chnl1_bp_imp = new("chnl1_bp_imp", this);
      chnl2_bp_imp = new("chnl2_bp_imp", this);
      fmt_bp_imp   = new("fmt_bp_imp", this);  
      reg_bp_imp   = new("reg_bp_imp", this);  

      chnl0_bgpk_imp = new("chnl0_bgpk_imp", this);
      chnl1_bgpk_imp = new("chnl1_bgpk_imp", this);
      chnl2_bgpk_imp = new("chnl2_bgpk_imp", this);

      reg_bg_imp    = new("reg_bg_imp", this);  

根据声明的import端口类型,实现其对应的方法。

	//实现put方法
    task put_chnl0(mon_data_t t);
      chnl_mbs[0].put(t);
    endtask
    task put_chnl1(mon_data_t t);
      chnl_mbs[1].put(t);
    endtask
    task put_chnl2(mon_data_t t);
      chnl_mbs[2].put(t);
    endtask
    task put_fmt(fmt_trans t);
      fmt_mb.put(t);
    endtask
    task put_reg(reg_trans t);
      reg_mb.put(t);
    endtask

	//实现get、peek方法
	    task peek_chnl0(output mon_data_t t);
      chnl_mbs[0].peek(t);
    endtask
    task peek_chnl1(output mon_data_t t);
      chnl_mbs[1].peek(t);
    endtask
    task peek_chnl2(output mon_data_t t);
      chnl_mbs[2].peek(t);
    endtask
    task get_chnl0(output mon_data_t t);
      chnl_mbs[0].get(t);
    endtask
    task get_chnl1(output mon_data_t t);
      chnl_mbs[1].get(t);
    endtask
    task get_chnl2(output mon_data_t t);
      chnl_mbs[2].get(t);
    endtask
    task get_reg(output reg_trans t);
      reg_mb.get(t);
    endtask

mcdf_refmod中声明用来与mcdf_checker中的import连接的端口,并且完成例化。完成声明和例化后,使用TLM端口呼叫方法。

	//声明端口
    uvm_blocking_get_port #(reg_trans) reg_bg_port;
    uvm_blocking_get_peek_port #(mon_data_t) in_bgpk_ports[3];
	
	//端口例化
	reg_bg_port = new("reg_bg_port", this);

mcdf_envconnect_phase()阶段,完成monitor的TLM port与mcdf_checker的TLM import的连接。

      chnl_agts[0].monitor.mon_bp_port.connect(chker.chnl0_bp_imp);
      chnl_agts[1].monitor.mon_bp_port.connect(chker.chnl1_bp_imp);
      chnl_agts[2].monitor.mon_bp_port.connect(chker.chnl2_bp_imp);
      reg_agt.monitor.mon_bp_port.connect(chker.reg_bp_imp);
      fmt_agt.monitor.mon_bp_port.connect(chker.fmt_bp_imp);

mcdf_envconnect_phase()阶段,完成refmod的TLM port与mcdf_checker的TLM import的连接。

      refmod.in_bgpk_ports[0].connect(chnl0_bgpk_imp);
      refmod.in_bgpk_ports[1].connect(chnl1_bgpk_imp);
      refmod.in_bgpk_ports[2].connect(chnl2_bgpk_imp);

      refmod.reg_bg_port.connect(reg_bg_imp);

二、TLM通信管道

TLM通信的优点:

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

如果使用TLM端口,并且不用实现具体的put()/get()/peek()方法,可以使用uvm_tlm_fifo类。

将原本在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);

将原本在mcdf_checker中的exp_mbs[3]的邮箱句柄数组,替换为uvm_blocking_get_port类型句柄数组,并且做相应的例化以及变量名替换。

uvm_blocking_get_port #(fmt_trans) exp_bg_ports[3];

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_testmcdf_full_random_test的类库实现方式(即类继承方式)修改为毁掉函数的实现方式,所以类的复用除了可以使用继承,还可以使用回调函数。

uvm_callback类中,预先定义需要的虚方法。(定义)

  typedef class mcdf_base_test;

  class cb_mcdf_base extends uvm_callback;
    `uvm_object_utils(cb_mcdf_base)
    mcdf_base_test test;
    function new (string name = "cb_mcdf_base");
      super.new(name);
    endfunction

    virtual task cb_do_reg();
       
    endtask

    virtual task cb_do_formatter();
       
    endtask

    virtual task cb_do_data();
 
    endtask
  endclass

使用callback对应的宏,完成目标uvm_test类型与目标uvm_callback类型的关联。(绑定)

    `uvm_register_cb(mcdf_base_test, cb_mcdf_base)

在目标uvm_test类型指定的方法中,完成uvm_callback的方法回调指定。(插入)

    // do register configuration
    virtual task do_reg();
      
      `uvm_do_callbacks(mcdf_base_test, cb_mcdf_base, cb_do_reg())
    endtask

    // do external formatter down stream slave configuration
    virtual task do_formatter();
       
      `uvm_do_callbacks(mcdf_base_test, cb_mcdf_base, cb_do_formatter())
    endtask

    // do data transition from 3 channel slaves
    virtual task do_data();
       
      `uvm_do_callbacks(mcdf_base_test, cb_mcdf_base, cb_do_data())
    endtask

实现uvm_callback和对应test类的定义(添加)

  class cb_mcdf_data_consistence_basic extends cb_mcdf_base;
    `uvm_object_utils(cb_mcdf_data_consistence_basic)
    function new (string name = "cb_mcdf_data_consistence_basic");
      super.new(name);
    endfunction
    task cb_do_reg();
      bit[31:0] wr_val, rd_val;
      super.cb_do_reg();
      // slv0 with len=8,  prio=0, en=1
      wr_val = (1<<3)+(0<<1)+1;
      test.write_reg(`SLV0_RW_ADDR, wr_val);
      test.read_reg(`SLV0_RW_ADDR, rd_val);
      void'(test.diff_value(wr_val, rd_val, "SLV0_WR_REG"));

      // slv1 with len=16, prio=1, en=1
      wr_val = (2<<3)+(1<<1)+1;
      test.write_reg(`SLV1_RW_ADDR, wr_val);
      test.read_reg(`SLV1_RW_ADDR, rd_val);
      void'(test.diff_value(wr_val, rd_val, "SLV1_WR_REG"));

      // slv2 with len=32, prio=2, en=1
      wr_val = (3<<3)+(2<<1)+1;
      test.write_reg(`SLV2_RW_ADDR, wr_val);
      test.read_reg(`SLV2_RW_ADDR, rd_val);
      void'(test.diff_value(wr_val, rd_val, "SLV2_WR_REG"));

      // send IDLE command
      test.idle_reg();
    endtask

    task cb_do_formatter();
      super.cb_do_formatter();
      void'(test.fmt_gen.randomize() with {fifo == LONG_FIFO; bandwidth == HIGH_WIDTH;});
      test.fmt_gen.start();
    endtask

    task cb_do_data();
      super.cb_do_data();
      void'(test.chnl_gens[0].randomize() with {ntrans==100; ch_id==0; data_nidles==0; pkt_nidles==1; data_size==8; });
      void'(test.chnl_gens[1].randomize() with {ntrans==100; ch_id==1; data_nidles==1; pkt_nidles==4; data_size==16;});
      void'(test.chnl_gens[2].randomize() with {ntrans==100; ch_id==2; data_nidles==2; pkt_nidles==8; data_size==32;});
      fork
        test.chnl_gens[0].start();
        test.chnl_gens[1].start();
        test.chnl_gens[2].start();
      join
      #10us; // wait until all data haven been transfered through MCDF
    endtask
  endclass: cb_mcdf_data_consistence_basic





	//cb_mcdf_data_consistence_basic_test 中并没有实现do_reg()、do_formatter()、do_data(),
	//运行test时会调用cb_mcdf_data_consistence_basic这个回调函数,这里面实现了那三种方法。
  class cb_mcdf_data_consistence_basic_test extends mcdf_base_test;
    cb_mcdf_data_consistence_basic cb;
    `uvm_component_utils(cb_mcdf_data_consistence_basic_test)

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

    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);		//添加回调函数
    endfunction

    function void connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      cb.test = this;
    endfunction
  endclass: cb_mcdf_data_consistence_basic_test

四、UVM仿真控制方法

mcdf_base_test类中添加新的phase函数end_of_elaboration_phase()。同时利用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

---------------------
作者:煎丶包
来源:CSDN
原文:https://blog.csdn.net/qq_39794062/article/details/114236625
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值