路科验证的个人空间 https://blog.eetop.cn/1561828 [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

UVM世界观之八:phase机制(下)

已有 7863 次阅读| 2017-6-12 00:23 |个人分类:验证系统思想|系统分类:芯片设计


如何开始UVM仿真
如果只是从UVM的应用角度来看,要在仿真开始时建立验证环境,那么用户可以考虑选择下面几种方式:
  • 可以通过全局函数(由uvm_pkg提供)run_test()来选择性地指定要运行哪一个uvm_test。这里的test类均继承于uvm_test。这样的话,指定的test类将被例化并指定为顶层的组件。一般而言,run_test()函数可以在合适的module中initial进程块中调用。
  • 如果没有任何参数传递给run_test(),那么用户可以在仿真时通过传递参数+UVM_TESTNAME=<test_name>,来指定仿真时调用的uvm_test。当然,即便run_test()函数在调用时已经有test传递进去,在仿真时的+UVM_TESTNAME=<test_name>也可以从顶层覆盖底层的指定。这种方式使得在仿真开始时,不需要通过再次修改run_test()调用的test名字和重复编译,而可以灵活选定test。

无论上面哪一种方式,都必须有顶层调用全局函数run_test(),用户可以考虑不传递test名字作为参数,而在仿真时通过传递参数+UVM_TESTNAME=<test_name>实现。全局函数run_test()的重要性,正是从uvm_root创建了一个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

uvm-1.2/base/uvm_globals.svh

这里需要先来了解UVM的顶层类uvm_root。该类也继承与uvm_component,说明它必然是UVM环境结构中的一员,而他可以作为顶层结构类,它提供了一些像run_test这种方法,来充当了UVM世界中的核心角色。在uvm_pkg中,有且只有一个顶层类uvm_root例化的对象,即uvm_top。这就同“道生一,一生二,二生三,三生万物”的古语一般。在UVM的世界中,“道”就是uvm_pkg,“一”就是uvm_top,而后来的“万物”就是uvm_top下例化的uvm_test及其更多的子组件。

uvm_top充当的主要核心任务包括:
  • 作为隐形的UVM世界顶层,任何其它的组件都在它之下,通过创建组件时指定parent来构成层次。如果parent设定为null,那么它将作为uvm_top的子组件。
  • phase控制。控制所有组件的phase顺序。
  • 索引功能。通过层次名称来索引组件实例。
  • 报告配置。通过uvm_top来全局配置报告的繁简度(verbosity)。
  • 全局报告设备。由于uvm_top全局可以访问,因此UVM的报告配置在组件内部和组件外部(例如module和seequence)都可以访问。

通过uvm_top调用方法run_test(test_name),uvm_top做了如下的初始化:
  • 得到正确的test_name。
  • 初始化objection机制。
  • 创建uvm_test_top实例。
  • 调用phase控制方法,安排所有组件的phase方法执行顺序。
  • 等待所有phase执行结束,关闭phase控制进程。
  • 报告总结和结束仿真。

下面从源代码中精简的例码,对照着上面对uvm_root::run_test()初始化的内容:
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; // 存储forked线程用于最终的进程清理

testname_plusarg = 0;

uvm_objection::m_init_objections();

... // 获得 test_name

// 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();
...
// 创建 uvm_test_top
$cast(uvm_test_top, factory.create_component_by_name(test_name,
"", "uvm_test_top", null));
...
end

...
// phase控制方法,按phase顺序执行所有组件的phase
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
// 等待组件的phase执行完毕
wait (m_phase_all_done == 1);
// 关闭phase控制线程
phase_runner_proc.kill();

// 报告总结
l_rs = uvm_report_server::get_server();
l_rs.report_summarize();

// 结束仿真
if (finish_on_completion)
$finish;
endtask

uvm-1.2/base/uvm_root.svh


如何结束UVM仿真
UVM的仿真开始相对于仿真结束要容易一些,毕竟只需要在仿真时传递test名字,就可以在仿真开始时创建对应的test顶层实例。而对于结束仿真来说,要理解和合理地利用UVM结束机制就显得困难一些了。在之前介绍的所有phase当中,只有run phase(对应12个run-time phase)是task,何时可以结束仿真,实际上同run phase何时结束直接相关。

