《UVM实战卷Ⅰ》学习笔记 第二章 一个简单的UVM验证平台(1)
写在前面:本章作者想通过一个极为简单的验证环境让读者初始UVM,因此该章学习有一些建议.第一点,有的细节暂时不用深究,因为后续章节会详细讲解.做好备忘,回头再温习即可.第二点,个人认为作者关于UVM中语法点或者说新机制的引入是从"需求--->解决方案"的思路来讲解的,所以可以从这个角度理解,抓大放小.
目录
2.代码2-2中第11行main_phase(uvm_phase phase)的参数列表是什么意思?
10.run_test为什么创建的是uvm_test_top?
2.1 验证平台的组成
无
2.2 只有driver的验证平台
1.想要在使用UVM实现一个功能时,第一步应该做什么?
这里主要谈的是面向对象编程.从开始讲,UVM本质上是一个库,库中有各种各样已经定义好的类(class),这里的定义好讲的是class中有丰富的task/function/成员变量,不需要我们每次都再自行定义.注意阅读本章的代码,在new()中super的作用.另外后续也有父类中已有成员变量再子类中不用定义,直接使用.
在前言中,我提及学习UVM时可以参考源代码,这里的源代码往往就是UVM中已经定义类的代码.
2.代码2-2中第11行main_phase(uvm_phase phase)的参数列表是什么意思?
如前面所讲本章的东西可以先不深究,该知识点主要是phase机制.讲解在本书的5.2.2.之前我自己也写过一篇blog探讨过这个问题.详见UVM phase机制中各个phase function/task 的参数列表(uvm_phase phase)怎么理解?_IC-V的博客-CSDN博客
3.怎么理解get_full_name()函数的路径索取?
这里路径索取需要结合UVM树形结构理解,也就是涉及到uvm_compoent的内容,可以先不深究,因为这里只有driver可能不易理解,随着后续加入env/agent等对driver进行层层封装的时候自己可以尝试把2-3代码加入my_driver中(例如2.3.4),根据打印结果就会有更加深的理解.
4.初识factory机制
关于factory机制,第八章有专门的更加丰富的讲解,这里作者从实际需求出发,先行引入使用factory机制.需求:自动化top_tb中driver,解决:使用factory
从代码2-6的23-28行可知,目前在top_tb中例化driver并调用其中main.phase还是和systemve rilog中的方法一样,全程手动.引入factory之后便可以全自动进行该过程.使用factory机制的要点:必须要先注册.关于"注册"这里先可不深究,另外注意compoent和object注册使用的语句不同(关于compoent和objec的不同,后续会慢慢有认知)
5.初识objection机制
引入factory之前的2.2.1仿真打印结果
# *********** IMPORTANT RELEASE NOTES ************
#
# You are using a version of the UVM library that has been compiled
# with `UVM_NO_DEPRECATED undefined.
# See http://www.eda.org/svdb/view.php?id=3313 for more details.
#
# You are using a version of the UVM library that has been compiled
# with `UVM_OBJECT_MUST_HAVE_CONSTRUCTOR undefined.
# See http://www.eda.org/svdb/view.php?id=3770 for more details.
#
# (Specify +UVM_NO_RELNOTES to turn off this notice)
#
# UVM_INFO my_driver.sv(21) @ 100000: drv [my_driver] data is drived
.....//共计256个输出
# UVM_INFO my_driver.sv(21) @ 300000: drv [my_driver] data is drived
# UVM_INFO my_driver.sv(21) @ 51100000: drv [my_driver] data is drived
# **********************************Show drv**********************************
# ** Note: $finish : top_tb.sv(27)
# Time: 51300 ns Iteration: 1 Instance: /top_tb
引入factory之前的2.2.2仿真打印结果
# *********** IMPORTANT RELEASE NOTES ************
#
# You are using a version of the UVM library that has been compiled
# with `UVM_NO_DEPRECATED undefined.
# See http://www.eda.org/svdb/view.php?id=3313 for more details.
#
# You are using a version of the UVM library that has been compiled
# with `UVM_OBJECT_MUST_HAVE_CONSTRUCTOR undefined.
# See http://www.eda.org/svdb/view.php?id=3770 for more details.
#
# (Specify +UVM_NO_RELNOTES to turn off this notice)
#
# UVM_INFO my_driver.sv(8) @ 0: uvm_test_top [my_driver] new is called
# UVM_INFO @ 0: reporter [RNTST] Running test my_driver...
# UVM_INFO my_driver.sv(14) @ 0: uvm_test_top [my_driver] main_phase is called
#
# --- UVM Report Summary ---
#
# ** Report counts by severity
# UVM_INFO : 3
# UVM_WARNING : 0
# UVM_ERROR : 0
# UVM_FATAL : 0
# ** Report counts by id
# [RNTST] 1
# [my_driver] 2
# ** Note: $finish : D:/questasim64_10.6c/verilog_src/uvm-1.1d/src/base/uvm_root.svh(430)
# Time: 0 ps Iteration: 207 Instance: /top_tb
对比发现引入factory后data并没有像刚开始一样输出256数据.于是需求:引入factory后可以正常输出数据,解决:引入objection.
关于objection有几个要点:①成对出现②raise_objection需出现在第一个消耗仿真时间的语句之前
引入objection后2.2.3仿真结果如下
# *********** IMPORTANT RELEASE NOTES ************
#
# You are using a version of the UVM library that has been compiled
# with `UVM_NO_DEPRECATED undefined.
# See http://www.eda.org/svdb/view.php?id=3313 for more details.
#
# You are using a version of the UVM library that has been compiled
# with `UVM_OBJECT_MUST_HAVE_CONSTRUCTOR undefined.
# See http://www.eda.org/svdb/view.php?id=3770 for more details.
#
# (Specify +UVM_NO_RELNOTES to turn off this notice)
#
# UVM_INFO my_driver.sv(8) @ 0: uvm_test_top [my_driver] new is called
# UVM_INFO @ 0: reporter [RNTST] Running test my_driver...
# UVM_INFO my_driver.sv(15) @ 0: uvm_test_top [my_driver] main_phase is called
......//共计256
# UVM_INFO my_driver.sv(24) @ 52300000: uvm_test_top [my_driver] data is drived
#
# --- UVM Report Summary ---
#
# ** Report counts by severity
# UVM_INFO : 259
# UVM_WARNING : 0
# UVM_ERROR : 0
# UVM_FATAL : 0
# ** Report counts by id
# [RNTST] 1
# [my_driver] 258
# ** Note: $finish : D:/questasim64_10.6c/verilog_src/uvm-1.1d/src/base/uvm_root.svh(430)
# Time: 52500 ns Iteration: 98 Instance: /top_tb
6.初识interface
需求:增加验证平台的可移植性.编程语言基础薄弱的人可能比较不好理解,会觉得有点抽象.
先理解绝对路径,通过阅读2.2.3中代码my_driver和top_tb,my_driver使用top_tb的成员变量时,都是通过top_tb.*(*代表所有成员变量)的形式引用,相当于driver和tb直接相连,这便是绝对路径.那相对路径是什么呢?那就是在两者中间引入一个中转站(interface),这里的"相对"便是指的相对于interface的路径.
如果没有interface,但凡tb中某个成员变量发生变化(例如经过封装层次发生变化),driver中top_tb.*也就要发生修改,这往往带来的是繁琐无意义的代码修改.解决:引入interface
试图形象化理解interface,需要在模块间流动的数据,都在接口中声明,而接口又在模块中例化,从而使各个模块可以使用接口中的数据.从字面意思理解interface就是接口的意思,可以将接口比作一个数据中转站,无论是手机还是平板,只要和数据发生联系的都可以和该中转站进行连接,中转站内部会存储数据,各个电子设备可以对数据进行读写.
7.初识virtual interface
需求:验证环境(class)是软件环境,dut(module)是硬件.class中无法使用interface(module可以使用).解决:引入virtual interface
8.初识config_db机制
需求:将验证环境和硬件例化的接口连接起来,但是由于factory机制的引入,使用run_test自动化原来tb中driver部分(run_test到底做了什么?后面探讨一下)建立了新的层次结构,致使tb.my_driver不存在了,无法使用"top_tb.my_dut.input_if=top_tb.my_driver.vif"完成赋值.
解决:引入config_db机制
9.config_db中set和get源代码
作者在书中对set/get参数列表讲的不是很清楚.他们的源代码如下:
static function void set(uvm_component cntxt,
string inst_name,
string field_name,
T value);
static function bit get(uvm_component cntxt,
string inst_name,
string field_name,
inout T value);
个人认为前三个参数就是最后一个变量的路径.
10.run_test为什么创建的是uvm_test_top?
这里就涉及到UVM学习的一个痛点,因为本身是一个库,预定义了很多东西,用起来很方便,但是有时候理解起来就很抽象,还是那句话如果不耽误大框架学习,可以先跳过.
相关源代码如下
task run_test (string test_name="");
uvm_root top;
uvm_coreservice_t cs;
cs = uvm_coreservice_t::get();
top = cs.get_root();
top.run_test(test_name);
endtask
task uvm_root::run_test(string test_name="");
uvm_report_server l_rs;
uvm_coreservice_t cs = uvm_coreservice_t::get();
uvm_factory factory=cs.get_factory();
bit testname_plusarg;
int test_name_count;
string test_names[$];
string msg;
uvm_component uvm_test_top;
process phase_runner_proc; // store thread forked below for final cleanup
testname_plusarg = 0;
// Set up the process that decouples the thread that drops objections from
// the process that processes drop/all_dropped objections. Thus, if the
// original calling thread (the "dropper") gets killed, it does not affect
// drain-time and propagation of the drop up the hierarchy.
// Needs to be done in run_test since it needs to be in an
// initial block to fork a process.
uvm_objection::m_init_objections();
`ifndef UVM_NO_DPI
// Retrieve the test names provided on the command line. Command line
// overrides the argument.
test_name_count = clp.get_arg_values("+UVM_TESTNAME=", test_names);
// If at least one, use first in queue.
if (test_name_count > 0) begin
test_name = test_names[0];
testname_plusarg = 1;
end
// If multiple, provided the warning giving the number, which one will be
// used and the complete list.
if (test_name_count > 1) begin
string test_list;
string sep;
for (int i = 0; i < test_names.size(); i++) begin
if (i != 0)
sep = ", ";
test_list = {test_list, sep, test_names[i]};
end
uvm_report_warning("MULTTST",
$sformatf("Multiple (%0d) +UVM_TESTNAME arguments provided on the command line. '%s' will be used. Provided list: %s.", test_name_count, test_name, test_list), UVM_NONE);
end
`else
// plusarg overrides argument
if ($value$plusargs("UVM_TESTNAME=%s", test_name)) begin
`uvm_info("NO_DPI_TSTNAME", "UVM_NO_DPI defined--getting UVM_TESTNAME directly, without DPI", UVM_NONE)
testname_plusarg = 1;
end
`endif
// if test now defined, create it using common factory
if (test_name != "") begin
uvm_coreservice_t cs = uvm_coreservice_t::get();
uvm_factory factory=cs.get_factory();
if(m_children.exists("uvm_test_top")) begin
uvm_report_fatal("TTINST",
"An uvm_test_top already exists via a previous call to run_test", UVM_NONE);
#0; // forces shutdown because $finish is forked
end
$cast(uvm_test_top, factory.create_component_by_name(test_name,
"", "uvm_test_top", null));
if (uvm_test_top == null) begin
msg = testname_plusarg ? {"command line +UVM_TESTNAME=",test_name} :
{"call to run_test(",test_name,")"};
uvm_report_fatal("INVTST",
{"Requested test from ",msg, " not found." }, UVM_NONE);
end
end
if (m_children.num() == 0) begin
uvm_report_fatal("NOCOMP",
{"No components instantiated. You must either instantiate",
" at least one component before calling run_test or use",
" run_test to do so. To run a test using run_test,",
" use +UVM_TESTNAME or supply the test name in",
" the argument to run_test(). Exiting simulation."}, UVM_NONE);
return;
end
begin
if(test_name=="")
uvm_report_info("RNTST", "Running test ...", UVM_LOW);
else if (test_name == uvm_test_top.get_type_name())
uvm_report_info("RNTST", {"Running test ",test_name,"..."}, UVM_LOW);
else
uvm_report_info("RNTST", {"Running test ",uvm_test_top.get_type_name()," (via factory override for test \"",test_name,"\")..."}, UVM_LOW);
end
// phase runner, isolated from calling process
fork begin
// spawn the phase runner task
phase_runner_proc = process::self();
uvm_phase::m_run_phases();
end
join_none
#0; // let the phase runner start
wait (m_phase_all_done == 1);
// clean up after ourselves
phase_runner_proc.kill();
l_rs = uvm_report_server::get_server();
l_rs.report_summarize();
if (finish_on_completion)
$finish;
endtask