在linux系统中,使用bsub命令可以向lsf提交作业。使用GNU 的make工具能够比较容易构建工程,整个工程的编译只需要一个命令就可以完成编译、连接以至于最后的执行。不过这需要我们投入一些时间去完成一个或者多个称之为Makefile 文件的编写。
Makefile 文件描述了整个工程的编译、连接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译、需要创建哪些库文件以及如何创建这些库文件、如何最后产生我们想要的可执行文件。如可以设置仿真工具,仿真分支是rtl分支或asic分支等,仿真变量,是否启动回归、覆盖率收集,打印信息冗余度,是否带波形等。是用xrun还是vcs跑。尽管看起来可能是很复杂的事情,但是为工程编写Makefile 的好处是能够使用一行命令来完成“自动化编译”,一旦提供一个(通常对于一个工程来说会是多个)正确的 Makefile,编译整个工程你所要做的事就是在shell 提示符下输入make命令。整个工程完全自动编译,极大提高了效率。Makefile在绝大多数的IDE 开发环境中都在使用,已经成为一种工程的编译方法。make是一个命令工具,它解释Makefile 中的指令。
仿真文件,如先指向rtl.v文件,再指向验证中的filelist.f文件,通常是tb_top,再指向testcase文件。这样就先编译rtl文件,再编译tb_top,再编译testcase。
在tb_top文件中会例化duv,intf及include connect文件,通过run_test启动testcase。
testcase派生自testbase,在testbase中include top文件,top文件则include了uvm库、vip、reg_model、agent.h、refm、scb、env等各个文件。
top文件是包含各个文件的top文件。
在编译时,涉及到不同的文件用到其它文件的变量,因此在top文件中include时需要注意include顺序,用到A文件中变量的B文件,B要放在A之后。
`ifndef TESTBASE
`define TESTBASE
`include top.sv
`ifndef TOP
`define TOP
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "define.sv"
`include "refm.sv"
`include "scb.sv"
`include "input_agent.svh"
`include "output_agent.svh"
`include "env.sv"
`endif
例化DUV时,其模块名duv_top为验证模块顶层,为rtl.f包含的top.v文件声明的模块。声明intf后,在connect文件中可直接使用intf接口和宏DUV_TOP_HDLPATH将激励信号传递给DUV或把DUV信号通过intf采集。
`ifndef DEMO_TB_TOP
`define DEMO_TB_TOP
`define TB_TOP_HDLPATH demo_tb_top
`define DUV_TOP_HDLPATH demo_tb_top.duv
module demo_tb_top();
`include "intf.sv"
duv_top duv();
include "connect.sv"
initial
begin
$timeformat(-9,1,"ns",10);
run_test();
end
endmodule
`endif
DUV例化有两种方式,第一种是通过命名端口连接,在例化模块中的端口不要求按照例化模块的端口排列顺序一一对应。即.A/.B/.C/.D的书写顺序可以更改,括号的对应关系要保持一致。top为模块名,top_a为例化名,一个模块可以例化多个对象。.A .B .C .D为模块端口信号,括号中的可以直接赋值或连接其它信号,来自其它模块的端口信号的话,就实现外部模块端口和内部模块端口信号的连接,或者生成的某个中间信号。另一种连接是位置对应例化或顺序对应例化,要求严格按照模块定义的接口顺序连接,不用标明原模块定义时规定的端口名。
module top(
A,
B,
C,
D
);
input A;
input B;
input C;
output D;
endmodule
top top_a(
.A(1'b0),
.B(b),
.C(c),
.D(d)
);
initial块中的$timeformat四个参数,第一个表示系统时间精度,-9表示ns,-12则表示ps;第二个表示打印时间值时精确的小数点个数;第三个表示时间值后面打印的一个字符串;第四个则表示时间值和字符串合起来的最小长度,不足补空格。
UVM提供UVM_TESTNAME,从命令行中寻找测试用例的名字,创建它的实例并运行。可以在命令中提供符合格式的种子号及仿真分支等进行仿真。在验证平台tb_top中有run_test()命令。在uvm中提供了不加参数的run_test()的支持,通过在输入仿真指令时指定跑哪个用例,无论传递给run_test的参数是什么,创建的实例的名字都为uvm_test_top。
此时UVM树的顶层是uvm_test_top(case)。通过run_test启动验证平台,执行uvm_test_top(case)的build_phase,在其中例化env,seq_cfg、reg_cfg等类、设置cfg类中参数值等,代码继续执行env的build_phase,在其中例化各个agent、refm、scb、vseqr、regmodel等组件,再执行agent的build_phase,例化drv、seqr及mon等。依次执行build_phase,build_phase执行顺序深度优先,形成完整的UVM树。执行完build_phase则开始顺序执行UVM树各节点的connect_phase,怎么判断执行完了各个节点的build_phase?build_phase是function_phase,不消耗仿真时间,有空间上的执行顺序(代码执行顺序、cpu耗时),但都是同一仿真时刻执行完成。因此不需要通过objection机制进行仿真控制,UVM内部在build_phase时有下级component则继续执行,直到执行完毕建立所有component节点后自动进入下一个phase,connect_phase。connect_phase也是function_phase,不消耗仿真时间,其执行顺序是空间上的从下到上,及先在agent完成drv和seqr、drv和mon的连接,在env完成agent.mon和refm、refm和scb的连接。之后执行end_of_elaboration和start_of_simulation phase,这两个phase和connect_phase一样,代码执行顺序是空间上的从下到上。
之后就到了task_phase,消耗仿真时间的phase。由于要消耗仿真时间,UVM通过objection机制来控制仿真,在进入每个task_phase时,UVM会检测是否有objection被提起phase.raise_objection,有的话的等待objection被撤销phase.drop_objection,进入下一个task_phase,否则立即进入下一个task_phase。
首先是reset的pre_reset_phase,reset_phase主要模拟DUV的复位初始化操作,之后进入configure_phase,configure_phase主要模拟DUV的配置类工作,如寄存器值写入(耗时)。
之后进入main_phase,若使用到vseqr和vseq,一般在testbase里使用uvm_config_db#(uvm_object_wrapper)::set("this","env.vseqr.main_phase","default_seq",seq::type_id::get())将vseq设置到vseqr的main_phase,即在vseqr的main_phase会执行vseq,设置完后不需在seqr中get也不需要手动启动vir_seq(seq.start)。由于有多个seq和seqr,在vir_seq中使用uvm_do_on宏来指定哪个seq对应哪个seqr,并启动seq。也可手动启动seq.start(p_seqr.seqr0)。component中的phase.raise_objection和drop_objection转移到seq的pre_body和post_body来控制。shutdown_phase主要完成DUV的断电操作。之后进入extract_phase、check_phase、report_phase、final_phase。待所有phase执行完毕,结束仿真。
UVM的component、phase和objection是UVM平台运行的基础。
本文章参考《UVM实战》、《芯片验证漫游指南》等资料整理而成,仅作学习心得交流,如果涉及侵权烦请请告知,我将第一时间处理。