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按位与之后的结果。
    • 另外,写入的值和读出来的值应当是一致的。 ::: ``systemverilogifndef 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”);

    1. super.new(name);

    endfunction

    virtual task body();

    1. bit [31:0] addr, data;
    2. burst_size_enum bsize;
    3. super.body();
    4. `uvm_info("body","Entered...",UVM_LOW)
    5. for(int i=0; i<10; i++) begin
    6. std::randomize(addr) with {addr[1:0] == 0; addr inside {['h1000:'h1FFF]};};
    7. std::randomize(wr_val) with {wr_val == (i << 4) + i;};
    8. data = wr_val;
    9. `uvm_do_with(single_write, {addr == local::addr; data == local::data; bsize inside {BURST_SIZE_8BIT, BURST_SIZE_16BIT, BURST_SIZE_32BIT};})
    10. `uvm_do_with(single_read, {addr == local::addr;})
    11. rd_val = single_read.data;
    12. case(bsize)
    13. BURST_SIZE_8BIT : wr_val = wr_val & 32'hFF;
    14. BURST_SIZE_16BIT : wr_val = wr_val & 32'hFFFF;
    15. BURST_SIZE_32BIT : wr_val = wr_val & 32'hFFFFFFFF;
    16. default : `uvm_error("TYPEERR", "bsize is not recognized")
    17. endcase
    18. compare_data(wr_val, rd_val);
    19. end
    20. `uvm_info("body","EXited...", UVM_LOW)

    endtask

    endclass

`endif //AHB_RAM_DIFF_HSIZE_VIRT_SEQ_SV

  1. ```systemverilog
  2. `ifndef AHB_RAM_TESTS_SVH
  3. `define AHB_RAM_TESTS_SVH
  4. `include "ahb_ram_base_test.sv"
  5. `include "ahb_ram_smoke_test.sv"
  6. `include "ahb_ram_diff_hsize_test.sv"
  7. `endif // AHB_RAM_TESTS_SVH

【调试及结果】

image.png
image.png

一共比较了十次都成功 第一次hsize的值为0,写入的数据是8bit的,也就是说低8位写入的值是0,所以在读操作第二拍的下降沿采样到的数据低8位就是00,高位由于是不定的状态所以就是x值。 同样的,第二次hsize的值为1,写入和读出的数据都是16bit的。

为了确保对于一个16bit的数据而言,低8位和高8位都是有数据的,我们在给定测试激励的时候将i左移8位。
scoreboard和sequence分别比较出来的结果没有错误。


根据ahb的协议,我们还需要测试ahb_ram这个模块是否一定需要满足自对齐的问题

image.png

查看AHB的协议文档,我们可以发现,如果传输的字类型必须和地址的边界对齐的话,那么低两位都必须是0,如果是harfword也就是16bit的话,那么只需要最低位为0即可。

6.2 haddr测试的实现

同样的,我们将hsize的测试和测试序列复用到haddr中。 :::info 对于haddr的测试序列,我们需要考虑的问题就是:

  • 不需要一个序列的bsize变量;
  • 我们对一个序列对应的地址进行读写的时候,需要考虑:

    • 对地址随机化以后的范围;
    • 在给定激励的时候如何给到地址的边界? ::: ``systemverilogifndef 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”);

    1. super.new(name);

    endfunction

    virtual task body();

    1. bit [31:0] addr, data;
    2. super.body();
    3. `uvm_info("body","Entered...",UVM_LOW)
    4. for(int i=0; i<(cfg.addr_end >> 4); i++) begin
    5. std::randomize(addr) with {addr[1:0] == 0; addr inside {[cfg.addr_start:cfg.addr_end]};};
    6. std::randomize(wr_val) with {wr_val == (i << 8) + i;};
    7. data = wr_val;
    8. `uvm_do_with(single_write, {addr == local::addr; data == local::data;})
    9. `uvm_do_with(single_read, {addr == local::addr;})
    10. rd_val = single_read.data;
    11. compare_data(wr_val, rd_val);
    12. end
    13. `uvm_info("body","EXited...", UVM_LOW)

    endtask

    endclass

`endif //AHB_RAM_DIFF_HADDR_VIRT_SEQ_SV

  1. ```systemverilog
  2. `ifndef AHB_RAM_DIFF_HADDR_TEST_SV
  3. `define AHB_RAM_DIFF_HADDR_TEST_SV
  4. class ahb_ram_diff_haddr_test extends ahb_ram_base_test;
  5. `uvm_component_utils(ahb_ram_diff_haddr_test)
  6. function new(string name = "ahb_ram_diff_haddr_test", uvm_component parent);
  7. super.new(name, parent);
  8. endfunction
  9. function void build_phase(uvm_phase phase);
  10. super.build_phase(phase);
  11. endfunction
  12. task run_phase(uvm_phase phase);
  13. ahb_ram_diff_haddr_virt_seq seq = ahb_ram_diff_haddr_virt_seq::type_id::create("this");
  14. super.run_phase(phase);
  15. phase.raise_objection(this);
  16. seq.start(env.virt_sqr);
  17. phase.drop_objection(this);
  18. endtask
  19. endclass
  20. `endif //AHB_RAM_DIFF_HADDR_TEST_SV

【调试及结果】

image.png
image.png

可以看到,我们的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语句的判断,全等运算符是由确定比较结果的,而相等运算符的结果也有可能是不定的。

  1. `ifndef AHB_RAM_RESET_W2R_VIRT_SEQ_SV
  2. `define AHB_RAM_RESET_W2R_VIRT_SEQ_SV
  3. class ahb_ram_reset_w2r_virt_seq extends ahb_ram_base_virt_seq;
  4. `uvm_object_utils(ahb_ram_reset_w2r_virt_seq)
  5. function new(string name = "ahb_ram_reset_w2r_virt_seq");
  6. super.new(name);
  7. endfunction
  8. virtual task body();
  9. bit [31:0] addr, data;
  10. bit [31:0] addr_q[$];
  11. super.body();
  12. `uvm_info("body","Entered...",UVM_LOW)
  13. // Normale write & read test
  14. for(int i=0; i<10; i++) begin
  15. std::randomize(addr) with {addr[1:0] == 0; addr inside {['h1000:'h1FFF]};};
  16. std::randomize(wr_val) with {wr_val == (i << 4) + i;};
  17. data = wr_val;
  18. addr_q.push_back(addr);
  19. `uvm_do_with(single_write, {addr == local::addr; data == local::data;})
  20. `uvm_do_with(single_read, {addr == local::addr;})
  21. rd_val = single_read.data;
  22. compare_data(wr_val, rd_val);
  23. end
  24. // reset the memory
  25. vif.assert_reset(10);
  26. // check the rd_val & the reset value
  27. do begin
  28. addr = addr_q.pop_front();
  29. `uvm_do_with(single_read, {addr == local::addr;})
  30. rd_val = single_read.data;
  31. if(cfg.init_logic === 1'b0) begin
  32. compare_data(32'h0, rd_val);
  33. end
  34. else if(cfg.init_logic === 1'bx) begin
  35. compare_data(32'hx, rd_val);
  36. end
  37. else begin
  38. `uvm_error("TYPEERR", "rd_val is not recgonized")
  39. end
  40. `uvm_info("body","EXited...", UVM_LOW)
  41. end while(addr_q.size() > 0);
  42. endtask
  43. endclass
  44. `endif //AHB_RAM_RESET_W2R_VIRT_SEQ_SV

【调试及结果】

image.png

根据结果,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