【总结】

  1. virt_sequencer&agent的创建和连接:
    1. 在env的build_phase中完成创建;
    2. 在connect_phase中完成连接使得virt_sequencer不为空;

2.1 搭建环境

2.1.1 Makefile

  1. 首先将apb_watchdog的设计代码放在verilog文件夹中;
  2. 在verilog同级构建uvm文件夹;

    • 在uvm文件夹中构建子文件夹:
      1. mkdir cfg cov env reg seq_lib sim tb sim test vip_lib

      config\coverage\document\env\reg\sequence_library\sim(跑仿真的目录)\tb(连接design)\test\VIP

  3. 在sim/中构建Makefile

    1. gvim Makefile &
    1. 对tb的名称进行编辑;
    2. 编辑TESTNAME为apb_watchdog_default_test
    3. 添加设计文件由于设计文件中的cmsdk_apb_watchdog.v和cmsdk_apb_watchdog_frc.v文件中已经include了宏文件cmsdk_apb_watchdog_defs.v,所以实际上需要编译的文件就是这两个;
    4. 添加验证环境中的sv文件;
    5. 填写vcomp的内容:其中apb_watchdog和apb_watchdog_frc都include了宏文件,所以需要在上两层中进行搜索,也就需要将文件的地址inclde进来;
    6. 由于include宏文件很多,因此单独开一个关于宏的变量,同时需要编译的文件中也需要对VCOMP_INC这个变量进行引用,apb_pkg中include了许多其他的内容,因此需要导入;
    7. 添加GUI的选项让命令以图形化还是后台的形式来跑

image.png

?=的意思是仿真一开始是否要执行这个文件或者命令,如果需要,那默认值就是1,如果不需要就是0

2.1.2 tb文件

tb文件需要完成验证环境和DUT的连接

将apb_watchdog_tb添加到tb中

  1. touch apb_watchdog_tb.sv

2.1.2.1 例化DUT设计文件

  1. module apb_watchdog_tb;
  2. cmsdk_apb_watchdog dut();
  3. endmodule

2.1.2.2【调试】

Terminal输入:

  1. make run GUI=1 &

让tb以图像化的方式在simv中进行仿真

image.png


2.1.3 更新Makefile【观察DUT中的端口并添加VIP】

image.png

根据apb端口可以看出这是一个apb2的端口,原因是没有pready信号,所以可以采用v2.3中对apb_pkg的声明,一直apb的VIP环境。

将V2.3中的apb_pkg导入以后,Makefile需要再次发生变化。

2.1.4 env&test&sequence

2.1.4.1 添加env文件

image.png

  1. 根据APB的结构图可以发现,env将apb_master_agent包含在内,因此必须对它进行例化,在env环境中分别添加:挂载所有激励的virtual sequencer,env和将环境打包的pkg。

    1. touch apb_watchdog_env.sv apb_watchdog_virtual_sequencer.sv apb_watchdog_pkg.sv
  2. 环境中会存在config对他们进行配置,在config中添加配置文件:

    1. touch apb_watchdog_config.sv apb_watchdog_config.svh
  3. apb中的WDOGINT和WDOGRES会被apb以外的其他寄存器进行配置,所以这两个端口必须放在tb一层中而不必放在VIP里面,因此还需要一个interface来存放他们。

    1. touch apb_watchdog_if.sv
  4. 在cov中添加coverage文件

    1. touch apb_watchdog_coverage.svh
  5. 在seq_lib中添加文件

    1. touch apb_watchdog_seq_lib.svh

    最后得到的整体结构如下:
    image.png


2.1.4.2 补充env和apb_watchdog_pkg

ifndef 是为了避免仿真器重复编译文件,后接define,如果没有定义过就定义一次,如果定义过了就不会再执行第二次。

  1. 向apb_watchdog_pkg中添加文件(注意此处是apb_watchdog_pkg而不是apb的VIP中的apb_pkg)
    1. 在同一个pkg的域中,一个sv文件不能同时编译两次,否则会编译出错,为了避免这种情况,需要在pkg中添加前两行代码,同时需要避免在pkg中对同一个svh文件编译多次(只在软件中进行添加,如果是module等硬件模块,不需要添加,因为硬件只会产生一个而软件例化后会有多个对象);
    2. 将底层的组件按照编译的顺序添加到pkg中;
    3. 将UVM中默认的pkg添加到pkg中;
    4. apb_watchdog_pkg还调用了apb_pkg这个小型的vip,所以也需要添加到pkg中。 ``verilogifndef APB_WATCHDOG_PKG_SV `define APB_WATCHDOG_PKG_SV

package apb_watchdog_pkg;

import uvm_pkg::; `include “uvm_macros.svh” import apb_pkg::;

