GoogleがSystemVerilogのParserを開発していた。見つけたきっかけはGoogleのRISC-V向けランダムパタン生成ツールであるRISCV-DVを調査していたのだが、その最中に見つけたものだった。
余談であるがriscv-dvの方はUVMをサポートできるシミュレーション環境が必要で、個人では試行できないので試していない。ソースコードを読む時間があれば読んでみたいけど。
で、VeribleはC++で記述されているSystemVerilogのLexer/Parserのようで、開発にはFlex/Bisonを使用しているらしい。既にいくつかのツールを使用することができる。インストールの方法はREADMEに従おう。以下のコマンドを実行すれば/usr/local/bin
にインストールされる。
# For a system directory that requires root-access, call with -s option. # (Do _not_ run bazel with sudo.) bazel run :install -c opt -- -s /usr/local/bin
いくつかのツールがインストールされていることが確認できた。
$ ls -1 /usr/local/bin/verible-* /usr/local/bin/verible-patch-tool /usr/local/bin/verible-transform-interactive.sh /usr/local/bin/verible-verilog-diff /usr/local/bin/verible-verilog-format /usr/local/bin/verible-verilog-format-changed-lines-interactive.sh /usr/local/bin/verible-verilog-kythe-extractor /usr/local/bin/verible-verilog-lint /usr/local/bin/verible-verilog-obfuscate /usr/local/bin/verible-verilog-preprocessor /usr/local/bin/verible-verilog-syntax
verible-verilog-preprocessor
これはどうも単純なツールだ。プリプロセッサというのはVerilogのマクロのことではなく、ソースコードを処理する前の単純な文字列処理のために使用する。現在はコメントを除去することくらいしかできない。
試しに、昔作った簡単な4ビットカウンタを例に取り上げる。
counter_4bit.v
// Function from C-code import "DPI-C" context task dpi_c_func(input int in, output int out); module counter_4bit ( input logic clk, input logic reset_n, input logic en, output logic [3:0] cnt ); int return_value; task dpi_verilog_task(input int in); $display("dpi_verilog_task is called. in = %d", in); endtask // dpi_verilog_task // Function from Verilog Task export "DPI-C" task dpi_verilog_task; always_ff @(posedge clk, negedge reset_n) begin if (!reset_n) begin cnt <= 4'h0; end else begin if (en) begin cnt <= cnt + 4'h1; /* verilator lint_off WIDTH */ dpi_c_func (cnt, return_value); $display("return_value = %d", return_value); end end end
以下のようにコマンドを叩くとすべてのコメントが除去された。使用用途は良く分からないが、なるほど正しく動くようだ。
verible-verilog-preprocessor strip-comments counter_4bit.v
import "DPI-C" context task dpi_c_func(input int in, output int out); module counter_4bit ( input logic clk, input logic reset_n, input logic en, output logic [3:0] cnt ); int return_value; task dpi_verilog_task(input int in); $display("dpi_verilog_task is called. in = %d", in); endtask export "DPI-C" task dpi_verilog_task; always_ff @(posedge clk, negedge reset_n) begin if (!reset_n) begin cnt <= 4'h0; end else begin if (en) begin cnt <= cnt + 4'h1; dpi_c_func (cnt, return_value); $display("return_value = %d", return_value); end end end endmodule
verible-verilog-diff
これは2つのVerilogファイルの比較ツールだ。比較と言っても等価性検証ツールではなく、トークンベースで一致比較を行う。例えば、上記のcounter_4bit.v
をトークンと意味を崩さない程度に思いっきりフォーマットを破壊してみた。
counter_4bit_mod.v
// Function from C-code import "DPI-C" context task dpi_c_func(input int in, output int out); module counter_4bit( input logic clk, input logic reset_n, input logic en, output logic [3:0] cnt); int return_value; task dpi_verilog_task(input int in); $display("dpi_verilog_task is called. in = %d", in); endtask // dpi_verilog_task // Function from Verilog Task export "DPI-C" task dpi_verilog_task; always_ff @ (posedge clk, negedge reset_n) begin if (! reset_n) begin cnt <= 4'h0; end else begin if (en) begin cnt <= cnt + 4'h1; /* verilator lint_off WIDTH */ dpi_c_func (cnt, return_value); $display("return_value = %d", return_value);end end end endmodule // counter_4bit
verible-verilog-diff counter_4bit.v counter_4bit_mod.v Inputs match.
これだけフォーマットを崩してもファイルが等価であるということを確認できる。一方でbegin
とend
の対応や、コメント部分が異なっている場合には正しく検証できない。コメントに関してはverilog-preprocessor
を使えば何とかなりそうだが、begin
~end
については現在は手が無いようだ。
verible-verilog-obfuscate
これはVerilogファイルの暗号化ツールだ。Verilog内の変数に暗号化をかけて内容を読みにくくすることが目的のようだ。
例えば上記のcounter_4bit.v
を入力すると以下のようになる。
$ verible-verilog-obfuscate < counter_4bit.v
// Function from C-code import "DPI-C" context task WmqWxwAWVw(input int ZU, output int kCm); module iHyAeiXbPg65 ( input logic wNf, input logic ivsMflg, input logic Hs, output logic [3:0] lkM ); int RXJ2WqMPENW0; task vsRZkyKcHqiuWPhC(input int ZU); $display("dpi_verilog_task is called. in = %d", ZU); endtask // dpi_verilog_task // Function from Verilog Task export "DPI-C" task vsRZkyKcHqiuWPhC; always_ff @(posedge wNf, negedge ivsMflg) begin if (!ivsMflg) begin lkM <= 4'h0; end else begin if (Hs) begin lkM <= lkM + 4'h1; /* verilator lint_off WIDTH */ WmqWxwAWVw (lkM, RXJ2WqMPENW0); $display("return_value = %d", RXJ2WqMPENW0); end end end endmodule // counter_4bit
モジュール名から何から何まで、可能な限り変数名や関数名などもハッシュ化してしまう。これはIPを提供する会社が自社の内部デザインを読み取られないようにするために便利なツールだろう。
verible-verilog-format
これはVerilogのフォーマッターだ。フォーマットに関してはいくつかコンフィグレーションが存在しないと不便だと思うが、verible-verilog-format
ではオプションでフォーマットを指定するようだ。
$ verible-verilog-format --helpfull
... Flags from verilog/tools/formatter/verilog_format.cc: --case_items_alignment (Format case items: {align,flush-left,preserve,infer}); default: infer; --class_member_variables_alignment (Format class member variables: {align,flush-left,preserve,infer}); default: infer; --failsafe_success (If true, always exit with 0 status, even if there were input errors or internal errors. In all error conditions, the original text is always preserved. This is useful in deploying services where fail-safe behaviors should be considered a success.); default: true; ... --try_wrap_long_lines (If true, let the formatter attempt to optimize line wrapping decisions where wrapping is needed, else leave them unformatted. This is a short-term measure to reduce risk-of-harm.); default: false; --verify_convergence (If true, and not incrementally formatting with --lines, verify that re-formatting the formatted output yields no further changes, i.e. formatting is convergent.); default: true;
試しに、上記のフォーマットをぐちゃぐちゃにしたcounter_4bit_mod.v
を入力してみた。
$ verible-verilog-format counter_4bit_mod.v
// Function from C-code import "DPI-C" context task dpi_c_func(input int in, output int out); module counter_4bit ( input logic clk, input logic reset_n, input logic en, output logic [3:0] cnt ); int return_value; task dpi_verilog_task(input int in); $display("dpi_verilog_task is called. in = %d", in); endtask // dpi_verilog_task // Function from Verilog Task export "DPI-C" task dpi_verilog_task ; always_ff @(posedge clk, negedge reset_n) begin if (!reset_n) begin cnt <= 4'h0; end else begin if (en) begin cnt <= cnt + 4'h1; /* verilator lint_off WIDTH */ dpi_c_func(cnt, return_value); $display("return_value = %d", return_value); end end end endmodule // counter_4bit
おお、綺麗にフォーマットされた!まあ他人のコードですらここまで汚くするのはあまりないので、簡単に整形したい場合とか、チーム内でルールを設ける場合に使用すると良さそうだ。