apb_watchdog验证模块搭建(三、事件触发器与计数检查器)文章目录
前言
本章我们继续完成watchdog的验证工作,在验证环境中添加事件触发器与计数检查器。
一、事件触发器
事件触发器subscriber是指在环境中定义的事件触发模块,无论是scoreboard检查器还是覆盖率收集都需要基于一些事件的触发来进行功能比较与覆盖率收集。因此比较方便的做法是定义一个subscriber来完成事件的定义与一些事件的触发,而scoreboard与coverage模块继承于此父类,从而可以使用其中的事件与方法。
subscriber的代码如下:
`ifndef RKV_WATCHDOG_SUBSCRIBER_SV
`define RKV_WATCHDOG_SUBSCRIBER_SV
`uvm_analysis_imp_decl(_apb)
class rkv_watchdog_subscriber extends uvm_component;
rkv_watchdog_config cfg;
rkv_watchdog_rgm rgm;
virtual rkv_watchdog_if vif;
local uvm_event_pool _ep;
uvm_event wdg_inten_e; //interrupt enable event
uvm_event wdg_resen_e; //restet enable event
uvm_event wdg_load_e; //load access event
uvm_event wdg_reg_fd_e; //register access event(frontdoor)
uvm_event wdg_reg_bd_e; //
uvm_event wdg_assert_inrt_e; //interrupt asserted event
uvm_event wdg_int_clr_e; //interrupt clear event
uvm_event wdg_intdis_e; //interrupt disable event
uvm_event wdg_assert_res_e; //reset asserted event
bit enable;
uvm_analysis_imp_apb #(apb_transfer, rkv_watchdog_subscriber) apb_trans_observed_imp;
`uvm_component_utils(rkv_watchdog_subscriber)
function new (string name = "rkv_watchdog_subscriber", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
apb_trans_observed_imp = new("apb_trans_observed_imp",this);
if(!uvm_config_db#(rkv_watchdog_config)::get(this, "", "cfg",cfg)) begin
`uvm_fatal("GTCFG","cannot get cfg from configdb")
end
if(!uvm_config_db#(rkv_watchdog_rgm)::get(this, "", "rgm",rgm)) begin
`uvm_fatal("GTCFG","cannot get rgm from confgdb")
end
_ep = new("_ep");
wdg_reg_fd_e = _ep.get("wdg_reg_fd_e");
wdg_reg_bd_e = _ep.get("wdg_reg_bd_e");
wdg_load_e = _ep.get("wdg_load_e");
wdg_inten_e = _ep.get("wdg_inten_e");
wdg_resen_e = _ep.get("wdg_resen_e");
wdg_assert_inrt_e = _ep.get("wdg_assert_inrt_e");
wdg_int_clr_e = _ep.get("wdg_int_clr_e");
wdg_intdis_e = _ep.get("wdg_intdis_e");
wdg_assert_res_e = _ep.get("wdg_assert_res_e");
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
vif = cfg.vif;
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
do_events_check();
endtask
virtual function write_apb(apb_transfer t);
uvm_reg r;
r = rgm.map.get_reg_by_offset(t.addr);
wdg_reg_fd_e.trigger(r);
endfunction
virtual task do_events_check();
uvm_object tmp;
uvm_reg r;
fork
begin
forever begin
fork
wdg_reg_fd_e.wait_trigger_data(tmp);
wdg_reg_fd_e.wait_trigger_data(tmp);
join_any
disable fork;
void'($cast(r, tmp));
#1ps; // get the updated value from predictor
if(r.get_name() == "WDOGCONTROL") begin
if(rgm.WDOGCONTROL.INTEN.get() == 'b1) wdg_inten_e.trigger();
if(rgm.WDOGCONTROL.RESEN.get() == 'b0) wdg_resen_e.trigger();
if(rgm.WDOGCONTROL.INTEN.get() == 'b0) wdg_intdis_e.trigger();
end
else if(r.get_name() == "WDOGLOAD") begin
if(rgm.WDOGLOAD.LOADVAL.get() != 'b0 ) wdg_load_e.trigger();
end
else if(r.get_name() == "WDOGINTCLR") begin
if(rgm.WDOGINTCLR.INTCLR.get() == 'b1 ) wdg_int_clr_e.trigger();
end
end
end
join_none
endtask
endclass
`endif // RKV_WATCHDOG_subscriber_SV
subscriber中除了定义所需的interface,register model与config之外。提供了大量的事件,这些事件的触发可用于后面对scoreboard的行为进行影响(如计时开始,结束与重载等)。
同样的,为了实现事件的触发,subscriber必须从monitor中得到当前的寄存器访问信号。与之前寄存器配置中predictor的访问方式相同,apb vip中提供的analysis tlm port:item_collected_port能够帮助完成信号的访问监测。对于写入端(imp),需要完成write函数以实现访问,即上述代码中的write_apb()。在每次寄存器访问时会触发,并将访问的地址传递至触发等待端。触发等待端并得到地址之后,通过地址的值判断访问的事件进行相应的事件触发。
二、scoreboard
scoreboard继承于subscriber,能够完成计数检查,并在不同事件触发时开始,结束或者中断计数的触发。
scoreboard代码如下:
`ifndef RKV_WATCHDOG_SCOREBOARD_SV
`define RKV_WATCHDOG_SCOREBOARD_SV
class rkv_watchdog_scoreboard extends rkv_watchdog_subscriber;
int cur_load; //load value from load reg
int cur_count; //count value which will decrease when counting
bit inten_status = 0; //value of inten in reg
bit load_set = 0; //value of load in watchdog
bit inrt_asserted = 0; //means watchdog int has asserted
bit res_asserted = 0; //means watchdog res has asserted
bit res_able_check = 0; //reset check enable
`uvm_component_utils(rkv_watchdog_scoreboard)
function new (string name = "rkv_watchdog_scoreboard", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
do_countdown_check();
endtask
virtual task do_countdown_check();
fork
forever begin: wait_inten
wdg_inten_e.wait_trigger();
inten_status = 1;
disable load_count_check;
end
forever begin: wait_resen
wdg_resen_e.wait_trigger();
res_able_check = 1;
end
forever begin: wait_int_disable
wdg_intdis_e.wait_trigger();
inten_status = 0;
disable load_count_check;
end
forever begin: wait_load_count_val
wdg_load_e.wait_trigger();
load_set = 1;
disable load_count_check;
end
forever begin: wait_int_clr
wdg_int_clr_e.wait_trigger();
inrt_asserted = 0;
disable load_count_check;
end
forever begin
begin: load_count_check
do_localcount();
end
end
join_none
endtask
virtual function bit do_count_able_check();
if( inten_status && load_set && cfg.enable_scb ) return 1;
else return 0;
endfunction
virtual task do_localcount();
bit count_check;
count_check = do_count_able_check();
@(posedge vif.wdg_clk iff count_check );
cur_load = rgm.WDOGLOAD.LOADVAL.get();
cur_count = cur_load;
do begin
@(posedge vif.wdg_clk);
cur_count--;
end while( cur_count != 0 );
repeat(2) @(negedge vif.wdg_clk); //in load and clear check need one more clock to reload
res_able_check = rgm.WDOGCONTROL.RESEN.get();
if( !inrt_asserted && !res_asserted ) begin
wdg_assert_inrt_e.trigger();
if( vif.wdogint !=1 ) begin
cfg.scb_error_count++;
`uvm_error("CNTDWN", "wdogint is not asserted!!")
end
inrt_asserted = 1;
cfg.scb_check_count++;
end
else if( inrt_asserted && !res_asserted && res_able_check ) begin
if( !vif.wdogres) begin
cfg.scb_error_count++;
`uvm_error("CNTDWN","wdogres is not asserted!!!")
end
wdg_assert_res_e.trigger();
res_asserted = 1;
cfg.scb_check_count++;
end
endtask
endclass
`endif //
在scoreboard中对计数进行检查与事件触发监测。这里对do_localcount的逻辑功能进行简单介绍,在满足检查条件(检查器打开,inten为高,watchdog完成计数值加载)时会进行计数检查,计数检查完成之后,若中断信号未触发且watchdog复位信号未触发时,会进行中断触发检查。若中断已经触发且watchdog复位信号也未触发且watchdog复位使能为高时,会进行复位触发检查。
do_countdown_check的逻辑是同时不停地进行不同事件的触发等待以及do_localcount计数等待工作,不同的事件会将计数状态改变,比如中断清除事件会将inrt_asserted = 0;使得中断状态处于未触发状态,并重新开始计数检查。每一个forever begin中就包含了一次会对计数造成影响的寄存器访问行为。
这样,除去上锁功能,其他功能下的计数检查器就已经完成了。
仿真运行结果
运行不同的test,并在计数完成的检查判断中设置断点可得到如下仿真结果:
并且在report中也完成了scoreboard的检查。
可知scoreboard的计数检查是比较准确的。
总结
本章完成了watchdog的自动化计数检查,在不同事件的触发下,scoreboard会像watchdog一样对计数行为进行改变从而实现自动化的计数检查,下一章我们将完成watchdog的验证工作,即完成覆盖率的收集。