8.1 haddr的covergroup

在之前搭建环境的过程中,我们的cov是继承于subsriber的,subscriber类里面已经声明了uvm_analysis_import,用来接收来自monitor的事物,所以在cov中我们仅需要拿到传输的事物即可,也就是说我们需要实现write方法。 :::danger 【注意】
cov在拿事物的过程中,一定需要注意的是不能直接从mointor中拿事物,必须使用TLM_port来拿这样的一些事务,原因就是不知道monitor在环境中的哪个部分,而且我们的cov有可能从多个monitor中拿取事物,所以必须遵守一定的准则。 ::: :::info 首先确定一下我们需要收集覆盖率的几个内容:

  1. 对addr进行覆盖率的收集(有一定的划分依据):
    1. 首先需要确定能够对addr的边界进行覆盖,这个边界的值之前我们在使用的时候已经声明在cfg中了,subscriber也拿到了cfg这个变量,所以可以直接进行使用而不需要我们单独设定值;
    2. 对于addr进行收集的另外一个关键就是:我们必须确定地址是对齐的,所以可以使用连接运算符来将它们与低两位进行拼接;或者我们可以使用wildcard bins来设置不care的位数;
    3. 另外是对addr的一个byte的访问:
      1. 在地址非对齐的测试(ahb_ram_haddr_word_unaligned_test)中我们发现,对地址的访问可能是连续的:也就是00,01,02,03,对于00来说,可能给byte访问,也可能给word和halfword访问,所以它并不典型,我们选取更加典型的01和03来进行地址的收集。
      2. half_word比较典型的就是使用02进行访问;
      3. 此外我们还可以对越界的地址进行收集,使用地址的最后一位来进行越界的索引即可;
      4. 最后我们还需要对合法的地址范围进行收集 ::: ``systemverilogifndef AHB_RAM_COV_SV `define AHB_RAM_COV_SV

class ahb_ram_cov extends ahb_ram_subscriber;

`uvm_component_utils(ahb_ram_cov)

// Covergroup definition below // T1 ahb_ram_addr coverrage //

covergroup ahbram_addr_cg with function sample(bit [31:0] addr); option.name = “T1 ahb_ram_addr coverage”; ADDR : coverpoint addr { wildcard bins addr_start = {{cfg.addr_start[31:2], 2’b??}}; wildcard bins addr_end = {{cfg.addr_end[31:2], 2’b??}}; wildcard bins addr_byte_acc_b01 = {{30’h????????, 2’b01}}; wildcard bins addrbyte_acc_b11 = {{30’h????????, 2’b11}}; wildcard bins addrhalf_word_acc_b10 = {{30’h????????, 2’b10}}; wildcard bins addrword_acc_b00 = {{30’h????????, 2’b00}}; bins addr_out_of_range = {{cfg.addr_end + 1, 32’hFFFF_FFFF}}; bins legal_addr = {[cfg.addr_start:cfg.addr_end]}; } endgroup

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

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

task do_listen_events(); endtask

virtual function void write(ahb_transaction tr); ahb_ram_addr_cg.sample(tr.addr); endfunction

endclass

