【总结】
- 使用config机制来完成对接口和全局变量的配置,提高环境复用性;
- 采用APB_VIP中的序列来完成对寄存器的测试;
3.1 config在环境中的搭建和传递
3.1.1 编辑config文件
:::info (1)在watchdog的层次中引入config机制,自顶向下的完成对验证环境的配置并方便底层组件对全局变量的调用。 :::
`ifndef APB_WATCHDOG_CONFIG_SV
`define APB_WATCHDOG_CONFIG_SV
class apb_watchdog_config extends uvm_object;
`uvm_object_utils(apb_watchdog_config)
apb_config apb_cfg;
// USER to specify the config items
uvm_active_passive_enum is_active = UVM_ACTIVE;
function new (string name = "apb_watchdog_config");
super.new(name);
apb_cfg = apb_config::type_id::create("apb_cfg");
endfunction : new
endclass
`endif // APB_WATCHDOG_CONFIG_SV
3.1.2 Base_Test_layer
:::info (2)在test中声明并例化config,然后通过config_db参数类传递到底层,再通过vif或者其他接口向下传递。 :::
`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;
apb_watchdog_config cfg;//对config进行声明
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);
cfg = apb_watchdog_config::type_id::create("cfg");//对config进行例化
uvm_config_db#(apb_watchdog_config)::set(this,"env","cfg",cfg);
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
`ifndef APB_WATCHDOG_BASE_TEST_SV
`define APB_WATCHDOG_BASE_TEST_SV
`endif //APB_WATCHDOG_BASE_TEST
3.1.3 Env_layer
:::info
(3)使用if语句嵌套uvm_config_db来获取config;
将config向下传递;
:::
`ifndef APB_WATCHDOG_ENV_SV
`define APB_WATCHDOG_ENV_SV
class apb_watchdog_env extends uvm_env;
apb_watchdog_config cfg;
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);
//Get configuration from test layer
if(!uvm_config_db#(apb_watchdog_config)::get(this, "", "cfg",cfg)) begin
`uvm_fatal("GETCFG", "cannnot get config object from test layer")
end
uvm_config_db#(apb_watchdog_config)::set(this, "virt_sqr", "cfg", cfg);
uvm_config_db#(apb_config)::set(this, "apb_mst", "cfg",cfg.apb_cfg);
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
【注】
- 由于之前在test一层中已经例化了apb_watchdog_config,因此env环境中只需要将这个config向下传递即可,那么就只需要声明句柄不需要创建对象来开辟空间,因此在build_phase中也没有create。但是apb_master_agent和virtual_sequencer都是初次在环境中声明的,因此也必须使用create进行创建。
- 在env中将apb_cfg向底层set的时候,apb_watchdog_config中已经包含了apb_cfg。所以使用的是cfg.apb_cfg。
【调试及结果】
- make elab; make run GUI=1 &
- 在DVE命令中输入run 0来观察环境是否搭建好(build_phase是否顺利结束)
- 在uvm debug中可以看到phase卡到了build_phase的阶段;
- 同时在窗口中打印出env没办法拿到来自test的config。
c. 结果发现是env中拿取config的路径和test中set下来的路径不一样,更改路径以后,phase可以移动到了run_phase
:::danger
【Tips】
- 如何查看config有无进行正常的传递呢?
- 设置断点;
- 在uvm_debug_resource中查看:
- 在name中输入与config相关的名称cfg:
:::
可以看到cfg是在test的一层中set进来的,set进来的value就apb_watchdog_config;同时在下面也有env进行承接,在这里进行get。
同样的,可以类比apb_config有无进行有效地传递。
- 但是apb_watchdog_config并没有传递到virt_sqr中,原因是它只有set但是virt_sqr并没有get。
一般来说set但不get是可以的,但get不set是不行的,因为get不到是会有返回值的,所以可以使用if条件语句来进行判断是否get到,如果没有get到就会返回0报fatal。
- 或者可以点击set calls without get也可以过滤出来需要的信息。
3.2 APB寄存器的直接访问
APB寄存器的直接访问就是调用APB_VIP中的序列,将他们在对应的seq中挂载到env中与apb_mst_sqr连接的virt_sqr上来完成测试激励的发送
3.2.1 Test_layer
:::info (1)在Test_layer一层中将对应的seq挂载到env中的virt_sqr上 :::
`ifndef APB_WATCHDOG_APBACC_TEST_SV
`define APB_WATCHDOG_APBACC_TEST_SV
class apb_watchdog_apbacc_test extends apb_watchdog_base_test;
`uvm_component_utils(apb_watchdog_apbacc_test)
function new(string name = "apb_watchdog_apbacc_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_apbacc_virt_seq seq = apb_watchdog_apbacc_virt_seq::type_id::create("this");
super.run_phase(phase);
phase.raise_objection(this);
seq.start(env.virt_sqr);
phase.drop_objection(this);
endtask
endclass
`endif //APB_WATCHDOG_APBACC_TEST_SV
【注】
- apb_watchdog_base_test是一个虚类,不能做注册,但是apb_watchdog_apbacc_test是一个子类,必须注册以后才能继续使用;
- 如果没有注册的话会导致在仿真的时候runtestname中找不到这个测试,run_test相当于uvm的后台从已经编译到工厂中的类中去寻找,如果没有注册是找不到的。
3.2.3 测试序列_layer
:::info (2) :::
- 在搭好上述框架以后就可以开始写测试用例了,由于现在的接口只有apb,所以可以采用UVM项目实战中发送一些transfer;
如果要写一些sequence,那么最好就是写到base_virtual_sequence中,因为其他的一些sequence可能需要复用这些sequence。
``verilog
ifndef APB_WATCHDOG_BASE_VIRT_SEQ_SV `define APB_WATCHDOG_BASE_VIRT_SEQ_SVclass apb_watchdog_base_virt_seq extends uvm_sequence;
apb_master_single_write_sequence apb_wr_seq; apb_master_single_read_sequence apb_rd_seq; apb_master_write_read_sequence apb_wr_rd_seq;
uvm_object_utils(apb_watchdog_base_virt_seq)
uvm_declare_p_sequencer(apb_watchdog_virtual_sequencer)function new(string name = “apb_watchdog_base_virt_seq”);
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
```verilog
`ifndef APB_WATCHDOG_APBACC_VIRT_SEQ_SV
`define APB_WATCHDOG_APBACC_VIRT_SEQ_SV
class apb_watchdog_apbacc_virt_seq extends apb_watchdog_base_virt_seq;
`uvm_object_utils(apb_watchdog_apbacc_virt_seq)
function new(string name = "apb_watchdog_apbacc_virt_seq");
super.new(name);
endfunction
virtual task body();
`uvm_info("body","Entered...",UVM_LOW)
// Todo in sub_class
// Inline constraint with apb address, data
`uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'hFE0;})
`uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'hFE4;})
`uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'hFE8;})
`uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'hFEc;})
`uvm_info("body","Exiting...",UVM_LOW)
endtask
endclass
`endif //APB_WATCHDOG_APBACC_VIRT_SEQ_SV
【注】
- test一层中将virtual_seq通过seq.start挂载到virtual_sequencer上的;
- sequence一层中使用`uvm_do_on_with完成了对于seq的创建,挂载和随机化的过程,将seq挂载到了p_sequencer上,这里的p_sequencer指的就是env中的apb_watchdog_virtual_sequencer;
- 给的约束就是addr,查阅arm提供的apb_watchdog手册中的ID:
3.3 验证结构图
【调试】
编译
make elab
make run GUI=1 TESTNAME=apb_watchdog_apbacc_test
test发生了变化,所以在run_test的时候也需要对test的名称做出变化
DVE中输入run 0先建立整个环境
此时信息提示停留在了app_watchdog_apbacc_virt_seq上,双击进去以后可以设置断点
- 在apb_watchdog_apbacc_virt_seq上设置断点
根据波形来看,有以下几个bug:
- PRESET的和PCLK不同步;
- 对于APB总线的读访问应该发生在reset复位以后,而不是reset拉高的时候。
首先解决第1个bug:
- 在对寄存器进行初始配置之前可以先等reset信号复位完成,所以在tb中对do_init_regs()编写,这个rstn复位信号是从接口中拿来的;
- 在config中定义这样的一个vif; ```verilog
ifndef APB_WATCHDOG_CONFIG_SV
define APB_WATCHDOG_CONFIG_SV
class apb_watchdog_config extends uvm_object;
apb_config apb_cfg; virtual apb_watchdog_if vif;
`uvm_object_utils(apb_watchdog_config)
// USER to specify the config items
function new (string name = “apb_watchdog_config”); super.new(name); apb_cfg = apb_config::type_id::create(“apb_cfg”); endfunction : new
endclass `endif // apb_watchdog_CONFIG_SV
> 在config中声明好vif,在tb一层中直接传递给底层的vif中。
3. **_base_test中通过config_db来get vif;_**
```verilog
`ifndef APB_WATCHDOG_BASE_TEST_SV
`define APB_WATCHDOG_BASE_TEST_SV
virtual class apb_watchdog_base_test extends uvm_test;
apb_watchdog_config cfg;
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);
cfg = apb_watchdog_config::type_id::create("cfg");
if(!uvm_config_db#(virtual apb_watchdog_if)::get(this,"","vif", cfg.vif)) begin
`uvm_fatal("GETCFG","cannot get virtual interface from config DB")
end
uvm_config_db#(apb_watchdog_config)::set(this, "env", "cfg", cfg);
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();
// wait reset release
//@(posedge cfg.vif.)
endtask
endclass
`endif //APB_WATCHDOG_BASE_TEST
获取cfg中的vif并传递到test一层的vif中,需要注意顺序,先创建cfg以后才能拿到vif。
- 在apb_watchdog_if中声明reset信号,这个信号来自于tb ```verilog
interface 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;
endinterface
5. **_在tb中将信号传递给接口并将接口通过config_db传递给底层(test、virtual sequencer、env和apb_master_agent)_**
```verilog
module apb_watchdog_tb;
import uvm_pkg::*;
`include "uvm_macros.svh"
import apb_watchdog_pkg::*;
bit apb_clk;
bit apb_rstn;
bit wdg_clk;
bit wdg_rstn;
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), // APB write data
.WDOGCLK(wdg_clk), // Watchdog clock
.WDOGCLKEN(1'b1), // Watchdog clock enable
.WDOGRESn(wdg_rstn), // Watchdog clock reset
.ECOREVNUM(apb_wdg_if_inst.ecorevnum), // ECO revision number
.PRDATA(apb_if_inst.prdata), // APB read data
.WDOGINT(apb_wdg_if_inst.wdog_int), // Watchdog interrupt
.WDOGRES(apb_wdg_if_inst.wdog_res) // Watchdog timeout reset
);
apb_if apb_if_inst(apb_clk, apb_rstn);
apb_watchdog_if apb_wdg_if_inst();
assign apb_wdg_if_inst.apb_clk = apb_clk;
assign apb_wdg_if_inst.apb_rstn = apb_rstn;
assign apb_wdg_if_inst.wdg_clk = wdg_clk;
assign apb_wdg_if_inst.wdg_rstn = wdg_rstn;
initial begin: gen_clk
fork
forever #5ns apb_clk <= !apb_clk; //100MHz
forever #25ns wdg_clk <= !wdg_clk; //20MHz
join
end
initial begin: gen_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", "vif", apb_wdg_if_inst);
uvm_config_db#(virtual apb_watchdog_if)::set(uvm_root::get(), "uvm_test_top.env", "vif", apb_wdg_if_inst);
uvm_config_db#(virtual apb_watchdog_if)::set(uvm_root::get(), "uvm_test_top.env.virt_sqr", "vif", apb_wdg_if_inst);
run_test("");
end
endmodule
【注】 在initial begin过程块中使用config将内容传递给了下面的几个层次,但是需要注意以下这种使用通配符传递方式是不行的:
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*", "vif", apb_wdg_if_inst);
run_test("");
end
原因是直接传递给uvm_test_top这个层次中会将apb_master_agent中的vif覆盖掉。
- test中的寄存器如何等复位信号呢?
- 等一个下降沿再等一个上升沿;
- 等一个固定的时间。 ```verilog virtual task do_init_regs(); // wait reset release repeat(10) @(posedge cfg.vif.apb_clk); endtask
7. **_对总线读回来的数据进行检查(在apb_base_virtual_sequence中添加compare函数)_**
```verilog
virtual function void compare_data(logic[31:0] val1 , logic[31:0] val2);
if(val1 === val2)
`uvm_info("COMPSUC",$sformatf("val1 'h%0x === val2 'h%0x", val1, val2), UVM_LOW)
else
`uvm_error("COMPSUC",$sformatf("val1 'h%0x !== val2 'h%0x", val1, val2))
endfunction
`uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'hFE0;})
compare_data(apb_rd_seq.data, 'h24);
`uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'hFE4;})
compare_data(apb_rd_seq.data, 'hB8);
`uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'hFE8;})
compare_data(apb_rd_seq.data, 'h1B);
`uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'hFEc;})
compare_data(apb_rd_seq.data, 'hB0);
`uvm_info("body","Exiting...",UVM_LOW)
- 设置比较次数的变量和比较错误的变量:
- 将计数功能放在config中的好处就是可以做一个全局的掌控而不必在各自的phase中来解决这个问题;
- 对于seq而言,它不是一个component,没有自己的report_phase,产生的计数如果放在pkg中,在调用每一个pkg的时候都需要对这个包中的函数进行调用;
- 如果想要放在一个全局的环境中,那么最好使用的就是env,因为环境需要复用,放在report_phase中就可以复用到其他层次。 ```verilog int seq_check_count; int seq_check_error; int scb_check_count; int scb_check_error;
apb_config apb_cfg; virtual apb_watchdog_if vif;
**_b. 由于base_virtual_seq是没办法直接拿到cfg的,但是seq挂载到了p_seqencer上,所以可以使用p_sequencer来获取cfg和其中的变量_**
```verilog
virtual task body();
`uvm_info("body","Entered...",UVM_LOW)
//Todo in sub_class
cfg = p_sequencer.cfg;
`uvm_info("body","Exiting...",UVM_LOW)
endtask
virtual function void compare_data(logic[31:0] val1 , logic[31:0] val2);
cfg.seq_check_count++;
if(val1 === val2)
`uvm_info("COMPSUC",$sformatf("val1 'h%0x === val2 'h%0x", val1, val2), UVM_LOW)
else
cfg.seq_check_error++;
`uvm_error("COMPSUC",$sformatf("val1 'h%0x !== val2 'h%0x", val1, val2))
endfunction
c. 最后需要在仿真的阶段加上report_phase
function report_phase(uvm_phase phase);
string report;
super.report_phase(phase);
reports = $sformatf("CURRENT TEST SUMMARY");
reports = $sformatf("SEQUENCE CHECK COUNT : %0d", cfg.seq_check_count);
reports = $sformatf("SEQUENCE CHECK ERROR : %0d", cfg.seq_check_error);
reports = $sformatf("SCOREBOARD CHECK COUNT : %0d", cfg.scb_check_count);
reports = $sformatf("SCOREBOARD CHECK COUNT : %0d", cfg.scb_check_error);
endfunction
【附件】
3.4 【补充】寄存器测试
3.4.1 寄存器读写测试
按照验证计划,对RW寄存器进行了single_write_read测试,测试过程中发现当寄存器上锁以后,任何读写值都是不确定的,因此对Lock寄存器的测试需在最后一位。
`ifndef APB_WATCHDOG_APBACC_VIRT_SEQ_SV
`define APB_WATCHDOG_APBACC_VIRT_SEQ_SV
class apb_watchdog_apbacc_virt_seq extends apb_watchdog_base_virt_seq;
`uvm_object_utils(apb_watchdog_apbacc_virt_seq)
function new(string name = "apb_watchdog_apbacc_virt_seq");
super.new(name);
endfunction
virtual task body();
super.body();
`uvm_info("body","Entered...",UVM_LOW)
...
// Single write & single read seq
`uvm_do_on_with(apb_wr_seq, p_sequencer.apb_mst_sqr, {addr == 'h00; data == 'hFF;})
`uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'h00;})
compare_data(apb_rd_seq.data, apb_wr_seq.data);
// Single read imediately after single write
wr_val = $urandom_range('h0,'hFF);
`uvm_do_on_with(apb_wr_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'h00; data == wr_val;})
rd_val = apb_wr_rd_seq.data;
compare_data(rd_val, wr_val);
wr_val = $urandom_range('b00,'b11);
`uvm_do_on_with(apb_wr_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'h08; data == wr_val;})
rd_val = apb_wr_rd_seq.data;
compare_data(rd_val, wr_val);
//-- wr_val = $urandom_range('h00, 'hFFFFFFFF);
//-- `uvm_do_on_with(apb_wr_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'hC00; data == wr_val;})
//-- rd_val = apb_wr_rd_seq.data;
//-- compare_data(rd_val[31:1], wr_val[31:1]);
wr_val = 'b1;
`uvm_do_on_with(apb_wr_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'hF00; data == wr_val;})
rd_val = apb_wr_rd_seq.data;
compare_data(rd_val, wr_val);
`uvm_info("body","Exiting...",UVM_LOW)
endtask
endclass
`endif //APB_WATCHDOG_APBACC_VIRT_SEQ_SV
3.4.2 寄存器稳定性测试
3.4.2.1 对非法地址进行读写测试
`ifndef APB_WATCHDOG_ILLEGAL_ACC_VIRT_SEQ_SV
`define APB_WATCHDOG_ILLEGAL_ACC_VIRT_SEQ_SV
class apb_watchdog_illegal_acc_virt_seq extends apb_watchdog_base_virt_seq;
`uvm_object_utils(apb_watchdog_illegal_acc_virt_seq)
function new(string name = "apb_watchdog_illegal_acc_virt_seq");
super.new(name);
endfunction
virtual task body();
super.body();
`uvm_info("body","Entered...",UVM_LOW)
// illegal address register write $ read test
wr_val = 'hFF;
`uvm_do_on_with(apb_wr_seq, p_sequencer.apb_mst_sqr, {addr == 'h01; data == wr_val;})
`uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'h01;})
compare_data(wr_val, apb_rd_seq.data);
// RO register write reserved field and check
wr_val = 'hFFFFFFFF;
`uvm_do_on_with(apb_wr_seq, p_sequencer.apb_mst_sqr, {addr == 'h0C; data == wr_val;})
`uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'h0C;})
diff_value(1, (wr_val & apb_rd_seq.data), "WDOGINTCLR_REG");
`uvm_info("body","Exiting...",UVM_LOW)
endtask
endclass
`endif //APB_WATCHDOG_illegal_acc_VIRT_SEQ_SV
对非法地址进行测试以后发现subscriber中没有办法在RGM中查找到对应的寄存器。
3.4.2.2 对读写寄存器的保留域进行测试
`ifndef APB_WATCHDOG_ILLEGAL_ACC_VIRT_SEQ_SV
`define APB_WATCHDOG_ILLEGAL_ACC_VIRT_SEQ_SV
class apb_watchdog_illegal_acc_virt_seq extends apb_watchdog_base_virt_seq;
`uvm_object_utils(apb_watchdog_illegal_acc_virt_seq)
function new(string name = "apb_watchdog_illegal_acc_virt_seq");
super.new(name);
endfunction
virtual task body();
super.body();
`uvm_info("body","Entered...",UVM_LOW)
// illegal address register write $ read test
wr_val = 'hFF;
`uvm_do_on_with(apb_wr_seq, p_sequencer.apb_mst_sqr, {addr == 'h01; data == wr_val;})
`uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'h01;})
compare_data(wr_val, apb_rd_seq.data);
// RO register write reserved field and check
//-- wr_val = 'hFFFFFFFF;
//-- `uvm_do_on_with(apb_wr_seq, p_sequencer.apb_mst_sqr, {addr == 'h0C; data == wr_val;})
//-- `uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'h0C;})
//-- diff_value(1, (wr_val & apb_rd_seq.data), "WDOGINTCLR_REG");
// RW register reserved field check
wr_val = 'hFF;
`uvm_do_on_with(apb_wr_seq, p_sequencer.apb_mst_sqr, {addr == 'h08; data == wr_val << 2;})
`uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'h08;})
compare_data(wr_val << 2, apb_rd_seq.data);
`uvm_info("body","Exiting...",UVM_LOW)
endtask
endclass
`endif //APB_WATCHDOG_illegal_acc_VIRT_SEQ_SV
写入的值是’h3fc,读到的值是默认值0,说明对寄存器的保留域无法修改
3.4.2.3 对只读寄存器进行写测试
`ifndef APB_WATCHDOG_ILLEGAL_ACC_VIRT_SEQ_SV
`define APB_WATCHDOG_ILLEGAL_ACC_VIRT_SEQ_SV
class apb_watchdog_illegal_acc_virt_seq extends apb_watchdog_base_virt_seq;
`uvm_object_utils(apb_watchdog_illegal_acc_virt_seq)
function new(string name = "apb_watchdog_illegal_acc_virt_seq");
super.new(name);
endfunction
virtual task body();
super.body();
`uvm_info("body","Entered...",UVM_LOW)
// RO register write reserved field and check
wr_val = 'hFFFFFFFF;
`uvm_do_on_with(apb_wr_seq, p_sequencer.apb_mst_sqr, {addr == 'h0C; data == wr_val;})
`uvm_do_on_with(apb_rd_seq, p_sequencer.apb_mst_sqr, {addr == 'h0C;})
diff_value(1, (wr_val & apb_rd_seq.data), "WDOGINTCLR_REG");
`uvm_info("body","Exiting...",UVM_LOW)
endtask
endclass
`endif //APB_WATCHDOG_illegal_acc_VIRT_SEQ_SV
对只读寄存器进行写操作,结果发现无法写入,读取到的是默认值,和写入的值按位与以后不为1。