SV -- Coverage 覆蓋率
本文內容來自:
————————————————
版權聲明:本文為CSDN博主「bleauchat」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/bleauchat/article/details/90445713
@(SV)
1. Coverage概念
覆蓋率用來衡量設計中已經被測部分和未測部分的比例,通常被定義為已達到所需驗證部分的百分比.
目標覆蓋率是指在驗證計划中規定的需要驗證點的目標值。 在驗證計划中, 當驗證點實際覆蓋率沒有達到 100% 的時候, 說明驗證工作還未完成目標方案。 沒有達到 100% 的項目需要通過\(\color{red}{添加測試用例或者修改約束}\)等來對其進行充分的驗證;
驗證計划中列出的項目都要一一被測試, 當然這需要一個比較全面和完整的驗證計划。為此, 在驗證環境搭建的前期, 制定驗證計划, 明確驗證點並確定目標覆蓋率是一項艱巨而且細致的工作;
制定驗證計划中的功能點的時候, 需要考慮如下三個問題:
1) 哪些功能點需要檢查?
2) 這個功能點的哪些數據需要檢查?
3) 如何對這些數據進行采樣?
哪些功能點需要檢查呢? 這要根據設計的具體情況而定, 一般情況下, 以下幾類是參考的對象: 功能要求、 接口要求、 系統規范、 協議規范等。 具體驗證計划中可能表現為:FIFO 是否溢出和空讀、 外部接口是否遵從以太網物理層的傳輸協議、 是否滿足系統規范要求的支持發送超長包、 內部的AMBA 總線是否符合協議要求等;
主要有兩種coverage評估方式:
- 代碼覆蓋率
- 代碼覆蓋率度量執行了多少“設計代碼”。
- 包括行覆蓋率、FSM狀態機覆蓋率、分支覆蓋率、條件覆蓋率 和path路徑覆蓋率。
- 行覆蓋率: 檢查某行代碼是否被執行過
- 分支覆蓋率: 檢查條件分支是否都被執行過
- 條件覆蓋率, 表達式覆蓋率: 通過真值表分析表達式各種邏輯組合
- 有限狀態機覆蓋率: 檢查每個狀態是否被覆蓋, 狀態之間的跳轉是否被執行
- 仿真工具將自動從設計代碼中提取代碼覆蓋率.代碼覆蓋率就算達到100%,這並不意味着不存在bug.
- 功能覆蓋率
- 功能覆蓋是一個用戶定義的度量,它度量在驗證中執行了多少設計規范。
- 面向數據的覆蓋(Data-oriented Coverage)——檢查數據值的組合。對已進行的數據組合檢查.我們可以通過編寫覆蓋組(coverage groups)、覆蓋點(coverage points)和交叉覆蓋(cross coverage)獲得面向數據的覆蓋率.
- 面向控制的覆蓋(Control-oriented Coverage)——檢查行為序列(sequences of behaviors)是否已經發生.通過編寫SVA來獲得斷言覆蓋率(assertion coverage).
2. 功能覆蓋率
使用覆蓋組結構定義覆蓋模型.覆蓋組結構(covergroup construct)是一種用戶自定義的類型,一旦被定義就可以創建多個實例就像類一樣,也是通過new()來創建實例的.覆蓋組可以定義在module、program、interface以及class中.在動手編寫測試代碼之前,我們需要首先弄清楚相關設計的關鍵特性、邊界情形和可能的故障模式,這其實就是驗證計划的內容
每一個覆蓋組都必須明確一下內容:
- 覆蓋點(coverage points),也就是需要測試的變量;
- 一個時鍾事件以用來同步對覆蓋點的采樣(sampling of coverage points);
- 可選的形式參數(Optional formal arguments);
- 覆蓋點之間的交叉覆蓋(Cross coverage between coverage points);
- 覆蓋選項(Coverage options);
語法:
covergroup cov_grp @(posedge clk);
cov_p1: coverpoint a;//定義覆蓋點
endgroup
cov_grp cov_inst = new();//實例化覆蓋組
上面例子使用時鍾上升沿作為采樣點。
covergroup cov_grp;
cov_p1: coverpoint a;//cov_p1為覆蓋點名,a為覆蓋點中的變量名,也就是模塊中的變量名
endgroup
cov_grp cov_inst = new();
@(abc) cov_inst.sample();
通過.sample()函數在外部申明采樣點。
此外,覆蓋組中允許帶形式參數,外部在引用覆蓋組時可以通過傳遞參數,從而對該覆蓋組進行復用,如下:
covergroup address_cov (ref logic [7:0] address,
input int low, int high) @ (posedge ce);
ADDRESS : coverpoint address {
bins low = {0,low};
bins med = {low,high};
}
endgroup
address_cov acov_low = new(addr,0,10);
address_cov acov_med = new(addr,11,20);
address_cov acov_high = new(addr,21,30);
3. 覆蓋點
一個覆蓋組可以包含多個覆蓋點,一個覆蓋點可以是一個整型變量也可以是一個整型表達式(integral variable or an integral expression);在驗證環境中,覆蓋點可以放置在下面四個位置:

