電気回路/HDL/SystemPerl の導入
SystemPerl を入れてカバレッジ測定をしてみたい†
Verilator と SystemPerl を組み合わせて使うとカバレッジ測定ができるそうです。
カバレッジ解析について >> http://www.altima.jp/products/3rdparty/eda/code_coverage.html
電気回路/HDL/Verilator の導入(C++モード) および 電気回路/HDL/SystemC の導入 の続きです。
SystemPerl の導入†
ここでは cygwin 環境にて、Perl のモジュール管理システムである CPAN を使ってインストールを行いました。
(Linux のディストリビューションにバイナリーパッケージが用意されている場合には、 ディストリビューション固有のパッケージ管理ソフトを使ってインストールした方が よいので、注意してください)
LANG:console
$ cpan
cpan shell -- CPAN exploration and modules installation (v1.9402)
Enter 'h' for help.
cpan[1]> i /SystemPerl/
CPAN: Storable loaded ok (v2.20)
Going to read '/home/osamu/.cpan/Metadata'
Database was generated on Fri, 29 Oct 2010 09:30:12 GMT
Distribution id = W/WS/WSNYDER/SystemPerl-1.335.tar.gz
CPAN_USERID WSNYDER (Wilson Snyder <wsnyder@wsnyder.org>)
CONTAINSMODS SystemC::Coverage SystemC::Coverage::Item SystemC::Coverage::ItemKey SystemC::Netli
st SystemC::Netlist::AutoCover SystemC::Netlist::AutoTrace SystemC::Netlist::Cell SystemC::Netlist::
Class SystemC::Netlist::CoverGroup SystemC::Netlist::CoverPoint SystemC::Netlist::File SystemC::Netl
ist::Method SystemC::Netlist::Module SystemC::Netlist::Net SystemC::Netlist::Pin SystemC::Netlist::P
ort SystemC::Parser SystemC::Template
cpan[2]> install W/WS/WSNYDER/SystemPerl-1.335.tar.gz
...
Running make for W/WS/WSNYDER/SystemPerl-1.335.tar.gz
CPAN.pm: Going to build W/WS/WSNYDER/SystemPerl-1.335.tar.gz
Checking if your kit is complete...
Looks good
Warning: prerequisite Verilog::Getopt 2.211 not found.
Warning: prerequisite Verilog::Netlist 3.2 not found.
Writing Makefile for SystemC::Parser
Writing Makefile for SystemC::Netlist
-Info: SystemC isn't in the environment, 'make test' will skip tests
---- Unsatisfied dependencies detected during ----
---- WSNYDER/SystemPerl-1.335.tar.gz ----
Verilog::Netlist [requires]
Verilog::Getopt [requires]
Shall I follow them and prepend them to the queue
of modules we are processing right now? [yes] yes
...
Appending installation info to /usr/lib/perl5/5.10/i686-cygwin/perllocal.pod
WSNYDER/SystemPerl-1.335.tar.gz
/usr/bin/make install -- OK
cpan[5]> q
Lockfile removed.
$ sp_preproc
$ which sp_preproc
/usr/local/bin/sp_preproc
導入できました。
Verilator + SystemPerl によるカバレッジ測定†
http://www.veripool.org/projects/verilator/wiki/Manual-verilator#how_do_i_do_coverage_analysis
の How do I do coverage analysis? を参考にしました。
Verilator を SystemPerl モードで起動し、--coverage オプションを付けます。
その際、SYSTEMPERL および SYSTEMPERL_INCLUDE 環境変数が必要になります。
SYSTEMPERL_INCLUDE は、どうするのが正しいのかわからなかったのですが、 どうもヘッダファイル (*.h) はインストール時にどこにもコピーされていないようなので、 しかたなく SystemPerl をビルドしたソースフォルダを指すようにしています。
LANG:console
$ export SYSTEMPERL=/usr/lib/perl5/site_perl/5.10/i686-cygwin/SystemC/
$ export SYSTEMPERL_INCLUDE=~/.cpan/build/SystemPerl-1.335-t4zlPN/src/
$ verilator -sp -Wno-lint --top-module bin2bcd bin2bcd.v --exe bin2bcd_sc.cpp --coverage
$ cd obj_dir/
$ ls
Vbin2bcd.mk Vbin2bcd__Syms.cpp Vbin2bcd__ver.d Vbin2bcd_classes.mk
Vbin2bcd.sp Vbin2bcd__Syms.h Vbin2bcd__verFiles.dat
$ sp_preproc --preproc *.sp
$ ls
Vbin2bcd.cpp Vbin2bcd.mk Vbin2bcd__Syms.cpp Vbin2bcd__ver.d Vbin2bcd_classes.mk
Vbin2bcd.h Vbin2bcd.sp Vbin2bcd__Syms.h Vbin2bcd__verFiles.dat
$ make -j -f Vbin2bcd.mk Vbin2bcd
g++ -I. -MMD -I/home/osamu/verilator-3.804/include -I/home/osamu/verilator-3.804/include/vltstd -DV
L_PRINTF=printf -DVM_TRACE=0 -DVM_COVERAGE=1 -I/home/osamu/systemc/systemc-2.2.0//include -I/
home/osamu/.cpan/build/SystemPerl-1.335-t4zlPN/src/ -DSYSTEMPERL -c -o bin2bcd_sc.o ../bin2bcd_sc.c
pp
g++ -I. -MMD -I/home/osamu/verilator-3.804/include -I/home/osamu/verilator-3.804/include/vltstd -DV
L_PRINTF=printf -DVM_TRACE=0 -DVM_COVERAGE=1 -I/home/osamu/systemc/systemc-2.2.0//include -I/
home/osamu/.cpan/build/SystemPerl-1.335-t4zlPN/src/ -DSYSTEMPERL -c -o verilated.o /home/osamu/veri
lator-3.804/include/verilated.cpp
g++ -I. -MMD -I/home/osamu/verilator-3.804/include -I/home/osamu/verilator-3.804/include/vltstd -DV
L_PRINTF=printf -DVM_TRACE=0 -DVM_COVERAGE=1 -I/home/osamu/systemc/systemc-2.2.0//include -I/
home/osamu/.cpan/build/SystemPerl-1.335-t4zlPN/src/ -DSYSTEMPERL -c -o Sp.o /home/osamu/.cpan/build
/SystemPerl-1.335-t4zlPN/src/Sp.cpp
/usr/bin/perl /home/osamu/verilator-3.804/bin/verilator_includer Vbin2bcd.cpp > Vbin2bcd__ALLcls.cpp
/usr/bin/perl /home/osamu/verilator-3.804/bin/verilator_includer Vbin2bcd__Syms.cpp > Vbin2bcd__ALLs
up.cpp
g++ -I. -MMD -I/home/osamu/verilator-3.804/include -I/home/osamu/verilator-3.804/include/vltstd -DV
L_PRINTF=printf -DVM_TRACE=0 -DVM_COVERAGE=1 -I/home/osamu/systemc/systemc-2.2.0//include -I/
home/osamu/.cpan/build/SystemPerl-1.335-t4zlPN/src/ -DSYSTEMPERL -c -o Vbin2bcd__ALLsup.o Vbin2bcd_
_ALLsup.cpp
g++ -I. -MMD -I/home/osamu/verilator-3.804/include -I/home/osamu/verilator-3.804/include/vltstd -DV
L_PRINTF=printf -DVM_TRACE=0 -DVM_COVERAGE=1 -I/home/osamu/systemc/systemc-2.2.0//include -I/
home/osamu/.cpan/build/SystemPerl-1.335-t4zlPN/src/ -DSYSTEMPERL -c -o Vbin2bcd__ALLcls.o Vbin2bcd_
_ALLcls.cpp
Archiving Vbin2bcd__ALL.a ...
ar r Vbin2bcd__ALL.a Vbin2bcd__ALLcls.o Vbin2bcd__ALLsup.o
ar: creating Vbin2bcd__ALL.a
ranlib Vbin2bcd__ALL.a
g++ -L/home/osamu/systemc/systemc-2.2.0//lib-cygwin bin2bcd_sc.o verilated.o Sp.o Vbin2bcd__ALL.
a -o Vbin2bcd -lm -lstdc++ -lsystemc 2>&1 | c++filt
$ ls
Sp.d Vbin2bcd.mk Vbin2bcd__ALLcls.o Vbin2bcd__Syms.h bin2bcd_sc.o
Sp.o Vbin2bcd.sp Vbin2bcd__ALLsup.cpp Vbin2bcd__ver.d verilated.d
Vbin2bcd.cpp Vbin2bcd__ALL.a Vbin2bcd__ALLsup.d Vbin2bcd__verFiles.dat verilated.o
Vbin2bcd.exe* Vbin2bcd__ALLcls.cpp Vbin2bcd__ALLsup.o Vbin2bcd_classes.mk
Vbin2bcd.h Vbin2bcd__ALLcls.d Vbin2bcd__Syms.cpp bin2bcd_sc.d
$ ./Vbin2bcd.exe
SystemC 2.2.0 --- Oct 29 2010 15:47:29
Copyright (c) 1996-2006 by all Contributors
ALL RIGHTS RESERVED
done
$ ls logs
ls: cannot access logs: No such file or directory
あれ?計測結果は logs/coverage.pl というファイルとして保存されるはずなんだけど・・・
/verilator-3.804/test_sp/ をよく見たところ、 カバレッジ測定結果を保存するにはテストベンチに手を加える必要があるようなので、
bin2bcd_sc.cpp の先頭に、
LANG:cpp #ifdef SYSTEMPERL #include "systemperl.h" // SystemC + SystemPerl global header #include "sp_log.h" // Logging cout to files #include "SpTraceVcd.h" #include "SpCoverage.h" #endif
sc_main の最後に、
LANG:cpp #ifdef SYSTEMPERL SpCoverage::write(); // Writes logs/coverage.pl #endif
を加えることにしました。
LANG:console
$ make -j -f Vbin2bcd.mk Vbin2bcd
g++ -I. -MMD -I/home/osamu/verilator-3.804/include -I/home/osamu/verilator-3.804/include/vltstd -DV
L_PRINTF=printf -DVM_TRACE=0 -DVM_COVERAGE=1 -I/home/osamu/systemc/systemc-2.2.0//include -I/
home/osamu/.cpan/build/SystemPerl-1.335-t4zlPN/src/ -DSYSTEMPERL -c -o bin2bcd_sc.o ../bin2bcd_sc.c
pp
g++ -L/home/osamu/systemc/systemc-2.2.0//lib-cygwin bin2bcd_sc.o verilated.o Sp.o Vbin2bcd__ALL.
a -o Vbin2bcd -lm -lstdc++ -lsystemc 2>&1 | c++filt
$ ./Vbin2bcd.exe
SystemC 2.2.0 --- Oct 29 2010 15:47:29
Copyright (c) 1996-2006 by all Contributors
ALL RIGHTS RESERVED
done%Error: Can't Write logs/coverage.pl
Aborted (core dumped)
$ mkdir logs
$ ./Vbin2bcd.exe
SystemC 2.2.0 --- Oct 29 2010 15:47:29
Copyright (c) 1996-2006 by all Contributors
ALL RIGHTS RESERVED
done
$ ls logs
coverage.pl
$ vcoverage
%Error: No such file or directory bin2bcd.v
$ vcoverage --help
...
+incdir+*dir* =item -I*dir*
Specifies a directory for finding include files.
...
$ vcoverage +incdir+..
Total coverage (22/23) 95.65%
See lines with '%00' in logs/coverage_source
$ less logs/coverage_source/bin2bcd.v
とした結果は、
LANG:verilog(linenumber)
// Coverage analysis on Fri Oct 29 21:02:38 2010
// 2進数を BCD に直す回路
// bin に変換元の符号なし整数をセットして start に1を立てる
// 次クロックで busy が立つので、降りるまで待って bcd を読む
// BITS により、任意ビット数に対応可能
// 計算時間は (BITS-1) * 6 クロック程度
module bin2bcd #(
parameter BITS = 32
) (clk, rst, start, busy, bin, bcd);
// log 16 = 1.20412 < 1205/1000 を使って BCD 表現に必要な桁数を求める
localparam BCD_DIGITS = BITS * 1205 / 1000 / 4 + 1;
localparam BCD_DIGITS_BITS
= BCD_DIGITS < 2 ? 1 :
BCD_DIGITS < 4 ? 2 :
BCD_DIGITS < 8 ? 3 :
BCD_DIGITS < 16 ? 4 :
BCD_DIGITS < 32 ? 5 :
BCD_DIGITS < 64 ? 6 :
BCD_DIGITS < 128 ? 7 : 8;
122920 input wire clk;
%000002 input wire rst;
002048 input wire start;
002048 output wire busy;
002036 input wire [BITS-1:0] bin;
022924 output wire [BCD_DIGITS*4-1:0] bcd;
// 割り算用レジスタ
015406 reg [BITS-1:0] numerator; // 分子
364542 reg [BITS+3-1:0] denominator; // 分母
017099 reg [3:0] quotient; // 商
// 10^n を保持する ROM
reg [BITS-1:0] denominators[1:BCD_DIGITS-1];
integer i;
initial // ROM の初期化
for (i=1; i<BCD_DIGITS; i=i+1)
denominators[i] = 10 ** i;
// ステートマシンは state, digit を変数として動作する
110592 reg [2:0] state;
016386 reg [BCD_DIGITS_BITS-1:0] digit;
localparam stIdle = 0;
localparam stDivide0 = 1;
localparam stDivide1 = 2;
localparam stDivide2 = 3;
localparam stDivide3 = 4;
localparam stDivide4 = 5;
localparam stNext = 6;
// 計算結果を保持するレジスタ
000019 reg [3:0] bcd_internal[2:BCD_DIGITS-1];
always @(posedge clk)
000011 if (rst) begin
state <= stIdle;
end else
061449 case (state)
006153 stIdle: begin
digit <= BCD_DIGITS - 1;
001024 if (start) begin
numerator <= bin;
state <= stDivide0;
end
end
009216 stDivide0: begin
// 分母に 10^n << 3 を用意する
denominator <= denominators[digit] << 3;
state <= stDivide1;
end
036864 stDivide1, stDivide2, stDivide3, stDivide4: begin
// 割り算して商を得る
003038 if (numerator >= denominator) begin
quotient <= { quotient, 1'b1 };
numerator <= numerator - denominator;
033826 end else begin
quotient <= { quotient, 1'b0 };
end
denominator <= denominator >> 1;
state <= state + 1;
end
009216 stNext: begin
// 結果を格納して次へ
bcd_internal[digit] <= quotient;
001024 if (digit==1) begin
state <= stIdle;
008192 end else begin
digit <= digit - 1;
state <= stDivide0;
end
end
endcase
// 出力に繋ぐ
assign busy = state != stIdle;
generate
genvar j;
for (j=2; j<BCD_DIGITS; j=j+1) begin: bcd_connection
assign bcd[j*4 +: 4] = bcd_internal[j];
end
endgenerate
assign bcd[7:4] = quotient;
assign bcd[3:0] = numerator[3:0];
endmodule
となって、行頭の数値が恐らく呼び出し回数ですね。
先頭に % が付いている行は「テストが足りないのでは?」 と注意してくれている行になります。
たしかに rst 信号は2回しか変化してないので、注意されても仕方がない???
上記測定結果で
LANG:console Total coverage (22/23) 95.65%
と出ているのは、23 行中 1 行に % がついたので、それを除いた 22 行を全体で割った値のようです。
うまくいっているようですね。
ソースを変更したせいで†
ただ、カバレッジ計測のためにテストベンチのソースをいじらなければならないのは感心しないところです。
上記ソースのまま、--coverage を付けず -sc でコンパイルしようとすると、
LANG:console $ cd .. $ rm -r obj_dir/ $ verilator -sc -Wno-lint bin2bcd.v --exe bin2bcd_sc.cpp $ cd obj_dir/ $ make -j -f Vbin2bcd.mk Vbin2bcd g++ -I. -MMD -I/home/osamu/verilator-3.804/include -I/home/osamu/verilator-3.804/include/vltstd -DV L_PRINTF=printf -DVM_TRACE=0 -DVM_COVERAGE=0 -I/home/osamu/systemc/systemc-2.2.0//include -c -o bin2bcd_sc.o ../bin2bcd_sc.cpp g++ -I. -MMD -I/home/osamu/verilator-3.804/include -I/home/osamu/verilator-3.804/include/vltstd -DV L_PRINTF=printf -DVM_TRACE=0 -DVM_COVERAGE=0 -I/home/osamu/systemc/systemc-2.2.0//include -c -o verilated.o /home/osamu/verilator-3.804/include/verilated.cpp /usr/bin/perl /home/osamu/verilator-3.804/bin/verilator_includer Vbin2bcd.cpp > Vbin2bcd__ALLcls.cpp /usr/bin/perl /home/osamu/verilator-3.804/bin/verilator_includer Vbin2bcd__Syms.cpp > Vbin2bcd__ALLs up.cpp g++ -I. -MMD -I/home/osamu/verilator-3.804/include -I/home/osamu/verilator-3.804/include/vltstd -DV L_PRINTF=printf -DVM_TRACE=0 -DVM_COVERAGE=0 -I/home/osamu/systemc/systemc-2.2.0//include -c -o Vbin2bcd__ALLsup.o Vbin2bcd__ALLsup.cpp g++ -I. -MMD -I/home/osamu/verilator-3.804/include -I/home/osamu/verilator-3.804/include/vltstd -DV L_PRINTF=printf -DVM_TRACE=0 -DVM_COVERAGE=0 -I/home/osamu/systemc/systemc-2.2.0//include -c -o Vbin2bcd__ALLcls.o Vbin2bcd__ALLcls.cpp ../bin2bcd_sc.cpp: In function ‘int sc_main(int, char**)’: ../bin2bcd_sc.cpp:60: error: ‘SpCoverage’ has not been declared make: *** [bin2bcd_sc.o] Error 1 make: *** Waiting for unfinished jobs....
となって止まってしまいます。
#ifdef SYSTEMPERL で囲っても駄目なのはあたりまえなので、
--coverage を付けた時だけ有効になるような定義済みマクロがないか探しているところです。
1つのモジュールを複数のテストベンチで検証する場合の解析方法†
module_A に対して、module_A_sc1.cpp, module_A_sc2.cpp, module_A_sc3.cpp など、複数のテストベンチを作って検証した場合、 単一のテストベンチによるカバレッジを解析しても意味がありません。
全部のテストベンチを動かしたうえで、 それでもカバーされていないコードを見つけられるようにするために、 Verilator のカバレッジ解析では logs/coverage.pl は上書きではなく 追加モードで書き込まれるそうです。
これを利用すれば、あるディレクトリに logs を掘って、 そこで複数のテストベンチを連続して走らせ、 最後に vcoverage で集計することで、 総合のカバレッジ解析ができるわけです。
三項演算子も解析できるか?†
LANG:verilog a <= (b > c) ? b : c;
のような三項演算子 ( ? と : ) は、 もしかするとカバレッジ計測をする上での鬼門かもしれませんね。
どのような解析結果になるのか、後で調べる必要がありそうです。