`endif // AHB_RAM_COV_SV

  1. <a name="Dek2F"></a>
  2. ## 【调试及结果】
  3. 我们在之前的测试中有关于diff_addr的测试,因此在这里我们可以直接对它进行调用<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22348254/1657974917803-ba1a8400-49fa-45a9-a9b4-65ad9acbfdc2.png#clientId=u2c4e0861-f66e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=290&id=u7da512d1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=363&originWidth=1298&originalType=binary&ratio=1&rotation=0&showTitle=false&size=19499&status=done&style=none&taskId=ub3dd3e84-6742-481f-abec-530f621397e&title=&width=1038.4)
  4. > 调试以后发现报错是Null object access
  5. :::info
  6. 1. 首先我们切换到object_uvm_components中观察层次构建到哪一步了
  7. :::
  8. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/22348254/1657975109283-873c2183-e85c-447d-9358-9e1de0c536ea.png#clientId=u2c4e0861-f66e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=134&id=u77198a2b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=249&originWidth=994&originalType=binary&ratio=1&rotation=0&showTitle=false&size=10399&status=done&style=none&taskId=u1a17c151-8e96-4115-a3e5-bb2678eb60c&title=&width=536)
  9. > 可以看到ahb_master和virt_sqr都已经构建成功了:
  10. > virt_sqr下面也有了fifo和tlm端口;但是ahb_master中还没有构建出任何内容,说明可能是在connect_phase中出现了问题。
  11. :::info
  12. 我们在代码中端口连接的地方设置断点,然后restart
  13. :::
  14. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/22348254/1657975299533-e96c892d-eb3e-4438-98e2-9741645509c5.png#clientId=u2c4e0861-f66e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=244&id=u7464de66&margin=%5Bobject%20Object%5D&name=image.png&originHeight=709&originWidth=1563&originalType=binary&ratio=1&rotation=0&showTitle=false&size=38254&status=done&style=none&taskId=ufa76fdf8-9329-4d81-a099-1e50c96e2e8&title=&width=538)
  15. > 结果还是报错,那说明在connnect_phase的前一步build_phase的时候句柄就悬空了。
  16. :::info
  17. 2. 我们找到build_phase中的scb和cov,结果发现做cov的时候报错,因此我们进一步找到cov中的new函数的位置
  18. :::
  19. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/22348254/1657976067145-9b21a8f5-55c6-4035-8531-d520ca0d657a.png#clientId=u2c4e0861-f66e-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=265&id=ub7450ca7&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1157&originWidth=2315&originalType=binary&ratio=1&rotation=0&showTitle=false&size=108943&status=done&style=none&taskId=u9df596ee-20c8-4eee-9ea4-5b2dbfa6308&title=&width=530)
  20. > 设置断点,当运行到这一行的时候点击跳出的按钮,结果发现报错
  21. > 分析:我们在new函数中对covergroup进行了例化,例化的过程中cov对cfg进行了访问,但是cfg是在父类的build_phase中才得到的:
  22. > 在对covergroup进行例化的时候会对bins开辟空间,开辟空间我们需要确认这里面的范围是什么,但是这个时候调用不到cfg,所以句柄悬空了最后导致报错。
  23. ---
  24. :::info
  25. 解决办法:
  26. :::
  27. 对于不能认cfg中的变量,我们的解决方法就是先给它设定一个形参,在采样的时候把实参传进去。
  28. ```systemverilog
  29. `ifndef AHB_RAM_COV_SV
  30. `define AHB_RAM_COV_SV
  31. class ahb_ram_cov extends ahb_ram_subscriber;
  32. `uvm_component_utils(ahb_ram_cov)
  33. // Covergroup definition below
  34. // T1 ahb_ram_addr coverrage
  35. //
  36. covergroup ahb_ram_addr_cg with function sample(bit [31:0] addr, bit [31:0] addr_start, bit [31:0] addr_end);
  37. option.name = "T1 ahb_ram_addr coverage";
  38. ADDR : coverpoint addr {
  39. wildcard bins addr_start = {{addr_start[31:2], 2'b??}};
  40. wildcard bins addr_end = {{addr_end[31:2], 2'b??}};
  41. wildcard bins addr_byte_acc_b01 = {{30'h????_????, 2'b01}};
  42. wildcard bins addr_byte_acc_b11 = {{30'h????_????, 2'b11}};
  43. wildcard bins addr_half_word_acc_b10 = {{30'h????_????, 2'b10}};
  44. wildcard bins addr_word_acc_b00 = {{30'h????_????, 2'b00}};
  45. bins addr_out_of_range = {[addr_end[31:0] + 1 : 32'hFFFF_FFFF]};
  46. bins legal_addr = {[addr_start : addr_end]};
  47. }
  48. endgroup
  49. function new (string name = "ahb_ram_cov", uvm_component parent);
  50. super.new(name, parent);
  51. ahb_ram_addr_cg = new();
  52. endfunction
  53. function void build_phase(uvm_phase phase);
  54. super.build_phase(phase);
  55. endfunction
  56. task do_listen_events();
  57. endtask
  58. virtual function void write(ahb_transaction tr);
  59. ahb_ram_addr_cg.sample(tr.addr, cfg.addr_start, cfg.addr_end);
  60. endfunction
  61. endclass
  62. `endif // AHB_RAM_COV_SV

image.png

结果仍然是报错,在使用的时候它不认我们声明的变量的范围

