【UVM练习】实验lab3

 知识点:

TLM定义:TLM是事务级建模的缩写,所谓transaction level是相对于DUT中各个模块之间信号线级别的通信来说的,就是把某一个特定功能的一组信息封装成一个类。端口类型可以分为三种:port、export和imp。

单项通信就是从initiator到target之间的数据流向是单一的,多向通信指的是initiator与target之间相同的TLM端口数目超过一个的时候的解决办法。

多向通信的宏命名方式:

通信管道:需要在两个组件中例化端口,然后同时在target中实现相应的传输方法,uvm_tlm_fifo类是一个新的组件,继承于uvm_component,已经预先定义了多个端口实现了多个对应方法可以使用。uvm_tlm_fifo功能类似mailbox

1、TLIM单向通信和多项通信:

1.1 在monitor中的用来与checker中的邮箱通信的mon_mb句柄替换为对应的uvm_blocking_put_port类型。

class chnl_monitor extends uvm_monitor;
    local virtual chnl_intf intf;
    uvm_blocking_put_port #(mon_data_t) mon_bp_port;

    `uvm_component_utils(chnl_monitor)

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

    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_bp_port.put(m);
        `uvm_info(get_type_name(), $sformatf("monitored channel data 'h%8x", m.data), UVM_HIGH)
      end
    endtask
  endclass: chnl_monitor

以前在SV里面是通过mailbox的句柄进行传递的,mailbox #(mon_data_t) mon_mb; 在uvm里面通过替换为对应的uvm_blocking_put_port 类型,function new里面进行例化,只能采用new()的形式,因为是object组件;以前是通过mailbox的句柄往里面put传输数据,现在是通过端口往里面put,所以把句柄更改为端口名称就可以了。无论是chnl_pkg还是fmt_pkg、reg_pkg都是一样的,都是在monitor里面传递的。

1.2 声明import的端口类型,并且完成例化:

  `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);  

因为checker里面有多个相同类型的import,所以是多向的通信,为了避免方法名的冲突,所以要占用宏的声明,

利用uvm_blocking_put_imp_decl(SFX)来声明导入的import端口,左边部分的五个import端口,还有邮箱右边的的get和get/peek类型的import,利用宏已经声明了端口的类型,然后用new例化

1.3 根据声明的 import 端口类型,请分别实现其对应的方法:

实现五种不同名的put,以及不同的get()和get/peek()的任务,目的还是把以前通过mailbox句柄的连接改为端口的连接。

    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

    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

1.4在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);
/********************************************************************/
    this.reg_bg_port.get(t);

1.5在mcdf_env的connect_phase部分完成mcdf_refmod的TLM port和mcdf)checker的TLM import连接:把各个发起端(chnl_agent)里面的agent中端口和checker里面的端口进行连接,可以理解为图中左边五个put端口的连接。

      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);

1.6 在mcdf_checker的connect_phase部分完成mcdf_refmod的TLM port和mcdf_checker的TLM import连接:可以理解为中间部分的连接,方向是从reference_model向checker。

      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);

2、TLM通信管道:

TLM通信的优点:通信函数可以定制化put()/get()/peek()的内容和参数,这其 实比 mailbox 的通信更加灵活;将组件实现了完全的隔离,就可以很好地避免直接将不同层次的数据缓存对象的句柄进行 “空中传递”.而 TLM 端口按照层次的连接,可以使得组件之间保持很好的独立性。

2.1将原本在 mcdf_refmod 中的out_mb 替换为 uvm_tlm_fifo 类型,并且完成例化,以及对应的变量名替换。uvm_tlm_fifo 既有缓存的功能,也有import的功能,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);

2.2  将原本在mcdf_checker中的exp_mbs[3]的邮箱句柄数组,替换为 uvm_blocking_get_port 类型句柄数组,并且做相应的例化以及变量名替换.也就是图中最右边的三个进行替换。

 uvm_blocking_get_port #(fmt_trans) exp_bg_ports[3];

 foreach(exp_bg_ports[i]) exp_bg_ports[i] = new($sformatf("exp_bg_ports[%0d]", i), this);

 this.exp_bg_ports[mont.ch_id].get(expt);

2.3 在mcdf_checker中,完成在mcdf_checker中的TLM端口端口到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

3、UVM回调类:

目的:将原有的mcdf_data_consistence_basic_test 和 mcdf_full_random_test 的类实现方式 ( 即类继承方式 ) 修改为回调函数的实现方式。

3.1  在路桑给的uvm_callback类中,预先定义需要的几个虚方法:首先是设置了一个call_back的类--cb_mcdf_base ,然后在类里面预定义一些空函数

    virtual task cb_do_reg();
      // User to define the content
    endtask

    virtual task cb_do_formatter();
      // User to define the content
    endtask

    virtual task cb_do_data();
      // User to define the content
    endtask

3.2 使用 callback 对应的宏,完成目标 uvm_test 类型与目标 uvm_callback 类型的关联:首先是定义call_back,然后是绑定和插入,3.2便是绑定部分。

    `uvm_register_cb(mcdf_base_test, cb_mcdf_base)

