【总结】
- virt_sequencer&agent的创建和连接:
- 在env的build_phase中完成创建;
- 在connect_phase中完成连接使得virt_sequencer不为空;
2.1 搭建环境
2.1.1 Makefile
- 首先将apb_watchdog的设计代码放在verilog文件夹中;
在verilog同级构建uvm文件夹;
- 在uvm文件夹中构建子文件夹:
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
- 在uvm文件夹中构建子文件夹:
在sim/中构建Makefile
gvim Makefile &
- 对tb的名称进行编辑;
- 编辑TESTNAME为apb_watchdog_default_test
- 添加设计文件由于设计文件中的cmsdk_apb_watchdog.v和cmsdk_apb_watchdog_frc.v文件中已经include了宏文件cmsdk_apb_watchdog_defs.v,所以实际上需要编译的文件就是这两个;
- 添加验证环境中的sv文件;
- 填写vcomp的内容:其中apb_watchdog和apb_watchdog_frc都include了宏文件,所以需要在上两层中进行搜索,也就需要将文件的地址inclde进来;
- 由于include宏文件很多,因此单独开一个关于宏的变量,同时需要编译的文件中也需要对VCOMP_INC这个变量进行引用,apb_pkg中include了许多其他的内容,因此需要导入;
- 添加GUI的选项让命令以图形化还是后台的形式来跑
?=的意思是仿真一开始是否要执行这个文件或者命令,如果需要,那默认值就是1,如果不需要就是0
2.1.2 tb文件
tb文件需要完成验证环境和DUT的连接
将apb_watchdog_tb添加到tb中
touch apb_watchdog_tb.sv
2.1.2.1 例化DUT设计文件
module apb_watchdog_tb;
cmsdk_apb_watchdog dut();
endmodule
2.1.2.2【调试】
Terminal输入:
make run GUI=1 &
让tb以图像化的方式在simv中进行仿真
2.1.3 更新Makefile【观察DUT中的端口并添加VIP】
根据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文件
根据APB的结构图可以发现,env将apb_master_agent包含在内,因此必须对它进行例化,在env环境中分别添加:挂载所有激励的virtual sequencer,env和将环境打包的pkg。
touch apb_watchdog_env.sv apb_watchdog_virtual_sequencer.sv apb_watchdog_pkg.sv
环境中会存在config对他们进行配置,在config中添加配置文件:
touch apb_watchdog_config.sv apb_watchdog_config.svh
apb中的WDOGINT和WDOGRES会被apb以外的其他寄存器进行配置,所以这两个端口必须放在tb一层中而不必放在VIP里面,因此还需要一个interface来存放他们。
touch apb_watchdog_if.sv
在cov中添加coverage文件
touch apb_watchdog_coverage.svh
在seq_lib中添加文件
touch apb_watchdog_seq_lib.svh
最后得到的整体结构如下:
2.1.4.2 补充env和apb_watchdog_pkg
ifndef 是为了避免仿真器重复编译文件,后接
define,如果没有定义过就定义一次,如果定义过了就不会再执行第二次。
- 向apb_watchdog_pkg中添加文件(注意此处是apb_watchdog_pkg而不是apb的VIP中的apb_pkg)
- 在同一个pkg的域中,一个sv文件不能同时编译两次,否则会编译出错,为了避免这种情况,需要在pkg中添加前两行代码,同时需要避免在pkg中对同一个svh文件编译多次(只在软件中进行添加,如果是module等硬件模块,不需要添加,因为硬件只会产生一个而软件例化后会有多个对象);
- 将底层的组件按照编译的顺序添加到pkg中;
- 将UVM中默认的pkg添加到pkg中;
- apb_watchdog_pkg还调用了apb_pkg这个小型的vip,所以也需要添加到pkg中。
``verilog
ifndef 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
> 【Question】需要注意的是,这里面导入的一些内容是svh,例如apb_watchdog_config.svh,而在apb_watchdog_config.svh中又导入了apb_watchdog_config.sv。为什么不直接把apb_watchdog_config.sv导入呢?
> - 因为一个apb_watchdog_config.svh中可能导入了多个sv文件,而且在提供VIP的时候为了进行保护,不会将sv中的方法进行公开,只会在外部提供一些变量或者端口。
2. **_向interface中添加内容,需要注意的是由于interface是一个硬件,因此编译多少次都可,不需要添加`ifndef_**
```verilog
interface apb_watchdog_if;
endinterface
- 接下来搭建env环境
按照结构图的示意,env环境中例化了master_agent对它进行了包裹,所以必须在这个类中声明apb_master_agent并对它进行创建
`ifndef APB_WATCHDOG_ENV_SV
`define APB_WATCHDOG_ENV_SV
class apb_watchdog_env extends uvm_env;
apb_master_agent apb_mst;
apb_watchdog_virtual_sequencer virt_sqr;
`uvm_component_utils(apb_watchdog_env)
function new (string name = "apb_watchdog_env", uvm_component parent);
super.new(name,parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
this.apb_mst = apb_master_agent::type_id::create("apb_mst", this);
virt_sqr = apb_watchdog_virtual_sequencer::type_id::create("virt_sqr", this);
endfunction
function void connect_phase(uvm_phase phase);
virt_sqr.apb_mst_sqr = apb_mst.sequencer;
endfunction
endclass
`endif
2.1.4.3 添加test文件
在test的目录下添加几个test用于测试:
touch apb_watchdog_base_test.sv apb_watchdog_regaccess_test.sv apb_watchdog_apbaccess_test.sv apb_watchdog_integration_test.sv
第二个test主要是对watchdog的寄存器进行测试,第三个是对apb进行测试
在apb_watchdog_tests.svh中添加所有的tests,这一部分仿真apb_pkg中的添加即可
``verilog
ifndef APB_WATCHDOG_TESTS_SVH `define APB_WATCHDOG_TESTS_SVHinclude "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
> 注意这里的apb_watchdog_tests是一个.svh文件,而之前的而apb_watchdog_pkg是一个.sv文件,所以两者在重定义处的写法还是不一样的。
3. **_更新apb_watchdog_base_test文件_**
```verilog
`ifndef APB_WATCHDOG_BASE_TEST_SV
`define APB_WATCHDOG_BASE_TEST_SV
virtual class apb_watchdog_base_test extends uvm_test;
apb_watchdog_env env;
function new(string name = "apb_watchdog_base_test", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = apb_watchdog_env::type_id::create("env", this);
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
phase.raise_objection(this);
do_init_clks();
do_init_regs();
phase.drop_objection(this);
endtask
virtual task do_init_clks();
endtask
virtual task do_init_regs();
endtask
endclass
`endif //APB_WATCHDOG_BASE_TEST
【注】这个测试的基类定义为虚类,所以之后必须继承该基类且实例化以后才能调用其中的方法
- 在base_test中添加几个virtual task:
- do_init_clks:使能clk;
- do_init_regs
- 在run_phase中还需要添加raise_objection和drop_objection来进行配置,在耗时之前将它拉起来。
如果将base_test声明成了一个虚类,那么不需要在工厂中进行注册
更新apb_watchdog_integration_test
``verilog
ifndef APB_WATCHDOG_INTEGRATION_TEST_SV `define APB_WATCHDOG_INTEGRATION_TEST_SVclass 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
> 继承于base_test中的子类test需要将对应的sequence挂载到sequencer上。
<a name="iJJeN"></a>
### 2.1.4.4 添加sequence文件和编辑seq_lib文件
1. **_添加seq文件_**
在test中介绍了子类test的功能是将sequence挂载到sequencer上,因此需要在seq_lib中添加一些sequence的文件,这些sequence文件需要与test文件相对应
```shell
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
编辑apb_watchdog_seq_lib文件
``verilog
ifndef APB_WATCHDOG_SEQ_LIB_SVH `define APB_WATCHDOG_SEQ_LIB_SVHinclude "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
3. **_编辑apb_watchdog_base_virt_seq.sv_**
```verilog
`ifndef APB_WATCHDOG_BASE_VIRT_SEQ_SV
`define APB_WATCHDOG_BASE_VIRT_SEQ_SV
class apb_watchdog_base_virtual_sequence extends uvm_sequence;
`uvm_object_utils(apb_watchdog_base_virtual_sequence)
`uvm_declare_p_sequencer(apb_watchdog_virtual_sequencer)
function new(string name = "apb_watchdog_base_virtual_sequence");
super.new(name);
endfunction
virtual task body();
`uvm_info("body","Entered...",UVM_LOW)
//Todo in sub_class
`uvm_info("body","Exiting...",UVM_LOW)
endtask
endclass
`endif //APB_WATCHDOG_BASE_VIRT_SEQ
编辑apb_watchdog_integration_virt_seq.sv
``verilog
ifndef APB_WATCHDOG_INTEGRATION_VIRT_SEQ_SV `define APB_WATCHDOG_INTEGRATION_VIRT_SEQ_SVclass 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
5. **_更新apb_watchdog_integration_test_**
```verilog
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.sqr);
phase.drop_objection(this);
endtask
在integration_test中例化对应的sequence并将sequence挂载到env的sqr上。
2.1.4.5 更新env环境
将对应的sequence挂载到了env.sqr上,此时env中还没有对virtual sequencer进行例化
更新apb_watchdog_virtual_sequencer
``verilog
ifndef APB_WATCHDOG_VIRTUAL_SEQUENCER_SV `define APB_WATCHDOG_VIRTUAL_SEQUENCER_SVclass 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
> 将apb_master_sequencer放在virtual_sequencer中
2. **_在env中例化这个这个virtual_sequencer_**
将Virtual sqr在env中进行例化并将agent中的sequencer与virtual sequencer相连接避免句柄悬空
```verilog
`ifndef APB_WATCHDOG_ENV_SV
`define APB_WATCHDOG_ENV_SV
class apb_watchdog_env extends uvm_env;
apb_master_agent apb_mst;
apb_watchdog_virtual_sequencer virt_sqr;
`uvm_component_utils(apb_watchdog_env)
function new (string name = "apb_watchdog_env", uvm_component parent);
super.new(name,parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
this.apb_mst = apb_master_agent::type_id::create("apb_mst", this);
virt_sqr = apb_watchdog_virtual_sequencer::type_id::create("virt_sqr", this);
endfunction
function void connect_phase(uvm_phase phase);
virt_sqr.apb_mst_sqr = apb_mst.sequencer;
endfunction
endclass
`endif
经过上述更新以后,此时的环境变成了下图:
需要注意的是virt_seq的例化是在test中的run_phase中完成例化的,并挂载到virtual_sqr上。
2.1.5 更新tb文件,连接软件和硬件
此时软件的环境搭建完毕,需要将软件和硬件的环境连接起来。
- env中有apb_master_agent,所以必须传入两个interface的实例并使用initial与硬件连接在一起(分别是apb_interface和apb_watchdog_interface),另外apb_if上的形参也可以传递进来。
- apb_if中有传入的形参clk和rstn,因此在tb中将这两个信号产生出来,时钟信号和复位信号一般不作为apb和watchdog的信号线,他们都是在tb中产生的,因此必须在tb中单独进行声明;
- 添加DUT在tb中的映射(硬件中存在pclk和wdogclk两个时钟信号,所以这两个时钟信号必须区分开来并且都需要在initial块中产生);
- 将DUT中的apb信号线与软件连接,需要注意的是硬件硬件中的addr是[11:2]bit的;
- 在apb_watchdog_if中声明与DUT相对应的信号线,并将DUT中的wdog信号线与软件连接。
传递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
> 产生不同参数的clk和rstn可以参考SV系统验证计数的V1实验部分:[SV系统验证技术V1实验_clk和rstn的产生](https://www.yuque.com/milanotang/mw4ipw/hxg586)
> 【注】
> - 实际过程中,watchdog的时钟频率是要比apb上的时钟频率慢的;
<a name="rGks5"></a>
## 2.1.6 更新Makefile
1. **_添加需要编辑的几个interface和pkg文件;_**
1. **_在VCOMP_INC中添加需要查找的目录,包括apb_pkg,cfg,cov,env,tests,seq_lib,reg_**
![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)
<a name="d3ccQ"></a>
###
<a name="DMDS4"></a>
# 【调试及结果】
1. **_首先在DVE中打开仿真_**
```verilog
make run GUI=1 &
- 运行1ps,点击UVM_debug:
可以在phase机制中看到已经运行到了run phase,说明能够正常编译仿真文件。
Makefile中运行的测试是apb_watchdog_integration_test。因此sequence也是挂载到了apb_watchdog_integration_virt_seq
双击sequence进入到sequence的代码中并设置三个断点。一步步运行可以打印这个info信息
过滤出Warning的信息,这个Warning信息是指底层的apb_master_agent没办法通过config_db的方式获得config的变量。
`ifndef APB_MASTER_AGENT_SV
`define APB_MASTER_AGENT_SV
function apb_master_agent::new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
function void apb_master_agent::build();
super.build();
// get config
if( !uvm_config_db#(apb_config)::get(this,"","cfg", cfg)) begin
`uvm_warning("GETCFG","cannot get config object from config DB")
cfg = apb_config::type_id::create("cfg");
end
// get virtual interface
if( !uvm_config_db#(virtual apb_if)::get(this,"","vif", vif)) begin
`uvm_fatal("GETVIF","cannot get vif handle from config DB")
end
monitor = apb_master_monitor::type_id::create("monitor",this);
monitor.cfg = cfg;
if(cfg.is_active == UVM_ACTIVE) begin
sequencer = apb_master_sequencer::type_id::create("sequencer",this);
sequencer.cfg = cfg;
driver = apb_master_driver::type_id::create("driver",this);
driver.cfg = cfg;
end
endfunction : build
function void apb_master_agent::connect();
assign_vi(vif);
if(is_active == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction : connect
function void apb_master_agent::assign_vi(virtual apb_if vif);
monitor.vif = vif;
if (is_active == UVM_ACTIVE) begin
sequencer.vif = vif;
driver.vif = vif;
end
endfunction : assign_vi
`endif // APB_MASTER_AGENT_SV
通过查看VIP中的apb_master_agent可以发现,通过config_db的方式获取apb_config,如果没有获取到就会报一个warnning信息。
- 观察波形
- restart仿真;
- 命令窗口中输入dump -add / -depth 0保存波形,在Hier设计层次中将DUT的所有信号添加到波形窗口上;
- 由于DUT中的apb_addr是[11:2]位的,所以将apb_if中的32位拉到波形窗口上。
- 对波形进行分组并分别命名为APB和WDOG;
- 将当前的波形信息保存起来:save_current_view,命名为apb_watchdog_debug_wave.tcl;
- 之后就可以在DVE的file_load_session中打开这个被保存的波形了
更新Makefile
- 之前在DVE中保存了波形的Tcl文件,所以在Makefile中需要添加DOTCL
在variable中添加DOTCL; 在Environment variables中添加SUMRUNFILE和ifeq endif命令。
在sim目录中添加apb_watchdog_sim_run.do文件
touch apb_watchdog_sim_run.do
将之前保存的apb_watchdog_debug_wave.tcl文件更改和该文件名一致:
mv apb_watchdog_debug_wave.tcl apb_watchdog_debug_wave.do
更新wap_watchdog_sim_run.do文件
- 如果默认TCL是1,在仿真以后就会执行一个sim_run.do文件;
- 将加载波形的命令添加到.do文件下,do命令实际上也可以在DVE中运行;
- 后续可以将其他的波形也添加到wave.do文件中方便对波形进行调试
# GUI mode or Batch mode execution branch for different purpose
if {[info command guiIsActive]==""} {
run
} else {
echo "GUI mode"
dump -add / -depth 0
do ./apb_watchdog_debug_wave.do
}
if的结果如果是1,那么直接在后台以Batch的模式run;如果不是就以GUI的模式图形化打开; 【注】 }和{之间一定要有空格,不然这个branch会报错。
在Terminal中输入history可以查看之前编译仿真的过程中运行的所有命令,查找
至此就完成了对V0版本的搭建
【附件】
apb_watchdog_v0版本下载:
https://github.com/Milanotang/Verify_Program/tree/main/apb_watchdog_v0