从UVM-1.1开始已经将仿真结束机制做了大量的简化,为了做出一个仿真结束机制的比对,我们将UVM-1.0和UVM-1.1之后的run phase执行机制分别介绍,以便读者可以了解和比较这两种不同的机制版本。

UVM-1.0版本的结束机制
我们将run phase执行分为两个阶段和三种执行的线程。这两个阶段是活跃期(active stage)和停止中断期(stop-interrupt stage),如下图所示:

从这一版本的结束机制可以看到:
  • 默认情况下,如果没有objection反停止标记挂起的,所有的run_phase任务在执行时,会直接放入到fork-join_none进程当中。因此run_phase()中的任务会在后台执行,但不会阻止run_phase()结束,进入下一个phase。因此,objection机制是控制仿真退出run phase的一种办法。
  • 另外,如果在任何的组件中,有设置enable_stop_interrupt比特,那么要退出run_phase(),除过需要考虑之前的objection机制,用户还可以在run_phase()中调用global_stop_request()。调用global_stop_request()后,run_phase()会立即结束活跃期,进入停止中断期,继而执行用户自定义的stop_phase()任务。但是,如果在调用global_stop_request()时,已经有组件挂起了objection反停止标记,那么global_stop_request()的停止活跃期的要求会被忽略,结束机制只会遵循objection机制。


UVM-1.1版本之后的结束机制
上面的UVM-1.0主要两种控制run_phase()的结束方式,同OVM的结束机制是一致的。而到了UVM-1.1版本以后,结束机制得到了更多的简化。上面的两种结束方式,global_stop_request()的结束方式已经被废除,也就是说run phase不再拥有停止中断期,只依靠objection机制来结束仿真。

uvm_objection类提供了一种供所有component和sequence共享的计数器。如果有组件来挂起objection,那么它还应该记得落下objection。参与到objection机制中的参与组件,可以独立的各自挂起objection,来防止run phase退出,但是只有这些组件都落下objection后,uvm_objection共享的counter才会变为0,这意味run phase推出的条件满足,因此可以退出run phase。

对于uvm_objection类,用来反停止的控制方法包括:
  • raise_objection ( uvm_object bj = null, string description = "" , int count = 1) 挂起objection
  • drop_objection ( uvm_object bj = null, string description = "" , int count = 1) 落下objection
  • set_drain_time ( uvm_object bj = null, time drain) 设置退出时间

对这几种典型方法,在实际应用中的建议包括:
  • 对于component()而言,用户可以在run_phase()中使用phase.raise_objection()/phase.drop_objection()来控制run phase退出。
  • 用户最好提供对于description字符串参数用来描述,这有利于后期的调试。
  • 应该使用默认count值。
  • 对于uvm_top或者uvm_test_top应该尽可能少使用set_drain_time()。

在简化了run phase退出机制之后,用户为了让使得run phase在全部激励完毕、DUT数据全部发送完毕以及所有数据比较结束之后才退出,需要懂得在何时挂起和落下objection。下面是一段典型的objection机制应用代码:

class test1 extends uvm_test;
`uvm_component_utils(test1)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("run_phase", "entered ..", UVM_LOW)
#1us;
`uvm_info("run_phase", "exited ..", UVM_LOW)
phase.drop_objection(this);
endtask
endclass

initial begin
run_test("test1");
end
endmodule

输出结果:
UVM_INFO @ 0: reporter [RNTST] Running test test1...
UVM_INFO @ 0: uvm_test_top [run_phase] entered ..
UVM_INFO @ 1000000: uvm_test_top [run_phase] exited ..
UVM_INFO @ 1000000: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO @ 1000000: reporter [UVM/REPORT/CATCHER]

