三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

SV学习记录(九)

SV学习记录(九)

目录

9.1 覆盖率类型

introduction

代码覆盖率

功能覆盖率

漏洞率

断言覆盖率

9.2 功能覆盖策略

收集information而非data

只测量你用得到的

测试的完备性

9.3 功能覆盖率的例子

9.4 cover group解析

9.5 触发covergroup

用callback采样

带事件触发的covergroup

用SVA触发

9.6 数据采样

bin和coverage

自动创建bin

限制自动创建bin的数量

采样表达式

用户自定义的bin发现bug


9.1 覆盖率类型

introduction

如果我们采用覆盖率来判断验证的完备性,那么设计流程就是如下所示的反馈环路

怎样收集覆盖率呢?通过改变种子生成不同的测试,你就可以得到数据流过的部分,知道覆盖率大小

一个测试应该跑多个种子,确保覆盖。

只有测试成功得到的覆盖率才是有效的,测试失败的覆盖率需要丢弃。

测试过程中,如果覆盖率仍然在增长,那就改变种子继续测试;如果几乎平稳了,那就应该改变约束;如果你认为只剩下最后一个特定的部分还没被测,那可以针对这部分写定向测试,但是其他部分还是要用随机测试。

代码覆盖率

line coverage:有多少行代码被覆盖了;path多少条路径被覆盖了;toggle多少个单bit变量被覆盖了;FSM多少个状态机和状态机中的多少个状态被覆盖了。

代码覆盖率衡量的是我们写的test对DUT的测试有多彻底,因此就算代码覆盖率达到了100%,也可能会出问题。比如,就算测试到了某几行,这几行实现的功能一定是对的吗?

下面展示了一个D触发器,它的代码覆盖率可以达到100,但是功能是错的。

Sample 9.1 Incomplete D-flip flop model missing a path module dff(output logic q, q_l, input logic clk, d, reset_l); always @(posedge clk or negedge reset_l) begin q <= d; q_l <= !d; end endmodule

功能覆盖率

设计准则规定了设备应该怎么运行,同时也告诉测试人员一项功能该怎么去测试、验证、衡量。如果你正在收集覆盖的功能数,那就是在计算功能覆盖率。

功能覆盖率和设计意图紧密相连,因此它也叫做“设计规范覆盖率”

漏洞率

衡量发现的bug数量也能测量覆盖率,越接近流片,每周发现的bug应该越少

断言覆盖率

断言是一次性或一段时间内检查两个信号关系的声明,虽然你也可以用程序性代码实现,但是SVA会更简单。

断言有局部变量,可以做简单的数据检查。如果要检查更复杂的协议,那用程序性代码会好一点。

断言经常用于查找错误,比如两个信号是否互斥、请求是否被许可。断言也可以用来检查仲裁算法、FIFO和其他硬件。这时候用assert property。

断言也可以用于查找感兴趣的信号值或设计状态,这时候要用cover property。

9.2 功能覆盖策略

在写测试代码之前,你应该思考什么是关键设计特征、边界情形和可能的失败情况,主要的困难部分就是对这些进行测试。

收集information而非data

下面以FIFO为例。假如你有一个1K大小的FIFO,你可以比较读写的索引,但是这么做得到的数据太多了。

在更抽象的层次看,FIFO里可以装0——N-1个数据,如果你往里放了100个,又放了100个,那你需不需要知道FIFO在这个过程中数据达到过150个呢?答案是不需要,你只需要能成功读出来就行。

FIFO的边界情形是满和空,如果空->满->空没问题,那么剩下的情形都被包含在这个过程中了。

通过上面说的,你可能看出来我们想关注的状态和FIFO的大小是无关的。

如果设计的范围太大,那就应该拆成小范围+边界。

只测量你用得到的

功能测试需要性能,所用时间也很长,所以最好只测量你用的到的部分。

你可以在编译、初始化、触发的时候收集覆盖率。但是最后一个通常不用,因为它会导致报告中全是覆盖率为0的模块。

测试的完备性

我们最终希望达到右上角的状态,也即功能覆盖率和代码覆盖率都很高。

如果功能覆盖率高,代码覆盖率低。你应该回去根据设计准则检查测试平台。

如果代码覆盖率高,但是功能覆盖率低。首先要看DUT是否正确实现了设计准则中要求的功能。如果没问题,那么你可能需要用工具提取设计状态并产生激励。

如果二者都很高了,别忘了看看bug rate。

9.3 功能覆盖率的例子

测试平台中,我们通常会采样一些变量和表达式的值,这些采样点我们通常称为覆盖点。

下面设计的事务有八种不同的情形。通过检测port的值来进行测试。

