:::info 分析测试点:
- WDOGCONTROL寄存器:
- INT使能和关闭;
- reset使能和关闭;
:::对寄存器的各种状态进行了配置以后,covergroup什么时候做采样,做的采样是否准确十分重要。
对寄存器进行测试的时候covergroup不应该做采样,只有再做功能测试的时候才应该做采样。
8.1 reset、enable & reload test覆盖率收集
8.1.1 subscriber一侧代码
`ifndef APB_WATCHDOG_SUBSCRIBER_SV`define APB_WATCHDOG_SUBSCRIBER_SV`uvm_analysis_imp_decl(_apb)class apb_watchdog_subscriber extends uvm_component;// analysis importuvm_analysis_imp_apb #(apb_transfer, apb_watchdog_subscriber) apb_trans_observed_imp;// Declare eventsuvm_event wdg_regacc_fd_e;uvm_event wdg_regacc_bd_e;uvm_event wdg_reg_inten_e;uvm_event wdg_reg_resen_e;uvm_event wdg_reg_load_e;uvm_event wdg_reg_intr_assert_e;uvm_event wdg_intrclr_e;protected uvm_event_pool _ep;apb_watchdog_config cfg;apb_watchdog_rgm rgm;virtual apb_watchdog_if vif;bit enable;`uvm_component_utils(apb_watchdog_subscriber)function new(string name = "apb_watchdog_subscriber", uvm_component parent);super.new(name, parent);endfunctionfunction void build_phase(uvm_phase phase);super.build_phase(phase);apb_trans_observed_imp = new("apb_trans_observed_imp", this);// Get configuration from test layerif(!uvm_config_db#(apb_watchdog_config)::get(this, "", "cfg", cfg)) begin`uvm_fatal("GETCFG", "cannot get conifg object from config db")endvif = cfg.vif;rgm = cfg.rgm;// Local event pool and events creation_ep = new ("_ep");wdg_regacc_fd_e = _ep.get("wdg_regacc_fd_e");wdg_regacc_bd_e = _ep.get("wdg_regacc_bd_e");wdg_reg_inten_e = _ep.get("wdg_reg_inten_e");wdg_reg_resen_e = _ep.get("wdg_reg_resen_e");wdg_reg_load_e = _ep.get("wdg_reg_load_e");wdg_reg_intr_assert_e = _ep.get("wdg_reg_intr_assert_e");wdg_intrclr_e = _ep.get("wdg_intrclr_e");endfunctionfunction void end_of_elaboration_phase(uvm_phase phase);super.end_of_elaboration_phase(phase);endfunctiontask run_phase(uvm_phase phase);super.run_phase(phase);do_events_trigger();do_listen_events();endtaskvirtual function void write_apb(apb_transfer tr);uvm_reg r;r = rgm.map.get_reg_by_offset(tr.addr);wdg_regacc_fd_e.trigger(r);endfunctionvirtual task do_events_trigger();uvm_object tmp;uvm_reg r;forkforever beginforkwdg_regacc_fd_e.wait_trigger_data(tmp);wdg_regacc_bd_e.wait_trigger_data(tmp);join_anydisable fork;void'($cast(r, tmp));#1ps;if(r.get_name() == "WDOGCONTROL") beginif(rgm.WDOGCONTROL.INTEN.get() == 1'b1) wdg_reg_inten_e.trigger();if(rgm.WDOGCONTROL.RESEN.get() == 1'b1) wdg_reg_resen_e.trigger();endelse if(r.get_name() == "WDOGLOAD") beginif(rgm.WDOGLOAD.LOADVAL.get() != 0) wdg_reg_load_e.trigger();endelse if(r.get_name() == "WDOGINTCLR") beginif(rgm.WDOGINTCLR.INTCLR.get() == 1) wdg_intrclr_e.trigger();endendjoin_noneendtaskvirtual task do_listen_events();endtaskendclass`endif // APB_WATCHDOG_subscriber_SV
cov和scoreboard都继承于subscriber,subscriber的作用就是声明事件,触发,监听事件; 因此可以将监听事件在subscriber中声明,子类只需要继承即可。
8.1.2 cov一侧代码
`ifndef APB_WATCHDOG_COV_SV`define APB_WATCHDOG_COV_SVclass apb_watchdog_cov extends apb_watchdog_subscriber;`uvm_component_utils(apb_watchdog_cov)// Covergroup definition below// T1 Watchdog overall control// T1.1 Interrupt enable & disable(0 -> 1 & 1 -> 0);// T1.2 Reset enable & disable( 0-> 1 & 1 -> 0);covergroup apb_watchdog_t1_overall_control_cg with function sample (bit [31:0] val, string field);option.name = "T1 Watchdog overall control";INTEN : coverpoint val iff (field == "INTEN") {bins to_enable = (1'b0 => 1'b1);bins to_disale = (1'b1 => 1'b0);}endgroup// T2 Watchdog load & reload value// T2.1 Initial load (counter 0 -> load value)// T2.2 Load value to reload value (laod value -> reload value)// T2.3 load value range(min value, max value and others)covergroup apb_watchdog_t2_value_reload_value with function sample (bit [31:0] val);option.name = "T2 Wathcdog load & reload value";INLOAD : coverpoint val {bins iniload = (0 => [1:32'hFFFFFFFF]);}RELOAD : coverpoint val {bins reload = ([1:32'hFFFFFFFF] => [1:32'hFFFFFFFF]);}LOADRANGE : coverpoint val {bins min = {32'h1};bins max = {32'hFFFFFFFF};bins others = default;}endgroupfunction new (string name = "apb_watchdog_cov", uvm_component parent);super.new(name, parent);apb_watchdog_t1_overall_control_cg = new();apb_watchdog_t2_value_reload_value = new();endfunctionfunction void build_phase(uvm_phase phase);super.build_phase(phase);endfunctiontask do_listen_events();uvm_object tmp;uvm_reg r;forkforever beginwait(cfg.cov_enable);wdg_regacc_e.wait_trigger_data(tmp);void'($cast(r, tmp));if(r.get_name() == "WDOGCONTROL") beginapb_watchdog_t1_overall_control_cg.sample(rgm.WDOGCONTROL.INTEN.get(), "INTEN");apb_watchdog_t1_overall_control_cg.sample(rgm.WDOGCONTROL.RESEN.get(), "RESEN");endelse if(r.get_name() == "WDOGLOAD") beginapb_watchdog_t2_value_reload_value.sample(rgm.WDOGLOAD.LOADVAL.get());endendjoin_noneendtaskendclass`
8.1.3 更新 Makefile
【调试及结果】
:::info
- Terminal运行
:::
运行完测试以后点击Terminatemake elab COV=1make run COV=1 GUI=1 TESTNAME=apb_watchdog_countdown_test seed=100