每一個覆蓋點都與"bin(倉)"關聯,在每一個采樣時鍾仿真器都會自增關聯的bin值(increment the associated bin value)
bin可以自動創建或者顯示定義:
自動或隱式bin(Automatic Bins or Implicit Bins)
對於覆蓋點變量范圍內的每一個值都會有一個對應的bin,這種稱為自動或隱式的bin.例如,對於一個位寬為nbit的覆蓋點變量,2^n個自動bin將會被創建.看下面例子:
module cov;
logic clk;
logic [7:0] addr;
logic wr_rd;
covergroup cg @(posedge clk);
c1: coverpoint addr;
c2: coverpoint wr_rd;
endgroup : cg
cg cover_inst = new();
...
endmodule
對於覆蓋點addr,將會有c1.auto[0] c1.auto[1] c1.auto[2] … c1.auto[255]等256個bin被自動創建;
對於覆蓋點wr_rd,將會有c2.auto[0]這一個bin被創建;
隱式bin示例:
module tb;
// Declare some variables that can be "sampled" in the covergroup
bit [1:0] mode;
bit [2:0] cfg;
// Declare a clock to act as an event that can be used to sample
// coverage points within the covergroup
bit clk;
always #20 clk = ~clk;
// "cg" is a covergroup that is sampled at every posedge clk
covergroup cg @ (posedge clk);//覆蓋組中只有mode,取值范圍為0~3,而且沒有創建bin,這樣系統自動創建4個bin
coverpoint mode;
endgroup
// Create an instance of the covergroup
cg cg_inst;
initial begin
// Instantiate the covergroup object similar to a class object
cg_inst= new();
// Stimulus : Simply assign random values to the coverage variables
// so that different values can be sampled by the covergroup object
for (int i = 0; i < 5; i++) begin
@(negedge clk);
mode = $random;
cfg = $random;
$display ("[%0t] mode=0x%0h cfg=0x%0h", $time, mode, cfg);
end
end
// At the end of 500ns, terminate test and print collected coverage
initial begin
#500 $display ("Coverage = %0.2f %%", cg_inst.get_inst_coverage());
$finish;
end
endmodule
輸出:

例二:
module tb;
bit [1:0] mode;
bit [2:0] cfg;
bit clk;
always #20 clk = ~clk;
// "cg" is a covergroup that is sampled at every posedge clk
// This covergroup has two coverage points, one to cover "mode"
// and the other to cover "cfg". Mode can take any value from
// 0 -> 3 and cfg can take any value from 0 -> 7
covergroup cg @ (posedge clk);//覆蓋組定義了4個coverpoint
// Coverpoints can optionally have a name before a colon ":"
cp_mode : coverpoint mode;
cp_cfg_10 : coverpoint cfg[1:0];
cp_cfg_lsb : coverpoint cfg[0];
cp_sum : coverpoint (mode + cfg);
endgroup
cg cg_inst;
initial begin
cg_inst= new();
for (int i = 0; i < 5; i++) begin
@(negedge clk);
mode = $random;
cfg = $random;
$display ("[%0t] mode=0x%0h cfg=0x%0h", $time, mode, cfg);
end
end
initial begin
#500 $display ("Coverage = %0.2f %%", cg_inst.get_coverage());
$finish;
end
endmodule
輸出:


