2.10 objections
前言
本文以uvm-1.2/examples/simple/objections为例,通过代码了解UVM中objections机制的一些用法。通过这个例子可以基本了解以下知识点:
- 什么是objections机制
- 如何使用objections机制
- set_drain_time的使用
一、基本介绍
objection翻译过来就是反对、异议的意思,在UVM中,通过举手(raise_objection)和放手(drop_objection)的方式来控制仿真平台的运行。
在张强《UVM实战》5.2节中,对UVM的objections机制有一个比较详细的介绍。其中有个例子较为形象有趣:将UVM的树形结构的每个componet组件,比作一层房子的每个房间,每一层的房间布局一样,将每一层比作一种类型的phase,一棵叫UVM的植物负责依次检查每层(phase)的各个房间(componet)是否有僵尸(是否raise_objection),直到所有的僵尸消失(drop_objection)。这个类比的例子说明几点问题:首先,由于每层的布局都一样,竖着来看这个房子,可以发现每个房间(componet)的层数(phase)都一样,这说明每个UVM的组件都拥有相同的phase数量;其次,任意一层的任意一个房间都有可能存在僵尸,这说明任意一个组件的任意一个phase都可以raise_objection,最后,发现了僵尸就一定要将其消灭,这说明raise_objection和drop_objection要成对存在。
这个例子设置了drain time为10ns,之后通过fork同时启动了4个并行的进程,当最后一个进程在50ns结束时,drain time起效,整个仿真在60ns结束。同时,这个例子还设置了drop_objection的回调函数,每执行一次drop_objection结束一个进程,该回调函数就会被调用一次。
二、代码分析
这个简单的测试用例只有simple.sv一个文件。
2.1 simple.sv
`timescale 1ns/1ns
module test;
// This simple example shows how the uvm_test_done objection is
// used to coordinate end of test activity. For an example of
// using end of test coordination in the context of a full
// environment, refer to the xbus example.
// In this example, a drain time of 10 is set on the component.
// The component then forks 4 processes which consume different
// amounts of time. When the last process is done (time 50),
// the drain time takes effect. The test is completed at time
// 60.
// The example also shows usage of the component objection
// callbacks. In this case, the dropped callback is used, but
// the raised and all_dropped work similarly (except the
// all dropped is a time-consuming task).
import uvm_pkg::*;
`include "uvm_macros.svh"
class simple_test extends uvm_test;
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new
// Register with the factory.
`uvm_component_utils(simple_test)
virtual task run_phase(uvm_phase phase);
// Set a drain time on the objection if needed
uvm_report_info("drain", "Setting drain time of 10", UVM_NONE);
uvm_test_done.set_drain_time(this,10);
// Run a bunch of processes in parallel
fork
doit(35);
doit(25);
doit(50);
doit(15);
join
endtask
// A simple task that consumes some time.
task doit (time delay);
static int s_inst = 0; int inst = s_inst++;
//Raise an objection before starting the activity
uvm_test_done.raise_objection(this);
uvm_report_info("doit", $sformatf("Starting doit (%0d) with delay %0t",
inst, delay), UVM_NONE);
#delay;
uvm_report_info("doit", $sformatf("Ending doit (%0d)", inst), UVM_NONE);
//Drop the objection when done
uvm_test_done.drop_objection(this);
endtask
// Use an objection callback do something when objections are raised or
// dropped (or all dropped). This example prints some information on each
// drop.
virtual function void dropped (uvm_objection objection,
uvm_object source_obj, string description, int count);
uvm_report_info("dropped",
$sformatf("%d objection(s) dropped from %s, total count is now %0d",
count, source_obj.get_full_name, objection.get_objection_total(this)),
UVM_NONE);
endfunction
endclass : simple_test
// Run the test
initial
run_test("simple_test");
endmodule
第1行指定了仿真的时间单位和时间精度,斜杠前面是时间单位,斜杠后面是时间精度。
第24到73行定义了一个simple_test的类。
25到27行是这个类的构造函数;
30行将这个类注册到UVM的factory机制中;
32到44行定义了run_phase,其中,35行设置了drain_time=10,38到43行利用fork,同时调用了doit这个task,启用了四个并行的进程,但传递的参数不一样。
47到60行定义了doit这个task,在这个task中,48行利用静态变量的唯一性,保存doit被调用的次数,51行和59行构成了一对“举手”和“放手”,这中间执行的是消耗时间的仿真,并打印了相关信息。
65到71行定义了drop_objection的回调函数dropped,也就是没执行一次drop_objection,这个dropped函数就自动执行一次。
77行启动并执行仿真。
2.2 仿真结果
UVM_INFO @ 0: reporter [RNTST] Running test simple_test...
UVM_INFO @ 0: uvm_test_top [drain] Setting drain time of 10
UVM_INFO @ 0: uvm_test_top [doit] Starting doit (0) with delay 35
UVM_INFO @ 0: uvm_test_top [doit] Starting doit (1) with delay 25
UVM_INFO @ 0: uvm_test_top [doit] Starting doit (2) with delay 50
UVM_INFO @ 0: uvm_test_top [doit] Starting doit (3) with delay 15
UVM_INFO @ 15: uvm_test_top [doit] Ending doit (3)
UVM_INFO @ 15: uvm_test_top [dropped] 1 objection(s) dropped from uvm_test_top, total count is now 3
UVM_INFO @ 25: uvm_test_top [doit] Ending doit (1)
UVM_INFO @ 25: uvm_test_top [dropped] 1 objection(s) dropped from uvm_test_top, total count is now 2
UVM_INFO @ 35: uvm_test_top [doit] Ending doit (0)
UVM_INFO @ 35: uvm_test_top [dropped] 1 objection(s) dropped from uvm_test_top, total count is now 1
UVM_INFO @ 50: uvm_test_top [doit] Ending doit (2)
UVM_INFO @ 50: uvm_test_top [dropped] 1 objection(s) dropped from uvm_test_top, total count is now 0
UVM_INFO ../../../src/base/uvm_objection.svh(1270) @ 60: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO ../../../src/base/uvm_report_server.svh(847) @ 60: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
** Report counts by severity
UVM_INFO : 16
UVM_WARNING : 0
UVM_ERROR : 0
UVM_FATAL : 0
** Report counts by id
[RNTST] 1
[TEST_DONE] 1
[UVM/RELNOTES] 1
[doit] 8
[drain] 1
[dropped] 4
$finish called from file "../../../src/base/uvm_root.svh", line 517.
$finish at simulation time 60
V C S S i m u l a t i o n R e p o r t
Time: 60 ns
CPU Time: 2.940 seconds; Data structure size: 0.3Mb
从仿真结果来看,在0ns时刻,利用fork同时调用了四个doit进程。由于进程3传递的delay=15,所以在15ns时刻,进程3首先结束,并调用drop_objection的回调函数dropped;其次分别在25ns、35ns和50ns处,进程1、0、2依次分别结束;最后由于drain_time=10ns,所以,仿真在60ns处结束。
总结
通过这个测试用例,可以进一步理解UVM中运行的objection机制,同时了解set_drain_time()方法的使用,该方法在实际验证环境中往往很有用,可以有效避免激励还没有完成,仿真就结束的问题。