uvm_event、uvm_event_pool和uvm_event_callback
uvm_barrier、uvm_barrier_pool
uvm_callback
,不但具备普通回调函数可以在函数执行前后调用的特点,还增加了丰富的特性来完成层次化的调用。uvm_event类相比于event:
->
触发之后,会触发使用@
等待该事件的对象,uvm_event通过trigger()
触发,会触发使用wait_trigger()
等待该事件的对象。reset()
方法重置初始状态,再使用trigger()
触发。trigger(T data=null)
的可选参数,将伴随触发的数据对象都写入到该触发事件中,而等待该事件的对象可以通过方法wait_trigger_data(output T data)
来获取事件触发时写入的数据对象。add_callback(uvm_event_callback cb,bit append=1)
函数添加回调函数。get_num_waiters()
来获取等待它的进程数目。uvm_event_pool
(全局资源池)实现,它是uvm_object_string_pool #(T)
的子类,可以生成和获取通过字符串索引的uvm_event对象;环境中的任何组件都可以从这个全局资源池获取共享的对象句柄,这就避免了组件之间的互相依赖。uvm_event应用
class edata extends uvm_object; int data; `uvm_object_utils(edata) ... endclass class ecb extends uvm_event_callback; `uvm_object_utils(ecb) ... function bit pre_trigger(uvm_event e, uvm_object data=null); `uvm_info("EPRETRIG", $sformatf("before trigger event %s", e.get_name()), UVM_LOW) return 0; endfunction function void post_trigger(uvm_event e, uvm_object data=null); `uvm_info("EPOSTTRIG", $sformatf("after trigger event %s", e.get_name()), UVM_LOW) endfunction endclass class comp1 extends uvm_component; uvm_event e1; `uvm_component_utils(comp1) ... function void build_phase(uvm_phase phase); super.build_phase(phase); e1 = uvm_event_pool::get_global("e1"); //notice endfunction task run_phase(uvm_phase phase); edata d = new(); ecb cb = new(); d.data = 100; #10ns; e1.add_callback(cb); e1.trigger(d); `uvm_info("ETRIG", $sformatf("trigger sync event at %t ps", $time), UVM_LOW) endtask endclass class comp2 extends uvm_component; uvm_event e1; `uvm_component_utils(comp2) ... function void build_phase(uvm_phase phase); super.build_phase(phase) e1 = uvm_event_pool::get_global("e1"); //若没有e1会自动创建 endfunction task run_phase(uvm_phase phase); uvm_object tmp; edata d; `uvm_info("ESYNC", $sformatf("wait sync event at %t ps", $time), UVM_LOW) e1.wait_trigger_data(tmp); //等待触发同时等待数据 void'($cast(d, tmp)); //父类转为子类 `uvm_info("ESYNC", $sformatf("get data %0d after sync at %t ps", d.data, $time), UVM_LOW) endtask endclass class env1 extends uvm_env; comp1 c1; comp2 c2; `uvm_component_utils(env1) ... endclass 输出结果: UVM_INFO @0: reporter [RNTST] Running test test1... UVM INFO @0: uvm_test_top.env.c2 [ESYNC] wait sync event at 0 ps UVM INFO @10000: reporter [EPRETRIG] before trigger event e1 UVM INFO @10000: reporter [EPOSTRIG] after trigger event e1 UVM INFO @10000: uvm_test_top.env.c1 [ETRIG] trigger sync event at 10000 ps UVM INFO @10000: uvm_test_top.env.c2 [ESYNC] get data 100 after sync at 10000 ps
uvm_event e1
传递数据edata并调用回调函数类ecb的pre_trigger()
和post_trigger()
方法。uvm_event_pool::get_global("e1")
获取同一个名称的uvm_event对象,即便该对象不存在,uvm_event_pool
资源池也会在第一次调用get_global()
函数时创建这样一个对象以供使用。uvm_event: :trigger(T data =null)
传递数据对象。而在等待uvm_event一侧的组件,则需要通过uvm_event: :wait_trigger_data(output T data)
获取该对象。uvm_event_callback
类,定义uvm_event被trigger前后的调用方法pre_trigger()
和post_trigger()
。pre_trigger()需要有返回值,如果返回值为1则表示uvm_event不会被trigger,也不会再执行post_trigger()方法;如果返回值为0,则会继续trigger该事件对象。wait_ptrigger()
和wait_ptrigger_data()
完成等待。这样即便在调用事件等待方法之前该事件已经被触发,等待方法仍然不会被阻塞并且可以继续执行结束。uvm_event总结
uvm_barrier
对多个组件进行同步协调,同时为了解决组件独立运作的封闭性需要,也定义了新类uvm_barrier_pool
全局管理这些uvm_barrier对象。uvm_barrier_pool
同之前的uvm_event_pool
一样,也是基于通用参数类uvm_object_string_pool
来定义的。typedef uvm_object_string_pool #(uvm_barrier) uvm_barrier_pool; typedef uvm_object_string_pool #(uvm_event #(uvm_object)) uvm_event_pool;
uvm_barrier
可以设置一定的等待阈值(threshold),当有不少于该阈值的进程在等待该对象时才会触发该事件,同时激活所有正在等待的进程,使其可继续进行。uvm_barrier应用
class comp1 extends uvm_component; uvm_barrier b1; `uvm_component_utils(comp1) ... function void build_phase(uvm_phase phase); super.build_phase(phase); b1 = uvm_barrier_pool::get_global("b1"); //类似于uvm_event endfunction task run_phase(uvm_phase phase); #10ns; `uvm_info("BSYNC", $sformatf("c1 wait for b1 at %0t ps", $time), UVM_LOW) b1.wait_for(); //等待阈值满足则立即执行下一行 `uvm_info("BSYNC", $sformatf("c1 is activated at %0t ps", $time), UVM_LOW) endtask endclass class comp2 extends uvm_component; uvm_barrier b1; `uvm_component_utils(comp2) ... function void build_phase(uvm_phase phase); super.build_phase(phase); b1 = uvm_barrier_pool::get_global("b1"); endfunction task run_phase(uvm_phase phase); #20ns; `uvm_info("BSYNC", $sformatf("c2 wait for b1 at %0t ps", $time), UVM_LOW) b1.wait_for(); `uvm_info("BSYNC", $sformatf("c2 is activated at %0t ps", $time), UVM_LOW) endtask endclass class env1 extends uvm_env; comp1 c1; comp2 c2; uvm_barrier b1; `uvm_component_utils(env1) ... function void build_phase(uvm_phase phase); super.build_phase(phase); c1 = comp1::type_id::create("c1", this); c2 = comp2::type_id::create("c2", this); b1 = uvm_barrier_pool::get_global("b1"); endfunction task run_phase(uvm_phase phase); b1.set_threshold(3); //设置等待阈值为3 `uvm_info("BSYNC", $sformatf("env set b1 threshold %d at %0t ps", b1.get_threshold(), $time), UVM_LOW) #50ns; b1.set_threshold(2); `uvm_info("BSYNC", $sformatf("env set b1 threshold %d at %0t ps", b1.get_threshold(), $time), UVM_LOW) endtask endclass 输出结果: UVM_INFO @0: reporter [RNTST] Running test test.. UVM_INFO @0: uvm_test_top.env [BSYNC] env set b1 threshold 3 at 0ps UVM_INFO @ 10000: uvm_test_top.env.c1 [BSYNC] c1 wait for b1 at 10000 ps UVM_INFO @ 20000: uvm_test_top.env.c2 [BSYNC] c2 wait for b1 at 20000 ps UVM_INFO @ 50000: uvm_test_top.env [BSYNC] env set b1 threshold 2 at 50000 ps UVM_INFO @ 50000: uvm_test_top.env.c1 [BSYNC] c1 is activated at 50000 ps UVM_INFO @ 50000: uvm_test_top.env.c2 [BSYNC] c2 is activated at 50000 ps
uvm_barrier b1
。wait_for()
来等待激活,而env1可以设置阈值来调控什么时间来“开阀”。uvm_barrier::set_threshold()
和uvm_barrier::wait_for()
这样的方式,可以实现多个组件之间的同步,同时可以保持各个组件之间的独立性。copy()/do_copy()
print()/do_print()
compare()/do_compare()
pack()/do_pack()
unpack()/do_unpack()
record()/do_record()
do_XXX
是定义为空的。uvm_object::copy()
,那么在函数执行末尾会自动执行uvm_object::do_copy()
;do_copy()是copy()的回调函数,uvm_object会在copy()的执行尾端勾住callback函数即do_copy()。uvm_callback_iter
和uvm_callbacks #(T,CB)
实现。uvm_callback应用
class edata extends uvm_object; int data; `uvm_object_utils(edata) ... endclass class cb1 extends uvm_callback; `uvm_object_utils(cb1) ... virtual function void do_trans(edata d); d.data = 200; `uvm_info("CB", $sformatf("cb1 executed with data %0d", d.data), UVM_LOW) endfunction endclass class cb2 extends cb1; `uvm_object_utils(cb2) ... function void do_trans(edata d); //这里不需要super,因为do_trans相当于override d.data = 300; `uvm_info("CB", $sformatf("cb2 executed with data %0d", d.data), UVM_LOW) endfunction endclass class comp1 extends uvm_component; `uvm_component_utils(comp1) `uvm_register_cb(comp1, cb1) //comp1和cb1进行关联(注册/绑定) ... task run_phase(uvm_phase phase); edata d = new(); d.data = 100; `uvm_info("RUN", $sformatf("proceeding data %0d", d.data), UVM_LOW) `uvm_do_callbacks(comp1, cb1, do_trans(d)) //插入callback endtask endclass class env1 extends uvm_env; comp1 c1; cb1 m_cb1; cb2 m_cb2; `uvm_component_utils(env1) function new(string name, uvm_component parent); super.new(name, parent); m_cb1 = new("m_cb1"); //例化callback m_cb2 = new("m_cb2"); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); c1 = comp1::type_id::create("c1", this); uvm_callbacks #(comp1)::add(c1, m_cb1); //顶层添加callback uvm_callbacks #(comp1)::add(c1, m_cb2); endfunction endclass 输出结果 UVM_INFO @0: reporter [RNTST] Running test test1... UVM_INFO @0: uvm_test_top.erv.c1 [RUN] proceeding data 100 UVM_INFO @0: reporter [CB] cb1 executed with data 200 UVM_INFO @0: reporter [CB] cb2 executed with data 300
uvm_callback总结:
'uvm_register_cb(T,CB)
实现。在用户养成注册的习惯之后,如果以后调用的T与CB不匹配,那么在检查完匹配注册表之后系统会打印warning信息,提示用户使用回调函数的潜在问题。'uvm_callbacks #(T,CB,METHOD)
实现。它最直观的作用在于会循环执行已经与该对象结对的uvm_callback类的方法。此外宏'uvm_do_callbacks_exit_on #(T,CB,METHOD,VAL)
可以进一步控制执行回调函数的层次,回调函数会保持执行直到返回值与给入的VAL值相同才会返回,这一点使得回调方法在执行顺序上面有了更多的可控性。'uvm_do_callbacks
宏还不够,在执行回调方法时,依赖的是已经例化的uvm_callback对象,所以最后一步需要例化uvm_callback对象,上例分别例化了cb1和cb2,通过“结对子”的方式,通过uvm_callbacks #(T,CB)
类的静态方法add()
添加成对的uvm_object对象和uvm_callback对象。uvm_register_cb
以及uvm_do_callbacks
绑定和插入CB对应的方法 - > 在顶层例化组件和CB,add()添加CB。