UVM使用双顶层的用法

在UVM中,我们一般都是使用单顶层的模式。也就是只有一个uvm_test_top顶层,然后下面有env,env下面有agent等。如下图所示:

通过uvm_top.print_topology()函数,可以打印uvm的拓扑结构。比如如下我的一个uvm环境,打印的拓扑结构如下:

如果我有另外一个uvm环境,那么怎么可以简单的,将两个uvm环境给集成到一起,进行整体验证了?

此时,就要用到uvm的双顶层结构。

一、uvm双顶层实现

其实uvm,并没有限定uvm_top下,只能有一个叶子节点,也就是uvm_test,也可以有多个叶子节点,也就是多个uvm_test。只不过,两个字节点的名字,都不能叫做uvm_test_top。

如果两个字节点的名字,是一样的,比如都叫uvm_test_top,那么仿真的时候会报错:

使用2个uvm_test顶层,结构如下:

如上图,有2个uvm_test顶层,一个uvm_test顶层名字叫做uvm_test_top,另外一个uvm_test顶层名字叫做uvm_test_top1。

这样的话,每个uvm_test下面,可以有自己的uvm环境。使用这种方式,就可以很容易的将两个,或者多个uvm环境,给集成到一起。

下面说一下,如何进行集成:

比如有另外一个uvm环境,叫做your uvm,那么首先要将这个uvm环境,封装成一个module的顶层wrapper,这样其他uvm环境使用这个uvm环境的时候,只需要例化这个顶层wrapper到他的环境即可。

如下图所示,将your uvm环境需要的RTL信号,定义在module的端口信号列表中,内部在连接到your uvm环境的interface中。

module test_bfm_top(clk,rstn,opcode);
input clk;
input rstn;
input [8:0] opcode;
 
test_bfm_if test_bfm_intf();
assign test_bfm_intf.clk = clk;
assign test_bfm_intf.rstn = rstn;
assign test_bfm_intf.opcode = opcode; 

下面就是关键的代码了:

initial begin

uvm_config_db#(virtual test_bfm_if)::set(uvm_root::get(),"*","test_bfm_vif",test_bfm_intf);
`ifndef USER_RUN
    run_test("test_bfm_base_test");
`else
    begin
        string testname = "test_bfm_base_test";
        `ifdef UVM_1_1
            uvm_factory fact = uvm_factory::get();
        `else
            uvm_coreservice_t coreservice = uvm_coreservice_t::get();
            uvm_factory fact = coreservice.get_factory();
        `endif            if($value$plusargs("+UVM_YOUR_TESTNAME",testname))
            $display("found your testname: %s" testname);
        else
            $display("use default testname:test_bfm_base_teset");
        fact.create_component_by_name(testname,"","bfm_test_top",uvm_top);                                                                          
        $display("init_test_bfm_teset:Root has %d children",uvm_top.get_num_children());
    end
`endif

end 

首先使用uvm_config_db机制,将interface,传递给your uvm环境中,需要使用的interface的component中去。

增加了USER_RUN这个宏,用来判断,your uvm环境,是单独运行,还是要放到其他的uvm环境中运行。

如果是单独运行,那么直接调用run_test函数,即可启动your uvm环境。

如果是放到其他的uvm环境中运行,那么就不能调用run_test,因为在uvm环境中,run_test不能调用2次。此时,需要自己将your uvm环境中的uvm_test,手动创建一个实例,然后给挂到uvm_top下。

关键的代码就在fact.create_component_by_name这行代码。使用factory的创建component实例机制,根据传入的testname,创建一个命名为bfm_test_top的实例,,并指定父节点为uvm_top。这样创建后,在uvm_top下,就会多一个bfm_test_top这个子节点,这样整个uvm环境,就多了一个uvm_test顶层。

为了让bfm_test_top这个uvm_test顶层,能够通过仿真参数,动态的修改需要跑的testcase,增加了+UVM_YOUR_TESTNAME这个仿真参数,来指定。

在顶层,直接将your uvm环境封装的顶层wrapper例化,然后将模块的端口信号连接一下即可。这样就完成了2个UVM环境的集成。

module tb_top();

reg clk;
reg rstn;
hw_wrapper u_hw_wrapper(
    .clk(clk),
    .rstn(rstn)
);
initial begin
    clk = 1'b0;
    forever #10 clk = ~clk;
end
initial begin
    rstn = 1'n0;
    repeat(100) @(posedge clk);
    rstn = 1'b1;
end
// my uvm test
initial begin
    uvm_config_db#(virtual sync_if)::set(uvm_root::get(),
                                         "*",
                                         "sync_intf",
        tb_top.u_hw_wrapper.sync_intf);
end
// your uvm test
wire [8:0] opcode = u_hw_wrapper.opcode;
test_bfm_top u_test_bfm_top(clk,rstn,opcode);

endmodule 

当然,在编译的时候,要加入+define+USER_RUN这个宏。

通过uvm_top.print_topology()函数,可以打印此时uvm的拓扑结构:

从打印可以看出,此时UVM环境,有2个uvm test顶层,每个顶层下面有自己的uvm环境。

通过以上的方式,就将2个uvm环境,给集成到了一个uvm环境中,并且可以通过仿真参数,+UVM_TESTNAME和+UVM_YOUR_TESTNAME,分别指定2个UVM环境,仿真需要跑的testcase。

二、双顶层的好处

以上只是一个简单的例子,来说明,在怎么将2个uvm环境给集成到一起,那么双顶层有什么好处了?

我认为,好处是可以增强uvm环境的复用性。在模块级别,搭建uvm环境验证,当模块层次上升,到ip级,那么这个uvm环境依然还是可以用。从ip级到top级,这个uvm环境依然可以使用。

下面举个例子来说明下。比如对于一个IP,分成2个模块A和B。数据流程从A到B。

模块A交由验证甲负责,模块B交由验证乙来负责。

在验证开始,验证甲搭建uvm环境,验证模块A。验证乙搭建uvm验证,验证模块B。

当验证甲和验证乙,分别验证完毕后,此时要进行IP级别的验证,那么此时IP集成的验证,就可以使用上面的双顶层方式,将模块A的uvm验证环境和模块B的uvm验证环境给集成到一起,进行IP级的验证,这样就实现了UVM环境的复用。

当然在这种情况下,需要将模块B验证环境中的uvm_driver给去掉,因为输入是来源于模块A,但是monitor以及scoreboar等都是可以复用的。

当IP验证完毕后,需要集成到top进行验证,那top也可以复用这个IP的uvm验证环境。

所以说,双顶层或者多顶层,能够提高uvm环境的复用性。让顶层验证,变得容易。

更多相关阅读

cadence indago征程(四)仿真加速indago database
cadence indago征程(五)最强cpu debug工具-eswd
cadence indago征程(六) eswd工具配置与仿真

原文首发于骏的世界博客
作者:卢骏
更多IC设计相关的文章请关注IC设计极术专栏,每日更新。

发表评论

邮箱地址不会被公开。 必填项已用*标注