include "apb_watchdog_config.svh"include “apb_watchdog_reg.svh” `include “apb_watchdog_cov.svh”

include "apb_watchdog_virtual_sequencer.sv"include “apb_watchdog_env.sv”

include "apb_watchdog_seq_lib.svh"include “apb_watchdog_tests.svh”

endpackage

`endif //apb_watchdog_pkg

  1. > Question】需要注意的是,这里面导入的一些内容是svh,例如apb_watchdog_config.svh,而在apb_watchdog_config.svh中又导入了apb_watchdog_config.sv。为什么不直接把apb_watchdog_config.sv导入呢?
  2. > - 因为一个apb_watchdog_config.svh中可能导入了多个sv文件,而且在提供VIP的时候为了进行保护,不会将sv中的方法进行公开,只会在外部提供一些变量或者端口。
  3. 2. **_interface中添加内容,需要注意的是由于interface是一个硬件,因此编译多少次都可,不需要添加`ifndef_**
  4. ```verilog
  5. interface apb_watchdog_if;
  6. endinterface
  1. 接下来搭建env环境

按照结构图的示意,env环境中例化了master_agent对它进行了包裹,所以必须在这个类中声明apb_master_agent并对它进行创建

  1. `ifndef APB_WATCHDOG_ENV_SV
  2. `define APB_WATCHDOG_ENV_SV
  3. class apb_watchdog_env extends uvm_env;
  4. apb_master_agent apb_mst;
  5. apb_watchdog_virtual_sequencer virt_sqr;
  6. `uvm_component_utils(apb_watchdog_env)
  7. function new (string name = "apb_watchdog_env", uvm_component parent);
  8. super.new(name,parent);
  9. endfunction
  10. function void build_phase(uvm_phase phase);
  11. super.build_phase(phase);
  12. this.apb_mst = apb_master_agent::type_id::create("apb_mst", this);
  13. virt_sqr = apb_watchdog_virtual_sequencer::type_id::create("virt_sqr", this);
  14. endfunction
  15. function void connect_phase(uvm_phase phase);
  16. virt_sqr.apb_mst_sqr = apb_mst.sequencer;
  17. endfunction
  18. endclass
  19. `endif

2.1.4.3 添加test文件

  1. 在test的目录下添加几个test用于测试:

    1. touch apb_watchdog_base_test.sv apb_watchdog_regaccess_test.sv apb_watchdog_apbaccess_test.sv apb_watchdog_integration_test.sv

    第二个test主要是对watchdog的寄存器进行测试,第三个是对apb进行测试

  2. 在apb_watchdog_tests.svh中添加所有的tests,这一部分仿真apb_pkg中的添加即可 ``verilogifndef APB_WATCHDOG_TESTS_SVH `define APB_WATCHDOG_TESTS_SVH

    include "apb_watchdog_base_test.sv"include “apb_watchdog_apbaccess_test.sv” include "apb_watchdog_integration_test.sv"include “apb_watchdog_regaccess_test.sv”

`endif //APB_WATCHDOG_TESTS_SVH

  1. > 注意这里的apb_watchdog_tests是一个.svh文件,而之前的而apb_watchdog_pkg是一个.sv文件,所以两者在重定义处的写法还是不一样的。
  2. 3. **_更新apb_watchdog_base_test文件_**
  3. ```verilog
  4. `ifndef APB_WATCHDOG_BASE_TEST_SV
  5. `define APB_WATCHDOG_BASE_TEST_SV
  6. virtual class apb_watchdog_base_test extends uvm_test;
  7. apb_watchdog_env env;
  8. function new(string name = "apb_watchdog_base_test", uvm_component parent);
  9. super.new(name, parent);
  10. endfunction
  11. function void build_phase(uvm_phase phase);
  12. super.build_phase(phase);
  13. env = apb_watchdog_env::type_id::create("env", this);
  14. endfunction
  15. task run_phase(uvm_phase phase);
  16. super.run_phase(phase);
  17. phase.raise_objection(this);
  18. do_init_clks();
  19. do_init_regs();
  20. phase.drop_objection(this);
  21. endtask
  22. virtual task do_init_clks();
  23. endtask
  24. virtual task do_init_regs();
  25. endtask
  26. endclass
  27. `endif //APB_WATCHDOG_BASE_TEST