顯式bin
"bins"關鍵字被用來顯示定義一個變量的bin,可以為給定范圍內的變量的每個值創建單獨的bin,也可以將一個或多個bin指向變量的某個范圍.使用顯示bin,也就是用戶自定義bin可以增加覆蓋的准確度!它可以將變量的取值范圍限定在你感興趣的區域內!
顯示bin緊跟在對應的覆蓋點后面,用{ }包圍起來,關鍵字"bins"后跟着bin名以及變量的值或范圍。
格式如下:
covergroup 覆蓋組名 @(posedge clk);//時鍾可以沒有
覆蓋點名1: coverpoint 變量名1{ bins bin名1 = (覆蓋點取值范圍);
bins bin名2 = (覆蓋點取值范圍);
bins bin名3 = (覆蓋點取值范圍);
.......
}//一般會將bin的數目限制在8或16
覆蓋點名2: coverpoint 變量名2{ bins bin名1 = (覆蓋點取值范圍);
bins bin名2 = (覆蓋點取值范圍);
bins bin名3 = (覆蓋點取值范圍);
.......
}
。。。。。。
endgroup : 覆蓋組名
//注意對coverpoint的bin的聲明使用的是{},這是因為bin是聲明語句而非程序語句,后者才用begin..end
//圍起來,而且{}后也沒有加分號,這和end是一樣的
示例:
module cov;
logic clk;
logic [7:0] addr;
logic wr_rd;
covergroup cg @(posedge clk);
c1: coverpoint addr { bins b1 = {0,2,7};
bins b2[3] = {11:20};
bins b3 = {[30:40],[50:60],77};
bins b4[] = {[79:99],[110:130],140};
bins b5[] = {160,170,180};
bins b6 = {200:$};
bins b7 = default;}
c2: coverpoint wr_rd {bins wrrd};
endgroup : cg
cg cover_inst = new();
...
endmodule
上面幾個倉的含義如下:
bins b1 = {0,2,7 }; //bin “b1” increments for addr = 0,2 or 7
bins b2[3] = {11:20}; //creates three bins b2[0],b2[1] and b2[3].
//and The 10 possible values are distributed as follows: (11,12,13),(14,15,16)
//and (17,18,19,20) respectively.當不能均等分配時,最后一個數組要多
bins b3 = {[30:40],[50:60],77}; //bin “b3” increments for addr = 30-40 or 50-60 or 77
bins b4[] = {[79:99],[110:130],140};//creates three bins b4[0],b4[1] and b4[2] with values 79-99,50-60 and 77 respectively
bins b5[] = {160,170,180}; //creates three bins b5[0],b5[1] and b5[2] with values 160,170 and 180 respectively
bins b6 = {200:$}; //bin “b6” increments for addr = 200 to max value i.e, 255.
default bin; // catches the values of the coverage point that do not lie within any of the defined bins.
- 說白了,覆蓋組的作用就是將覆蓋點的取值范圍分為了多個bin,每個bin表示了一段取值范圍
- bin就表示了addr的取值范圍,如果這個范圍內有一個值被取到了,則這個bin就被覆蓋了
- 如果所有的bin都被覆蓋,則覆蓋率為100%
- default 不會用於計算覆蓋率,default的意思就是其他值
bins for transitions (bin值域的轉變)
可以通過指定序列(sequence)來進行覆蓋點的轉換:
value1 => value2;
range_list_1 => range_list_2;//覆蓋點的值從value1轉變到value2,value1和value2可以是某個值,也可以是范圍
covergroup cg @(posedge clk);
c1: coverpoint addr{ bins b1 = (10=>20=>30);
bins b2[] = (40=>50),(80=>90=>100=>120);
bins b3 = (1,5 => 6, 7);
bins b4 = default sequence;}
c2: coverpoint wr_rd;
endgroup : cg
bins b1 = (10=>20=>30); // addr的值轉換次序為 10->20->30,如果沒有執行這個次序,則這個bins沒有覆蓋
bins b2[] = (40=>50),(80=>90=>100=>120); // b2[0] = 40->50 and b2[1] = 80->90->100->120
bins b3 = (1,5 => 6, 7); // b3 = 1=>6 or 1=>7 or 5=>6 or 5=>7
bins b4 = default sequence; //其余沒有出現的轉換序列(sequence)
consecutive repetions creation 連續轉換
有的時候希望某個變量值連續出現幾次,這個時候就需要用來連續轉換序列,如下:
WRITE=>WRITE=>WRITE=>WRITE;//WRITE出現4次
//上面的寫法過於復雜,可以寫成下面:
WRITE[*4];
//例子:
covergroup address_cov () @ (posedge ce);
ADDRESS : coverpoint addr {
bins adr_0_2times = (0[*2]);//0連續出現2次
bins adr_1_3times = (1[*3]);//1連續出現3次
bins adr_2_4times = (2[*4]);//2連續出現4次
bins adr1[] = (1[*1:2]);//1連續出現1~2次
}
endgroup
Non consecutive repetition 非連續轉換
上面介紹的重復都要求是連續的,下面介紹非連續重復,這用到關鍵字->和=:
WRITE[->2];
等效於:
......=>WRITE.......=>WRITE;
covergroup address_cov () @ (posedge ce);
ADDRESS : coverpoint addr {
bins adr = (0=>2[->2]=>1);//addr=0,然后2出現2次,不要求連續,然后1
}
endgroup
或者:
WRITE[=2];//WRITE至少出現兩次,不要求連續
等效於:
....=>WRITE.....=>WRITE.....=>WRITE;
例子:
covergroup address_cov () @ (posedge ce);
ADDRESS : coverpoint addr {
bins adr = (0=>2[=2]=>1);//addr=0,然后2至少出現兩次,不要求連續,然后1
}
endgroup
wildcard bins 不定值bins
可以用x,z,?來表示某一位可以不定。
wildcard bins abc = {2'b1?};//覆蓋10,11
wildcard bins abc = (2'b1x => 2'bx0};
//覆蓋 10=>00 ,10=>10 ,11=>00 ,11=>10
covergroup address_cov () @ (posedge ce);
ADDRESS : coverpoint addr {
// Normal transition bibs
wildcard bins adr0 = {3'b11?};
// We can use wildcard in transition bins also
wildcard bins adr1 = (3'b1x0 => 3'bx00);
wildcard bins adr2 = (3'b1?0 => 3'b?00);
}
endgroup
ignore_bins 忽略bin
一組與覆蓋點相關聯的值的轉換可以顯式地進行,除了被ignore_bins修飾的bin,ignore_bins用於排除一些取值,如一個3位的數據,假如它的取值僅在0~5之間,如果使用自動bin那么它的覆蓋率永遠不會到100%,這個時候就可以使用ignore_bins來忽略6、7的取值; ignore_bins 不會用於計算覆蓋率;
covergroup cg @(posedge clk);
c1: coverpoint addr{ ignore_bins b1 = {6,60,66};
ignore_bins b2 = (30=>20=>10); }//被ignore_bins修飾的值都不在覆蓋范圍之內
endgroup : cg
illegal_bins 非法bin
觸發illegal bins會終止仿真並報錯
covergroup cg @(posedge clk);
c1: coverpoint addr{ illegal_bins b1 = {7,70,77};
ignore_bins b2 = (7=>70=>77);}
endgroup : cg
Examples
module tb;
bit [2:0] mode;
// This covergroup does not get sample automatically because
// the sample event is missing in declaration
covergroup cg;
coverpoint mode {
// Declares 4 bins for the total range of 8 values
// So bin0->[0:1] bin1->[2:3] bin2->[4:5] bin3->[6:7]
bins range[4] = {[0:$]};//將mode的取值分為4個bin,如上
}
endgroup
// Stimulus : Simply randomize mode to have different values and
// manually sample each time
initial begin
cg cg_inst = new();
for (int i = 0; i < 5; i++) begin
#10 mode = $random;
$display ("[%0t] mode = 0x%0h", $time, mode);
cg_inst.sample();//聲明的覆蓋組沒有定義時鍾,所以得調用sample()方法進行采樣
end
$display ("Coverage = %0.2f %%", cg_inst.get_inst_coverage());
end
endmodule
輸出:

4. Cross Coverage 交叉覆蓋
交叉覆蓋是在覆蓋點或變量之間指定的,必須先指定覆蓋點,然后才能定義覆蓋點之間的交叉覆蓋.
可以通過覆蓋點名或者變量名來定義交叉覆蓋,看下面例子:
//通過覆蓋點來定義交叉覆蓋
bit [3:0] a, b;
covergroup cg @(posedge clk);
c1: coverpoint a;
c2: coverpoint b;
c1Xc2: cross c1,c2;
endgroup : cg
//通過變量名來定義交叉覆蓋
bit [3:0] a, b;
covergroup cov @(posedge clk);
aXb : cross a, b;
endgroup
//交叉覆蓋的通用定義格式:
交叉覆蓋名:cross 交叉覆蓋點名1,交叉覆蓋點名2;
由於上面每個覆蓋點都有16個bin,所以它們的交叉覆蓋總共有256個交叉積(cross product),也就對應256個bin。
bit [3:0] a, b, c;
covergroup cov @(posedge clk);
BC : coverpoint b+c;
aXb : cross a, BC;
endgroup
注意:覆蓋點中的表達式位寬按照表達式中最大數的位寬來看,所以b+c的位寬仍然是4,倉內有16個數。要符合實際情況(兩個4位相加為5位)可以寫為a+b+5'b0.
上例的交叉覆蓋總共有256個交叉積(cross product),也對應256個bin.
User defined cross bins
在交叉覆蓋中,除了使用上面自動創建的bins之外,還可以用戶自定義交叉bins,這就用到關鍵字binsof和intersect,如下:
covergroup address_cov () @ (posedge ce);
ADDRESS : coverpoint addr {
bins addr0 = {0};
bins addr1 = {1};
}
CMD : coverpoint cmd {
bins READ = {0};
bins WRITE = {1};
bins IDLE = {2};
}
CRS_USER_ADDR_CMD : cross ADDRESS, CMD {
bins USER_ADDR0_READ = binsof(CMD) intersect {0};//默認的bins本來應該是2*3=6個,但是這里只定義了兩個bins
}
CRS_AUTO_ADDR_CMD : cross ADDRESS, CMD {
ignore_bins AUTO_ADDR_READ = binsof(CMD) intersect {0};
ignore_bins AUTO_ADDR_WRITE = binsof(CMD) intersect {1} && binsof(ADDRESS) intersect{0};
}
endgroup
5. Coverage Options 覆蓋率選項
覆蓋率選項用來控制覆蓋組、覆蓋點和交叉覆蓋之間的行為.用下面幾個關鍵字來控制:
at_least
定義一個bin在執行代碼過程中至少觸發的次數,低於這個觸發次數的話,這個bin不算覆蓋,默認值是1;
auto_bin_max
當沒有bin為顯示創建時,定義一個覆蓋點的自動bin的最大數量,默認值為64;
cross_auto_bin_max
定義一個交叉覆蓋的交叉積(cross product)的自動bin的最大數量,沒有默認值;
covergroup cg @(posedge clk);
c1: coverpoint addr { option.auto_bin_max = 128;}//addr自動bin的數目最大為128
c2: coverpoint wr_rd { option.atleast = 2;}//wr_rd的每個bin至少要觸發兩次,否則不算覆蓋
c1Xc2: cross c1, c2 { option.cross_auto_bin_max = 128;}//交叉積的自動bin數目最大為128
endgroup : cg
//覆蓋選項如果是在某個coverpoint中定義的,那么其作用范圍僅限於該coverpoint;
//如果是在covergroup中定義的,那么其作用范圍是整個covergroup;
6. Coverage methods 覆蓋率方法
- void sample() : 觸發覆蓋組的采樣
- real get_coverage() : 返回覆蓋組覆蓋率
- real get_inst_coverage() 返回覆蓋組實例的覆蓋率
- void set_inst_name(string) : 設置實例名
- void start() : 開啟覆蓋率收集
- void stop() : 結束收集覆蓋率
樣例:
module test();
logic [2:0] addr;
wire [2:0] addr2;
assign addr2 = addr + 1;
covergroup address_cov;
ADDRESS : coverpoint addr {
option.auto_bin_max = 10;
}
ADDRESS2 : coverpoint addr2 {
option.auto_bin_max = 10;
}
endgroup
address_cov my_cov = new;
initial begin
my_cov.ADDRESS.option.at_least = 1;
my_cov.ADDRESS2.option.at_least = 2;
// start the coverage collection
my_cov.start();
// Set the coverage group name
my_cov.set_inst_name("ASIC-WORLD");
$monitor("addr 8'h%x addr2 8'h%x",addr,addr2);
repeat (10) begin
addr = $urandom_range(0,7);
// Sample the covergroup
my_cov.sample();
#10;
end
// Stop the coverage collection
my_cov.stop();
// Display the coverage
$display("Instance coverage is %e",my_cov.get_coverage());
end
endmodule
7. Coverage system task 覆蓋率系統任務
- $set_coverage_db_name(name) : sets the filename of the coverage database into which coverage information is saved at the end of a simulation run.
- $load_coverage_db(name) :loads from the given filename the cumulative coverage information for all coverage group types.
- $get_coverage() :returns as a real number in the range of 0 to 100 the overall coverage of all coverage group types. This number is computed as described above
module test();
logic [2:0] addr;
wire [2:0] addr2;
assign addr2 = addr + 1;
covergroup address_cov;
ADDRESS : coverpoint addr {
option.auto_bin_max = 10;
}
ADDRESS2 : coverpoint addr2 {
option.auto_bin_max = 10;
}
endgroup
address_cov my_cov = new;
initial begin
// Set the database name
$set_coverage_db_name("asic_world");
$monitor("addr 8'h%x addr2 8'h%x",addr,addr2);
repeat (10) begin
addr = $urandom_range(0,7);
my_cov.sample();
#10;
end
// Get the final coverage
$display("Total coverage %e",$get_coverage());
end
endmodule