6.1 hsize测试的实现
- 由于memory的限制,该ahb_ram是只支持SINGLE读写操作的,所以我们不再考虑其他transfer_type的情况;
但是激励的不同transfer_size的情况我们没有进行测试,所以接下来我们对不同位数的transaction进行测试。 :::info
同样的,我们复用一下之前的ahb_ram_smoke_virt_seq测试序列;
- 在single_write()和single_read中已经声明了hsize变量,所以在这个seq中我们对它进行约束即可;
由于sequence继承于base_virt_seq,因此它使用了父类中的compare_data函数,另外我们自己还搭建了用于进行比较的scoreboard,通过这两部分可以对写入和读出来的值进行比较。
- 需要注意的是diff_hsize_virt_seq的激励该如何给呢?存放在scoreboard中mem的数据是data按位与之后的结果。
- 另外,写入的值和读出来的值应当是一致的。
:::
``systemverilog
ifndef AHB_RAM_DIFF_HSIZE_VIRT_SEQ_SV `define AHB_RAM_DIFF_HSIZE_VIRT_SEQ_SV
class ahb_ram_diff_hsize_virt_seq extends ahb_ram_base_virt_seq;
`uvm_object_utils(ahb_ram_diff_hsize_virt_seq)
function new(string name = “ahb_ram_diff_hsize_virt_seq”);
super.new(name);
endfunction
virtual task body();
bit [31:0] addr, data;
burst_size_enum bsize;
super.body();
`uvm_info("body","Entered...",UVM_LOW)
for(int i=0; i<10; i++) begin
std::randomize(addr) with {addr[1:0] == 0; addr inside {['h1000:'h1FFF]};};
std::randomize(wr_val) with {wr_val == (i << 4) + i;};
data = wr_val;
`uvm_do_with(single_write, {addr == local::addr; data == local::data; bsize inside {BURST_SIZE_8BIT, BURST_SIZE_16BIT, BURST_SIZE_32BIT};})
`uvm_do_with(single_read, {addr == local::addr;})
rd_val = single_read.data;
case(bsize)
BURST_SIZE_8BIT : wr_val = wr_val & 32'hFF;
BURST_SIZE_16BIT : wr_val = wr_val & 32'hFFFF;
BURST_SIZE_32BIT : wr_val = wr_val & 32'hFFFFFFFF;
default : `uvm_error("TYPEERR", "bsize is not recognized")
endcase
compare_data(wr_val, rd_val);
end
`uvm_info("body","EXited...", UVM_LOW)
endtask
endclass
`endif //AHB_RAM_DIFF_HSIZE_VIRT_SEQ_SV
```systemverilog
`ifndef AHB_RAM_TESTS_SVH
`define AHB_RAM_TESTS_SVH
`include "ahb_ram_base_test.sv"
`include "ahb_ram_smoke_test.sv"
`include "ahb_ram_diff_hsize_test.sv"
`endif // AHB_RAM_TESTS_SVH
【调试及结果】
一共比较了十次都成功 第一次hsize的值为0,写入的数据是8bit的,也就是说低8位写入的值是0,所以在读操作第二拍的下降沿采样到的数据低8位就是00,高位由于是不定的状态所以就是x值。 同样的,第二次hsize的值为1,写入和读出的数据都是16bit的。
为了确保对于一个16bit的数据而言,低8位和高8位都是有数据的,我们在给定测试激励的时候将i左移8位。
scoreboard和sequence分别比较出来的结果没有错误。
根据ahb的协议,我们还需要测试ahb_ram这个模块是否一定需要满足自对齐的问题
查看AHB的协议文档,我们可以发现,如果传输的字类型必须和地址的边界对齐的话,那么低两位都必须是0,如果是harfword也就是16bit的话,那么只需要最低位为0即可。
6.2 haddr测试的实现
同样的,我们将hsize的测试和测试序列复用到haddr中。 :::info 对于haddr的测试序列,我们需要考虑的问题就是:
- 不需要一个序列的bsize变量;
我们对一个序列对应的地址进行读写的时候,需要考虑:
- 对地址随机化以后的范围;
- 在给定激励的时候如何给到地址的边界?
:::
``systemverilog
ifndef AHB_RAM_DIFF_HADDR_VIRT_SEQ_SV `define AHB_RAM_DIFF_HADDR_VIRT_SEQ_SV
class ahb_ram_diff_haddr_virt_seq extends ahb_ram_base_virt_seq;
`uvm_object_utils(ahb_ram_diff_haddr_virt_seq)
function new(string name = “ahb_ram_diff_haddr_virt_seq”);
super.new(name);
endfunction
virtual task body();
bit [31:0] addr, data;
super.body();
`uvm_info("body","Entered...",UVM_LOW)
for(int i=0; i<(cfg.addr_end >> 4); i++) begin
std::randomize(addr) with {addr[1:0] == 0; addr inside {[cfg.addr_start:cfg.addr_end]};};
std::randomize(wr_val) with {wr_val == (i << 8) + i;};
data = wr_val;
`uvm_do_with(single_write, {addr == local::addr; data == local::data;})
`uvm_do_with(single_read, {addr == local::addr;})
rd_val = single_read.data;
compare_data(wr_val, rd_val);
end
`uvm_info("body","EXited...", UVM_LOW)
endtask
endclass
`endif //AHB_RAM_DIFF_HADDR_VIRT_SEQ_SV
```systemverilog
`ifndef AHB_RAM_DIFF_HADDR_TEST_SV
`define AHB_RAM_DIFF_HADDR_TEST_SV
class ahb_ram_diff_haddr_test extends ahb_ram_base_test;
`uvm_component_utils(ahb_ram_diff_haddr_test)
function new(string name = "ahb_ram_diff_haddr_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);
ahb_ram_diff_haddr_virt_seq seq = ahb_ram_diff_haddr_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 //AHB_RAM_DIFF_HADDR_TEST_SV
【调试及结果】
可以看到,我们的seq和scoreboard分别进行了4095次检查且没有错误产生。
:::danger 【思考】
- 在之前的测试中我们只是进行了single_write和single_read的测试。
- 那么我们在对AHB协议进行检查的时候,如果对进行了一次single_write以后,我们对该地址进行复位,复位以后再对同一个地址做一次single_read,那么这时候读回来的值应该为x。但是scoreboard我们没有设定使他支持这样的功能。对于这种scoreboard不支持的测试序列而言我们常用的思路有两种:
- 更新scoreboard使他支持这些复杂的测试序列;
- 直接将scoreboard关闭,我们只在seq中进行检查。
- 如果DUT中的一些功能特别复杂的时候,我们可以考虑只在seq中检查特定的功能,或者我们考虑在scoreboard中写多个小的task对这些功能进行测试检查。
:::
6.3 reset_w2r测试的实现
这一部分的测试就是完成我们在6.2中末尾提到的思考中的第二点。 :::info 首先我们进行一个正常的single_write和single_read操作;
之后我们对memory进行复位,通常来说对一个memory的复位操作我们已经放在了if中;
复位以后该地址对应的初始值我们可以作为配置信息放在cfg中。 ::: 由于我们进行single_write,single_read和复位以后读的都是同一个地址,所以将这个地址我们放在一个fifo中来进行存储,这样方便随时使用。使用全等运算符的原因是我们要使用他们的比较值来进行if语句的判断,全等运算符是由确定比较结果的,而相等运算符的结果也有可能是不定的。
`ifndef AHB_RAM_RESET_W2R_VIRT_SEQ_SV
`define AHB_RAM_RESET_W2R_VIRT_SEQ_SV
class ahb_ram_reset_w2r_virt_seq extends ahb_ram_base_virt_seq;
`uvm_object_utils(ahb_ram_reset_w2r_virt_seq)
function new(string name = "ahb_ram_reset_w2r_virt_seq");
super.new(name);
endfunction
virtual task body();
bit [31:0] addr, data;
bit [31:0] addr_q[$];
super.body();
`uvm_info("body","Entered...",UVM_LOW)
// Normale write & read test
for(int i=0; i<10; i++) begin
std::randomize(addr) with {addr[1:0] == 0; addr inside {['h1000:'h1FFF]};};
std::randomize(wr_val) with {wr_val == (i << 4) + i;};
data = wr_val;
addr_q.push_back(addr);
`uvm_do_with(single_write, {addr == local::addr; data == local::data;})
`uvm_do_with(single_read, {addr == local::addr;})
rd_val = single_read.data;
compare_data(wr_val, rd_val);
end
// reset the memory
vif.assert_reset(10);
// check the rd_val & the reset value
do begin
addr = addr_q.pop_front();
`uvm_do_with(single_read, {addr == local::addr;})
rd_val = single_read.data;
if(cfg.init_logic === 1'b0) begin
compare_data(32'h0, rd_val);
end
else if(cfg.init_logic === 1'bx) begin
compare_data(32'hx, rd_val);
end
else begin
`uvm_error("TYPEERR", "rd_val is not recgonized")
end
`uvm_info("body","EXited...", UVM_LOW)
end while(addr_q.size() > 0);
endtask
endclass
`endif //AHB_RAM_RESET_W2R_VIRT_SEQ_SV
【调试及结果】
根据结果,seq中一共比较了20次,其中复位前比较了10次,复位后比较了10次。 报的10个错误都是复位后比较报错,按照我们对于DUT的理解来看,在reset以后,memory中读取到的数据应该是不定态X。为了对这个bug进行调试,我们需要追踪到reset在DUT中的驱动方式。
我们可以用以下流程来check信号线的驱动问题。
https://www.yuque.com/milanotang/dkl2fm/dhox41/edit?toc_node_uuid=e3lLpKWL9RD9_65o