> 文章列表 > 路科验证UVM入门与进阶详解实验2

路科验证UVM入门与进阶详解实验2

路科验证UVM入门与进阶详解实验2

一、验证组件和层次构建

首先将各个package中的SV组件替换为UVM组件,替换过程中需要遵循以下规则:

1、实现组件对应原则

2、在进行类的转换时需要注意:

  • SV的上述类均需继承于其对应的UVM类
  • 类定义过程中一定要'uvm_component_utils()'uvm_object_utils()进行注册

3、使用上述工厂注册宏的时候会伴随着域的自动化。一般建议在定义sequence item时,也要进行域的自动化声明,方便后续对sequence item对象进行拷贝、打印等操作。

4、一定要注意构建函数new()的声明方式,uvm_component的构建函数有两个参数new(string name, uvm_component parent),而uvm_object的构建函数只有一个参数new(string name)。 

5、在组件之间的层次关系构建中,依然按照之前SV组件的层次关系,只需要在不同的phase阶段完成组件的例化和连接。

注意:在改写sv文件为UVM文件时需要注意可能出现的错误。

  • 漏了注册、或者parent参数
  • build_phase里需要用type_id::create来创建组件,不采用new

哪些phase应该加上super.xxxx_phase, 哪些又可以不加呢? 

  • 对于build_phase来说, uvm_component对其做的最重要的事情就是自动获取通过config_db::set设置的参数。 如果要关掉这个功能, 可以在自己的build_phase中不调用super.build_phase
  • 除了build_phase外, UVM在其他phase中几乎没有做任何相关的事情,完全可以不必加上super.xxxx_phase语句
  • 上述说法只适用于直接扩展自uvm_component的类。 如果是扩展自用户自定义的类, 如base_test类, 且在其某个phase, 如connect_phase中定义了一些重要内容, 那么在具体测试用例的connect_phase中就不应该省略super.connect_phase。

 代码变化部分:

chnl_pkg 

1、do_drive()中为什么修改后需要加上动态转换? 

因为SVlab里我们自己定义的clone函数返回的是子类chnl_trans句柄,可以直接传递句柄;而uvm调用的clone函数返回的永远是uvm_object父类句柄。而我们需要访问子类对象,所以需要将父类的句柄转化为子类句柄。转化可以成功,因为该父类句柄指向的是子类对象。 

 

2、chnl_generator不是要改成sequence和sequencer吗? 

在uvmlab2里暂时不改造,等我们学到相应的部分再进行修改。(uvmlab4再改)这里先暂时继承于uvm_component。 

3、create和new分别在什么时候用? 

①如果是object类,在不需要被覆盖或者配置的前提下,那么用new或者type_id::create来创建都可以;如果有可能被覆盖,那就用type_id::create来创建。
对小白来说,可以统一在build_phase里用type_id::create来创建;
如果是组件,那就一定要采用create创建组件。这不仅可以在工厂里注册,方便做覆盖,而且帮助实现了层次化结构,为之后的配置提供方便。
③既不是object也不是component类的,用new来创建。比如端口类port是继承于uvm_void,就只能用new来创建。

4、改造后的chnl_generator里的sprint不加super会如何?

如果省略super,那么会存在一个同名的问题。因为uvm这个函数也叫sprint,所以编译器会认为你要调用的是当前的函数。

 如果把当前的sprint函数改下名字,那么问题就可以解决。因为当前类不存在sprint函数,所以会自动去父类里搜索sprint函数。首先去uvm_component搜,搜不到就继续往上,直到uvm_object里。借助域的自动化,找到sprint。所以此时不加super也没问题。

5、driver、monitor、agent声明中的local string name为什么不需要了? 

在组件中,name是已经预先定义的变量名,不需要再定义了。new函数中的name指的就是预先定义好的变量。 

6、chnl_agent里例化组件部分放置的位置区别

在sv中,创建组件就是在new函数中进行;而在uvm中,创建组件一般在build_phase中进行。更详细的说明见reg_pkg部分的第一点说明:mailbox实例的创建是放在new还是build_phase里

7、uvm中不需要分别调用子组件的run,那谁在控制运行? 

 

层次的执行以及phase之间的顺序都由uvm_root自动安排,不需要手动执行子组件的run函数了。
对于run_phase这个空的方法,其实可以直接整个删掉也无所谓。此处保留只是为了对比而已。

