コードクローンファインダー for verilog
(上記サイトより引用)
コードクローンとは
コードクローンとはソースコード中での類似または一致した部分を表します. コードクローンは,「コピーとペースト」によるプログラミングや,意図的に同一処理を繰り返して書くことにより,プログラムテキスト中に作りこまれます.
コードクローンの弊害
一般的に,コードクローンはソフトウェアの保守を困難にする要因の一つである,といわれています. 例えば,あるコードクローン上にフォールトが発見された場合に,開発者はそれと対応する全てのコードクローンを確認して,必要があれば全てのコードクローンに同様の修正を行う必要があるからです. 特に大規模システムでは,チームによる開発が通常行われており,上述したように一人の開発者がサブシステムを確認し,全てのコードクローンに対して一貫した修正を行うことはきわめて困難な作業であるといえます.
そのような事態を例えばCなどの言語ではコードクローンファインダーとして、
CCFinderのようなソフトが発表されている。
verilogでもこういうのあったら便利だけどニッチな言語だから見当たらないので、
Pyverilogを利用して作ってみました。実はPyverilog作者のshtaxxx氏が発案者だったりします。感謝。
開発指針
私が感じているCなどのソフトウェア言語とverilogの違い。
C
・ほとんどのコードはメソッド(関数orプロシージャ)の中に存在する。
Verilog
・ほとんどのコードはalways文(そしてそのほとんどはFFに代表されるレジスタ)の中に存在
verilogにもfunction文はあるが、使用頻度は多くない。
また参照はmoduleの中でのみ行われるため、クローンを探す需要はあまりない。
というわけでalways文で記述されるレジスタのクローンを探すことにしました。
verilogにおいてクローンを減らすということは回路面積を減らすことにもつながります。
機能説明
現在codeclone_finderで発見することができるクローンは以下の二つ。
・論理的に全く同一なレジスタ
例えばこのようなこーど。
always @(posedge CLK or negedge RST) begin
if(RST) begin
reg1 <= 1'b0;
end else begin
reg1 <= IN;
end
end
assign in1 = IN;
always @(posedge CLK or negedge RST) begin
if(RST) begin
reg2 <= 1'b0;
end else begin
reg2 <= in1;
end
end
in1とIN1の実体は同じであるためreg1とreg2は毎クロック必ず同じ値を保持します。
reg2の記述を消し、reg1を使いまわすべきです。
・論理的に全く反転しているレジスタ
always @(posedge CLK or negedge RST) begin
if(RST) begin
reg2 <= 1'b0;
end else begin
reg2 <= in1;
end
end
always @(posedge CLK or negedge RST) begin
if(RST) begin
reg3 <= 1'b1;
end else begin
reg3 <= !in1;
end
end
上記のコードでは、reg2とreg3は毎クロック必ず同じ値を保持します。
遅延のために後ろにインバータを使いたくないというような非常に稀な状況を除いて、このようなリファクタリングが有効です。
always @(posedge CLK or negedge RST) begin
if(RST) begin
reg2 <= 1'b0;
end else begin
reg2 <= in1;
end
end
assign reg3 = !reg2;
これで保守性が上がるとともにレジスタが1個減ります。
codeclone_finderの使い方
まずは
を参考に、Pyverilog_toolboxをインストールしてください。
そして
コマンドプロンプト上で
(Pyverilog_toolboxをインストールしたディレクトリ)/verify_tool
に移動し、
$ Python codeclone_finder xxxx.v
と入力します。(xxxx.vは自分で用意したverilogコード)
試しにPyverilog_toolboxに梱包されているテストコードで動かすと
$ Python codeclone_finder ../testcode/reg_clone.v
Invert reg pairs: *1, *2, *3
Clone reg pairs: *4, *5
のようにコードクローンを指摘してくれます。
最後に
将来的には類似していてリファクタリングが可能なレジスタを見つけたり、
リファクタリングまで児童でしてくれるブラウザが作れればいいかなーと思いつつ。
Pyverilog_toolboxの紹介
拙作、Pyverilog_toolboxについて紹介してみる。
Pyverilog_toolboxはPyverilogの構文解析機能を用いて、verilogによるデジタル設計/検証を助けるツールとして開発を行いました。
現在実装されている機能は、組み合わせ回路ループファインダー、コードクローンファインダー、レジスタマップアナライザー、カウンタアナライザーです。
これらの機能の紹介は後で説明するとして、まずはインストールの方法について説明します。
インストール方法
主にwindows環境について。
Python:
2.7系の最新版をインストールする。3系は…私は動かしたことはありません。
具体的なインストール方法は以下を参照してください↓
http://sphinx-users.jp/gettingstarted/install_windows.html
Icarus verilog:
↓ここでインストーラをダウンロードし、起動。
筆者はiverilog-0.9.7_setup.exe (latest stable release)と記述されるバージョン、
つまり最新の安定版を使用している。(2015/05/23現在)
Pyverilog:
↓のページの"InstallationI"を読んでインストール。
http://shtaxxx.github.io/Pyverilog/
他にもPGraphvizとPygraphvizを入れることで、
データフローの図示といったPyverilogの素敵機能を発動できるので本当はやりたいんだけどwindos環境ではなんか難しくてできんかった…。Pyverilog_toolboxには必要ないのでとりあえず省略。jinja2も入れても入れなくてもええです。
Pyverilog_toolbox:
(2015/7/8追記)
pipに対応しました。
$ pip install pyverilog_toolbox
でOKです。
上記ページの、右のほうにある"Download ZIP"をクリックし、任意のディレクトリで解凍する。
もしくはgithubを使用できる環境があるなら
$git clone https://github.com/fukatani/Pyverilog_toolbox.git
でいれてもよい。
動作確認(飛ばしてもよい):
コマンドプロンプトを立ち上げ、
(Pyverilog_toolbox展開先のディレクトリ)/pyverilog_toolbox/testcode
に移動する。
$ python test_ra.py
と打ち、
Ran 10 test in ****s
OK
というメッセージが表示されれば無事全ての機能が動いている。
試しにcombloop_finderを使ってみる。
組み合わせ回路ループと言うのはなかなかに厄介な問題である。
組み合わせ回路ループとは例えば以下の図のような回路のことで、
赤い矢印の部分のようにループにFFが挟まれていない回路のことである。
一度組み合わせ回路ループが出来てしまうと、発振してシミュレーションが全く進まなくなったり、ラッチが形成され思わぬ動作をしてしまったり、上司から冷たい目で見られたり、世界を崩壊から救えなかったりする危険性がある。
勿論セレブリチーなCADを使えば発見できるのだが、お金の力で環境を整えられない場合にはエンジニアが目で探すことになる。これがまた、しばしばノーヒントで大変な作業なのだ。
そのような問題を(無料で!)発見してくれるのが、
Pyverilog_toolboxに含まれるcombloop_finderです。
使い方は簡単で、コマンドプロンプトで
(Pyverilog_toolbox展開先のディレクトリ)/pyverilog_toolbox/verify_tool
に移動し、
python combloop_finder.py xxxx.v
を実行すればよい。ここでxxxx.vは任意のverilogファイルである。testcodeを使用したい場合、
python combloop_finder.py ../combloop.v
としてみましょう。
組み合わせ回路ループがある回路では
CombLoopException: Combinational loop is found @TOP.wire3
と例外として投げられる。
基点となっている信号(この場合はTOP.wire3)
も表示されるので探しに行く必要がない。便利だ。
組み合わせ回路ループが存在しない回路では
There is no combinational loop.
とメッセージが表示されます。
最後に注意として、
Pyverilogで読み込まれる回路のトップモジュール名はデフォルトでは"TOP"となっている。そしてTOPというモジュールが無い場合はエラーを吐く。
トップモジュール名が"TOP"でないときは、-tオプションでトップモジュール名を指定する。例えば先のcombloop_finderを使用する場合、
python combloop_finder.py xxxx.v -t TOP_DIG
のようにすることで、"TOP_DIG"モジュールをトップとして扱うことが出来る(存在しない場合はエラー)。
他の機能にはついてはまた今度。ミスとか質問とかあったらお気軽にどぞ。
Pyverilogの紹介
verilogで設計をしてる時に、回路の構造をプログラム的に処理することにより、
何か悪い構造などをサーチしたいときがある。
そんなとき、私が頼りにしているのがPyverilogだ。
Verilog HDLのデザイン解析・コード生成のためのPythonベースのオープンソースツールキットPyverilogをリリースしました - shtaxxx日記
オープンソースで当然フリー。プログラミング言語Pythonによって記述されている。
インストールにはいくつか条件があるが、私はコード生成器 (ast_code_generator)やデータフロー可視化のためのGraphvizとPygraphviz は使わず、最低限必要なIcarus Verilogのみをインストールして使用している。
Pyverilogでたとえばデータフロー解析器を使用すると、各信号がどのような式で記述されているかを理解することが出来る。
例えば
always @(posedge CLK) begin
if(RST) begin
reg0[1:0] <= 0;
reg1 <= 0;
end else if(WRITE) begin
case(ADDR)
0:reg0[1:0] <= WRITE_DATA;
1:reg1 <= WRITE_DATA[0];
endcase
end
end
always @* begin
case(ADDR)
0:READ_DATA[1:0] = reg0[1:0];
1:READ_DATA[1:0] = {1'b0,reg1};
endcase
end
のような読み書き可能なレジスタを解析してみる。
すると、
Bind:
(Bind dest:TOP._rn1_READ_DATA msb:'d1 lsb:'d0 tree:(Concat Next:(IntConst 1'b0),(Terminal TOP.reg1)))
(Bind dest:TOP.reg0 msb:'d1 lsb:'d0 tree:(Branch Cond:(Terminal TOP.RST) True:(IntConst 0) False:(Branch Cond:(Terminal TOP.WRITE) True:(Branch Cond:(Operator Eq Next:(Terminal TOP.ADDR),(IntConst 0)) True:(Terminal TOP.WRITE_DATA)))))
(Bind dest:TOP.reg1 tree:(Branch Cond:(Terminal TOP.RST) True:(IntConst 0) False:(Branch Cond:(Terminal TOP.WRITE) True:(Branch Cond:(Operator Eq Next:(Terminal TOP.ADDR),(IntConst 0)) False:(Branch Cond:(Operator Eq Next:(Terminal TOP.ADDR),(IntConst 1)) True:(Partselect Var:(Terminal TOP.WRITE_DATA) MSB:(IntConst 0) LSB:(IntConst 0)))))))
(Bind dest:TOP._rn0_READ_DATA msb:'d1 lsb:'d0 tree:(Partselect Var:(Terminal TOP.reg0) MSB:(IntConst 1) LSB:(IntConst 0)))
(Bind dest:TOP.READ_DATA msb:'d1 lsb:'d0 tree:(Branch Cond:(Operator Eq Next:(Terminal TOP.ADDR),(IntConst 0)) True:(Terminal TOP._rn0_READ_DATA) False:(Branch Cond:(Operator Eq Next:(Terminal TOP.ADDR),(IntConst 1)) True:(Terminal TOP._rn1_READ_DATA))))
のような形で出てくる。
なんのこっちゃという感じではあるが、PythonプログラムがverilogHDLにおける構造を理解できるということはレジスタマップ推定や組み合わせ回路ループの把握などを有料のEDAなしにできるということである。
次回はこれらの実装であるPyverilog_toolboxライブラリについて紹介したい。