本文主要介绍,利用system verilog编写功能覆盖率的基本语法。主要内容包括:覆盖组covergroup的定义、覆盖点coverpoints的定义、交叉覆盖率cross的定义、覆盖率选项、覆盖率系统任务和函数。
covergroup类型构造是用户定义的。类型定义编写一次,可以在不同的上下文中创建该类型的多个实例。与类类似,一旦定义,covergroup实例就可以通过new()操作符创建。covergroup可以在包、模块、程序、接口、检查器或类中定义。
covergroup关键字定义了覆盖模型的名称。使用这个关键字,可以创建任意数量的覆盖率模型实例。例如:
covergroup cg; ... endgroup
cg cg_inst = new;
上面的示例定义了一个名为cg的covergroup。cg的实例声明为cg_inst,并使用new操作符创建。
output或者inout不能作为covergroup的参数。由于covergroup不能修改new操作符的任何实参,因此ref实参将被视为只读的const ref实参。不能使用层次名称访问covergroup的形参(不能在covergroup声明之外访问形参)。
如果指定了一个时钟事件,它将在定义的事件上对覆盖点进行采样。因为它在covergroup的范围内,时钟事件可以基于covergroup的ref参数。如果没有指定时钟事件,用户必须通过内置的sample()方法,手动的触发覆盖率采样。预定义的sample()方法不接受参数,但是用户可以通过指定一个带有参数列表的采样方法,作为触发函数来覆盖原有的采样函数。
一个覆盖组可以包含一个或多个覆盖点。覆盖点可以覆盖变量或表达式。
enum { red, green, blue } color;
covergroup g1 @(posedge clk);
c: coverpoint color;
endgroup
上面的例子定义了覆盖组g1,它有一个与变量color相关联的单一覆盖点。变量color的值在指定的时钟事件采样:信号clk的正边缘。因为覆盖点没有显式定义任何仓(bins),所以工具自动创建三个仓,每个仓对应枚举类型的可能值。
一个覆盖组可以包含一个或多个覆盖点。覆盖点指定要覆盖的完整表达式。每个覆盖点包括,一组与所覆盖表达式的,采样值或转换相关联的仓。仓可以由用户显式定义,也可以由SystemVerilog自动创建。当覆盖组被采样时,将对覆盖点表达式(及其启用iff条件,如果有的话)进行评估。
iff构造中的表达式指定了一个可选的条件,该条件禁用该覆盖点的覆盖。如果保护表达式在采样点计算为false,则忽略覆盖点。
covergroup g4;
coverpoint s0 iff(!reset);
endgroup
在上面的示例中,覆盖点s0仅在值重置为false时被覆盖。
default关键字,定义了一个不与任何已定义值仓相关联的仓。默认仓捕获覆盖点的值,不属于任何已定义的仓。default对于捕获未计划的或无效的值非常有用。
bins结构允许为给定范围列表中的每个值创建单独的仓,或者为整个范围的值创建单独的仓。要为每个值创建单独的仓(容器数组),方括号[]应该跟在容器名称后面。要为一组值创建固定数量的仓,可以在方括号内指定一个正整数表达式。bin名称和可选方括号后面是covergroup_range_list,该列表指定与bin相关联的值集。在covergroup_value_range的形式[expression: $]或[$:expression]中使用$是合法的,分别代表上限和下限。
bins fixed [4] = { [1:10], 1, 4, 7 };
在上面例子中,13个值被分为四个仓,分别为:<1,2,3>,<4,5,6>,<7,8,9>,<10,1,4,7>。如果仓的数量超过值的数量,那么一些仓将是空的。
bit [9:0] v_a;
covergroup cg @(posedge clk);
coverpoint v_a
{
bins a = { [0:63],65 };
bins b[] = { [127:150],[148:191] }; // note overlapping values
bins c[] = { 200,201,202 };
bins d = { [1000:$] };
bins others[] = default;
}
endgroup
在上面的例子中,第一个bins将仓a与变量v_a之间的值关联起来,取值为0到63,以及65。第二个bins构造创建了一个由65个bins组成的集合b[127], b[128],…b[191]。同样,第三个bins构造创建了3个bins: c[200], c[201]和c[202]。第四个bins构造创建了仓d,取值范围为1000到1023($表示变量v_a的上限)。剩下的与a、b[]、c[]或d不匹配的值,被添加到others[]中,并各自单独建仓。
通用覆盖组可以通过将其特征作为参数传递给构造函数来编写。
covergroup cg (ref int ra, input int low, int high ) @(posedge clk);
coverpoint ra // sample variable passed by reference
{
bins good = { [low : high] };
bins bad[] = default;
}
endgroup
...
int va, vb;
cg c1 = new( va, 0, 50 ); // cover variable va in the range 0 to 50
cg c2 = new( vb, 120, 600 ); // cover variable vb in the range 120 to 600
这个例子定义了一个覆盖组cg,其中要采样的信号和覆盖范围被指定为参数。稍后,将创建coverage组的两个实例,每个实例采样一个不同的信号并覆盖不同范围的值。
with关键字指定covergroup_range_list中,包含在bin中,并满足给定表达式的值。
a: coverpoint x
{
bins mod3[] = {[0:255]} with (item % 3 == 0);
}
这个bin定义选择从0到255中能被3整除的所有值。
wildcard关键字修饰的bin,定义所有的X、Z或?,被视为0或1的通配符。
wildcard bins g12_15 = { 4'b11?? };
当采样变量在12到15之间时,bin g12_15的计数增加:
1100 1101 1110 1111
通过将与覆盖点相关的一组值或表达式,指定为ignore_bins,可以显式地从覆盖中排除。
covergroup cg23;
coverpoint a
{
ignore_bins ignore_vals = {7,8};
ignore_bins ignore_trans = (1=>3=>5);
}
endgroup
通过将与覆盖点相关的一组值或表达式指定为illegal_bins,可以将它们标记为非法。
covergroup cg3;
coverpoint b
{
illegal_bins bad_vals = {1,2,3};
illegal_bins bad_trans = (4=>5=>6);
}
endgroup
如果出现非法值或表达式,仿真将运行时报错。非法仓优先于任何其他仓,也就是说,即使它们包含在另一个合法仓中,也会导致运行时报错。非法的仓不能指定一个无边界或未确定可变长度的序列。
bit [2:0] p1; // type expresses values in the range 0 to 7
bit signed [2:0] p2; // type expresses values in the range –4 to 3
covergroup g1 @(posedge clk);
coverpoint p1 {
bins b1 = { 1, [2:5], [6:10] };
bins b2 = { -1, [1:10], 15 };
}
coverpoint p2 {
bins b3 = {1, [2:5], [6:10] };
bins b4 = { -1, [1:10], 15 };
}
endgroup
——对于b1,对范围[6:10]发出警告。b1被视为{1, [2:5], [6:7]}
——对于b2,对范围[1:10]和值-1和15发出警告。B2被视为具有规范{[1:7]}。
——对于b3,对范围[2:5]和[6:10]发出警告。B3被视为具有规范{1,[2:3]}。
——对于b4,对范围[1:10]和值15发出警告。B2被视为具有规范{-1,[1:3]}。
覆盖组可以指定两个或多个覆盖点或变量之间的交叉覆盖。
交叉覆盖是使用cross作为关键字。
bit [3:0] a, b;
covergroup cov @(posedge clk);
aXb : cross a, b;
endgroup
上例中的覆盖组cov指定两个4位变量a和b的交叉覆盖。SystemVerilog隐式地为每个变量创建一个覆盖点。每个覆盖点有16个仓,即auto[0]…auto[15]。因此,a和b的叉乘(标记为aXb)有256个,每个叉乘的值都是aXb的仓。
bit [31:0] a_var;
bit [3:0] b_var;
covergroup cov3 @(posedge clk);
A: coverpoint a_var { bins yy[] = { [0:9] }; }
CC: cross b_var, A;
endgroup
覆盖组cov3将变量b_var与覆盖点A(标记为CC)交叉。变量b_var自动创建16个bin (auto[0]…auto[15])。覆盖点A明确创建10个仓(yy[0]…yy[9]),两个覆盖点的交叉产生16x10 = 160个交叉仓,即:
…
…
binsof构造生成其表达式的bins,可以是覆盖点(为单个变量显式定义或隐式定义)或覆盖点仓。binsof生成的结果可以进一步通过intersect关键字,选择关联值与所需值集相交的仓。
binsof( x ) intersect { y }
表示覆盖点x的仓,其值与y给出的范围相交。
! binsof( x ) intersect { y }
表示覆盖点x的仓,其值不与y给出的范围相交。
bit [7:0] v_a, v_b;
covergroup cg @(posedge clk);
a: coverpoint v_a
{
bins a1 = { [0:63] };
bins a2 = { [64:127] };
bins a3 = { [128:191] };
bins a4 = { [192:255] };
}
b: coverpoint v_b
{
bins b1 = {0};
bins b2 = { [1:84] };
bins b3 = { [85:169] };
bins b4 = { [170:255] };
}
c : cross a, b
{
bins c1 = ! binsof(a) intersect {[100:200]};// 4 cross products
bins c2 = binsof(a.a2) || binsof(b.b2);// 7 cross products
bins c3 = binsof(a.a1) && binsof(b.b4);// 1 cross product
}
endgroup
上面的例子定义了一个名为cg的覆盖组。如果没有特殊的指定,交叉覆盖c将会产生16个交叉覆盖仓:
第一个用户定义的交叉仓c1,指定c1应该只包括,不相交于100到200范围的覆盖点a的交叉积。这个选择表达式排除了仓a2、a3和a4。因此,c1将只包含
第二个用户定义的交叉仓c2,指定了c2交叉覆盖点的值,包括了a2或者b2的仓点。这个选择表达式包括以下七个交叉仓:< a2, b1 >, < a2, b2 >,
最后一个用户定义的交叉仓c3,指定c3应该只包括覆盖点a1和b4。因此,这个选择表达式只包括一个交叉仓:
覆盖率选项控制覆盖组、覆盖点和交叉覆盖的行为。有两种类型的选项:特定于covergroup实例的选项,即option,以及指定covergroup类型整体选项的选项,即type_option。
下表列出了实例特定的covergroup选项及其描述。covergroup的每个实例都可以将实例特定选项初始化为不同的值。初始化的选项值只影响该实例。
选项名称 | 默认值 | 描述 |
---|---|---|
weight=number | 1 | 如果在covergroup语法级别设置,它将指定该covergroup实例的权重,用于计算模拟的整体实例覆盖率。如果设置在coverpoint(或cross)语法级别,它将指定coverpoint(或cross)的权重,用于计算封闭covergroup的实例覆盖率。 |
goal=number | 90 | 为covergroup实例,或coverpoint实例或cross实例指定目标。 |
name=string | 名字要唯一 | 为covergroup实例指定一个名称。如果未指定,工具将自动生成每个实例的唯一名称。 |
comment=string | ”“ | 与covergroup实例,或coverpoint实例或cross实例一起出现的注释。注释保存在覆盖率数据库中,并包含在覆盖率报告中。 |
at_least=number | 1 | 每个仓的最少命中次数。命中数小于number的仓不被视为覆盖。 |
detect_overlap=boolean | 0 | 当为true时,如果覆盖点的两个覆盖点仓的范围列表(或转换列表)有重叠,将发出一个警告。 |
auto_bin_max=number | 64 | 当没有显式定义仓时,自动创建覆盖点仓的最大数量。 |
cross_auto_bin_max=number | unbounded | 为一个交叉覆盖点自动创建仓的最大数量。 |
cross_num_print_missing=number | 0 | 必须保存到覆盖率数据库,并打印在覆盖率报告中的缺失(未覆盖)交叉覆盖仓的数量。 |
per_instance=boolean | 0 | 每个实例都对covergroup类型的总体覆盖率做出贡献。当为true时,表示这个覆盖组是单例,。 |
上面提到的特定于实例的选项可以在covergroup定义中设置。在covergroup定义中设置这些选项的语法是:
option.option_name = expression ;
下面显示了一个示例。
covergroup g1 (int w, string instComment) @(posedge clk) ;
// track coverage information for each instance of g1 in addition
// to the cumulative coverage information for covergroup type g1
option.per_instance = 1;
// comment for each instance of this covergroup
option.comment = instComment;
a : coverpoint a_var
{
// Create 128 automatic bins for coverpoint “a” of each instance of g1
option.auto_bin_max = 128;
}
b : coverpoint b_var
{
// This coverpoint contributes w times as much to the coverage of an
instance of g1 than coverpoints "a" and "c1"
option.weight = w;
}
c1 : cross a_var, b_var ;
endgroup
covergroup定义中的选项分配语句在covergroup实例化时进行赋值。per_instance选项只能在covergroup定义中设置。其他实例特定的选项可以在covergroup实例化之后设置,例如:
covergroup gc @(posedge clk) ;
a : coverpoint a_var;
b : coverpoint b_var;
endgroup
...
gc g1 = new;
g1.option.comment = "Here is a comment set for the instance g1";
g1.a.option.weight = 3; // Set weight for coverpoint “a” of instance g1
下表总结了可以指定实例选项的语法级别(covergroup、coverpoint或cross)。所有实例选项都可以在covergroup级别指定。除了weight、goal、comment和per_instance选项外,covergroup语法级别设置的所有其他选项,都作为covergroup中所有coverpoint和cross的对应选项的默认值。单独的覆盖点或交叉点可以覆盖这些默认值。当在covergroup级别设置时,weight、goal、comment和per_instance选项不会作为较低语法级别的默认值。
选项名字 | covergroup | coverpoint | cross |
---|---|---|---|
name | Yes | No | No |
weight | Yes | Yes | Yes |
goal | Yes | Yes | Yes |
comment | Yes | Yes | Yes |
at_least | Yes (default for coverpoints & crosses) | Yes | Yes |
detect_overlap | Yes (default for coverpoints) | Yes | No |
auto_bin_max | Yes (default for coverpoints) | Yes | No |
cross_auto_bin_max | Yes (default for crosses) | No | Yes |
cross_num_print_missing | Yes (default for crosses) | No | Yes |
per_instance | Yes | No | No |
下表列出了从整体上描述covergroup类型的特定特性(或属性)的选项。它们类似于类的静态数据成员。
选项名字 | 默认值 | 描述 |
---|---|---|
weight=constant_number | 1 | 如果在covergroup语法级别设置,它将指定该covergroup的权重,用于计算保存的数据库的总体累积(或类型)覆盖率。如果设置在覆盖点(或交叉)语法级别,它将指定用于计算封闭覆盖组的累积(或类型)覆盖率的覆盖点(或交叉)的权重。 |
goal=constant_number | 90 | 为covergroup实例,或coverpoint实例或cross实例指定目标。 |
comment=string_literal | ”“ | 与covergroup实例,或coverpoint实例或cross实例一起出现的注释。注释保存在覆盖率数据库中,并包含在覆盖率报告中。 |
strobe=constant_number | 0 | 如果设置为1,所有的采样发生在仿真结束的时候,类似系统函数$strobe |
上面提到的covergroup类型选项可以在covergroup定义中设置。在covergroup定义中设置这些选项的语法是:
type_option.option_name = expression ;
covergroup的不同实例不能将不同的值分配给类型选项。这在语法上是不允许的,因为这些选项只能通过常量表达式进行初始化。下面是一个例子:
covergroup g1 (int w, string instComment) @(posedge clk) ;
// track coverage information for each instance of g1 in addition
// to the cumulative coverage information for covergroup type g1
option.per_instance = 1;
type_option.comment = "Coverage model for features foo and bar";
type_option.strobe = 1; // sample at the end of the time slot
// comment for each instance of this covergroup
option.comment = instComment;
a : coverpoint a_var
{
// Use weight 2 to compute the coverage of each instance
option.weight = 2;
// Use weight 3 to compute the cumulative (type) coverage for g1
type_option.weight = 3;
// NOTE: type_option.weight = w would cause syntax error.
}
b : coverpoint b_var
{
// Use weight w to compute the coverage of each instance
option.weight = w;
// Use weight 5 to compute the cumulative (type) coverage of g1
type_option.weight = 5;
}
endgroup
type_option可以在仿真的任何时候进行设置,例如:
covergroup gc @(posedge clk) ;
a : coverpoint a_var;
b : coverpoint b_var;
endgroup
...
gc::type_option.comment = "Here is a comment for covergroup g1";
// Set the weight for coverpoint "a" of covergroup g1
gc::a::type_option.weight = 3;
gc g1 = new;
下表总结了可以指定类型选项的语法级别(covergroup、coverpoint或cross)。当在covergroup级别设置时,类型选项不会作为较低语法级别的默认值。
Option name | covergroup | coverpoint | cross |
---|---|---|---|
weight | Yes | Yes | Yes |
goal | Yes | Yes | Yes |
comment | Yes | Yes | Yes |
strobe | Yes | No | No |
covergroup cg (int xb, yb, ref int x, y) ;
coverpoint x {bins xbins[] = { [0:xb] }; }
coverpoint y {bins ybins[] = { [0:yb] }; }
endgroup
cg cv1 = new (1,2,a,b); // cv1.x has 2 bins, cv1.y has 3 bins
cg cv2 = new (3,6,c,d); // cv2.x has 4 bins, cv2.y has 7 bins
initial begin
cv1.x.get_inst_coverage(covered,total); // total = 2
cv1.get_inst_coverage(covered,total); // total = 5
cg::x::get_coverage(covered,total); // total = 6
cg::get_coverage(covered,total); // total = 16
end
用一个接受参数的采样函数,覆盖预定义的sample()方法,便于从上下文(而不是包含covergroup声明的范围)中采样覆盖数据。例如,可以使用不同的参数调用覆盖的样例方法,以将要从自动任务或函数中、或从流程的特定实例中、或从并发断言的序列或属性中采样的数据直接传递给covergroup。由于并发断言具有特殊的采样语义(值在Preponed区域采样),因此将它们的值作为参数传递给覆盖的样本方法有助于管理断言覆盖率的各个方面,例如通过一个属性对多个covergroup进行采样,通过同一个covergroup对多个属性进行采样,或者通过任意覆盖群对序列或属性(包括局部变量)的不同分支进行抽样。
例如:
covergroup p_cg with function sample(bit a, int x);
coverpoint x;
cross x, a;
endgroup : p_cg
p_cg cg1 = new;
property p1;
int x;
@(posedge clk)(a, x = b) ##1 (c, cg1.sample(a, x));
endproperty : p1
c1: cover property (p1);
function automatic void F(int j);
bit d;
...
cg1.sample( d, j );
endfunction
上面的例子声明了covergroup p_cg,它的示例方法被覆盖以接受两个参数:a和x。这个covergroup (cg1)实例的示例方法然后从属性p1和自动函数F()中直接调用。
覆盖样例方法的形式参数与covergroup的形式参数属于相同的词法范围(由covergroup new操作符使用)。因此,在两个参数列表中指定相同的参数名将是一个错误。
例如:
covergroup C1 (int v) with function sample (int v, bit b); // error (v)
coverpoint v;
option.per_instance = b;// error: b may only designate a coverpoint
option.weight = v; // error: v is ambiguous
endgroup
本文主要的知识基础翻译自《SystemVerilog_3.1a》的第20章,以及《1800-2017 - IEEE Standard for SystemVerilog–Unified Hardware Design, Specification, and Verification Language》的第19章。
另外本文不是纯粹的翻译原文,而是比较重要的知识点和例子拎出来,便于理解。为了方便查阅,本文以关键字作为标题索引,主要从覆盖组covergroup的定义、覆盖点coverpoints的定义、交叉覆盖率cross的定义、覆盖率选项、覆盖率系统任务和函数,这五个方面详细描述了应该如何去构建一个功能覆盖率模型。