和工厂类似,回调是一种可以在环境之外来影响或监控验证环境的方法。回调和工厂不同之处在于,开发者必须在类的内部某些特定点处,插入对回调函数/任务的调用。然后,应用者通过继承来创建回调类,并将之连接到一个或者多个所希望的对象上。
4.9.1 使用回调
回调使用模型被分成两部分,开发者部分和应用者部分。
4.9.1.1 开发者
开发者要做的首要事情是决定回调的接口。通常,回调函数需要包含一个指向调用这个回调函数的对象的指针,以及回调用户需要知道的一些额外信息。
就以第46页“层次信息功能”的例子来说,我们可能希望对city类增加一个回调函数,在打印父/子层次信息之前执行。为了达到此目的,我们做如下事情: 1. 2. 3. 4. 5. 6. 7.
typedef class city;
virtual class city_cbs extends uvm_callback; function new(string name=\ super.new(name); endfunction
pure virtual function void pre_info (city c); endclass
接下来,开发者必须注册使用此回调类的回调类型。注册使得UVM在为某个特定类增加回调的时候进行类型检查。如果没有注册,用户将会从UVM得到此回调类型没有被想使用它的对象注册的警告信息。 1. 2. 3. 4.
class city extends uvm_component; `uvm_register_cb(city, city_cbs) ... endclass
注意: 如果使用回调的类的父类也使用了回调,必须使用宏`uvm_set_super_type来让UVM明白类型的层次关系,才能够正确处理回调。
最后,开发者需要在代码中加入对回调函数的调用。使用宏`uvm_do_callbacks可以简化这个过程。开发者也可以选择使用uvm_callback_iter进行手动迭代调用。这样对回调函数的执行提供了很高的灵活性。
1. 2. 3. 4. 5. 6. 7. 8. 9.
task run();
uvm_component child, parent; string pr, ch;
child = get_child(\ parent = get_parent(); pr = parent.get_full_name(); ch = child.get_name();
`uvm_do_callbacks(city, city_cbs, pre_info(this))
`uvm_info(get_type_name(), $psprintf(\UVM_LOW)
10. endtask : run
为了方便起见,开发者应该创建回调类的类型别名(typedef). typedef uvm_callbacks#(city, city_cbs) city_cb;
4.9.1.2 应用者
现在,回调类型已经定义并使用,用户可以在需要的时候进行回调。对于终端中户,首要事情是扩展回调类,使其具有所需要的功能。 1. 2. 3. 4. 5. 6. 7. 8.
class my_city_cb extends city_cbs; function new(string name=\ super.new(name); endfunction
virtual function void pre_info(city c);
`uvm_info(\c.get_full_name()), UVM_LOW) endfunction endclass
下一步,用户需要将此回调类装载到某个特定的对象实例或者某类型的所有对象实例上。空句柄(null)参数表示是类型的所有实例。 1. 2. 3. 4.
my_city_cb cb = new; initial begin
city_cb::add(null, cb); run_test();
5. end
此段代码和前面例子运行结果如下:
UVM_INFO @ 0: reporter [my_city_cb] pre_info called for city New_York.capital_city UVM_INFO @ 0: New_York.capital_city [city] Parent:New_York Child:Main_St UVM_INFO @ 0: New_York.capital_city.Main_St [INFO1] I vacationed here in 2010 UVM_INFO @ 0: reporter [my_city_cb] pre_info called for city Florida.capital_city UVM_INFO @ 0: Florida.capital_city [city] Parent:Florida Child:Main_St UVM_INFO @ 0: Florida.capital_city.Main_St [INFO1] I vacationed here in 2010
4.9.2 回调使用规则
4.9.2.1 开发者规则
? ? ?
为回调创建一个纯的抽象类,以纯抽象函数作为接口. 回调函数以对象句柄作为参数.
将数据对象句柄作为参数传递的时候要特别注意,因为他们可以被改变(这或许是正想要的,那就没问题)【译者注:比如估计给某个封包增加错误来验证设计的容错性】
? ?
用typedef定义一个uvm_callbacks#(T,CB)类型别名T_cb. 如果父类也使用回调,请使用宏`uvm_set_super_type.
4.9.2.2 应用者规则
? ? ?
创建扩展的回调类来具体实现回调类中的每一个纯虚函数。 每增加一个回调,需要分配一个新的回调对象。
如果希望回调影响所有类型的实例,使用类型范畴的回调(使用null参数);若果仅仅只想对某个特殊的对象实例进行回调,使用实例范畴的回调。
注意: 实例范畴的回调将使得此对实例的句柄存储在一个队列中,直到所有的对象都被删除之后。因此当将实例范畴的回调连接到一个瞬态数据对象的时候,要格外小心,因为这个数据对象将不会被垃圾收集器回收,知道所有的引用都被删掉为止。对瞬态对象进行回调,如果能够满足要求,最好使用类型范畴的回调。
4.9.3 内建的消息捕捉回调
UVM拥有一些内建的回调类。其中一个是消息捕捉器。消息捕捉器回调是在消息被输出前进行调用。在这个回调用,可以捕捉一些消息让之不在进一步处理;也可以修改消息类型(降低错误级别等等),甚至修改消息内容。
下面是一个实例,演示如何使用消息捕捉器将特定类型的错误(error)级别的消息降低为信息(info)级别。这种情况有可能是某种测试希望产生特定的错误条件。 1. 2. 3. 4. 5. 6. 7. 8. 9.
module test; import uvm_pkg::*; `include \
class my_catcher extends uvm_report_catcher; virtual function action_e catch();
if(get_severity() == UVM_ERROR && get_id() == \ set_severity(UVM_INFO); end
return THROW;
10. endfunction 11. endclass
12. my_catcher catcher = new; 13. initial begin
14. uvm_report_cb::add(null,catcher);
15. `uvm_error(\16. catcher.callback_mode(0); //disable the catcher 17. `uvm_error(\18. end 19. endmodule
此例子将得到如下输出结果:
UVM_INFO @ 0: reporter [MYID] This one should be demoted UVM_ERROR @ 0: reporter [MYID] This one should not be demoted
总结
本章介绍了UVM库的几个关键特点。由于这些特点功能强且具有灵活性,使得高级验证得以实现,加速了验证过程,下一章节将介绍开发设计界面协议的可重用验证组件的UVM方法。