3.3 在uvm_test类型指定的方法中,完成 uvm_callback 的方法回调指定:3.3便是插入,mcdf_base_test和cb_mcdf_base是刚绑定的父类,cb_do_reg便是父类里面的方法。

    // do register configuration
    virtual task do_reg();
      //TODO-3.3 Use callback macro to link the callback method
      `uvm_do_callbacks(mcdf_base_test, cb_mcdf_base, cb_do_reg())
    endtask

    // do external formatter down stream slave configuration
    virtual task do_formatter();
      //TODO-3.3 Use callback macro to link the callback method
      `uvm_do_callbacks(mcdf_base_test, cb_mcdf_base, cb_do_formatter())
    endtask

    // do data transition from 3 channel slaves
    virtual task do_data();
      //TODO-3.3 Use callback macro to link the callback method
      `uvm_do_callbacks(mcdf_base_test, cb_mcdf_base, cb_do_data())
    endtask

3.4 完成uvm_callback和对应测试类的定义:调整 mcdf_data_consistence_basic_test 到cb_mcdf_data_consistence_basic_test 嵌入内容的类型,继承do_reg()/do_formatter()/do_data()的方法到回调类,并在测试中链接回调。

mcdf_data_consistence_basic_test和cb_mcdf_data_consistence_basic_test在内容上是一致的,都是实现do_reg()、do_formatter()、do_data()这三个功能,只不过实现的方式不同,一个是继承,一个是call_back。就是把事先关联的call_back类,填充对应的方法,然后把当前的类型和call_back类关联就可以了。 需要注意的是数据仍然是mcdf_base_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

  //TODO-3.4 define cb_mcdf_data_consistence_basic_test
  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

4、UVM仿真控制方法:

4.1 在mcdf_base_test类中添加新的phase函数end_of_elaboration_phase(),同时利用uvm_root类来将信息的冗余度设置为UVM_HIGH,以此来允许更多低级别的信息打印出来:end_of_elaboration_phase是在build_phase、connect_phase之后,run_phase之前,是在一切环境准备就绪的时候,运行之前再对环境做一些配置

4.2 请利用uvm_root::set_timeout()设置仿真的最大时间长度,同时由于此功能的生效,可以清除原有方法 do.watchdog()的定义和调用:以前是通过do_watchdog在run_phase运行来停止仿真,这里是通过set_timeout来停止仿真。

    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

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 《UVM入门进阶实验1文档》是一份用于帮助初学者进一步理解和学习UVM实验指导文档。该文档提供了UVM编程框架的第一个实验,旨在帮助读者熟悉UVM的基本概念和用法。 实验1的目标是创建一个简单的UVM测试环境,并展示如何使用UVM框架进行测试。实验包括以下几个主要步骤: 1. 创建一个UVM测试环境:在这一步骤,我们需要定义测试环境的结构和组件。包括创建一个顶层测试模块、一个顶层环境模块,以及其他必要的组件,如驱动器、监视器、生成器等。 2. 编写测试用例:在这一步骤,我们需要编写一个简单的测试用例来验证被测设计的功能。测试用例需要继承自UVM的`uvm_test_case`类,并在`run_phase`定义测试过程。 3. 编写环境配置:在这一步骤,我们需要将测试用例和测试环境进行连接,并设置一些必要的运行时参数。通过配置对象的方式,我们可以很方便地配置测试环境的各个组件。 4. 运行仿真:在这一步骤,我们需要运行仿真并观察测试结果。通过在测试用例创建一个sequence对象,我们可以在运行时动态生成测试序列。 《UVM入门进阶实验1文档》详细说明了每一步的具体实现方法,并提供了代码示例和可参考的资源链接。 通过完成实验1,读者可以对UVM的基本概念和使用方法有一个更深入的了解。这将为进一步学习和掌握UVM提供坚实的基础,并为以后的工作打下良好的基础。同时,实验1还可以帮助读者培养UVM编程的思维方式和调试技巧,提高工作效率。 总之,《UVM入门进阶实验1文档》是一份非常有价值的学习资料,通过按照文档的指导完成实验,读者可以在短时间内快速入门并掌握UVM的基本用法。 ### 回答2: 《UVM入门进阶实验1文档》是一本介绍UVM(Universal Verification Methodology)的入门实验指导书。UVM是一种用于验证硬件设计的方法学,它提供了一套面向对象的验证框架,可以用于设计验证的自动化和重用。 本文档首先简要介绍了UVM的概念和特点,然后详细讲解了实验1的内容。实验1主要涉及到UVM最基础的概念和类的使用方法。首先,介绍了UVM的基础类,如uvm_component、uvm_object和uvm_sequence等,以及它们的继承关系和功能。然后,介绍了如何创建和管理UVM环境,并讲解了如何使用UVM Testbench的各种组件来进行设计验证。最后,讲解了一些常用的调试技巧和工具,如波形查看器和消息记录器等。 在实验1,学员将通过几个简单的示例,来熟悉UVM的基本概念和使用方法。例如,学员将学习如何创建一个简单的UVM Testbench,并使用UVM的配置机制来对其进行配置。此外,学员还将学习如何创建和管理UVM Sequences,并在Testbench使用它们来生成随机的输入数据。最后,学员将学习如何使用UVM里的Transaction来封装输入输出数据,以及如何使用Scoreboard来进行结果验证。 通过完成实验1,学员将掌握UVM最基本的概念和使用方法,为后续的进阶实验打下基础。同时,学员将对UVM的工作原理和设计验证的流程有一个清晰的认识,为进一步深入学习和应用UVM提供了基础。 ### 回答3: 《UVM入门进阶实验1文档》是一份详细介绍了如何使用UVM进行验证的教程。UVM是一种用于硬件验证的开放式框架,能够帮助工程师更高效地开发和执行验证环境。 该文档首先简要介绍了UVM的背景和原理,包括UVM Testbench的组成结构和工作流程。然后,文档逐步指导读者完成实验1,并提供了实验所需的样例代码和测试平台。 在实验1,文档首先指导读者创建一个简单的UVM环境,并介绍了UVM的基本类和功能。然后,通过一个简单的例子演示了如何创建一个UVM测试,包括定义测试类、产生和驱动测试向量、分析和比较结果等。读者可以按照文档提供的步骤和示例代码,逐步完成实验。 在实验进行过程,文档还不断提供了一些常见问题和解决方法。这些问题和解决方法能够帮助读者更好地理解和应用UVM,解决遇到的困惑和难题。 该文档还包括对实验的详细说明和解析,比如UVM环境的搭建、测试向量的生成和分析等。通过这些详细说明和解析,读者可以更深入地理解UVM的工作原理和实现方式。 总之,《UVM入门进阶实验1文档》是一份非常实用的教程,帮助读者快速入门和进阶使用UVM进行硬件验证。通过该文档的学习实验,读者能够掌握UVM的基本概念和使用方法,为日后的硬件验证工作打下坚实的基础。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值