:::info 那么我们就只能考虑另外一种办法:
在创建covergroup的时候就将变量作为参数传递进去,之后再考虑动态传递的问题 :::

  1. `ifndef AHB_RAM_COV_SV
  2. `define AHB_RAM_COV_SV
  3. class ahb_ram_cov extends ahb_ram_subscriber;
  4. `uvm_component_utils(ahb_ram_cov)
  5. // Covergroup definition below
  6. // T1 ahb_ram_addr coverrage
  7. //
  8. covergroup ahb_ram_addr_cg(bit [31:0] addr_start, bit [31:0] addr_end) with function sample(bit [31:0] addr);
  9. option.name = "T1 ahb_ram_addr coverage";
  10. ADDR : coverpoint addr {
  11. wildcard bins addr_start = {{addr_start[31:2], 2'b??}};
  12. wildcard bins addr_end = {{addr_end[31:2], 2'b??}};
  13. wildcard bins addr_byte_acc_b01 = {{30'h????_????, 2'b01}};
  14. wildcard bins addr_byte_acc_b11 = {{30'h????_????, 2'b11}};
  15. wildcard bins addr_half_word_acc_b10 = {{30'h????_????, 2'b10}};
  16. wildcard bins addr_word_acc_b00 = {{30'h????_????, 2'b00}};
  17. bins addr_out_of_range = {[addr_end[31:0] + 1 : 32'hFFFF_FFFF]};
  18. bins legal_addr = {[addr_start : addr_end]};
  19. }
  20. endgroup
  21. function new (string name = "ahb_ram_cov", uvm_component parent);
  22. super.new(name, parent);
  23. ahb_ram_addr_cg = new(32'h0000, 32'hFFFF);
  24. endfunction
  25. function void build_phase(uvm_phase phase);
  26. super.build_phase(phase);
  27. endfunction
  28. task do_listen_events();
  29. endtask
  30. virtual function void write(ahb_transaction tr);
  31. ahb_ram_addr_cg.sample(tr.addr);
  32. endfunction
  33. endclass
  34. `endif // AHB_RAM_COV_SV

我们在声明covergroup的时候就将参数传递进去,那么在new函数的时候就需要给定一个初始的值。

接下来就是运行测试并观察覆盖率
image.png

可以看到ADDR这个组的覆盖率到达了87.5,对于边界地址的hit有510次,但是边界地址应该是只有一次的,这显然是不合理的。

:::info 对coverpoint进行修改:

  1. 我们将对地址是否按字节、字、半字访问放在一个coverpoint中:
    1. 需要注意的是对于一个字类型访问而言,按照地址0,4,8,c进行访问,所以0,1,2,3我们都可以看作是开始时候的边界值;
    2. 同样的,一个地址时候的边界我们可以看作是addr_end-3开始; ::: ``systemverilogifndef AHB_RAM_COV_SV `define AHB_RAM_COV_SV

class ahb_ram_cov extends ahb_ram_subscriber;

`uvm_component_utils(ahb_ram_cov)

// Covergroup definition below // T1 ahb_ram_addr coverrage //

covergroup ahb_ram_addr_cg(bit [31:0] addr_start, bit [31:0] addr_end) with function sample(bit [31:0] addr); option.name = “T1 ahb_ram_addr coverage”; ADDR : coverpoint addr { bins addr_start = {[addr_start : addr_start+3]}; bins addr_end = {[addr_end-3 : addr_end]}; bins addr_out_of_range = {[addr_end+1 : 32’hFFFF_FFFF]}; bins legal_range[16] = {[addr_start : addr_end]}; } BYTEACC : coverpoint addr[1:0]{ wildcard bins addr_byte_acc_b01 = {2’b01}; wildcard bins addr_byte_acc_b11 = {2’b11};
wildcard bins addr_half_word_acc_b10 = {2’b10};
wildcard bins addr_word_acc_b00 = {2’b00};
} endgroup

function new (string name = “ahb_ram_cov”, uvm_component parent); super.new(name, parent); ahb_ram_addr_cg = new(32’h0000, 32’hFFFF); endfunction

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

task do_listen_events(); endtask

virtual function void write(ahb_transaction tr); ahb_ram_addr_cg.sample(tr.addr); endfunction

endclass

