- 一 、UVM 打印分级介绍
1.1 uvm verbosity分级策略
一个芯片验证功能工程师,除了开发验证环境之外,其实大部分时间都在调试和debug,那么验证工作主要的debug方式就是通过环境的打印信息进行问题定位和试验。UVM为此提供了丰富的打印功能,并按照严重性分级。
uvm的打印分级策略如下图所示:
其中,`uvm_fatal宏的严重等级最高,发生uvm_fatal,仿真会直接停止;其次是`uvm_error,发生uvm_error,不会导致仿真直接停止,仿真会运行完成,只不过运行到最后,用例会fail,指明有bug,可能是环境问题,也可能是dut问题。然后是uvm_warning,起到提醒作用,说明当前点可能会发生错误,有的地方验证出口严格,会让逐条解释uvm_warning,甚至消除warning。
最后是`uvm_info宏,这个表示uvm的日志打印,和sv $display的功能类似,但是比display功能更加强大,`uvm_info宏内置了不同的打印等级,从上到下一次为:UVM_NONE,UVM_LOW,UVM_MEDIUM,UMV_HIGH,UVM_FULL,UVM_DEBUG。声明一个`uvm_info按照如下语法声明:
`uvm_info(“info_id”,“context”,UVM_HIGH);
这个宏有三个参数,第一个是当前uvm_info的id,用户自定义,是一个字符串类型的变量,最终会显示到屏幕上,通常情况下,这个变量在某个组件中打印,会使用get_type_name()方法,该方法存在与uvm基类中,返回当前组件的路径。第二个参数是要打印的内容,可以使用$sformatf系统函数传递打印参数。第三个参数就是uvm信息的打印等级。这个是什么意思呢?
uvm默认的打印等级是UVM_MEDIUM,也就是说,使用`uvm_info打印时,第三个参数传递的为UVM_NONE,UVM_LOW,UVM_MEDIUM,最终会被打印到标准输出,而传递参数为UVM_HIGH,UVM_FULL,UVM_DEBUG,则不会打印到标准输出。从这个角度理解,打印等级实际上是一个阈值的概念,当阈值设定为某个级别时,当前级别往下的等级,都可以打印,而级别之上的就不会打印。级别顺序是:
UVM_NONE->UVM_LOW->UVM_MEDIUM->UVM_HIGH->UVM_FULL->UVM_DEBUG;
也就是说,声明为UVM_NONE的信息,一定会打印,而声明为UVM_DEBUG的信息,会受到答应阈值的控制。理解这层含义过后,那么天然就会想象一个场景,如果一个验证环境趋向于golden,那么一些标准的信息,比如比对包的内容,检查的结果等等,声明为UVM_NONE,即一定会打印;那么如果一个验证环境在调试初期,因环境本身不ready,内部可能会有很多bug,调试的信息即可声明为UVM_DEBUG,uvm在运行过程中,将阈值设定为UVM_DEBUG,以打印丰富的信息,以方便对环境进行调试。
当然这里对uvm的打印等级制度并非只有这一种用法,只要能适配用户的需要,玩儿法会多种多样,就比较考验一个工程师的创造力。
1.2 uvm的打印宏和对仿真运行的影响
`uvm_fatal宏突发的时候,仿真会直接停止,也就是说,验证环境只要运行到一个`uvm_fatal,那么就不会继续往后面执行,仿真停止。那么除了`uvm_fatal之外,实际上`uvm_error也会造成仿真的停止,体现在两个方面:
在build_phase、connect_phase、end_of_elaboration_phase中,发生`uvm_error,会造成仿真的停止,因为在这三个phase中发生uvm_error,uvm会认为在环境build阶段突发的错误都是严重的,会调用uvm_fatal,而结束仿真。
uvm实际上会对uvm_error做计数统计,当计数统计达到阈值,验证环境也会直接停止。在编码的时候可以通过get_max_quit_count()函数获取当前环境中退出仿真的阈值,如果返回值为0,说明无论有多少个uvm_error发生,也不会退出。使用的时候要注意环境的执行顺序,一般在uvm树形结构建立完,在顶层节点的connect_phase中可以查询。
- 二、UVM 打印设定和重载
2.1 设定组件的打印策略
uvm提供了对某个组件独立设定和查询当前组件的打印等级,这部分功能较为简单,大多是对用法的熟练,首先给出查询和设定某个组件的打印等级相关的函数和用法:
- get_report_verbosity_level():获取某个特定的组件的打印等级;
使用方法:component.get_report_verbosity_level(),component可以使用路径的方式,比如在base_test中查看driver的打印等级,可以在connect_phase中调用:env.i_agt.drv.get_report_verbosity_level()查看,该函数的返回值为一个int类型的变量,实际上,UVM的打印等级是一个enmu类型的变量:
- set_report_verbosity_level(xxx):设定某个特定的组件的打印等级;
使用方法:component.set_report_verbosity_level(xxx),component可以使用路径的方式,比如在base_test中设定driver的打印等级,可以在connect_phase中调用:env.i_agt.drv.set_report_verbosity_level(UVM_DEBUG),将driver的`uvm_info全部设定为UVM_DEBUG级别。
- set_report_verbosity_level_hier(xxx):递归设定某个特定的组件的打印等级,该方法会将当前组件节点往下的所有节点打印等级设定为目标等级。
使用方法:component.set_report_verbosity_level_hier(xxx),component可以使用路径的方式,比如在base_test中设定i_agt以下所有节点的打印等级,可以在connect_phase中调用:
env.i_agt.set_report_verbosity_level_hier(UVM_NONE),将i_agt及其下根节点:driver,sqr,monitor的`uvm_info全部设定为UVM_DEBUG级别。
上述设定方法为设定组件的打印级别阈值,而不是改写某个uvm_info的原始设定,也就是说,当调用set_report_verbosity_level设定driver的打印级别为UVM_NONE时,那么在driver中,只有uvm_info被设定为UVM_NONE的打印会输出,设定为UVM_HIGH的则不会输出。
set_report_verbosity_level或者set_report_verbosity_level_hier会对组件内的所有uvm_info产生影响,即使组件中的uvm_info使用了不同的id。如果要对某个组件的某个id的uvm_info设定,选用如下方法:
- set_report_id_verbosity(“id_name”,xxx):将某个特定的组件,id名为id_name的uvm_info打印等级设定为目标等级。
使用方法:component.set_report_id_verbosity(“ID_1”, UVM_HIGH),component支持使用路径,比如在base_test中将driver内部id为driver_id0的uvm_info设定为UVM_NONE,在base_test的connect_phase中调用:env.i_agt.drv.set_report_id_verbosity(“driver_id0”, UVM_NONE)。
- set_report_id_verbosity_hier(“id_name”,xxx):递归的将某个特定的组件,id名为id_name的uvm_info打印等级设定为目标等级,也就是会将这个组件往下所有的节点,id为id_name的设定为目标等级。
使用方法:component.set_report_id_verbosity(“ID_1”, UVM_HIGH),component支持使用路径,比如在base_test中将i_agt及以下所有组件内部id为id0的uvm_info设定为UVM_NONE,在base_test的connect_phase中调用:env.i_agt.set_report_id_verbosity_hier(“id0”, UVM_NONE)。
此外,还可以通过vcs命令行键入,设置验证环境的信息输出级别,使用方法为:
<vcs command> +UVM_VERBOSITY=UVM_HIGH
上述命令会将验证环境中的所有打印输出级别设定为UVM_HIGH,等效于在base_test中调用了this.set_report_verbosity_level_hier(UVM_HIGH)。
2.2 打印verbosity的重载
uvm还提供了打印严重等级的重载,比如将driver中的`uvm_warning重载为`uvm_error,使验证环境的检查变得更为严格。相关函数为:
- set_report_severity_override(source_lvl, target_lvl):将某个特定的组件内的“source_lvl”等级重载为“target_lvl”
使用方法:component.set_report_severity_override(source, target),component支持路径调用,例如在base_test中将driver的UVM_INFO改为UVM_WARNING,可以在base_test的connect_phase中调用:env.i_agt.drv.set_report_severity_override(UVM_INFO, UVM_WARNING)。
- set_report_severity_id_override(source_lvl, “ID_NAME”,target_lvl):将某个特定的组件内的,名为“ID_NAME”的“source_lvl”等级重载为“target_lvl”
使用方法:component.set_report_severity_id_override(source, “id”, target),component支持路径调用,例如在base_test中将driver内ID为“my_drv”的UVM_INFO改为UVM_WARNING,可以在base_test的connect_phase中调用:env.i_agt.drv.set_report_severity_id_override(UVM_INFO, “my_drv”, UVM_WARNING)。
上述实例中,使用重载函数过后,driver中被声明为uvm_info的信息,最终在标准输出上会打印为uvm_warning,(如果设定id,只有名为id的uvm_info会被打印为uvm_warning),uvm对四种打印严重等级:uvm_fatal,uvm_error,uvm_warning,uvm_info都支持两两相互重载,且重载不支持递归,只支持对某个特定的组件进行打印严重等级重载。
此外,uvm也可以通过命令行的方式设置:
<vcs command> +uvm_set_severity=<component>,<id>,<source lvl>, <target_lvl>
例如,将driver中名为“my_drv”的uvm_warning重载为uvm_error:
<vcs command>
+uvm_set_severity=“uvm_test_top.env.i_agt.drv,my_drv,UVM_WARNING,UVM_ERROR”
如果要将driver中的所有uvm_warning重载,可以使用_ALL_:
<vcs command>
+uvm_set_severity=“uvm_test_top.env.i_agt.drv,_ALL_,UVM_WARNING,UVM_ERROR”
总结一下:
- 针对某个特定的组件,可以重载其打印输出的严重等级,支持uvm_fatal,uvm_error,uvm_warning,uvm_info两两之间的相互重载。重载过后会显示重载的等级,例如如果将uvm_info重载为uvm_error,那么最终标准打印,会将代码中的uvm_info输出为uvm_error并统计。
- 重载打印等级不支持递归操作,只能针对某个特定的组件进行设定,一般在组件可以路径式调用,例如在base_test中设定driver的严重等级,一般在connect_phase中设定。
- 有两个设定的函数,一个是设定所有,包括全部id的:set_report_severity_override(source_lvl, target_lvl);另一个是设定某个特定ID的:set_report_severity_id_override(source_lvl,“id“, target_lvl)
- 可以通过vcs命令行的方式进行重载,用法为:
<vcs command> +uvm_set_severity=”<component_path>,<id>,<source>,<target>”
如果全部替换,id=_ALL_。
2.3设定打印的计数目标
uvm会对uvm_error进行计数统计,如果超过一定的数目,仿真会直接退出,那么计数值也是可以设定的,环境可以通过调用get_max_quit_count获取当前uvm_error的最大计数,如果返回为0,则证明uvm_error可以是无限大,即无论多少个uvm_error都不会中断仿真。相关function如下:
- set_report_max_quit_count(num):设定仿真环境的最大退出次数。
使用方法:一般在base_test中设定,(其实可以在任意组件中设定,一般在connect_phase中,因为仿真肯定希望在uvm树形结构建立完就设定,而不是在simulation之后才设定),例如在base_test的connect_phase中设定打印最大退出计数为100:set_report_max_quit_count(100)。
- get_max_quit_count():返回当前仿真环境中的最大退出次数,如果返回值为0,为无限多次。同样在base_test的connect_phase中查看。
此外,还可以通过vcs的命令行键入方式进行设定,用法如下:
<vcs command> +UVM_MAX_QUIT_COUNT=”num, YES/NO”
UVM_MAX_QUIT_COUNT的参数有两个,一个是设定的最大退出次数,第二个参数为是否可以被改写,如果设定为YES,会被环境中的设定改写,设定为NO则不会。
默认情况下,uvm会统计uvm_error,将其加入到统计计数中,实际上,uvm还支持将四种严重等级都加入到统计计数中,uvm针对不同的打印严重等级,或者是打印的id,或者是严重等级结合id,提供了action函数:
- set_report_severity_action(lvl,uvm_action_type):表示将某个特定的组件,打印级别为lvl的设定为目标属性。
- set_report_severity_action_hier(lvl,uvm_action_type):递归的将某个特定的组件及其子节点一直到根节点,打印级别为lvl的设定为特定的属性
- set_report_id_action(“id”,uvm_action_type):将某个特定的组件内某个名为ID的打印(无论哪个级别,只要被声明为ID),设定为特定的属性
- set_report_id_action_hier(“id”,uvm_action_type):递归的将某个特定的组件及其子节点一直到根节点,内部名为id的打印(无论打印严重等级),设定为目标属性
- set_report_severity_id_action(lvl, “id”, uvm_action_type):将某个特定的组件内,严重等级为lvl的,且名为id的打印,设定为目标属性
- set_report_severity_id_action_hier(lvl, “id”, uvm_action_type):递归的将某个特定的组件及其子节点直到根节点,打印等级为lvl,名为id的打印,设定为目标属性
这几个函数按照严重等级,id,严重等级和id分别分为3类方法,每种方法又按照是否可以递归分为两类,所以一共为6种方法。函数的传参,lvl和id很好理解,这里有必要说明一下uvm_action_type参数。实际上,uvm_action_type参数指明了打印的属性:
解释下这几类属性:
- UVM_NO_ACTION:表示无动作,即不执行相关操作,也不打印到屏幕输出
- UVM_DISPLAY:打印到屏幕输出,也就是无论打印级别,只要是uvm的打印宏,都会输出,也是uvm默认的打印属性。
- UVM_LOG:表示将当前打印输出到文件
- UVM_COUNT:表示将当前打印加入退出计数统计
- UVM_EXIT:表示当前打印会导致仿真退出,例如`uvm_fatal宏,就添加了该属性
- UVM_CALL_HOOK:调用的是一个回调函数,使用的情况较少
- UVM_STOP:表示执行到当前打印,会进入仿真暂停,进入调试阶段
上述加入目标计数的例子中,如果将某个打印类型,加入到打印计数,即可通过打印的属性进行设定,例如在driver中将严重等级为uvm_warning,id为my_drv的打印,加入到退出计数,可以在base_test中通过如下方式调用:
env.i_agt.drv.set_report_severity_id_action(UVM_WARNING, “my_drv”, UVM_DISPLAY | UVM_COUNT)。
实际上,也可以通过命令行的方式进行改写action:
<vcs command> +uvm_set_action=”<component_path>, <id>, <severity> <uvm_action_type>”
上例可以写为:
<vcs command> +uvm_set_action=”uvm_test_top.env.i_agt.drv, my_drv, UVM_NG, UVM_DISPLAY | UVM_COUNT”
如果要将全部id改写,使用_ALL_替换参数<id>:
<vcs command> +uvm_set_action=”uvm_test_top.env.i_agt.drv, _ALL_, UVM_NG, UVM_DISPLAY | UVM_COUNT”
实际上,对于uvm内部的四种标准打印等级,源码对其进行了默认设定,fatal具备UVM_EXIT属性,error具备UVM_COUNT属性,而info和warning只具备UVM_DISPLAY属性,uvm_action_type可以通过或操作符“|”进行属性的拼接,即拼接后的属性都具备。
- 三、UVM断点功能和调试
3.1 uvm的断点调试功能
经过上节set_uvm_action的相关方法介绍,验证环境中可以通过定义UVM_STOP属性来进行断点调试,使用方式就是将某个id,或某个打印严重等级,或某个id的打印严重等级,设定为UVM_STOP属性,即可实现在运行到当前打印行,暂停仿真,进入调试过程。
举个例子:如果我们想运行到driver的某一个名为“go_to_debug”打印下,认为仿真存在bug,需要进行调试,那么就可以在base_test的connect_phase中进行设置:env.i_agt.drv.set_report_id_action(“go_to_debug”, UVM_DISPLAY | UVM_STOP)。假设在driver中存在一行代码为:
`uvm_warning(“go_to_debug”, “run error: need debug trace”);
那么运行到这行代码,仿真会暂停,进入ucli调试流程。
3.2 环境控制打印行为方法
本节实际上是对uvm_action的属性进行归纳总结,uvm_action功能包括了可以设置打印退出的目标计数,设置显示,设置进入调试流程等行为,那么在此处做出关于uvm_set_action的总结:
- uvm可以通过set_action,将某些打印设定为特定的功能,相关函数包括:
set_report_severity_action(lvl, action) | 将lvl级别设定为目标action |
set_report_severity_action_hier(lvl, action) | 递归设定,将lvl级别设定为目标action |
set_report_id_action(id, action) | 将名为id的打印设定为目标action |
set_report_id_action_hier(id, action) | 递归设定,将名为id的打印设定为目标action |
set_report_severity_id_action(lvl, id, action) | 将名为id的严重等级设定为目标action |
set_report_severity_id_action_hier(lvl, id, action) | 递归设定,将名为id的严重等级设定为目标action |
- uvm_action包括7种功能,总结如下:
UVM_NO_ACTION | 无任何设定,不输出到屏幕 |
UVM_DISPLAY | 标准输出到屏幕 |
UVM_COUNT | 加入仿真退出计数统计 |
UVM_EXIT | 运行到该打印,仿真直接退出 |
UVM_STOP | 运行到该打印,仿真暂停,进入调试流程 |
UVM_LOG | 将打印输出到文件,配合file使用 |
UVM_CALL_HOOK | 调用回调函数 |
- uvm_action可以通过逻辑运算符“|”进行拼接,拼接后打印兼具所有拼接的属性
- 可以通过vcs命令行键入的方式改写action,使用方法为:
<vcs command> +uvm_set_action=“<cmp_path>, <id>,<severity>,<action>”
如果设定severity下的所有id,<id>=_ALL_
- 设定只能针对组件去设定,支持组件的递归设定,递归后,可以将组件和其子节点一直到根节点的组件全部进行设定。
- 四、UVM打印信息输出到文件
4.1 输出到文件用法
uvm_action_type存在一种用法是将打印信息输出到LOG文件,通过将action设定为UVM_LOG实现,那么这个功能需要配合文件输入输出实现,uvm同样根据severity,id,对应id的severity设置了三种文件输出的函数,每种文件输出函数又按照能否递归输出,分为两种:
set_report_severity_file(severity, uvm_file) | 将对应severity打印输出到uvm_file |
set_report_severity_file_hier(severity, uvm_file) | 递归的,将对应severity打印输出到uvm_file |
set_report_id_file(“id”, uvm_file) | 将名为id的打印输出到uvm_file |
set_report_id_file_hier(“id”, uvm_file) | 递归的,将名为id的打印输出到uvm_file |
set_report_severity_id_file(severity, “id”, uvm_file) | 将名为id的目标severity等级的打印输出到uvm_file |
set_report_severity_id_file_hier(severity, “id”, uvm_file) | 递归的,将名为id的目标severity等级的打印输出到uvm_file |
那么如何使用呢?想象一个场景,在集成验证中,往往会将两个单元的DUT合并验证集成的功能组合,环境往往重用了两个单元验证的环境,那么在继承验证env中,往往包含两个单元验证env,如果我们想将其中一个env中的uvm_warning和名为“debug_to_file”id的uvm_info,以及“need_debug”的打印属性,输出到对应的文件中,可以在top env中写如下代码:
1. class top_env extends uvm_env;
2. sub_env0 env0;
3. sub_env1 env1;
4. uvm_file uvm_info_debug_to_file;
5. uvm_file uvm_warning_file;
6. uvm_file need_debug_file;
7. ......
8. `uvm_component_utils(top_env);
9. extern function void new(string name="top_env", uvm_component parent);
10. extern vitrual function void build_phase(uvm_phase phase);
11. extern virtual function void connect_phase(uvm_phase phase);
12. ......
13. extern virtual function void final_phase(uvm_phase phase);
14. endclass
15.
16. function void top_env::new(string name="top_env", uvm_component parent);
17. super.new(name, parent);
18. endfunction:new
19.
20. function void top_env::build_phase(uvm_phase phase);
21. super.build_phase(phase);
22. env0 = sub_env0::type_id::create("env0", this);
23. env1 = sub_env1::type_id::create("env1", this);
24. endfunction:build_phase
25.
26. function void top_env::connect_phase(uvm_phase phase);
27. super.connect_phase(phase);
28. uvm_info_debug_to_file = $fopen("uvm_info_debug_to_file.log", "w");
29. uvm_warning_file = $fopen("uvm_warning_file.log", "w");
30. need_debug_file = $fopen("need_debug_file.log", "w");
31. //设定打印输出到文件
32. this.env0.set_report_severity_id_file_hier(UVM_INFO, "debug_to_file", uvm_info_debug_to_file);
33. this.env0.set_report_severity_file_hier(UVM_WARNING, uvm_warning_file);
34. this.env0.set_report_id_file_hier("need_debug", need_debug_file);
35. //设定打印属性,具备UVM_LOG
36. this.env0.set_report_severity_id_action_hier(UVM_INFO, "debug_to_file", UVM_DISPLAY | UVM_LOG);
37. this.env0.set_report_severity_action_hier(UVM_WARNING, UVM_DISPLAY | UVM_LOG);
38. this.env0.set_report_id_action_hier("need_debug", UVM_DISPLAY | UVM_LOG);
39. ......
40. endfunction:connect_phase
41. ......
42. function void top_env::finial_phase(uvm_phase phase);
43. super.finial_phase(phase);
44. .......
45. $fclose(uvm_info_debug_to_file);
46. $fclose(uvm_warning_file);
47. $fclose(need_debug_file);
48. endfunction:finial_phase
总结一下:
- 先在top类中声明uvm_file文件句柄,在connect_phase中通过$fopen打开文件。
- 然后在connect_phase中设定目标打印到目的文件,通过set_report_*_file*设定
- 然后在connect_phase中将打印action设定为带有输出到文件的属性,通过set_report_*_action*(*, * | UVM_LOG)的方式设定
- 最后在finial_phase中关闭文件句柄,一定要在finial_phase,也就是uvm运行结束前的phase中关闭文件,在中间phase中关闭,会造成打印的丢失。