covergroup可以理解为一个记录表,记录表本身不被执行,它的里面有coverpoint(覆盖点)和bin(仓库),coverpoint是我们要观测的变量或表达式,bin包含每个coverpoint可能的值。

ck.sample();是显式调用sample函数,测量这个时刻coverpoint的值。

Sample 9.2 Functional coverage of a simple object program automatic test(busifc.TB ifc); ​ class Transaction; rand bit [31:0] data; rand bit [ 2:0] port; // Eight port numbers endclass covergroup CovPort; coverpoint tr.port; // Measure coverage endgroup initial begin Transaction tr; CovPort ck; ck = new(); // Instantiate group tr = new(); repeat (32) begin // Run a few cycles assert(tr.randomize); // Create a transaction ifc.cb.port <= tr.port; // and transmit ifc.cb.data <= tr.data; // onto interface ck.sample(); // Gather coverage @ifc.cb; // Wait a cycle end end endprogram

下面是输出结果。

可以看到,auto[0]没有被产生,覆盖率不是100%。

at least表示这个事件至少要被触发的次数。hits是实际产生了的次数。

Sample 9.3 Coverage report for a simple object Coverpoint Coverage report CoverageGroup: CovPort Coverpoint: tr.port Summary Coverage: 87.50 Goal: 100 Number of Expected auto-bins: 8 Number of User Defined Bins: 0 Number of Automatically Generated Bins: 7 Number of User Defined Transitions: 0 Automatically Generated Bins Bin # hits at least ================================ auto[1] 7 1 auto[2] 7 1 auto[3] 1 1 auto[4] 5 1 auto[5] 4 1 auto[6] 2 1 auto[7] 6 1 ================================

对于上面这种没有完全覆盖的情况,解决办法是多跑几遍,或试一些新的种子。

如果一些coverpoint只有0-1次hit,那你可能就需要尝试新的策略。

下面是完全覆盖的情况。

Sample 9.4 Coverage report for a simple object, 100% coverage Coverpoint Coverage report CoverageGroup: CovPort Coverpoint: tr.port Summary Coverage: 100 Goal: 100 Number of Expected auto-bins: 8 Number of User Defined Bins: 0 Number of Automatically Generated Bins: 8 Number of User Defined Transitions: 0 Automatically Generated Bins Bin # hits at least ================================ auto[0] 1 1 auto[1] 7 1 auto[2] 7 1 auto[3] 1 1 auto[4] 5 1 auto[5] 4 1 auto[6] 2 1 auto[7] 6 1 ================================

9.4 cover group解析

covergroup和class是类似的,都是声明一次,例化一次或多次。它有cover points, options, formal arguments, and an optional trig ger。它有一个或多个数据点,都在同一时间采集。

covergroup的名字可以长一些,但是一定要清晰,因为这会有助于你读report。

covergroup可以在class、program和module内部定义。它可以采样任何可见的变量,定义在class中的covergroup可以采样类中的变量。

不要在数据类中定义covergroup。比如你想统计一个酒吧里顾客消费的啤酒数,没有必要从装货卸货开始,只需要统计每个顾客消费了多少就可以。

covergroup应该定义在合适的抽象层次。任何事物的采样都需要等到数据被DUT接收以后,如果你注入的错误是导致数据传输失败,那就应该修改对应的覆盖点。

一个class中可以有多个coverpoint。一个coverpoint必须例化以后才能采样。

下面的代码是在class中定义了coverpoint,如果你选择在class中定义,那么在实例化的时候不需要再赋别的名字,直接用定义名就可以。

Sample 9.5 Functional coverage inside a class class Transactor; Transaction tr; mailbox mbx_in; covergroup CovPort; coverpoint tr.port; endgroup function new(mailbox mbx_in); CovPort = new(); // Instantiate covergroup this.mbx_in = mbx_in; endfunction task main; forever begin tr = mbx_in.get; // Get next transaction ifc.cb.port <= tr.port; // Send into DUT ifc.cb.data <= tr.data; CovPort.sample(); // Gather coverage end endtask endclass

9.5 触发covergroup

功能覆盖率中两个主要部分是采样值和采样时间。你可以调用sample函数采样,也可以使用阻塞表达式采样。

如果你想显式触发采样行为,或者没有明显的事件或信号告诉你什么时候采样,或者多个covergroup是独立采样的,那么就使用sample函数。

用callback采样

在测试平台中集成功能覆盖率时,用callback比用mailbox更好。首先,用callback我们可以指定插入覆盖率采样点的位置,比如是在pre_tx还是post_tx;callback只有在push以后才会生效,这样就不会对现有的代码平台产生影响;callback不需要主动的收发线程,它是被动的。

下面的代码是使用callback采样的例子

Sample 9.6 Test using functional coverage callback program automatic test; Environment env; initial begin Driver_cbs_coverage dcc; env = new(); env.gen_cfg(); env.build(); // Create and register the coverage callback dcc = new(); env.drv.cbs.push_back(dcc); // Put into driverÕs Q env.run(); env.wrap_up(); end endprogram ​ Sample 9.7 Callback for functional coverage class Driver_cbs_coverage extends Driver_cbs; covergroup CovPort; ... endgroup virtual task post_tx(Transaction tr); CovPort.sample(); // Sample coverage values endtask endclass

带事件触发的covergroup

用事件触发的好处是能够借助已有的事件进行采样

Sample 9.8 Cover group with a trigger event trans_ready; covergroup CovPort @(trans_ready); coverpoint ifc.cb.port; // Measure coverage endgroup

用SVA触发

假如我们已经有了如下所示的一个SVA。

cover是相对于assert的SVA,当条件满足的时候会执行+1记录触发次数,不满足的时候不暂停仿真。

Sample 9.9 Module with SystemVerilog Assertion module mem(simple_bus sb); bit [7:0] data, addr; event write_event; cover property (@(posedge sb.clock) sb.write_ena==1) -> write_event; endmodule

利用SVA可以写出下面的测试代码

Sample 9.10 Triggering a cover group with an 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; initial begin wcg = new(); // Apply stimulus here sb.write_ena <= 1; ... #10000 $finish; end endprogram

9.6 数据采样

bin和coverage

bin是一个变量可能出现结果的集合,如果一个变量是3bit的,那么它的bin就可能包含0-7一共8个结果;coverage就是出现的bin数量/可能出现的总数量。如果采样过程中得到了一个值满足了7个bin,那么coverage就会变为7/8。

自动创建bin

SV会根据变量自动创建bin,一个n bit的变量就会有2^n个值。

枚举类型的值域是命名值的个数。

限制自动创建bin的数量

一个变量的默认bin最大数量是64个,如果变量的位数很多,那么会被分为多个变量,每个有64个bin。

实际应用过程中,64还是有点多,可以设为8个16个等等。

下面的代码设置这个变量的采样最多有2个bin。port是一个3bit变量,默认有8个bin。

Sample 9.11 Using auto_bin_max set to 2 covergroup CovPort; coverpoint tr.port { options.auto_bin_max = 2; } // Divide into 2 bins endgroup

这样设置后,覆盖率结果输出如下。

可以看到被合并为两个,auto[0:3]+1的条件是只要有一个被触发过,就+1

Sample 9.12 Report with auto_bin_max set to 2 Bin # hits at least ================================== auto[0:3] 15 1 auto[4:7] 17 1

上面的代码只是设置了一个覆盖点的bin,你也可以设置整个组的bin

Sample 9.13 Using auto_bin_max for all cover points covergroup CovPort; options.auto_bin_max = 2; // Affects port & data coverpoint tr.port; coverpoint tr.data; endgroup

采样表达式

我们是可以对表达式进行采样来收集覆盖率的,但是对表达式的采样要考虑宽度的问题。

对于下面的代码,我们想对hdr_len+payload_len进行采样,一个最大值是7,另一个是15,二者相加的最大值是22,因此它们这个表达式的取值范围是0:22,一共23种。

这里就有个问题了,23不是2的整数次幂。如果用len16,采样结果是4bit,一共16个bin,coverage如果是100也不对;如果用len32,一共是32个bin,coverage永远不会到100,也不对。

Sample 9.14 Using an expression in a cover point class Transaction; rand bit [2:0] hdr_len; // range: 0:7 rand bit [3:0] payload_len; // range: 0:15 rand bit [3:0] kind; // range: 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); endgroup

用户自定义的bin发现bug

你可以自己指定bin的范围。SV会自动为枚举类型的bin命名,你需要手动为其他类型的仓库命名。

下面的代码指定bin是0-23

Sample 9.15 Defining bins for transaction length covergroup CovLen; len: coverpoint (tr.hdr_len + tr.payload_len + 5Õb0) {bins len[] = {[0:23]}; } endgroup

可以看到23永远不会hit,因为最大值是22。

Sample 9.16 Coverage report for transaction length Bin # hits at least ============================ len_00 13 1 len_01 36 1 len_02 51 1 len_03 60 1 len_04 72 1 len_05 88 1 len_06 127 1 len_07 122 1 len_08 133 1 len_09 138 1 len_0a 115 1 len_0b 128 1 len_0c 125 1 len_0d 111 1 len_0e 115 1 len_0f 134 1 len_10 107 1 len_11 102 1 len_12 70 1 len_13 65 1 len_14 39 1 len_15 30 1 len_16 19 1 len_17 0 1 ============================
← 返回列表