7.1 搭建事件触发器框架

事件触发器subscriber的目的就是在agent_monitor一侧监听数据,无论是scoreboard检查器还是覆盖率收集都需要基于一些事件的触发来进行功能比较与覆盖率收集。因此比较方便的做法是定义一个subscriber来完成事件的定义与一些事件的触发,而scoreboard与coverage模块继承于此父类,从而可以使用其中的事件与方法。 :::info

  1. 在env下创建scoreboard;
  2. 添加event:
    1. 关键的event触发送到covergroup;
    2. 送到scoreboard做数据检查的关键实现。
  3. 在env中新建apb_watchdog_subscriber:
    1. 从monitor捕捉信息(事件)进行分析;
  4. 在cov下创建apb_watchdog_cov.sv文件并包含在.svh文件中 ::: ``verilogifndef APB_WATCHDOG_COV_SVH `define APB_WATCHDOG_COV_SVH

    `include “apb_watchdog_cov.sv”

`endif // APB_WATCHDOG_COV_SVH

  1. :::info
  2. 5. 搭建subscriber的结构:
  3. 1. subscriber中声明端口的名称并定义;
  4. 1. subscriber中导入可能用到的一些变量(声明成句柄configRGMvif);
  5. 1. 由于在subscriber中声明了一个import,所以必须在sunscriber中实现对应的方法:write_apb()
  6. 1. build_phase中例化端口,getconfig,连接句柄;
  7. :::
  8. ```verilog
  9. `ifndef APB_WATCHDOG_SUBSCRIBER_SV
  10. `define APB_WATCHDOG_SUBSCRIBER_SV
  11. `uvm_analysis_import_decl(_apb)
  12. class apb_watchdog_subscriber extends uvm_component;
  13. // analysis import
  14. uvm_analysis_import_apb #(apb_transfer, apb_watchdog_subscriber) apb_trans_observed_imp;
  15. apb_watchdog_config cfg;
  16. apb_watchdog_rgm rgm;
  17. virtual apb_watchdog_if vif;
  18. `uvm_component_utils(apb_watchdog_subscriber)
  19. function new(string name = "apb_watchdog_subscriber", uvm_component parent);
  20. super.new(name, parent);
  21. endfunction
  22. function void build_phase(uvm_phase phase);
  23. super.build_phase(phase);
  24. apb_trans_observed_imp = new("apb_trans_observed_imp", this)
  25. if(!uvm_config_db#(apb_watchdog_config)::get(this, "", "cfg", cfg)) begin
  26. `uvm_fatal("GETCFG", "cannot get conifg object from config db")
  27. end
  28. rgm = cfg.rgm;
  29. vif = cfg.vif;
  30. endfunction
  31. endclass
  32. `endif // APB_WATCHDOG_subscriber_SV

:::info

  1. 搭建scoreboard的框架: ::: ``verilogifndef APB_WATCHDOG_SCOREBOARD_SV `define APB_WATCHDOG_SCOREBOARD_SV

class apb_watchdog_scoreboard extends apb_watchdog_subscriber;

`uvm_component_utils(apb_watchdog_scoreboard)

function new(string name = “apb_watchdog_scoreboard”, uvm_component parent); super.new(name, parent); endfunction

function void build_phase(uvm_phase phase);

endfunction endclass

`endif // APB_WATCHDOG_SCOREBOARD_SV

  1. :::info
  2. 7. env中添加声明的组件并在build_phase中例化
  3. 1. 声明scoreboardcov并例化;
  4. 1. build_phase中将config向下面的covscb中都进行传递;
  5. 1. connect_phase中将apb_master_monitor的端口和scb以及cov中的端口进行连接。
  6. :::
  7. ```verilog
  8. `ifndef APB_WATCHDOG_ENV_SV
  9. `define APB_WATCHDOG_ENV_SV
  10. class apb_watchdog_env extends uvm_env;
  11. apb_master_agent apb_mst;
  12. apb_watchdog_config cfg;
  13. apb_watchdog_virtual_sequencer virt_sqr;
  14. apb_watchdog_rgm rgm;
  15. apb_watchdog_adapter adapter;
  16. apb_watchdog_scoreboard scb;
  17. apb_watchdog_cov cov;
  18. uvm_reg_predictor #(apb_transfer) predictor;
  19. `uvm_component_utils(apb_watchdog_env)
  20. function new (string name = "apb_watchdog_env", uvm_component parent);
  21. super.new(name, parent);
  22. endfunction
  23. function void build_phase(uvm_phase phase);
  24. super.build_phase(phase);
  25. // Get configuration from test layer
  26. if(!uvm_config_db#(apb_watchdog_config)::get(this,"","cfg", cfg)) begin
  27. `uvm_fatal("GETCFG","cannot get config object from config DB")
  28. end
  29. uvm_config_db#(apb_watchdog_config)::set(this, "virt_sqr", "cfg", cfg);
  30. uvm_config_db#(apb_watchdog_config)::set(this, "scb", "cfg", cfg);
  31. uvm_config_db#(apb_watchdog_config)::set(this, "cov", "cfg", cfg);
  32. uvm_config_db#(apb_config)::set(this, "apb_mst", "cfg", cfg.apb_cfg);
  33. apb_mst = apb_master_agent::type_id::create("apb_mst", this);
  34. virt_sqr = apb_watchdog_virtual_sequencer::type_id::create("virt_sqr", this);
  35. if(!uvm_config_db#(apb_watchdog_rgm)::get(this,"","rgm", rgm)) begin
  36. rgm = apb_watchdog_rgm::type_id::create("rgm", this);
  37. rgm.build();
  38. end
  39. adapter = apb_watchdog_adapter::type_id::create("adapter");
  40. predictor = uvm_reg_predictor#(apb_transfer)::type_id::create("predictor", this);
  41. scb = apb_watchdog_scoreboard::type_id::create("scb", this);
  42. cov = apb_watchdog_cov::type_id::create("cov", this);
  43. endfunction
  44. function void connect_phase(uvm_phase phase);
  45. super.connect_phase(phase);
  46. virt_sqr.apb_mst_sqr = apb_mst.sequencer;
  47. rgm.map.set_sequencer(apb_mst.sequencer, adapter);
  48. apb_mst.monitor.item_collected_port.connect(predictor.bus_in);
  49. predictor.map = rgm.map;
  50. predictor.adapter = adapter;
  51. apb_mst.monitor.item_collected_port.connect(scb.apb_trans_observed_imp);
  52. apb_mst.monitor.item_collected_port.connect(cov.apb_trans_observed_imp);
  53. endfunction
  54. function void report_phase(uvm_phase phase);
  55. string reports = "\n";
  56. super.report_phase(phase);
  57. reports = {reports, $sformatf("================================================= \n")};
  58. reports = {reports, $sformatf("CURRENT TEST SUMMARY \n")};
  59. reports = {reports, $sformatf("SEQUENCE CHECK COUNT : %0d \n", cfg.seq_check_count)};
  60. reports = {reports, $sformatf("SEQUENCE CHECK ERROR : %0d \n", cfg.seq_check_error)};
  61. reports = {reports, $sformatf("SCOREBOARD CHECK COUNT : %0d \n", cfg.scb_check_count)};
  62. reports = {reports, $sformatf("SCOREBOARD CHECK COUNT : %0d \n", cfg.scb_check_error)};
  63. reports = {reports, $sformatf("=================================================")};
  64. `uvm_info("TEST_SUMMARY",reports, UVM_LOW);
  65. endfunction
  66. endclass
  67. `endif

【调试及结果】

  1. make elab
  2. make run GUI=1 TESTNAME=apb_watchdog_countdown_test &

image.png

点击object选择uvm_components,可以看到顶层的环境中已经搭建好了cov和scb。

7.2 添加计数检查器

7.2.1 在subscriber中添加事件

subscriber捕捉了来自apb_monitor中的信息:主要是访问了什么寄存器,做了哪些读取操作。 :::danger 【思考】

  1. 如何抓去event,抓取哪些event?

阅读功能描述文档,在event declare中将一些关键的事件进行抓取。

  1. 如何触发这些事件?

观察每次捕捉到的apb_transfer进行了哪些访问,读写的寄存器是什么,内容是什么? ::: :::info

  1. 对event进行声明并与资源池中的句柄连接;
  2. 编辑write_apb函数,将拿到的apb_transfer广播出去使得rgm.map能够查找到;
  3. 将拿到的寄存器采用前门访问的方式再次广播;
  4. 在subscriber中添加enble的使能信号,在scoreboard和coverage中分别将enable和cfg中的enable信号连接。 ::: ``verilogifndef APB_WATCHDOG_SUBSCRIBER_SV `define APB_WATCHDOG_SUBSCRIBER_SV

`uvm_analysis_imp_decl(_apb)

class apb_watchdog_subscriber extends uvm_component;

// analysis import uvm_analysis_imp_apb #(apb_transfer, apb_watchdog_subscriber) apb_trans_observed_imp;

// Declare events uvm_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;

local 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); endfunction

function void build_phase(uvm_phase phase); super.build_phase(phase); apb_trans_observed_imp = new(“apb_trans_observed_imp”, this); // Get configuration from test layer if(!uvm_config_db#(apb_watchdog_config)::get(this, “”, “cfg”, cfg)) begin `uvm_fatal(“GETCFG”, “cannot get conifg object from config db”) end vif = 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”); endfunction

function void end_of_elaboration_phase(uvm_phase phase); super.end_of_elaboration_phase(phase); endfunction

task run_phase(uvm_phase phase); super.run_phase(phase); do_events_trigger(); endtask

virtual 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); endfunction

