覆盖组可以在程序、模块、类中定义,但都要进行明确地实例化后才可以采样。
类里的功能覆盖率
- class Transactor;
- Transaction tr;
- mailbox mbx_in;
-
- covergroup CovPort; //定义了一个名为 CovPort 的covergroup(覆盖组)
- coverpoint tr.port;
- //覆盖组里至少有一个覆盖点,不然采样谁?
- // tr.port 采样句柄里软件的变量,和数值,若没列举数值,则采样所有合法数值
- endgroup
-
- function new(mailbox mbx_in);
- CovPort = new(); //覆盖组例化
- this.mbx_in = mbx_in;
- endfunction
-
- task main;
- forever begin
- tr = mbx_in.get(); //获取下一个事务
- ifc.cb.port <= tr.port; //发送到待测设计中
- ifc.cb.data <= tr.data;
- CovPort.sample(); //发生采样,收集覆盖率 CovPort:这里相当于实例名称,不是类名
- end
- endtask
- endclass
使用回调函数可以在决定数据采集的位置和时间。
使用功能覆盖率回调函数的测试
- program automatic test;
- Environment env;
-
- initial begin
- Driver_cbs_coverage dcc; //把回调基类Driver_cbs扩展成Driver_cbs_coverage
- env = new();
- env.gen_cfg();
- env.build();
- //创建并登记覆盖率回调函数
- dcc = new();
- env.drv.cbs.push_back(dcc); //放进驱动器的队列中
- env.run();
- env.wrap_up();
- end
- endprogram
用于测试功能覆盖率的回调函数
- class Driver_cbs_coverage extends Driver_cbs;
- covergroup CovPort;
- ...
- endgroup
-
- virtual task post_tx(Transaction tr);
- CovPort.sample(); //采样覆盖率数值
- endtask
- endclass
与直接调用sample方法相比,使用事件触发的好处在于能够借助已有的事件进行采样。
带触发的覆盖组
- event trans_ready; //覆盖组CovPort在测试平台触发trans_ready事件时进行采样
- covergroup CovPort @(trans_ready);
- coverpoint ifc.cb.port; //测量覆盖率
- endgroup
带SV断言的模块
- module mem(simple_bus sb);
- bit[7:0] data, addr;
- event write_event;
-
- cover property
- (@(postedge sb.clock) sb.write_ena == 1)
- -> write_event;
- endmodule
使用SVA触发覆盖组
- program automatic test(simple_bus sb);
-
- covergroup Write_cg @($root.top.m1.write_event);
- coverpoint $root.top.m1.data;
- coverpoint $root.top.m1.addr;
- endgroup
-
- Write_cg wcg;
-
- intitle begin
- wcg = new();
- //在此处添加激励
- sb.write_ena <= 1;
- ...
- # 10000 $finish;
- end
- endprogram
SV会自动为覆盖点创建仓,它通过被采样的表达式的域来确定可能值得范围。对于一个位宽为N的表达式,有2^N个可能值。
覆盖组选项auto_bin_max指明了自动创建仓的最大数目,缺省值是64。值域超过指定的最大值,SV会把值域范围平分到auto_bin_max个仓中。
实际操作中,自动创建bin方法不实用,建议自定义仓或减少auto_bin_max的数值。
使用auto_bin_max并把仓数设置成2
- covergroup CovPort;
- coverpoint tr.port
- {options.auto_bin_max = 2;}
- endgroup
覆盖率报告:
在所有覆盖点中使用auto_bin_max
- covergroup CovPort;
- options.auto_bin_max = 2; //影响port和data
- coverpoint tr.port;
- coverpoint tr.data;
- endgroup
在覆盖点里使用表达式
- class Transaction;
- rand bit[2:0] hdr_len; //范围0-7
- rand bit[3:0] payload_len; //范围0-15
- rand bit[3:0] kind; //范围0-15
- ...
- endclass
-
- Transaction tr;
-
- covergroup CovLen;
- len16 : coverpoint(tr.hdr_len + tr.payload_len);
- len32 : coverpoint(tr.hdr_len + tr.payload_len + 5'b0); //带有额外常量哑元的表达式,可以计算达到5比特精度,从而把自动生成的最大仓数扩大到32。
- endgroup
自动生成的仓适用于匿名数值,如计数值、地址值或2的幂值,而对于其他数值,应该明确对仓命名。SV会自动为枚举类型的仓命名。
为事务长度定义仓
- covergroup CovLen;
- len : coverpoint(tr.hdr_len + tr.payload_len + 5'b0);
- {bins len[] = {[0:23]};}
- endgroup
覆盖率报告:
从图中可以看到长度为23,即十六进制17的一项没有出现过,最长的头是7,最长的负载是15。0-7一共有八种情况,用3bit来表示,0-15一共有16种情况,用4bit来表示。所以总共是7+15=22,而不是23!,我们应该在仓的声明中改成{bins len[] = {[0:22]};}才对。
当自定义仓时,SV不再自动创建仓,而且会忽略掉那些不被事先定义的仓所涵盖的数值,计算功能覆盖率时只会使用创建的仓。
指定仓名
- //对一个4比特变量kind进行采样
- covergroup CovKind;
- coverpoint tr.kind{
- bins zero = {0}; //一个仓zero,对kind采样值为0进行计数
- bins lo = {[1:3],5}; //lo仓代表1:3和5的值
- bins hi[] = {[8:$]}; //数组仓:8个独立的仓8-15,分别保存
- bins misc = default; //一个仓用来保存没有被选中的值
- }
- endgroup
使用关键字iff可以覆盖点添加条件。
条件覆盖——复位期间禁止
- covergroup CoverPort;
- //当reset=1时不要收集覆盖率数据
- coverpoint port iff(!bus_if.reset);
- endgroup
也可以使用start和stop函数来控制覆盖组里各个独立的实例
- initial begin
- CovPort ck = new(); //实例化覆盖组
- //复位期间停止收集覆盖率数据
- # 1ns ck.stop(); //关闭采样
- bus_if.reset = 1;
- # 100ns bus_if.reset = 0;
- ck.start(); //开始采样
- ...
- end
对于枚举类型,SV会为每个可能值创建一个仓。
枚举类型的功能覆盖率
- typedef enum{INIT,DECODE,IDLE} fsmstate_e;
- fsmstate_e pstate, nstate;
- covergroup cg_fsm;
- coverpoint pstate;
- endgroup
- covergroup CoverPort;
- coverpoint port{
- bins t1 = (0 => 1), (0 => 2), (0 => 3);
- }
- endgroup
使用范围表达式可以快速地确定多个翻转过程,(1,2 => 3,4)创建了四个翻转过程,分别是(1 => 3),(1 => 4),(2 => 3),(2 => 4)。
可以使用关键字wildcard来创建多个状态或翻转。在表达式中,任何X、Z或?都会被当成0或1的通配符。
用在覆盖点仓中的通配符
- bit[2:0] port;
- covergroup CoverPort;
- coverpoint port{
- wildcard bins even = {3'b?? 0};
- wildcard bins odd = {3'b?? 1};
- }
使用ignore_bins
排除掉不用来计算功能覆盖率的数值
- bit[2:0] low_ports_0_5; //只使用数值0-5
- covergroup CoverPort;
- coverpoint low_ports_0_5{
- ignore_bins hi = {[6,7]}; //忽略掉6,7两个仓
- }
- endgroup
使用illegal_bins
对仓进行标识,可以捕捉到那些被错误检查程序遗漏掉的状态
- bit[2:0] low_ports_0_5; //只使用数值0-5
- covergroup CoverPort;
- coverpoint low_ports_0_5{
- illegal_bins hi = {[6,7]}; //如果出现6,7两个仓就会报错
- }
- endgroup
如果在状态机上使用了覆盖组,那么就可以使用仓来列出特定的状态和翻转轨迹,使用代码覆盖率工具可以自动提取状态寄存器、状态以及翻转轨迹。