提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
主要学习内容:
- 工厂的注册、创建和覆盖机制
- 域的自动化以及uvm_object的常用方法
- phase机制
- config机制
- 消息管理
提示:以下是本篇文章正文内容,下面案例可供参考
一、工厂的注册、创建和覆盖机制
uvm只有两种注册宏:
- `uvm_object_utils(T)
- `uvm_component_utils(T)
UVM所有的类都继承于uvm_object或uvm_component类因此只需要两种注册宏。
添加factory_mechanism.sv,编译,输入仿真命令:
vsim -novopt -classdebug +UVM_TESTNAME=object_create work.factory_mechanism
+UVM_TESTNAME=<test_name>它的作用等同于lab0中的run_test(“test_name”),在仿真命令添加测试用例便于后续修改。
上述命令就是运行测试object_create
1.1完成类型创建
使用不同的方法完成对t1、t2、t3、t4uvm_object类
的对象的创建
- 方法一:
new(string name)
- 方法二:
uvm_object::type_id::create(string name)
👻该方法第一个参数是实例名称,创建uvm_object类型只有一个参数
- 方法三:
uvm_factory.create_object_by_name(uvm_object_wrapper requested_type,,string parent_inst_path="", string name)
👻此方法是直接通过工厂调用第一个参数指的是创建并返回的请求创建对象的类型(指导手册上写的the requested_type is a handle to the type’s proxy object不太理解这个 type’s proxy是什么意思),必须是uvm_object的子类,在这里指的是trans类型,通过trans::get_type()得到;第二个参数是结构层次路径,用于索引实例覆盖,通过调用parent(这里不是指父类)方法即uvm_componnet::get_full_name()方法来得到;第三个参数就是实例名称。
- 方法四:
uvm_component.create_object (string requested_type_name,string name="");
👻该方法使用的是uvm_component类自带的方法,可以创建uvm_object类型的对象,它实际上也是间接调用工厂创建对象,因此等同于方法三。第一个参数是请求创建对象类型的名称,第二个参数是实例名。
需要注意的是方法三、四返回的句柄类型。尝试编译可以发现报错信息,赋值类型不匹配
【类型创建的都是uvm_object类型的句柄,不能直接赋值给trans类型。需要进行类型转换,$cast即可作为函数调用也可以作为任务调用,作为函数调用时有int类型0/1的返回值,因此需要void’()使得函数转换成无返回值类型才可以单独调用,同时强制类型转换是隐性的赋值,并不是单纯的转换类型就结束了】
使用不同的方法完成对u1、u2、u3、u4uvm_component类
的对象的创建,方法同上。
需要注意的是创建uvm_componnet类型时方法三还有一个parent参数。
结果:
object_create测试字符串name没有传递进去?不知道为什么打印信息是默认的name,但是trans变量类型创建成功
component_create测试打印信息没问题,unit组件类型也创建成功但是可以看到u1与其他实例并不在top之下例化,原因可能是因为u1采用的是sv的例化方式,没有通过工厂创建。
1.2类型覆盖
在进行类型覆盖时一定要记住,覆盖发生在类型对象创建之前,因此子类调用父类的创建阶段时一定要在覆盖之后调用,此外原始类型必须在工厂注册过。
方法:
set_type_override_by_type(uvm_object_wrapper original_type,uvm_object_wrapper override_type,bit replace = 1)
👻该方法是uvm_factory::set_type_override_by_type方法的便捷办法,直接在uvm_component类调用通过类型type完成覆盖,第一个参数是原始类型,第二个参数是覆盖类型,第三个参数如果之前发生过覆盖,replace若为1,则继续覆盖,否则就不覆盖。
方法:
set_type_override(string original_type_name, string override_type_name,bit replace=1);
👻该方法是 uvm_factory::set_type_override_by_name方法的便捷办法,通过类型名称完成覆盖,这种方法必须保证注册时的类型type与字符串名字name一致。
结果:
从打印信息和Object窗口可以看到覆盖语句只对利用工厂创建的对象类型起作用。
组件类型的覆盖同样如此。
结论:覆盖是利用工厂的机制完成的,因此没有利用工厂创建的对象类型和组件类型自然没办法完成覆盖。
【思考】:去掉trans和unit的宏注册,编译是否出错,为什么?
通过调用宏注册实现类型定义type_id,接着完成类型注册也就是通过工厂完成type_id的注册;如果注释掉宏注册,type_id就没有在工厂完成定义注册,一旦没有注册,那么就无法通过type_id::create()完成创建(虽然这个函数最终还是通过调用factory的函数)。
二、域的自动化和uvm_object的常用方法
了解常见的域声明相关的宏,以及声明后uvm_object类具有的常见方法。
2.1 域的自动化声明
在uvm_object_methos.sv文件的事务类trans里完成所有成员变量域的自动化,需要注意两点:
- 多个相同类型变量在域的自动化宏声明时不能一起声明,需要分开
- 枚举类型变量在宏声明时有三个参数即类型,变量名,数据操作方法,其他类型只有后两个
2.2 uvm_object::compare()方法
在object_methods_test测试类里使用uvm_object::compare()方法
在run phase阶段完成事物的比较。
方法:
<uvm_object lhs>.compare(uvm_object rhs, uvm_comparer comparer = null)
👻比较两个uvm_object类lhs和rhs的成员变量,成功则返回1,失败返回0;参数comparer是最大输出的错误信息,如果为1,则表示比较出现错误后就不会进行后续比较。设置大一点的comparer可以得到更全面的比较信息。使用uvm_default_comparer.show_max
设置参数大小,这里可以直接在object_methods_test类调用uvm_default_comparer原因在于它是uvm_pkg的全局对象,可随时访问。
这里还用到消息处理方式——宏调用,详见第五节
2.3 自定义uvm_object的方法的回调函数
例如compare()的回调函数do_compare()
方法:
示例:
mytype = trans
- 在do_compare方法中例化新的句柄rhs_,它是trans类型,而传递进来的参数rhs是uvm_object类型,因此比较前需要进行句柄类型转换
- 子类需要调用super.do_compare()确保父类的属性也能比较(前提是有父类properties)
仿真结果,可以看到compare()先执行,然后紧接着调用回调函数do_compare()。
2.4 uvm_object::copy()、print()方法
方法:
传递参数为copy的对象
传递参数为打印机,即打印的方式。默认值为uvm_default_printer,同之前的uvm_default_comparer一样是uvm_pkg的全局变量。除此默认值之外还有uvm_default_table_printer、uvm_default_tree_printer、自定义打印等。
结果:
Before copy():
After copy():
UVM指导手册在描述uvm_object的常用方法中有这样一句话The compare/copy/print method is not virtual and should not be overloaded in derived classes. To compare/copy/print the fields of a derived class, that class should override the do_copy method意思是这些常用的方法都不是虚方法,子类不能覆盖这些父类方法,如果想进一步进行操作,只能自定义这些方法的回调函数。
总结:域的自动化可以帮助coder快速调用常用方法,无需自己定义,节省大量的code、time
三、config机制
在uvm_confug.sv中完成
- 接口传递
- 对象传递
- 单一变量传递
3.1 接口传递
方法:
uvm_config_db# (virtual interface)::set(...)
uvm_config_db# (virtual interface)::get(...)
👻先设置接口传递配置set函数,该函数需要发生在构建测试环境run_test()之前。
前三个参数提供完整的作用域(层次路径),最后一个参数是配置的变量。cntxt一般表示当前层次,通常用uvm_root::get()表示uvm_root的全局顶层实例
这里使用了通配符
*
,那么在uvm_test_top层次及以下的所有层次的实例都能获取该接口配置
class comp2同上,通过这种方式,只要uvm_component类在build phase阶段get到了接口,那么该类的实例都能用。
接口的传递需要注意以下几点:
- 接口的传递需要发生在run_test()之前,以保证在build phase阶段,virtual interface已经传递进unm_config_db中
- virtual interface和interface是不一样的,可以看到在声明interface和例化if0时并没有声明virtual,而是在传递的过程中使用的都是virtual interface,即实际接口的句柄。
- set()和get()的层次路径要保持一致,且#(T)传递类型T一致。
3.2 对象传递
方法:同上,传递类型不再是接口,而是uvm_object
传递的变量类型是config_db它是uvm_object类,所以是对象传递
该类的成员变量cfg获得了外部传递进来的配置,comp2同上
3.3 单一变量传递
方法同上,传递类型是该变量的数据类型
comp2 同上
仿真结果:
3.4 总结
- 接口传递必需先于run_test()
- uvm_config_db#(T)::set()先于对象创建
- 配置有优先级,层次越高的优先级越高
四、phase机制
在phase_order.sv中完成comp2和comp3中phase的定义
4.1主要的phase
- build phase、connect phase、run phase、report phase
❓run phase执行顺序怎么会是自顶向下,家人们谁懂啊
4.2 其他phase
- reset phase、main phase
想了一下应该是没有问题的,run phase 和 reset phase 一起开始是正常的,main phase 也确实应该在reset phase执行完以后再开始,因为他俩是顺序执行的。1us时run phase 和 reset phase结束,main phase在reset phase 结束后开始,再过1us,结束。所以一共是2us。所以由于这种有些混乱的执行顺序,run phase 不应该与细分的12个其他phase混合使用。
五、消息管理
在uvm_message.sv中完成:
5.1过滤所有消息
在测试用例build phase里屏蔽所有级别的消息,使uvm_message_test及其以下组件的消息在仿真时打印出来。
方法:uvm_component类的方法
👻此方法递归地设置此组件及其以下所有组件的报告的最大详细级别,此组件subtree中任何详细程度超过此最大值的报告都将被忽略。
仿真结果:可以看到在设置消息级别UVM_NONE以后,build phase之后的消息都没有打印,如果在end of elaboration phase调用,那么build phase阶段的info都会输出。TEST_DONE不知道是哪来的。
5.2根据ID过滤所有消息
方法:uvm_component类方法
👻该方法递归地将指定id的报告给定消息级别。
仿真结果与5.1一致
5.3 在顶层过滤消息
在5.1和5.2中仍有CREATE(在消息过滤前创建的info)和TOPTB消息输出,通过uvmroot::get()获取顶层(uvm_test_top?)然后过滤消息。
方法:
uvm_root::get().set_report_id_verbosity_hier()
结果一样
方法:uvm_root::get().set_report_verbosity_level_hier(UVM_NONE);
TEST_DONE消息没了
在run_tets()之前调用:所有信息已被过滤。
关于object_create没有将t2,t3,t4的name没有传递进去的问题
Questa仿真器自带的uvm存在一些问题,导致对象创建时name没办法传递进去。
仿真器默认使用uvm-1.1d版本,修改modelsim.ini文件使用uvm1.2版本时仿真会出现下面的错误: