電気回路/HDL/SystemPerl の導入 の履歴(No.3)
更新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;
のような三項演算子 ( ? と : ) は、 もしかするとカバレッジ計測をする上での鬼門かもしれませんね。
どのような解析結果になるのか、後で調べる必要がありそうです。