virtual task do_events_trigger(); uvm_object tmp; uvm_reg r; fork forever begin fork wdg_regacc_fd_e.wait_trigger_data(tmp); wdg_regacc_bd_e.wait_trigger_data(tmp); join_any disable fork; void’($cast(r, tmp));

  1. #1ps;
  2. if(r.get_name() == "WDOGCONTROL") begin
  3. if(rgm.WDOGCONTROL.INTEN.get() == 1'b1) wdg_reg_inten_e.trigger();
  4. if(rgm.WDOGCONTROL.RESEN.get() == 1'b1) wdg_reg_resen_e.trigger();
  5. end
  6. else if(r.get_name() == "WDOGLOAD") begin
  7. if(rgm.WDOGLOAD.LOADVAL.get() != 0) wdg_reg_load_e.trigger();
  8. end
  9. end
  10. join_none

endtask

endclass

`endif // APB_WATCHDOG_subscriber_SV

  1. > - apb_mst.monitor的端口在监测到apb_transfer以后就会将这个事物广播出去,同时交给了predictorsubscribersubscriber在拿到这样的一个事物后就会通过地址在RGMmap中查找对应的寄存器,查找到以后又会将这个事物再次广播出去。
  2. > - 在下面的耗时语句task中会通过前门或后门的方式来等待事件,等到以后就会终止线程,进一步查找是对哪一个寄存器进行了操作,最后当确认到信号发生了变化以后就触发这个对应的事件。
  3. > apb_transfer广播出去是一个函数,是非耗时的。如果想要构建耗时语句就需要再新建一个task
  4. :::danger
  5. 【思考】<br />为什么一定要一个采用一个耗时的task来完成等待事物的操作?<br />subscriber希望读取的数据是寄存器模型更新以后的数值。所以希望在一定时间以后来等待,这个时间就是1ps,因此这个时间是非常重要的。
  6. :::
  7. <a name="EZp5O"></a>
  8. ## 7.2.2 在scoreboard中递减计数测试功能
  9. :::info
  10. 1. scoreboard中需要完成对计数的检查,因此声明do_countdown_check();
  11. 1. 根据功能描述文档,当计数递减为0以后,INT信号会拉高
  12. :::
  13. > 首先根据功能描述文档写出计数递减的功能,也就是INTEN拉高同时由数读进来;
  14. > 将读进来的数据和当前的数据分别赋值给cur_loadcur_value
  15. > 在循环中实现:只要cur_value的值不为0就一直递减;
  16. > 递减到0以后拉高INT信号,并判断如果INT信号没有被拉高,报错:需要将INT信号拉高。
  17. ```verilog
  18. `ifndef APB_WATCHDOG_SCOREBOARD_SV
  19. `define APB_WATCHDOG_SCOREBOARD_SV
  20. class apb_watchdog_scoreboard extends apb_watchdog_subscriber;
  21. `uvm_component_utils(apb_watchdog_scoreboard)
  22. function new (string name = "apb_watchdog_scoreboard", uvm_component parent);
  23. super.new(name, parent);
  24. endfunction
  25. function void build_phase(uvm_phase phase);
  26. super.build_phase(phase);
  27. enable = cfg.scb_enable;
  28. endfunction
  29. task run_phase(uvm_phase phase);
  30. super.run_phase(phase);
  31. do_countdown_check();
  32. endtask
  33. virtual task do_countdown_check();
  34. bit [31:0] cur_load;
  35. bit [31:0] cur_count;
  36. fork
  37. forever begin
  38. fork
  39. wdg_reg_inten_e.wait_trigger();
  40. wdg_reg_load_e.wait_trigger();
  41. join
  42. @(posedge vif.wdg_clk);
  43. cur_load = rgm.WDOGLOAD.LOADVAL.get();
  44. cur_count = cur_load;
  45. do begin
  46. @(posedge vif.wdg_clk);
  47. cur_count--;
  48. end while(cur_count != 0);
  49. wdg_reg_intr_assert_e.trigger();
  50. // From logic timing after count reach zero
  51. if(vif.wdog_int != 1'b1) begin
  52. cfg.scb_check_error++;
  53. `uvm_error("COUNTDOWN_CHECK", "Wdog interrupt signal should be asserted!")
  54. end
  55. cfg.scb_check_count++;
  56. end
  57. join_none
  58. endtask
  59. endclass
  60. `endif // APB_WATCHDOG_SCOREBOARD_SV

在scoreboard中完成计数递减的功能检查:

  • 等待INTEN和LOADEN都被触发;
  • 将LOAD的值赋给两个变量;
  • 在循环体重作递减计数的功能,递减到0以后将INT信号拉高;

【调试及结果】

image.png
image.png

当断点停在int_assert事件trigger的时候,定向测试一侧中寄存器的值正好减为0。

:::danger 【思考】
当计数递减到0以后,在多长时间内INT信号会被拉起呢?如何对这个时间进行检查?
根据波形来看是在下一个时钟信号的上升沿,INT信号会被拉起,但是不能在这个地方进行检查因为存在delta_cycle的问题,因此要等两个时钟信号的下降沿再进行检查。 ::: image.png
在int拉起的事件触发以后继续添加若干个时钟信号的下降沿,可以看到在计数到0以后的一拍后就将INT信号拉高了。