covergroupは考え方はそんなに難しくないのですが、いつも書いているわけではないので、久しぶりに書こうとするとどうやって書くんだっけ? とわかならくなることが多々あります。私だけかもしれませんが。。。
そこでcovergroupの書き方をまとめてみることにしました。全体でかなりの量になるので、以下の記事に分割します。今回はcovergroup自体の説明に集中します。
なお、一連の記事で触れないcovergroupの主要な機能として状態遷移に対するbinがあります1。触れない理由は筆者がまだ使ったことがないからです。本機能の主要な用途はステートマシンの状態遷移に対するカバレッジだと思いますが、今まで使ってきたシミュレータはそれぞれ独自の方法でデザインのステートマシンの状態遷移カバレッジを自動収集する機能があったため、わざわざこの用途でcovergoupを記述する必要になったことがないからです。ただclassを使ったモデルのカバレッジ収集には有用そうですので、いつか使う機会があったらその結果を記事にまとめたいと思います。
クラス内に宣言する場合
記述例
以下がサンプルプログラム全文になります。class内のcovergroupの場合はsample()を使用してカバレッジを取ることが多いので、その例で書いてあります。信号の変化をトリガにカバレッジをとる方法は下の"module内に宣言する場合"を参照してください。
class MyTest;
rand logic [2:0] data;
covergroup cg;
option.per_instance = 1;
cp: coverpoint data;
endgroup
function new();
cg = new();
endfunction
task run();
for (int i = 0; i < 3; i++) begin
randomize();
cg.sample();
$display("data = %h", data);
end
endtask
endclass
program top;
MyTest myTest;
MyTest myTest2;
initial begin
myTest = new;
myTest2 = new;
myTest.cg.set_inst_name("renamed_cg1");
myTest.run();
myTest2.run();
end
endprogram
covergroup cgがcovergroupの記述です。covergroup内にoption.per_instanceを1に設定することにより、Instanceカバレッジの結果を取得するようにしています。本例ではMyTestのメンバーdataのカバレッジを取っています。例にはありませんが、スコープさえ通っていればクラスの外の変数に対してカバレッジを取ることも可能です。
covergroup cg;
option.per_instance = 1;
cp: coverpoint data;
endgroup
クラス内に記述されたcovergroupを使用する場合、そのクラスのnew()の中でそのcovergroupをnew()する必要があります。これを忘れてもエラーにはなりませんが、カバレッジは取得されません。
またこの時はcovergroupのnewの左辺のインスタンス名はcovergroupの名前と一致させます。 cg cg_inst = new();
のようにインスタンス名とcovergroup名を異なるものにすることはできません。コンパイルエラーとなります。
function new();
cg = new();
endfunction
本classのrun()タスクでは、変数dataをランダマイズして、cg.sample()でその結果をサンプリングしています。3回ループしたら終了です。
task run();
for (int i = 0; i < 3; i++) begin
randomize();
cg.sample();
$display("data = %h", data);
end
endtask
program topではMyTestクラスを2つnew()しています。myTestのオブジェクトハンドルでnew()したほうはset_inst_name()を使ってそのcovergroupに対して名前を設定しています。後は、両方のオブジェクトのrun()メソッドを順番に呼んでいます。
program top;
MyTest myTest;
MyTest myTest2;
initial begin
myTest = new;
myTest2 = new;
myTest.cg.set_inst_name("renamed_cg1");
myTest.run();
myTest2.run();
end
endprogram
実行結果
以下がcovergroupの取得結果レポートの抜粋になります。myTestのカバーグループのインスタンス名はset_inst_nameで指定した名前"renamed_cg1"に変更されています。myTest2のほうのカバーグループのインスタンス名は<UNNAMED2>となっています。この名前はツール依存です。本例はAldecのRiviera Pro 2017.02での結果です。
+++++++++++++++++++++++++++++++++++++++++++++
++++++++++ DESIGN UNITS ++++++++++
+++++++++++++++++++++++++++++++++++++++++++++
CUMULATIVE SUMMARY
=============================================
| Coverage Type | Weight | Hits/Total |
=============================================
| Covergroup Coverage | 1 | 25.000% |
|---------------------|--------|------------|
| Types | | 0 / 1 |
=============================================
CUMULATIVE DESIGN-BASED COVERAGE: 25.000%
COVERED DESIGN UNITS: 0 / 1
FILES: 1
CLASS - work.MyTest
SUMMARY
=============================================
| Coverage Type | Weight | Hits/Total |
=============================================
| Covergroup Coverage | 1 | 25.000% |
|---------------------|--------|------------|
| Types | | 0 / 1 |
=============================================
WEIGHTED AVERAGE: 25.000%
COVERGROUP COVERAGE
===============================================================
| Covergroup | Hits | Goal / | Status |
| | | At Least | |
===============================================================
| TYPE /MyTest/cg | 25.000% | 100.000% | Uncovered |
===============================================================
| INSTANCE renamed_cg1 | 25.000% | 100.000% | Uncovered |
|----------------------------|---------|----------|-----------|
| COVERPOINT renamed_cg1::cp | 25.000% | 100.000% | Uncovered |
|----------------------------|---------|----------|-----------|
| bin auto[0] | 1 | 1 | Covered |
| bin auto[1] | 0 | 1 | Zero |
| bin auto[2] | 0 | 1 | Zero |
| bin auto[3] | 2 | 1 | Covered |
| bin auto[4] | 0 | 1 | Zero |
| bin auto[5] | 0 | 1 | Zero |
| bin auto[6] | 0 | 1 | Zero |
| bin auto[7] | 0 | 1 | Zero |
===============================================================
| INSTANCE <UNNAMED2> | 25.000% | 100.000% | Uncovered |
|----------------------------|---------|----------|-----------|
| COVERPOINT <UNNAMED2>::cp | 25.000% | 100.000% | Uncovered |
|----------------------------|---------|----------|-----------|
| bin auto[0] | 0 | 1 | Zero |
| bin auto[1] | 0 | 1 | Zero |
| bin auto[2] | 0 | 1 | Zero |
| bin auto[3] | 0 | 1 | Zero |
| bin auto[4] | 0 | 1 | Zero |
| bin auto[5] | 2 | 1 | Covered |
| bin auto[6] | 0 | 1 | Zero |
| bin auto[7] | 1 | 1 | Covered |
===============================================================
module内に宣言する場合
記述例
次はmodule内の信号に対してcovergroupを取るサンプルプログラムです。実使用上はmodule内の信号に対するcovergroupの記述はデザインファイルとは分割してbindで対応をとることが多いと思いますが、ここではmodule内に直接記述しています。bindについて別記事"bindでデザインにSVAを紐づけする"を参照してください。
module sub(input logic clk,
input logic rst);
logic [1:0] d;
logic [1:0] xd;
assign xd = ~d;
always @(posedge clk or posedge rst) begin
if (rst) begin
d <= 0;
end else begin
d <= d + 1;
end
end
//
// All lines below in this class are for covergroup
//
covergroup cg1 @(posedge clk iff !rst);
option.per_instance = 1;
cp: coverpoint d;
endgroup
covergroup cg2;
option.per_instance = 1;
cp: coverpoint xd;
endgroup
cg1 cg1_hdl = new;
cg2 cg2_hdl = new;
always @(posedge clk or posedge rst) begin
if (rst) begin
;
end else begin
cg2_hdl.sample();
end
end
endmodule
module top;
logic clk;
logic rst;
initial begin
rst = 1;
#2ns;
rst = 0;
end
initial begin
clk = 0;
forever begin
#1ns;
clk = ~clk;
end
end
sub sub_ins1(.clk(clk), .rst(rst));
sub sub_ins2(.clk(clk), .rst(rst));
initial begin
sub_ins1.cg1_hdl.set_inst_name("renamed_cg1_hdl");
#7ns;
$finish;
end
endmodule
cg1では、特定のイベント発生の度に自動的にsampleする例です。@(posedge clk)なのでクロックの立ち上がりエッジ毎にsampleされます。対象はlogic [1:0] dで、rst信号が0の時(rst信号は正論理なのでリセットされていない時という意味です)のみサンプリングされます。
cg2は同様のことをsample()メソッドを使用して行う場合の例です。対象はd信号を反転したxd信号です。こちらもrst信号が0の時はカバレッジを取らないようにしています。例にはありませんが、スコープさえ通っていればモジュールの外の変数に対してカバレッジを取ることも可能です。
covergroup cg1 @(posedge clk iff !rst);
option.per_instance = 1;
cp: coverpoint d;
endgroup
covergroup cg2;
option.per_instance = 1;
cp: coverpoint xd;
endgrou
class内のcovergroupのnew()とスタイルが異なり、以下の書式になります。
<covergroup_name> <handle_to_covergroup> = new;
以下のようにmodule内でいきなりnewします。違和感がありますが、これで正しいです。
cg1 cg1_hdl = new;
cg2 cg2_hdl = new;
topモジュールではsubモジュールを2つインスタンシエーションしています。subの中にはカバーグループcg1とcg2があるので、合計で4つのカバーグループが生成されます。その中で、sub_ins1のcg1はset_inst_nameで名前を設定しています。
後は7nsだけ時間経過後にシミュレーション終了です。
initial begin
sub_ins1.cg1_hdl.set_inst_name("renamed_cg1_hdl");
#7ns;
$finish;
end
実行結果
以下がcovergroupの取得結果レポートの抜粋になります。
sub_ins1の下にあるcg1のみset_inst_nameで指定した名前"renamed_cg1_hdl"に変更されています。それ以外の3つのカバーグループの名前は<UNNAMED1>となっています。この名前はツール依存です。本例はAldecのRiviera Pro 2017.02での結果です。
+++++++++++++++++++++++++++++++++++++++++++++
++++++++++ DESIGN UNITS ++++++++++
+++++++++++++++++++++++++++++++++++++++++++++
CUMULATIVE SUMMARY
=============================================
| Coverage Type | Weight | Hits/Total |
=============================================
| Covergroup Coverage | 1 | 50.000% |
|---------------------|--------|------------|
| Types | | 0 / 4 |
=============================================
CUMULATIVE DESIGN-BASED COVERAGE: 50.000%
COVERED DESIGN UNITS: 0 / 1
FILES: 1
MODULE - work.sub
SUMMARY
=============================================
| Coverage Type | Weight | Hits/Total |
=============================================
| Covergroup Coverage | 1 | 50.000% |
|---------------------|--------|------------|
| Types | | 0 / 4 |
=============================================
WEIGHTED AVERAGE: 50.000%
COVERGROUP COVERAGE
===================================================================
| Covergroup | Hits | Goal / | Status |
| | | At Least | |
===================================================================
| TYPE /top/sub_ins1/cg1 | 50.000% | 100.000% | Uncovered |
===================================================================
| INSTANCE renamed_cg1_hdl | 50.000% | 100.000% | Uncovered |
|--------------------------------|---------|----------|-----------|
| COVERPOINT renamed_cg1_hdl::cp | 50.000% | 100.000% | Uncovered |
|--------------------------------|---------|----------|-----------|
| bin auto[0] | 1 | 1 | Covered |
| bin auto[1] | 1 | 1 | Covered |
| bin auto[2] | 0 | 1 | Zero |
| bin auto[3] | 0 | 1 | Zero |
===================================================================
| TYPE /top/sub_ins1/cg2 | 50.000% | 100.000% | Uncovered |
===================================================================
| INSTANCE <UNNAMED1> | 50.000% | 100.000% | Uncovered |
|--------------------------------|---------|----------|-----------|
| COVERPOINT <UNNAMED1>::cp | 50.000% | 100.000% | Uncovered |
|--------------------------------|---------|----------|-----------|
| bin auto[0] | 0 | 1 | Zero |
| bin auto[1] | 0 | 1 | Zero |
| bin auto[2] | 1 | 1 | Covered |
| bin auto[3] | 1 | 1 | Covered |
===================================================================
| TYPE /top/sub_ins2/cg1 | 50.000% | 100.000% | Uncovered |
===================================================================
| INSTANCE <UNNAMED1> | 50.000% | 100.000% | Uncovered |
|--------------------------------|---------|----------|-----------|
| COVERPOINT <UNNAMED1>::cp | 50.000% | 100.000% | Uncovered |
|--------------------------------|---------|----------|-----------|
| bin auto[0] | 1 | 1 | Covered |
| bin auto[1] | 1 | 1 | Covered |
| bin auto[2] | 0 | 1 | Zero |
| bin auto[3] | 0 | 1 | Zero |
===================================================================
| TYPE /top/sub_ins2/cg2 | 50.000% | 100.000% | Uncovered |
===================================================================
| INSTANCE <UNNAMED1> | 50.000% | 100.000% | Uncovered |
|--------------------------------|---------|----------|-----------|
| COVERPOINT <UNNAMED1>::cp | 50.000% | 100.000% | Uncovered |
|--------------------------------|---------|----------|-----------|
| bin auto[0] | 0 | 1 | Zero |
| bin auto[1] | 0 | 1 | Zero |
| bin auto[2] | 1 | 1 | Covered |
| bin auto[3] | 1 | 1 | Covered |
===================================================================
注意点
TypeカバレッジとInstanceカバレッジ
カバレッジの結果はデフォルトではそれぞれのcovergroup毎に取得されます。例えばclass内にcovergroupが宣言されており、そのclassが複数個所でnew()されている場合、その全てのオブジェクトのカバレッジを加算した結果が取得されます。これはTypeカバレッジと呼ばれています。
一方new()されたオブジェクト毎にカバレッジを取得するのがInstanceカバレッジになります。デフォルトではTypeカバレッジが取得されます。covergroupのper_instanceオプションでInstanceカバレッジに変更することができます。
Instanceカバレッジの運用については少々注意が必要です。実使用上はテストを複数回実行してその結果をマージしてカバレッジを見ることが多いと思いますが、この時にどのインスタンス同士をマージすればよいかが言語仕様では規定されていないためです。特にクラスアイテムに対するカバレッジは、クラスが動的に生成、ガベージコレクションされるため自動化が難しいです。
またカバーグループのインスタンス名に対するスコープ、set_inst_nameによりインスタンス名が重複した場合の対応も明確には言語仕様で規定されていません。
参考文献
"19 Functional coverage". 1800-2017 - IEEE Standard for SystemVerilog--Unified Hardware Design, Specification, and Verification Language. pp.553-590.
-
"19.5.2 Specifying bins for transitions". 1800-2017 - IEEE Standard for SystemVerilog--Unified Hardware Design, Specification, and Verification Language. pp.563-566. ↩