从输出结果来看,uvm_pkg::uvm_test_done实例会在test1的run_phase()执行完毕之后,才会退出run phase。这得益于test1::run_phase()中在仿真一开始就挂起了objection。在执行完毕之后,才落下了objection。这时,uvm_pkg::uvm_test_done认为run phase已经可以退出,进而转向了下一个extract phase。直到退出所有的phase之后,UVM进入了报告总结阶段。

那么如果没有挂起objection,UVM仿真会怎么样呢?
module objection_application;
import uvm_pkg::*;
`include "uvm_macros.svh"

class test1 extends uvm_test;
`uvm_component_utils(test1)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
`uvm_info("run_phase", "entered ..", UVM_LOW)
#1us;
`uvm_info("run_phase", "exited ..", UVM_LOW)
endtask
endclass

initial begin
run_test("test1");
end
endmodule

输出结果:
UVM_INFO @ 0: reporter [RNTST] Running test test1...
UVM_INFO @ 0: uvm_test_top [run_phase] entered ..
UVM_INFO @ 0: reporter [UVM/REPORT/CATCHER]

这个简单的例码说明,如果在整个test及其子组件和sequence中,都没有通过objection()机制来控制run phase退出,那么所有组件的run phase都会通过fork-join_none线程提交之后,立即转入到extract phase。所以objection机制的应用是必不可少的。那么,在什么时间点应该挂起objection呢?再来看下面这段例码:

module objection_application;
import uvm_pkg::*;
`include "uvm_macros.svh"

class test1 extends uvm_test;
`uvm_component_utils(test1)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
#1ps;
phase.raise_objection(this);
`uvm_info("run_phase", "entered ..", UVM_LOW)
#1us;
`uvm_info("run_phase", "exited ..", UVM_LOW)
phase.drop_objection(this);
endtask
endclass

initial begin
run_test("test1");
end
endmodule

输出结果:
UVM_INFO @ 0: reporter [RNTST] Running test test1...
UVM_INFO @ 0: reporter [UVM/REPORT/CATCHER]

这段代码中,看起来挂起objection()已经晚了,因为run phase还是立即退出了。这是因为在挂起objection之前还需要运行1ps,而处于fork-join_none后的run_phase任务在0时刻被调用后,run phase退出机制在0时刻发现没有挂起的objection,因此终止所有的run_phase()任务,继而转入了extract phase。所以,如果要在component中挂起objection,建议在一进入run_phase()后就挂起,保证objection counter及时被增加;另外,需要用户习惯在sequence中挂起objection,由于sequence不是uvm_component类,而是uvm_object类,因此它只有body()方法,而没有run_phase()方法。所以,在sequence中使用objection机制,可以在body()中的首尾部分分别调用uvm_test_done.raise_objection()和uvm_test_done.drop_objection()。有的用户习惯在pre_body()中调用uvm_test_done.raise_objection(),在post_body()中调用uvm_test_done.drop_objection(),这么做在多数情况下可以起到objection的防退出机制,但是一些情况下,sequence的pre_body()和post_body()并不会调用,所以objection机制也没有起到作用。因此,我们建议在sequence body()任务中raise/drop objection。

对于有OVM经验的用户,在使用objection机制时,他们会通过uvm_test_done.objection()/uvm_test_done.drop_objection()来控制run phase退出。这与phase.raise_objection()/phase.drop_objection()的作用是一致的,背后都是通过uvm_pkg的全局变量uvm_test_done(全局唯一的uvm_test_done_objection类的实例)来控制的。关于uvm_objection与uvm_test_done_objection类的关系,可以通过下面UML类图得到:

至此,phase机制和run phase的进入、退出机制已为读者们介绍到这里,希望读者们可以更好地理解phase机制,掌握run phase的进入、退出方法。
下一节课中,我们将为大家介绍UVM的另外一大魅力《config机制》。

谢谢你对路科验证的关注,也欢迎你分享和转发真正的技术价值,你的支持是我们保持前行的动力。

点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 253

    粉丝
  • 25

    好友
  • 33

    获赞
  • 45

    评论
  • 访问数
关闭

站长推荐 上一条 /1 下一条

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-3-29 19:29 , Processed in 0.024425 second(s), 19 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部