reg_pkg 

1、实例的创建是放在new还是build_phase里?

我们前面提到,在sv中,创建组件就是在new函数中进行;而在uvm中,创建组件需要在build_phase中进行。那么实例的创建要放在哪里咧?

分两种情况。
①如果组件是用new例化的,那么就放new函数里或者是build_phase都可以的。
②如果是使用create例化的,那就一定要放在build_phase中,才能保证覆盖、配置正常进行。

或者最简单的,可以把所有实例的创建都放在build_phase里,这样一定不会有问题。

mcdf_pkg

1、为什么要把do_compare改名为do_data_compare? 

do_compare是compare预定义好的回调函数,改名可以防止和预定义的函数起冲突。

2、为什么都用uvm_config_db传递接口了,还保留着set_interface函数? 

在mcdf_env中,路桑暂时保留了mcdf_env以及各个子组件的set_ingerface函数,只是改造了TB和mcdf_env的接口传递而已。具体就是:通过uvm_config_db完成了各接口从TB(硬件一侧)到验证环境mcdf_env(软件一侧)的传递,之后再通过mcdf_env与其各个子组件的set_interface进行接口的传递;

当然,我们也可以移除所有的set_interface函数,完全采用uvm_config_db set和get方法,从而使mcdf_env与其各个子组件也实现层次剥离,进一步促进组件之间的独立性。

3、改写成uvm后,$finish()去掉了,仿真如何结束? 

phase.raise_objection(this);
......
phase.drop_objection(this);

uvm中是通过在某个uvm_test里的run_phase里raise_objection,防止仿真退出,并在执行结束后drop_objection;不需要单独调用finish来结束仿真。因为所有phase执行结束后会自动退出仿真。

4、如果改写完后的某个phase是空的,可以删除吗?

virtual function void report_phase(uvm_phase phase);super.report_phase(phase);// this.env.do_report();// rpt_pkg::do_report();
endfunction

对于上面这个report_phase,其内容是空的,可以直接删掉。run_phase如果是空的也可以直接删去。

二、测试的开始和结束

代码改写 

tb

1、uvm_base_test还能通过set_interface来传递接口吗? 

以前是通过在仿真的时候传递一个TESTNAME,在运行的时候就可以直接把TESTNAME对应的set_interface和run函数调用起来(前提是已经提前把每个test都例化了),实现接口的层层传递,最终到达底层的组件。
而在uvm中是通过uvm_config_db来传递接口的,在组件还没有创建之前就先把接口放在中转库。运行则是通过run_test。

可能有人问,可否和之前一样还是通过set_interface来传递接口?

 

不行,调用组件方法set_interface的前提是组件已经例化了。而实际上,uvm中组件的例化是在run_test()中(因为run_test会执行所有的phase,包括build_phase),所以不行。

那如果把run_test放在set_interface之前呢?
也不行。因为run_test开始之后会进入build_phase。此时会发现没有interface的指针,所以也会有问题。

总结:
1、SV中接口传递的问题: 

  • 环境准备好了,组件例化了后才能传递;
  • 每一层都要有set_interface,然后层层传递进去(好几个类中都要声明set_interface)这不利于软件环境的封装和复用。

2、而通过uvm_config_db可以将接口先放在一个中转库,可以使得接口的传递和获取彻底分开,直接将接口传递一步到位,而不是层层传递;无需担心测试台层次结构中任何组件的深度。

2、run_test()中有无参数的区别何在?

如果有参数,意味着mcdf_data_consistence_basic_test是默认的test,如果仿真选项中没有指定UVM_TESTNAME,那么就默认执行它; 

run_test("mcdf_data_consistence_basic_test");

如果没有参数,也就意味着没有默认test。那么仿真选项就必须要指定test,否则会报错;
如果通过UVM_TESTNAME指定了某个test,那么UVM会从命令行中寻找测试用例的名字,创建它的实例并运行。 

run_test();
  1. 选择正确的test
  2. 通过test构建层次
  3. 让每个phase按顺序执行
  4. 通过objection机制控制仿真的结束

参考: 路科验证MCDF_uvmlab2_路科验证实验2_Hardworking_IC_boy的博客-CSDN博客