一、编译UVM代码
uvm_compile代码
module uvm_compile;
// NOTE:: it is necessary to import uvm package and macros
import uvm_pkg::*;
`include "uvm_macros.svh"
initial begin
`uvm_info("UVM", "Hello, welcome to RKV UVM training!", UVM_LOW)
#1us;
`uvm_info("UVM", "Bye, and more gifts waiting for you!", UVM_LOW)
end
endmodule
uvm_compile代码分析
import uvm_pkg::*
`include "uvm_macros.svh"
无论在什么地方,UVM验证顶层都必须有上面这两行代码,代表着预编译的UVM库(包括UVM开源库和Questa的UVM定制部分库)。
uvm_macros.svh是包含了众多宏定义的头文件,将其include进来可以让宏定义识别成功,否则编译时会报错;
而uvm_pkg是UVM库的预编译库,很庞大,包括了uvm_factory、uvm_config_db、各种phase等。
仿真后可以在下方位置看到。
import uvm_pkg::*
上面语句表示把默认编译好的库mtiUvm.uvm_pkg导入到uvm_compile里。如果没有import进来,那么编译就会报错。
关于import和include的认识可以参考下方链接。
SV中import和include的区别
当我们编译并且仿真uvm_compile,有如下信息:
其中,
- sv_std.std指的是systemverilog的内建包,定义了一些class、task、function和变量;
- mtiUvm.uvm_pkg表示mentor已经编译好的库,mti表示mentor公司;
- mtiUvm.questa_uvm_pkg是questa专门针对uvm定制的库。
当我们在使用questasim时,我们不需要独立编译uvm_pkg,因为已经提前帮我们编译好了。但如果我们使用的是其他仿真器,比如VCS时,就需要独立编译一下uvm_pkg。
run -all后,打印信息如下:
二、SV与UVM之间的关系
添加sv_class_inst.sv文件,编译,仿真,看看发生了什么?实际上这个实验是SV模块实验环节的抽象,它只是在顶层module容器中要例化软件验证环境的顶层,即 SV class top。在接下来的阶段,从打印出的信息可以看得出来,相当于从测试的开始,到验证环境的搭建,激励的发送,检查的执行等,最后又到了测试的结束。因此这是SV模块实验的“一”,即一生二,二生三,三生万物的那个“顶层” 。
sv_class_inst.sv代码
module sv_class_inst;
import uvm_pkg::*;
`include "uvm_macros.svh"
class top;
function new();
`uvm_info("SV_TOP", "SV TOP creating", UVM_LOW)
endfunction
endclass
initial begin
top t;
`uvm_info("SV_TOP", "test started", UVM_LOW)
t = new();
`uvm_info("SV_TOP", "test finished", UVM_LOW)
end
endmodule
sv_class_inst.sv代码分析
对文件sv_class_inst.sv进行编译、仿真、运行,结果如下:
如何查看类的例化情况?
有人可能想在仿真窗口查看一下t句柄指向的对象。
结果会发现object窗口啥都没有。这是因为此处是硬件的信号,无法查看软件信号。
方法一:
正确的方式应该是通过view-locals打开locals窗口,这里可以看到软件变量。并且,需要在仿真后在第17行设置断点(没有设断点那么运行后就结束了,也看不到t),运行,就可以在locals窗口看到了。@top代表这个对象是top类,@1表示是top类的第一个对象。
方法二:
首先,如下图打开class instances窗口。
然后在仿真命令中加入-classdebug选项后进行仿真,并在仿真后在第17行设置断点。
vsim -novopt work.sv_class_inst -classdebug
如下图操作后也能在class instances窗口看到断点处的类例化的情况,此处就是@top@1.
uvm_class_inst.sv代码
module uvm_class_inst;
import uvm_pkg::*;
`include "uvm_macros.svh"
class top extends uvm_component;
`uvm_component_utils(top)
function new(string name = "top", uvm_component parent = null);
super.new(name, parent);
`uvm_info("UVM_TOP", "SV TOP creating", UVM_LOW)
endfunction
endclass
initial begin
top t;
`uvm_info("UVM_TOP", "test started", UVM_LOW)
t = new("t", null);
`uvm_info("UVM_TOP", "test finished", UVM_LOW)
end
endmodule
uvm_class_inst.sv代码分析
uvm_class_inst定义的top类看似没什么内容,但是仿真、设断点之后发现里面别有洞天,有一层又一层的结构。这是因为top继承了uvm_component,所以也继承了他的结构和内容。
如果想要看到层次结构,可以如下图点开class tree-sim.
在class tree窗口即可看到。
三、UVM验证顶层与SV验证顶层的对比
uvm_test_inst代码
package test_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"
class top extends uvm_test;
`uvm_component_utils(top);
function new(string name = "top", uvm_component parent = null);
super.new(name,parent);
`uvm_info("UVM_TOP", "SV TOP creating", UVM_LOW)
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("UVM_TOP", "test is running", UVM_LOW)
phase.drop_objection(this);
endtask //run_phase
endclass
endpackage
module uvm_test_inst;
import uvm_pkg::*;
`include "uvm_macros.svh"
import test_pkg::*;
initial begin
`uvm_info("UVM_TOP", "test started", UVM_LOW)
run_test("top");
`uvm_info("UVM_TOP", "test finished", UVM_LOW)
end
endmodule
uvm_test_inst代码分析
仿真,运行后,消息如下:
可以发现打印消息按照重要程度以及id分别列出。此处UVM_TOP为什么只有3个,而不是4个呢?明明代码里有4个UVM_TOP出现啊?
因此下面这行代码没有被执行:
`uvm_info("UVM_TOP", "test finished", UVM_LOW)
原因如下:
整个运行过程从initial块开始,接着进入run_test(),开始执行class top的内容:先是function new,接着是task run_phase。需要注意的是,当run_phase drop_objection之后,因为没有别的phase进行raise_objection,因此运行到此结束。所以没有执行上述提到的代码,最后只出现了3个UVM_TOP。
去掉 raise/drop_objection会怎样?
我们将raise/drop_objection注释掉,并加入了10ns的延时以及UVM_TOP类型的uvm_info(注意,因为代码没有写仿真时间单位,默认是1ns,所以加入的延时如果小于1ns会被仿真器忽略)
task run_phase(uvm_phase phase);
// phase.raise_objection(this);
`uvm_info("UVM_TOP", "test is running", UVM_LOW)
#10ns;
`uvm_info("UVM_TOP", "test finished after 10ns", UVM_LOW);
// phase.drop_objection(this);
endtask
并如下图设好断点。
一步一步运行,可以发现程序在完成第15行之后就退出了,并且最后退出的时间是在0 ns处。
这就是注释掉raise/drop_objection产生的后果,即,进入到run_phase之后,由于没有举手,所以程序会跳过有延时的地方并直接退出。
sv和uvm中仿真窗口的区别
在sv的仿真中,仿真窗口只会出现硬件层次结构,不会出现sv的层次结构;
而到了uvm,仿真窗口会有双顶层结构出现,也就是uvm层次——uvm_root和硬件层次uvm_test_inst。
其中,top就是我们继承于uvm_test的类名称。
四、启动UVM验证的必要步骤
相比于uvm_class_inst,uvm_test_inst更适合作为UVM验证顶层容器。原因如下:
- 只有继承于uvm_test的类,才可以作为UVM验证环境的顶层
- 创建顶层验证环境,有且只能依赖于run_test(“UVM_TEST_NAME”)来传递,或者通过仿真参数传递,而不是通过构建函数new()。尽管new()可以创建一个对象,但是不能做与顶层验证环境相关的其他工作。具体的原因需要等对UVM有更深入了解时才能体会。
搭建验证框架 -> 验证组件之间的连接和通信 -> 编写测试用例,继而完成复用和覆盖率收敛。