可以看到,运行完测试以后的所有数据都在out目录下的db中
:::info
- 查看覆盖率(DVE自带)
:::
make mergecovmake dvecov

搜索apb_watchdog_cov可以查看到我们定义的功能覆盖率,并没有采样到数据,结果发现采样的是一个从0到非0的一个状态变化,但是0这个状态没有采样下来,因此必须在采样的时候添加status。
:::info
- 查看功能覆盖率(Verdi模式)
:::
make verdicov

在Verdi中查看发现inten和load值的变化这个事件没有采到,只有采到初始的值以后才能采到发生变化的这个事件,因此需要对cov进行以下修改:
`ifndef APB_WATCHDOG_COV_SV`define APB_WATCHDOG_COV_SVclass apb_watchdog_cov extends apb_watchdog_subscriber;`uvm_component_utils(apb_watchdog_cov)// Covergroup definition below// T1 Watchdog overall control// T1.1 Interrupt enable & disable(0 -> 1 & 1 -> 0);// T1.2 Reset enable & disable( 0-> 1 & 1 -> 0);covergroup apb_watchdog_t1_overall_control_cg with function sample (bit [31:0] val, string field);option.name = "T1 Watchdog overall control";INTEN : coverpoint val iff (field == "INTEN") {bins stat_en = {1'b1};bins stat_dis = {1'b0};bins to_enable = (1'b0 => 1'b1);bins to_disale = (1'b1 => 1'b0);}endgroup// T2 Watchdog load & reload value// T2.1 Initial load (counter 0 -> load value)// T2.2 Load value to reload value (laod value -> reload value)// T2.3 load value range(min value, max value and others)covergroup apb_watchdog_t2_value_reload_value with function sample (bit [31:0] val);option.name = "T2 Wathcdog load & reload value";INLOAD : coverpoint val {bins iniload = (0 => [1:32'hFFFFFFFF]);}RELOAD : coverpoint val {bins reload = ([1:32'hFFFFFFFF] => [1:32'hFFFFFFFF]);}LOADRANGE : coverpoint val {bins min = {32'h1};bins max = {32'hFFFFFFFF};bins others = default;}endgroupfunction new (string name = "apb_watchdog_cov", uvm_component parent);super.new(name, parent);apb_watchdog_t1_overall_control_cg = new();apb_watchdog_t2_value_reload_value = new();endfunctionfunction void build_phase(uvm_phase phase);super.build_phase(phase);endfunctiontask do_listen_events();uvm_object tmp;uvm_reg r;forkforever beginwait(cfg.cov_enable);wdg_regacc_e.wait_trigger_data(tmp);void'($cast(r, tmp));if(r.get_name() == "WDOGCONTROL") beginapb_watchdog_t1_overall_control_cg.sample(rgm.WDOGCONTROL.INTEN.get(), "INTEN");apb_watchdog_t1_overall_control_cg.sample(rgm.WDOGCONTROL.RESEN.get(), "RESEN");endelse if(r.get_name() == "WDOGLOAD") beginapb_watchdog_t2_value_reload_value.sample(rgm.WDOGLOAD.LOADVAL.get());endendjoin_noneendtaskendclass`endif
在采样的时候对状态初始状态也进行采样
:::danger 【思考】
- 之前在事件触发器中,为了保证采样的稳定,添加了1ps以保证每次trigger出来的都是寄存器模型更新以后的值;
所以cov采样的时候采样到的是寄存器模型更新以后的值, :::
``verilogifndef APB_WATCHDOG_REG_ENABLE_INTR_SV `define APB_WATCHDOG_REG_ENABLE_INTR_SVclass apb_watchdog_reg_enable_intr extends apb_watchdog_element_base_seq;
`uvm_object_utils(apb_watchdog_reg_enable_intr)
function new(string name = “apb_watchdog_reg_enable_intr”); super.new(name); endfunction
task body(); super.body();
uvm_info("body", "Entered...", UVM_LOW) rgm.WDOGCONTROL.mirror(status); rgm.WDOGCONTROL.INTEN.set(1'b1); rgm.WDOGCONTROL.update(status);uvm_info(“body”, “Exiting…”, UVM_LOW) endtaskendclass
`endif //APB_WATCHDOG_REG_ENABLE_INTR
> 当寄存器模型拿到通过mirror更新的值以后,还未进行采样就被set成1,因此采样到的值就是1,但是此时期望采到的是初始的0状态。> 时刻是115001ps:::info解决方法:1. 修改RGM的adapter,但是当对同一个寄存器进行操作的时候不建议这么做,因为会影响其他功能;1. 在interface中添加delta cycle;1. 在两个seq中分别等待这个delta cycle:::```veriloginterface apb_watchdog_if;logic [3:0] ecorevnum = 4'b1011;logic wdog_int;logic wdog_res;logic apb_clk;logic apb_rstn;logic wdg_clk;logic wdg_rstn;task wait_apb(int n);repeat(n) @(posedge apb_clk);endtasktask wait_wdg(int n);repeat(n) @(posedge wdg_clk);endtaskendinterface
`ifndef APB_WATCHDOG_REG_ENABLE_INTR_SV`define APB_WATCHDOG_REG_ENABLE_INTR_SVclass apb_watchdog_reg_enable_intr extends apb_watchdog_element_base_seq;`uvm_object_utils(apb_watchdog_reg_enable_intr)function new(string name = "apb_watchdog_reg_enable_intr");super.new(name);endfunctiontask body();super.body();`uvm_info("body", "Entered...", UVM_LOW)rgm.WDOGCONTROL.mirror(status);vif.wait_apb(1);rgm.WDOGCONTROL.INTEN.set(1'b1);rgm.WDOGCONTROL.update(status);`uvm_info("body", "Exiting...", UVM_LOW)endtaskendclass`endif //APB_WATCHDOG_REG_ENABLE_INTR
`ifndef APB_WATCHDOG_REG_LOADCOUNT_SV`define APB_WATCHDOG_REG_LOADCOUNT_SVclass apb_watchdog_reg_loadcount extends apb_watchdog_element_base_seq;rand bit[31:0] load_val;constraint load_cstr{soft load_val inside {['h1:'hFFFF]};}`uvm_object_utils(apb_watchdog_reg_loadcount)function new(string name = "apb_watchdog_reg_loadcount");super.new(name);endfunctiontask body();super.body();`uvm_info("body", "Entered...", UVM_LOW)rgm.WDOGLOAD.mirror(status);vif.wait_apb(1);rgm.WDOGLOAD.write(status, load_val);`uvm_info("body", "Exiting...", UVM_LOW)endtaskendclass`endif //APB_WATCHDOG_REG_LOADCOUNT
分析: subscriber在拿到apb_transfer,1ps以后将事件trigger出来;采样发生在1ps的位置; 而seq中是在1ps+delta cycle的时间以后再对enable或load的值进行改变。


通过sample采样的方式只能采样到静态的值,对于动态变化的值没有办法进行有效采样
8.1.4 采用event来进行动态值的采样
`ifndef APB_WATCHDOG_COV_SV`define APB_WATCHDOG_COV_SVclass apb_watchdog_cov extends apb_watchdog_subscriber;bit [31:0] reg_field_value;event regacc_sve;`uvm_component_utils(apb_watchdog_cov)// Covergroup definition below// T1 Watchdog overall control// T1.1 Interrupt enable & disable(0 -> 1 & 1 -> 0);// T1.2 Reset enable & disable( 0-> 1 & 1 -> 0);covergroup apb_watchdog_t1_overall_control_cg with function sample (bit [31:0] val, string field);option.name = "T1 Watchdog overall control";INTEN : coverpoint val iff (field == "INTEN") {bins stat_en = {1'b1};bins stat_dis = {1'b0};bins to_enable = (1'b0 => 1'b1);bins to_disale = (1'b1 => 1'b0);}RESEN : coverpoint val iff (field == "RESEN") {bins stat_en = {1'b1};bins stat_dis = {1'b0};bins to_enable = (1'b0 => 1'b1);bins to_disable= (1'b1 => 1'b0);}endgroupcovergroup apb_watchdog_t1_overall_control_clk_cg (ref bit [31:0] val) @(regacc_sve);option.name = "T1 Watchdog overall control";INTEN : coverpoint val[0]{bins stat_en = {1'b1};bins stat_dis = {1'b0};bins to_enable = (1'b0 => 1'b1);bins to_disale = (1'b1 => 1'b0);}RESEN : coverpoint val[1]{bins stat_en = {1'b1};bins stat_dis = {1'b0};bins to_enable = (1'b0 => 1'b1);bins to_disable= (1'b1 => 1'b0);}endgroup// T2 Watchdog load & reload value// T2.1 Initial load (counter 0 -> load value)// T2.2 Load value to reload value (laod value -> reload value)// T2.3 load value range(min value, max value and others)...function new (string name = "apb_watchdog_cov", uvm_component parent);super.new(name, parent);apb_watchdog_t1_overall_control_cg = new();apb_watchdog_t2_value_reload_value = new();apb_watchdog_t1_overall_control_clk_cg = new(this.reg_field_value);endfunctionfunction void build_phase(uvm_phase phase);super.build_phase(phase);endfunctiontask do_listen_events();uvm_object tmp;uvm_reg r;forkforever beginwait(cfg.cov_enable);wdg_regacc_e.wait_trigger_data(tmp);void'($cast(r, tmp));if(r.get_name() == "WDOGCONTROL") beginapb_watchdog_t1_overall_control_cg.sample(rgm.WDOGCONTROL.INTEN.get(), "INTEN");apb_watchdog_t1_overall_control_cg.sample(rgm.WDOGCONTROL.RESEN.get(), "RESEN");reg_field_value = rgm.WDOGCONTROL.get();->regacc_sve;endelse if(r.get_name() == "WDOGLOAD") beginapb_watchdog_t2_value_reload_value.sample(rgm.WDOGLOAD.LOADVAL.get());endendjoin_noneendtaskendclass`endif // APB_WATCHDOG_COV_SV
【调试及结果】

可以发现,对于状态的变化可以正常通过事件采到,注意要从寄存器模型中拿到值以后再进行采样。
8.2 集成测试覆盖率收集
在之前的APB寄存器直接访问中,对四个只读寄存器进行了读操作,这一部分cov的收集已经在RGM中完成,此处重点关注watchdog的功能覆盖率:
`ifndef APB_WATCHDOG_COV_SV`define APB_WATCHDOG_COV_SVclass apb_watchdog_cov extends apb_watchdog_subscriber;bit [31:0] reg_field_value;event regacc_sve;`uvm_component_utils(apb_watchdog_cov)...// T3 Watchdog integration// T3.1 Initial int & res value;// T3.2 Int & res value after intergration mode enablecovergroup apb_watchdog_t3_itcr_test_mode with function sample (bit [31:0] val, string field);option.name = "T3 Watchdog integration test";INTR_DISABLE : coverpoint val iff (field == "INTR_DISABLE"){bins inival = {1'b0};}INTR_ENABLE : coverpoint val iff (field == "INTR_ENABLE"){bins newval = {1'b1};}endgroupfunction new (string name = "apb_watchdog_cov", uvm_component parent);super.new(name, parent);apb_watchdog_t1_overall_control_cg = new();apb_watchdog_t2_value_reload_value = new();apb_watchdog_t1_overall_control_clk_cg = new(this.reg_field_value);apb_watchdog_t3_itcr_test_mode = new();endfunctionfunction void build_phase(uvm_phase phase);super.build_phase(phase);endfunctiontask do_listen_events();uvm_object tmp;uvm_reg r;forkforever beginwait(cfg.cov_enable);wdg_regacc_e.wait_trigger_data(tmp);void'($cast(r, tmp));...else if(r.get_name() == "WDOGITCR") beginif(rgm.WDOGITCR.ITME.get() == 1'b0) beginapb_watchdog_t3_itcr_test_mode.sample(rgm.WDOGITCR.ITME.get(), "INTR_DISABLE");apb_watchdog_t3_itcr_test_mode.sample(vif.wdog_int, "INTR_DISABLE");apb_watchdog_t3_itcr_test_mode.sample(vif.wdog_res, "INTR_DISABLE");endendelse if(r.get_name() == "WDOGITOP") beginapb_watchdog_t3_itcr_test_mode.sample(rgm.WDOGITCR.ITME.get(), "INTR_ENABLE");apb_watchdog_t3_itcr_test_mode.sample(vif.wdog_int, "INTR_ENABLE");apb_watchdog_t3_itcr_test_mode.sample(vif.wdog_res, "INTR_ENABLE");endendjoin_noneendtaskendclass`endif // APB_WATCHDOG_COV_SV
8.3 中断使能及使能禁止测试覆盖率收集
`ifndef APB_WATCHDOG_COV_SV`define APB_WATCHDOG_COV_SVclass apb_watchdog_cov extends apb_watchdog_subscriber;bit [31:0] reg_field_value;event regacc_sve;`uvm_component_utils(apb_watchdog_cov)...// T4 Disable interrupt test// T4.1 INT value after enable// T4.1 INT value after disablecovergroup apb_watchdog_t4_intr_disable_test_mode with function sample (bit [31:0] val, string field);option.name = "T4 disable interrupt test";INT_ENABLE : coverpoint val iff (field == "INT_ENABLE"){bins state_en = {1'b1};}INT_DISABLE : coverpoint val iff (field == "INT_DISABLE"){bins state_dis = {1'b0};bins state_raw = {1'b1};bins state_msk = {1'b0};}endgroupfunction new (string name = "apb_watchdog_cov", uvm_component parent);super.new(name, parent);apb_watchdog_t1_overall_control_cg = new();apb_watchdog_t2_value_reload_value = new();apb_watchdog_t1_overall_control_clk_cg = new(this.reg_field_value);apb_watchdog_t3_itcr_test_mode = new();apb_watchdog_t4_intr_disable_test_mode = new();endfunctionfunction void build_phase(uvm_phase phase);super.build_phase(phase);endfunctiontask do_listen_events();uvm_object tmp;uvm_reg r;forkforever beginwait(cfg.cov_enable);wdg_regacc_e.wait_trigger_data(tmp);void'($cast(r, tmp));if(r.get_name() == "WDOGCONTROL") beginapb_watchdog_t1_overall_control_cg.sample(rgm.WDOGCONTROL.INTEN.get(), "INTEN");apb_watchdog_t1_overall_control_cg.sample(rgm.WDOGCONTROL.RESEN.get(), "RESEN");reg_field_value = rgm.WDOGCONTROL.get();->regacc_sve;apb_watchdog_t4_intr_disable_test_mode.sample(rgm.WDOGCONTROL.INTEN.get(), "INT_ENABLE");apb_watchdog_t4_intr_disable_test_mode.sample(rgm.WDOGMIS.INT.get(), "INT_ENABLE");apb_watchdog_t4_intr_disable_test_mode.sample(rgm.WDOGRIS.RAWINT.get(), "INT_ENABLE");apb_watchdog_t4_intr_disable_test_mode.sample(rgm.WDOGCONTROL.INTEN.get(), "INT_DISABLE");apb_watchdog_t4_intr_disable_test_mode.sample(rgm.WDOGMIS.INT.get(), "INT_DISABLE");apb_watchdog_t4_intr_disable_test_mode.sample(rgm.WDOGRIS.RAWINT.get(), "INT_DISABLE");end...endjoin_noneendtaskendclass`endif // APB_WATCHDOG_COV_SV
8.4 寄存器上锁功能覆盖率收集
`ifndef APB_WATCHDOG_COV_SV`define APB_WATCHDOG_COV_SVclass apb_watchdog_cov extends apb_watchdog_subscriber;bit [31:0] reg_field_value;event regacc_sve;`uvm_component_utils(apb_watchdog_cov)...// T5 Reg lock & unlock function testcovergroup apb_watchdog_t5_reg_lock_test_mode with function sample (bit [31:0] val, string field);option.name = "T5 reg lock test";LOCK : coverpoint val iff (field == "LOCK"){bins lock_state = {1'b1};}UNLOCK : coverpoint val iff (field == "UNLOCK"){bins value = {'h1ACCE551};bins unlock_state = {1'b0};}endgroupfunction new (string name = "apb_watchdog_cov", uvm_component parent);super.new(name, parent);apb_watchdog_t1_overall_control_cg = new();apb_watchdog_t2_value_reload_value = new();apb_watchdog_t1_overall_control_clk_cg = new(this.reg_field_value);apb_watchdog_t3_itcr_test_mode = new();apb_watchdog_t4_intr_disable_test_mode = new();apb_watchdog_t5_reg_lock_test_mode = new();endfunctionfunction void build_phase(uvm_phase phase);super.build_phase(phase);endfunctiontask do_listen_events();uvm_object tmp;uvm_reg r;forkforever beginwait(cfg.cov_enable);wdg_regacc_e.wait_trigger_data(tmp);void'($cast(r, tmp));...else if(r.get_name() == "WDOGLOCK") beginapb_watchdog_t5_reg_lock_test_mode.sample(rgm.WDOGLOCK.get(), "LOCK");apb_watchdog_t5_reg_lock_test_mode.sample(rgm.WDOGLOCK.get(), "UNLOCK");endendjoin_noneendtaskendclass`endif // APB_WATCHDOG_COV_SV

运行寄存器上锁测试,最终覆盖率为100%
