what
自定义事件,重点是何时如何触发,而不关心事件本身。
why
常用于时序同步,
在uvm中也用于cover group的事务捕捉。
how
时序同步很好理解,相当于时序电路中的使能信号。
怎样捕捉事务呢?
uvm_event i2c_field_access_bd_e;
uvm_event i2c_field_access_fd_e;
virtual function void write_apb_master(uvm_reg r);
if(r != null)
i2c_field_access_fd_e.trigger(r);
endfunction
virtual task do_sample_reg();
uvm_object tmp;
uvm_reg r;
fork
i2c_field_access_fd_e.wait_trigger_data(tmp);
i2c_field_access_bd_e.wait_trigger_data(tmp);
join_any
...
endtask
在上述代码中,定义了两个event,在函数中调用了trigger(),① trigger()先 -> event,②然后会注册trigger_data = r。任务中调用了wait_trigger_data,wait_trigger_data包含③wait_trigger、④tmp = trigger_data。顺序为③①②④。在do_sample_reg中捕捉到了write_apb_master发来的信号。
为什么要这么麻烦
可能有人会问,直接拉一根线过去不就好了。
其实是为了uvm的方便维护,在验证后期框架已经搭好,我们不希望再改组件。所以实际代码应该长这样:
virtual function void write_apb_master(lvc_apb_transfer tr);
uvm_reg r;
if(tr.trans_status == lvc_apb_pkg::ERROR) return;
r = cfg.rgm.default_map.get_reg_by_offset(tr.addr);
rkv_i2c_field_access_fd_e.trigger(r);
endfunction: write_apb_master
virtual task do_sample_reg();
uvm_object tmp;
uvm_reg r;
fork
forever begin
wait(enable);
fork
rkv_i2c_field_access_fd_e.wait_trigger_data(tmp);
rkv_i2c_field_access_bd_e.wait_trigger_data(tmp);
join_any
disable fork;
.................
write_apb_master()做了一个拆包,do_sample_reg()做了一个隐性的指针传递。