文章结构:
- 通过一种标准化的方式打印信息
- 过滤(重要级别)信息
- 打印通道
在之前的介绍中,读者从四位verifier的验证组件实现中懂得了通过类和封装和数据随机化来实现stimulator、monitor和checker,而在这三个组件中间的信息输出也是无时不在的。伴随着验证组件的增多(横向的)和验证层次的加深(纵向的),验证环境对于信息报告的要求也趋于规范。为什么会有这样的要求呢?读者可以试想一下这几种场景:
- 伴随着组件的增多,任何的组件都有可能打印出来信息,当然同一组件的不同例化对象也会打印出来信息。那么,我们需要区分的是,这些打印出来的信息从什么组件、什么对象中打印出来,即这是关于标准化的信息打印方法,打印出来的信息需要包含“出处”和“内容”。
- 在一开始验证环境需要调试时,除了直接设置断点进行单步调试激励的产生、协议的握手之外,更直观的方式就是插入一些“琐碎”的打印信息来告诉verifier代码执行过的痕迹。而到了后期,一旦stimulator的激励稳定之后,这些原来用于调试的打印信息则变得不那么重要了,甚至有一点“多余”,这时候,我们反而需要关闭这些繁琐的信息,只需要保留一些重要的信息。那么从一个例子来看,不同信息之间需要有一个重要的区分,即信息的重要级别(severity level)。
- 信息之间的区别除了重要级别之外,另外一个常见的就是信息种类,这会包括有信息类型(info)、警告类型(warning)、错误类型(error)以及致命类型(fatal)。针对这些不同的种类,我们可以设置相应的处理方式,除了info类型一般只用来打印之外,warning类型和error类型可以额外设置为让仿真停止,而fatal类型则应该让仿真结束。
- 信息除了可以打印到仿真窗口之外,还可以打印到文本里。这使得验证的信息可以同其它仿真信息最直接的区别开来,便于信息的浏览和查找。所以,关于报告信息的打印通道是也是报告规范化的一项要求。
我们接下来,就上面关于验证环境报告标准化的要求推出一个简短易用的库来封装这些报告要求,并且可以通过在调用这些报告方法时,配置好信息的出处、种类和重要级别,同时也可以针对不同种类信息作出不同的处理方式。
信息报告库的例码
下面这个包report_pkg是一个简易的报告标准化库,除了定义跟信息类型和重要级别相关的枚举类型之外,就提供了一个封装好的报告信息函数rpt_msg()。这个函数的几个参数分别是,报告信息来源string s,报告信息内容string i,报告类型report r,信息重要级别severity_t s以及采取的动作action_t a。在调用该函数时,除了需要显式传递信息来源和信息内容之外,剩余的三个参数可以使用缺省值,即默认信息种类是INFO,重要级别是LOW,采取的行动是打印信息到屏幕和文本中。另外还提供了一个擦鞋报告文本的小函数clean_log()。在这个report_pkg中,变量severity_t svrt和string logname被定义为静态变量,是为了以后可以更好地控制过滤信息的重要级别,和信息报告的文件名称。
package report_pkg;
typedef enum {INFO, WARNING, ERROR, FATAL} report_t;
typedef enum {LOW, MEDIUM, HIGH, TOP} severity_t;
typedef enum {LOG, STOP, EXIT} action_t;
static severity_t svrt = LOW;
static string logname = "report.log";
function void rpt_msg(string src, string i, report_t r=INFO, severity_t s=LOW, action_t a=LOG);
integer logf;
string msg;
if(s >= svrt) begin
msg = $sformatf("@%0t [%s] %s : %s", $time, r, src, i);
logf = $fopen(logname, "a+");
$display(msg);
$fwrite(logf, $sformatf("%s\n", msg));
$fclose(logf);
if(a == STOP) begin
$stop();
end
else if(a == EXIT) begin
$finish();
end
end
endfunction
function void clean_log();
integer logf;
logf = $fopen(logname, "w");
$fclose(logf);
endfunction
endpackage
实际使用场景
在拥有了一个信息标准化的包之外,我们模拟了一个简单的层次化验证场景来说明有了这样一个信息标准化库之后带来的好处。下面的代码是一个模拟化的stimulator、monitor、checker、environment和tb。这些代码的共同地方在于它们都调用了report_pkg包中的报告方法rpt_msg。
class rpt_stm; // stimulator定义
string id;
function new(string name = "rpt_stm");
id = name;
rpt_msg(id, "build phase");
endfunction
task run();
int i=1;
rpt_msg(id, "run phase");
forever begin
#100;
rpt_msg(id, $sformatf("NO.%0d trans generated!",i));
i++;
end
endtask
endclass
class rpt_mon; // monitor定义
string id;
function new(string name = "rpt_mon");
id = name;
rpt_msg(id, "build phase");
endfunction
task run();
int i=1;
rpt_msg(id, "run phase");
#30;
forever begin
#80;
rpt_msg(id, $sformatf("NO.%0d input trans monitored!",i));
#20;
rpt_msg(id, $sformatf("NO.%0d ouput trans monitored!",i));
i++;
end
endtask
endclass
class rpt_chk; // checker定义
string id;
function new(string name = "rpt_chk");
id = name;
rpt_msg(id, "build phase");
endfunction
task run();
int i=1;
bit cmp;
rpt_msg(id, "run phase");
#40;
forever begin
#100;
std::randomize(cmp) with {cmp dist {1 :=3, 0:= 1};};
if(cmp)
rpt_msg(id, $sformatf("NO.%0d trans was compared with success",i), , HIGH);
else
rpt_msg(id, $sformatf("NO.%0d trans was compared with failure",i), ERROR, HIGH, STOP);
i++;
end
endtask
endclass
class rpt_env; // environment定义
string id;
rpt_stm stm;
rpt_mon mon;
rpt_chk chk;
function new(string name = "rpt_env");
id = name;
rpt_msg(id, "build phase");
stm = new("stm");
mon = new("mon");
chk = new("chk");
endfunction
task run();
rpt_msg(id, "run phase");
fork
stm.run();
mon.run();
chk.run();
join_none
endtask
endclass
module rpt_tb; // testbench定义
rpt_env env;
initial begin: build
rpt_msg("tb", "build phase");
env = new("env");
clean_log();
end
initial begin: run
#0;
rpt_msg("tb", "run phase");
env.run();
end
endmodule
这个testbench的结构也很简单,即在testbench中例化了一个软件顶层环境组件rpt_env,而在该顶层环境rpt_env中又进一步例化了三个必要的组件rpt_stm、rpt_mon和rpt_chk。上面这段模拟验证环境的代码中,每个组件都使用了标准化报告函数rpt_msg,因此在仿真开始后,打印出的结果如下:
# @0 [INFO] tb : build phase
# @0 [INFO] env : build phase
# @0 [INFO] stm : build phase
# @0 [INFO] mon : build phase
# @0 [INFO] chk : build phase
# @0 [INFO] tb : run phase
# @0 [INFO] env : run phase
# @0 [INFO] stm : run phase
# @0 [INFO] mon : run phase
# @0 [INFO] chk : run phase
# @100 [INFO] stm : NO.1 trans generated!
# @110 [INFO] mon : NO.1 input trans monitored!
# @130 [INFO] mon : NO.1 ouput trans monitored!
# @140 [INFO] chk : NO.1 trans was compared with success
# @200 [INFO] stm : NO.2 trans generated!
# @210 [INFO] mon : NO.2 input trans monitored!
# @230 [INFO] mon : NO.2 ouput trans monitored!
# @240 [INFO] chk : NO.2 trans was compared with success
# @300 [INFO] stm : NO.3 trans generated!
# @310 [INFO] mon : NO.3 input trans monitored!
# @330 [INFO] mon : NO.3 ouput trans monitored!
# @340 [INFO] chk : NO.3 trans was compared with success
# @400 [INFO] stm : NO.4 trans generated!
# @410 [INFO] mon : NO.4 input trans monitored!
# @430 [INFO] mon : NO.4 ouput trans monitored!
# @440 [ERROR] chk : NO.4 trans was compared with failure
从上面的报告结果可以看出:
- 在仿真一开始,软件环境(验证部分)的建立(build)是自顶向下的,而运行(run)也是自顶向下的。
- 在特定的时间点,所有组件都可以通过rpt_msg()函数打印出来包含信息源和信息内容的报告。
- 在可能出现错误的语句分支,可以打印出错误信息,并且告知仿真器停止。例如rpt_chk在比较发生错误之后,可以打印出ERROR类型的信息,并且让仿真暂停。
上面的输出结果是在仿真器的窗口中输出的,同时这些信息也一并打印到报告日志report.log中了。从输出结果的最后一行显示中可以发现,rpt_chk比较数据发生错误,因此报告了ERROR信息,同时让仿真停止,以便于verifier可以在发生错误的时间点附近调试数据比较的错误原因。
正如本文一开始谈到的,通过这个精简的规范报告包report_pkg,可以满足日常报告的基本需求即:
- 信息包括报告出处和内容。
- 具备信息类型。
- 具备信息重要级别。
- 有可控的仿真器行为和打印通道。
我们可以再进一步考虑实际中关于信息重要级别的一个应用,即伴随着环境的稳定性,我们只需要一些重要的信息,而舍弃一些关于验证结构搭建、运行顺序和细节的报告。那么,我们可以在顶层rpt_tb中的build阶段做出简单配置就可以对报告的内容做出过滤:
module rpt_tb;
rpt_env env;
initial begin: build
report_pkg::svrt = HIGH; // 修改可以打印的最低信息重要级别
report_pkg::logname = "test.log"; // 设置打印的报告日志名称
rpt_msg("tb", "build phase");
env = new("env");
clean_log();
end
initial begin: run
#0;
rpt_msg("tb", "run phase");
env.run();
end
endmodule
在rpt_tb的build阶段,我们首先修改report_pkg的两个静态变量svrt和logname,这样可以过滤一些低级别的信息,同时也可以重定向报告日志的输出文件。于是,在仿真器和新的报告日志test.log中,只出现了在HIGH级别以上的重要信息:
# @140 [INFO] chk : NO.1 trans was compared with success
# @240 [INFO] chk : NO.2 trans was compared with success
# @340 [INFO] chk : NO.3 trans was compared with success
# @440 [ERROR] chk : NO.4 trans was compared with failure
通过上面的精简库report_pkg,用户可以小窥到让报告标准化的魅力,而伴随着方法学的发展和验证环境的结构性和复杂性,UVM方法学自身提供的报告机制会更加充分(体量也更庞大一点)。同时,通过这个例子可以得到另外的提示,即在简单的SV验证组件实现过程中,我们可以考虑将日常用到的公共方法加以标准化,做成一些可以共享的公共库,在公司内部甚至验证行业大的生态中形成开源的公共库,这也是软件世界中常见的快速构建上层结构、避免“重复制造轮子”的有益做法。
到这里,关于《SV组件实现篇》的部分就介绍完了,希望通过这一章,读者们可以抓中SV的几项主要特性:类、随机约束和进程通信。关于SV的其它主要特性:功能覆盖率和断言,会在独立的章节中加以详述。到了下一章节,我们可以看到之前主要在独立作业的verifier们:梅、尤、娄和董要开始相互协作了,因为他们将要把自己的验证环境同其他人的进行适当的集成和最大化的复用,在FCDM的子系统级别展开环境集成和验证。
所以,请期待《SV系统集成篇》吧!
谢谢你对路科验证的关注,也欢迎你分享和转发真正的技术价值,你的支持是我们保持前行的动力。