`endif // AHB_RAM_COV_SV

  1. ---
  2. 修改完成以后我们再次进行覆盖率的收集:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22348254/1658064951980-2011055a-c346-48fc-916a-ee53895eb9af.png#clientId=u38e092e8-956f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=257&id=u3cc3c48a&margin=%5Bobject%20Object%5D&name=image.png&originHeight=627&originWidth=1315&originalType=binary&ratio=1&rotation=0&showTitle=false&size=62765&status=done&style=none&taskId=ub023f4b7-e937-4998-8c01-5602bcc2528&title=&width=540)
  3. > 可以看到在这个covergroup中的覆盖率正常了,没有覆盖到addr_startaddr_end的原因是在seq给定第一个地址的时候默认了最低位是0
  4. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/22348254/1658065155405-bb2f142c-563b-48c9-8e89-a98843161c9b.png#clientId=u38e092e8-956f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=246&id=uf2dd8b2c&margin=%5Bobject%20Object%5D&name=image.png&originHeight=601&originWidth=1317&originalType=binary&ratio=1&rotation=0&showTitle=false&size=34734&status=done&style=none&taskId=u0458d156-e008-4734-9157-85985389526&title=&width=539)
  5. > 另外我们可以看到在BYTEACC这个组中我们没有收集到关于半字和其他地址的访问,原因是我们跑的ahb_ram_diff_haddr_test就是字对齐的测试。
  6. > 为了解决这个问题,我们需要跑其他几个内存不对齐的测试,然后将他们的覆盖率结果merge
  7. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/22348254/1658065370222-2105b928-f449-4b8a-9011-12d617270831.png#clientId=u38e092e8-956f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=256&id=u69dca78c&margin=%5Bobject%20Object%5D&name=image.png&originHeight=624&originWidth=1313&originalType=binary&ratio=1&rotation=0&showTitle=false&size=49179&status=done&style=none&taskId=u5af0607c-5e1e-40e4-952b-1c5d53bc5b7&title=&width=539)<br />我们可以看到,跑了其他测试merge以后,cov提高到了92.11%。
  8. <a name="feN2c"></a>
  9. # 8.2 burst_size 和 burst_type的covergroup
  10. ```systemverilog
  11. `ifndef AHB_RAM_COV_SV
  12. `define AHB_RAM_COV_SV
  13. class ahb_ram_cov extends ahb_ram_subscriber;
  14. `uvm_component_utils(ahb_ram_cov)
  15. // Covergroup definition below
  16. // T1 ahb_ram_addr coverrage
  17. //
  18. covergroup ahb_ram_t1_addr_cg(bit [31:0] addr_start, bit [31:0] addr_end) with function sample(bit [31:0] addr);
  19. option.name = "T1 ahb_ram_addr coverage";
  20. ADDR : coverpoint addr {
  21. bins addr_start = {[addr_start : addr_start+3]};
  22. bins addr_end = {[addr_end-3 : addr_end]};
  23. bins addr_out_of_range = {[addr_end+1 : 32'hFFFF_FFFF]};
  24. bins legal_range[16] = {[addr_start : addr_end]};
  25. }
  26. BYTEACC : coverpoint addr[1:0]{
  27. bins addr_byte_acc_b01 = {2'b01};
  28. bins addr_byte_acc_b11 = {2'b11};
  29. bins addr_half_word_acc_b10 = {2'b10};
  30. bins addr_word_acc_b00 = {2'b00};
  31. }
  32. endgroup
  33. covergroup ahb_ram_t2_type_size_cg with function sample(burst_size_enum bsize, burst_type_enum btype);
  34. BURST_TYPE : coverpoint bsize{
  35. bins size_8bit = {BURST_SIZE_8BIT};
  36. bins size_16bit = {BURST_SIZE_8BIT};
  37. bins size_32bit = {BURST_SIZE_8BIT};
  38. bins size_64bit = {BURST_SIZE_8BIT};
  39. }
  40. BUSY_SIZE : coverpoint btype{
  41. bins type_single = {SINGLE};
  42. bins type_incr = {INCR};
  43. bins type_wrap4 = {WRAP4};
  44. bins type_icnr4 = {INCR4};
  45. }
  46. endgroup
  47. function new (string name = "ahb_ram_cov", uvm_component parent);
  48. super.new(name, parent);
  49. ahb_ram_t1_addr_cg = new(32'h0000, 32'hFFFF);
  50. ahb_ram_t2_type_size_cg = new();
  51. endfunction
  52. function void build_phase(uvm_phase phase);
  53. super.build_phase(phase);
  54. endfunction
  55. task do_listen_events();
  56. endtask
  57. virtual function void write(ahb_transaction tr);
  58. ahb_ram_t1_addr_cg.sample(tr.addr);
  59. ahb_ram_t2_type_size_cg.sample(tr.burst_size, tr.burst_type);
  60. endfunction
  61. endclass
  62. `endif // AHB_RAM_COV_SV

【调试及结果】

同样的,我们声明第二个covergroup,分别运行haddr、hsize和haddr_word_unaligned_test,将测试得到的结果的覆盖率进行merge,最后结果如下:

image.png
image.png


image.png
另外,我们在merge以后就会生成一个report到当前的sim文件夹下,我们输入pwd找到当前文件夹的目录,然后再firefox中可以打开报告。 :::info 覆盖率在以下这个路径中进行查看:urgReport/dashboard.html/groups ::: image.png
image.png