【注】这个测试的基类定义为虚类,所以之后必须继承该基类且实例化以后才能调用其中的方法

  • 在base_test中添加几个virtual task:
    • do_init_clks:使能clk;
    • do_init_regs
  • 在run_phase中还需要添加raise_objection和drop_objection来进行配置,在耗时之前将它拉起来。

如果将base_test声明成了一个虚类,那么不需要在工厂中进行注册

  1. 更新apb_watchdog_integration_test ``verilogifndef APB_WATCHDOG_INTEGRATION_TEST_SV `define APB_WATCHDOG_INTEGRATION_TEST_SV

    class apb_watchdog_integration_test extends apb_watchdog_base_test;

    `uvm_component_utils(apb_watchdog_integration_test)

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

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

    task run_phase(uvm_phase phase); apb_watchdog_integration_virt_seq seq = apb_watchdog_integration_virt_seq::type_id::create(“seq”); super.run_phase(phase); phase.raise_objection(this); //attach virtual seqeunce on the virtual sequencer seq.start(env.virt_sqr); phase.drop_objection(this); endtask

    endclass

`endif //APB_WATCHDOG_INTEGRATION_TEST

  1. > 继承于base_test中的子类test需要将对应的sequence挂载到sequencer上。
  2. <a name="iJJeN"></a>
  3. ### 2.1.4.4 添加sequence文件和编辑seq_lib文件
  4. 1. **_添加seq文件_**
  5. test中介绍了子类test的功能是将sequence挂载到sequencer上,因此需要在seq_lib中添加一些sequence的文件,这些sequence文件需要与test文件相对应
  6. ```shell
  7. touch apb_watchdog_base_virt_seq.sv apb_watchdog_integration_virt_seq.sv apb_watchdog_apbaccess_virt_seq.sv apb_watchdog_regaccess_virt_seq.sv
  1. 编辑apb_watchdog_seq_lib文件 ``verilogifndef APB_WATCHDOG_SEQ_LIB_SVH `define APB_WATCHDOG_SEQ_LIB_SVH

    include "apb_watchdog_base_virt_seq.sv"include “apb_watchdog_integration_virt_seq.sv” include "apb_watchdog_regaccess_virt_seq.sv"include “apb_watchdog_apbaccess_virt_seq.sv”

`endif //APB_WATCHDOG_SEQ_LIB.SVH

  1. 3. **_编辑apb_watchdog_base_virt_seq.sv_**
  2. ```verilog
  3. `ifndef APB_WATCHDOG_BASE_VIRT_SEQ_SV
  4. `define APB_WATCHDOG_BASE_VIRT_SEQ_SV
  5. class apb_watchdog_base_virtual_sequence extends uvm_sequence;
  6. `uvm_object_utils(apb_watchdog_base_virtual_sequence)
  7. `uvm_declare_p_sequencer(apb_watchdog_virtual_sequencer)
  8. function new(string name = "apb_watchdog_base_virtual_sequence");
  9. super.new(name);
  10. endfunction
  11. virtual task body();
  12. `uvm_info("body","Entered...",UVM_LOW)
  13. //Todo in sub_class
  14. `uvm_info("body","Exiting...",UVM_LOW)
  15. endtask
  16. endclass
  17. `endif //APB_WATCHDOG_BASE_VIRT_SEQ
  1. 编辑apb_watchdog_integration_virt_seq.sv ``verilogifndef APB_WATCHDOG_INTEGRATION_VIRT_SEQ_SV `define APB_WATCHDOG_INTEGRATION_VIRT_SEQ_SV

    class apb_watchdog_integration_virt_seq extends apb_watchdog_base_virtual_sequence;

    `uvm_object_utils(apb_watchdog_integration_virt_seq)

    function new(string name = “apb_watchdog_integration_virt_seq”); super.new(); endfunction

    task body(); super.body(); `uvm_info(“body”, “Entered…”, UVM_LOW)

    1us;

    `uvm_info(“SEQSTART”,”virtual sequence body started!”, UVM_LOW)

    1us;

    `uvm_info(“body”, “Exiting…”, UVM_LOW) endtask endclass

`endif //apb_watchdog_integration_virt_seq

  1. 5. **_更新apb_watchdog_integration_test_**
  2. ```verilog
  3. task run_phase(uvm_phase phase);
  4. apb_watchdog_integration_virt_seq seq = apb_watchdog_integration_virt_seq::type_id::create("seq");
  5. super.run_phase(phase);
  6. phase.raise_objection(this);
  7. //attach virtual seqeunce on the virtual sequencer
  8. seq.start(env.sqr);
  9. phase.drop_objection(this);
  10. endtask

在integration_test中例化对应的sequence并将sequence挂载到env的sqr上。

2.1.4.5 更新env环境

将对应的sequence挂载到了env.sqr上,此时env中还没有对virtual sequencer进行例化

  1. 更新apb_watchdog_virtual_sequencer ``verilogifndef APB_WATCHDOG_VIRTUAL_SEQUENCER_SV `define APB_WATCHDOG_VIRTUAL_SEQUENCER_SV

    class apb_watchdog_virtual_sequencer extends uvm_sequencer;

    apb_master_sequencer apb_mst_sqr;

    `uvm_component_utils(apb_watchdog_virtual_sequencer)

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

    endclass

`endif //APB_WATCHDOG_VIRTUAL_SEQUENCER

  1. > apb_master_sequencer放在virtual_sequencer
  2. 2. **_env中例化这个这个virtual_sequencer_**
  3. Virtual sqrenv中进行例化并将agent中的sequencervirtual sequencer相连接避免句柄悬空
  4. ```verilog
  5. `ifndef APB_WATCHDOG_ENV_SV
  6. `define APB_WATCHDOG_ENV_SV
  7. class apb_watchdog_env extends uvm_env;
  8. apb_master_agent apb_mst;
  9. apb_watchdog_virtual_sequencer virt_sqr;
  10. `uvm_component_utils(apb_watchdog_env)
  11. function new (string name = "apb_watchdog_env", uvm_component parent);
  12. super.new(name,parent);
  13. endfunction
  14. function void build_phase(uvm_phase phase);
  15. super.build_phase(phase);
  16. this.apb_mst = apb_master_agent::type_id::create("apb_mst", this);
  17. virt_sqr = apb_watchdog_virtual_sequencer::type_id::create("virt_sqr", this);
  18. endfunction
  19. function void connect_phase(uvm_phase phase);
  20. virt_sqr.apb_mst_sqr = apb_mst.sequencer;
  21. endfunction
  22. endclass
  23. `endif

经过上述更新以后,此时的环境变成了下图:
image.png

需要注意的是virt_seq的例化是在test中的run_phase中完成例化的,并挂载到virtual_sqr上。

2.1.5 更新tb文件,连接软件和硬件

此时软件的环境搭建完毕,需要将软件和硬件的环境连接起来。

  1. env中有apb_master_agent,所以必须传入两个interface的实例并使用initial与硬件连接在一起(分别是apb_interface和apb_watchdog_interface),另外apb_if上的形参也可以传递进来。
  2. apb_if中有传入的形参clk和rstn,因此在tb中将这两个信号产生出来,时钟信号和复位信号一般不作为apb和watchdog的信号线,他们都是在tb中产生的,因此必须在tb中单独进行声明;
  3. 添加DUT在tb中的映射(硬件中存在pclk和wdogclk两个时钟信号,所以这两个时钟信号必须区分开来并且都需要在initial块中产生);
  4. 将DUT中的apb信号线与软件连接,需要注意的是硬件硬件中的addr是[11:2]bit的;
  5. 在apb_watchdog_if中声明与DUT相对应的信号线,并将DUT中的wdog信号线与软件连接。
  6. 传递interface,在tb中将之前声明好的interface传递到所被需要的层次(apb_if和apb_watchdog_if两个接口); ```verilog module apb_watchdog_tb;

    bit apb_clk; bit apb_rstn; bit wdg_clk; bit wdg_rstn;

    apb_if apb_if_inst(clk, rstn); apb_watchdog_if apb_watchdog_if_inst();

    cmsdk_apb_watchdog dut( .PCLK(apb_clk), // APB clock .PRESETn(apb_rstn), // APB reset .PENABLE(apb_if_inst.penable), // APB enable .PSEL(apb_if_inst.psel), // APB periph select .PADDR(apb_if_inst.paddr[11:2]), // APB address bus .PWRITE(apb_if_inst.pwrite), // APB write .PWDATA(apb_if_inst.pwdata[31:0]), // APB write data

    .WDOGCLK(wdg_clk), // Watchdog clock .WDOGCLKEN(1’b1), // Watchdog clock enable .WDOGRESn(wdg_rstn), // Watchdog clock reset

    .ECOREVNUM, // ECO revision number

    .PRDATA, // APB read data

    .WDOGINT, // Watchdog interrupt .WDOGRES // Watchdog timeout reset );

    apb_if apb_if_inst(clk, rstn); apb_watchdog_if apb_watchdog_if_inst();

    initial begin: generate clk fork forever #5ns apb_clk <= !apb_clk; //100MHz forever #25ns wdg_clk <= !wdg_clk; //20MHz join end

    initial begin: generate rstn

    2ns

    apb_rstn <= 1;

    20ns

    apb_rstn <= 0;

    20ns

    apb_rstn <= 1; end

    assign wdg_rstn <= apb_rstn;

    initial begin: vif_assign uvm_config_db#(virtual apb_if)::set(uvm_root::get(), “uvm_test_top.env.apb_mst”, “vif”, apb_if_inst); uvm_config_db#(virtual apb_watchdog_if)::set(uvm_root::get(), “uvm_test_top.env”, “vif”, apb_watchdog_if); uvm_config_db#(virtual apb_watchdog_if)::set(uvm_root::get(), “uvm_test_top.env.virt_sqr”, “vif”, apb_watchdog_if); end

endmodule

  1. > 产生不同参数的clkrstn可以参考SV系统验证计数的V1实验部分:[SV系统验证技术V1实验_clkrstn的产生](https://www.yuque.com/milanotang/mw4ipw/hxg586)
  2. > 【注】
  3. > - 实际过程中,watchdog的时钟频率是要比apb上的时钟频率慢的;
  4. <a name="rGks5"></a>
  5. ## 2.1.6 更新Makefile
  6. 1. **_添加需要编辑的几个interfacepkg文件;_**
  7. 1. **_VCOMP_INC中添加需要查找的目录,包括apb_pkg,cfg,cov,env,tests,seq_lib,reg_**
  8. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/22348254/1653045645258-58ce5068-2d76-44f9-ae3e-1c396b6c8ca1.png#clientId=uc1100c6a-2d9e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=671&id=ue57afd0d&margin=%5Bobject%20Object%5D&name=image.png&originHeight=839&originWidth=1230&originalType=binary&ratio=1&rotation=0&showTitle=false&size=240381&status=done&style=none&taskId=udc62f476-38d3-42bd-bf36-fb496150222&title=&width=984)
  9. <a name="d3ccQ"></a>
  10. ###
  11. <a name="DMDS4"></a>
  12. # 【调试及结果】
  13. 1. **_首先在DVE中打开仿真_**
  14. ```verilog
  15. make run GUI=1 &
  1. 运行1ps,点击UVM_debug:

image.png

可以在phase机制中看到已经运行到了run phase,说明能够正常编译仿真文件。


image.png

Makefile中运行的测试是apb_watchdog_integration_test。因此sequence也是挂载到了apb_watchdog_integration_virt_seq


image.png

双击sequence进入到sequence的代码中并设置三个断点。一步步运行可以打印这个info信息


image.png

过滤出Warning的信息,这个Warning信息是指底层的apb_master_agent没办法通过config_db的方式获得config的变量。

  1. `ifndef APB_MASTER_AGENT_SV
  2. `define APB_MASTER_AGENT_SV
  3. function apb_master_agent::new(string name, uvm_component parent);
  4. super.new(name, parent);
  5. endfunction : new
  6. function void apb_master_agent::build();
  7. super.build();
  8. // get config
  9. if( !uvm_config_db#(apb_config)::get(this,"","cfg", cfg)) begin
  10. `uvm_warning("GETCFG","cannot get config object from config DB")
  11. cfg = apb_config::type_id::create("cfg");
  12. end
  13. // get virtual interface
  14. if( !uvm_config_db#(virtual apb_if)::get(this,"","vif", vif)) begin
  15. `uvm_fatal("GETVIF","cannot get vif handle from config DB")
  16. end
  17. monitor = apb_master_monitor::type_id::create("monitor",this);
  18. monitor.cfg = cfg;
  19. if(cfg.is_active == UVM_ACTIVE) begin
  20. sequencer = apb_master_sequencer::type_id::create("sequencer",this);
  21. sequencer.cfg = cfg;
  22. driver = apb_master_driver::type_id::create("driver",this);
  23. driver.cfg = cfg;
  24. end
  25. endfunction : build
  26. function void apb_master_agent::connect();
  27. assign_vi(vif);
  28. if(is_active == UVM_ACTIVE) begin
  29. driver.seq_item_port.connect(sequencer.seq_item_export);
  30. end
  31. endfunction : connect
  32. function void apb_master_agent::assign_vi(virtual apb_if vif);
  33. monitor.vif = vif;
  34. if (is_active == UVM_ACTIVE) begin
  35. sequencer.vif = vif;
  36. driver.vif = vif;
  37. end
  38. endfunction : assign_vi
  39. `endif // APB_MASTER_AGENT_SV

通过查看VIP中的apb_master_agent可以发现,通过config_db的方式获取apb_config,如果没有获取到就会报一个warnning信息。


  1. 观察波形
    1. restart仿真;
    2. 命令窗口中输入dump -add / -depth 0保存波形,在Hier设计层次中将DUT的所有信号添加到波形窗口上;
    3. 由于DUT中的apb_addr是[11:2]位的,所以将apb_if中的32位拉到波形窗口上。

image.png

  • 对波形进行分组并分别命名为APB和WDOG;
  • 将当前的波形信息保存起来:save_current_view,命名为apb_watchdog_debug_wave.tcl;
  • 之后就可以在DVE的file_load_session中打开这个被保存的波形了

更新Makefile

  1. 之前在DVE中保存了波形的Tcl文件,所以在Makefile中需要添加DOTCL

image.png

在variable中添加DOTCL; 在Environment variables中添加SUMRUNFILE和ifeq endif命令。

  1. 在sim目录中添加apb_watchdog_sim_run.do文件

    1. touch apb_watchdog_sim_run.do
  2. 将之前保存的apb_watchdog_debug_wave.tcl文件更改和该文件名一致:

    1. mv apb_watchdog_debug_wave.tcl apb_watchdog_debug_wave.do
  3. 更新wap_watchdog_sim_run.do文件

  • 如果默认TCL是1,在仿真以后就会执行一个sim_run.do文件;
  • 将加载波形的命令添加到.do文件下,do命令实际上也可以在DVE中运行;
  • 后续可以将其他的波形也添加到wave.do文件中方便对波形进行调试
    1. # GUI mode or Batch mode execution branch for different purpose
    2. if {[info command guiIsActive]==""} {
    3. run
    4. } else {
    5. echo "GUI mode"
    6. dump -add / -depth 0
    7. do ./apb_watchdog_debug_wave.do
    8. }

    if的结果如果是1,那么直接在后台以Batch的模式run;如果不是就以GUI的模式图形化打开; 【注】 }和{之间一定要有空格,不然这个branch会报错。

在Terminal中输入history可以查看之前编译仿真的过程中运行的所有命令,查找
image.png
至此就完成了对V0版本的搭建

【附件】

apb_watchdog_v0版本下载:
https://github.com/Milanotang/Verify_Program/tree/main/apb_watchdog_v0