uvm_callback讨论
但是callback究竟在uvm环境中究竟有多大的作用?有多少场景可以用到它?
或者说最好是用它而不是用别的手段来达到相同的目的?
对此我持怀疑态度!
在对callback机制的介绍中,使用的最多的两个例子是改变driver的行为,加一些delay,注入一些error,等等;
另外一个例子就是使用它来收集coverage。
就上面这个增强driver功能这个例子,我觉得有一些常规的方法来实现相同的功能,毫无疑问,那就是继承加上factory的overwrite功能。
那些用callback和用继承+factory的优缺点是什么呢?
用继承+factory的优点是最容易想到的也是最直观的;
但是它有一个致命的缺点:会引起组合爆炸(设计模式中的提法)。
举例来说:
class driver ;
task run();
pre_do();
xxx;
mid_do();
xxx;
post_do();
endtask
endclass
其中的pre_do,mid_do,post_do都是callback的方法。这些callback方法一般默认都是是空的。
如果我需要一个修改pre_do的driver,我会写:
classpre_driver extends driver; 然后重新改写pre_do方法;
如果我需要一个修改post_do的driver,我会写:
classpost_driver extends driver; 然后重新改写post_do方法;
........
如果我需要一个修改mid_do和post_do的driver,我会写:
classmid_post_driver extends driver; 然后重新改写mid_do和post_do方法;
........
然后在testcase里使用factory的overwrite用pre_driver, post_driver......替换环境里的正常的driver。
很明显,需要3个callback点的driver,它最多有可能需要7个xxx_driver派生类来应对各种可能的组合。
如果是4个callback点,那将是4+6+4+1=15种派生可能;
如果是5个callback点,那将是5+10+10+4+1=30种派生可能;
........
对于callback较多的情况,用继承+factory overwrite的方式显然是不可取的。
但是对于我们的uvm验证环境来说,有多少情形会出现比较多的callback需求点呢?
至少我目前还没有遇到过,请有此经历的同仁也发表一下你们的看法。
至于uvm中的callback核心机制,本身来说没有什么特别复杂的地方。
要使用uvm callback的功能,说实话我觉得操作起来并不是很爽,步骤也不算少,涉及的宏也不算少。
但是我们可以自己写一个简化版的callback来理解它的基本机制,如下:
typedef class callback ;
/////callbacks类可以看作是一个装类型T的callback的容器
class callbacks#(type T=callback) ;
static local callbacks#(T) _m_inst ; ////对一个类型T,只需要一个容器即可
static local T _next [$] = {}; ////容器的物理载体
local int index ; ////用来遍历容器,在uvm callback中有对应的类似机制,叫做iterator
static function callbacks#(T) get();
if(!_m_inst) _m_inst = new ;
return _m_inst ;
endfunction
local function new();
reset_index() ;
endfunction
/////提供给用户的接口,加入自己希望的callback,在testcase中使用
static function void add(T cb);
_next.push_back(cb);
endfunction
//////这两个方法用来遍历容器中的callback
function T get_next();
return _next[index++] ;
endfunction
function void reset_index();
index = 0 ;
endfunction
endclass
/////这个宏用来简化打算使用callback的class的一些申明
`define callback_registry(T) \
local callbacks#(T) hook = callbacks#(T)::get() ; \
local T each ;
/////这个宏用来简化callback的使用,在uvm中有类似的宏做类似的事情
`define do_callback(T,METHOD) \
each = hook.get_next() ;\
while(each)begin \
each.METHOD ; \
each = hook.get_next() ;\
end \
hook.reset_index();
//////打算使用callback的类
class driver ;
`callback_registry(callback)
function void run();
`do_callback(callback,pre_do)
$display("call driver's run()...");
`do_callback(callback,post_do)
endfunction
endclass
//////用户定义的callback类及其界面
class callback ;
virtual function void pre_do();
endfunction
virtual function void post_do();
endfunction
endclass
/////下面几个真正用来扩展功能的callback派生类
/////注意,有几个callback点就只需要几个派生类,而不再需要各种组合了!
class pre_callback extends callback;
function void pre_do();
$display("call pre_callback's pre_do()...");
endfunction
endclass
class post_callback extends callback;
function void post_do();
$display("call post_callback's post_do()...");
endfunction
endclass
/////testcase类
class testcase ;
driver drv ;
function new();
drv = new ;
endfunction
/////本testcase需要扩展pre_do和post_do
virtual function build();
pre_callback pre_cb = new() ;
post_callback post_cb = new() ;
callbacks#(callback)::add(pre_cb) ;
callbacks#(callback)::add(post_cb) ;
endfunction
function void run();
drv.run();
endfunction
endclass
////两外一个testcase类
class testcase2 extends testcase ;
/////本testcase只需要扩展post_do
virtual function build();
post_callback post_cb= new();
callbacks#(callback)::add(post_cb) ;
endfunction
endclass
个人结论:
callback点的设置需要个人的项目经验、项目需求、项目成熟度来决定,没有特定的公式可以套用;
在不是必须的情况下还是使用继承+factory overwrite比较容易和直观。
在uvm的callback机制中,说起来我觉得算是使用了两个软件工程中的设计模式,bridge模式和iterator模式。
另外,虽然模版技术是一项强大的技术,而C++中的模版技术早已发展成为一个流派;有些标准甚至非常依赖模版技术,比如SystemC。
但是我觉得对模版类的过度使用会使代码变得难以理解。
有幸读了小编关于uvm phase的文章,不错。改天和小编讨论下systemc相关,希望指点下