ModelSim で $messagelog を使う の履歴(No.5)
更新$messagelog が便利†
verilog のテストベンチで画面にテキストを表示するには、 通常 $display を使うのですが、
ModelSim では $display の代わりに $messagelog を使うと便利です。
例えばテストベンチに
LANG:verilog always @(posedge iclk_int) if (full & we) $messagelog("%:S Write strobe was asserted while fifo is full.", "Error")
と書いておけば、$display のように出力ウィンドウにメッセージを表示すると共に、 下図のように wave ウィンドウの message 欄にメッセージが表示された時刻を示す ▼ が表示されます(青丸)。
▼ の上にカーソルを持って行けば表示されたメッセージを見ることができますので、 $display に表示された時刻を目で追うような苦労から解放されます。
また、message 欄のタイトル部分(赤丸)をクリックすることで、 アクティブなカーソルを次の ▼ まで飛ばすことができます。
したがって、通常 ▼ を目で探す必要もありません。
メニューの [View] - [Messsage Viewer] にチェックを入れておくと、 wave ウィンドウの隣に Message Viewer タブが出ます(紫丸)。 ここでは次のように、表示されたメッセージを条件別に並べ替えて表示するようなことが可能です。
(ちょっとメッセージ内容が違っているのは下記のルーチンを使っているためです)
$messagelog では %:S にメッセージの種類を指定できて、 表示されるメッセージはこれを使って分類されます。
よく使われるのは "Note" と "Error" だと思いますが、
- "Note" ("Info", "Message" も同義)
- "Warning"
- "Error"
- "Fatal"
の4つが使えるそうです。
この %:S の内容で ▼ の色が変わるので、 重要なメッセージとそうでない物を一目で見分けられます。
その他の使い方については ModelSim の User's Manual を参照して下さい。
$messagelog を利用するマクロ†
上記の通り $messagelog はとても便利なのですが、 そのままソースコードに書き込んでしまうと、 ModelSim 以外でテストするのが難しくなってしまいます。
そこで、$messagelog を直接呼ぶのではなくマクロを使って記述することにしました。
simulation.inc
LANG:verilog(linenumber) `ifndef VERBOSE `define VERBOSE 0 `endif `define INFO(msg) \ if(`VERBOSE) \ $messagelog(`"%:S msg.`", "Note") `define ERROR(msg) \ $messagelog(`"%:S msg.`", "Error") `define DONE \ $messagelog(`"%:S Done.`", "Note") `define ASSERT(variable,value) \ if((variable)!=(value)) \ $messagelog(`"%:S Assertion [ variable == value ] was not met.`", "Error") `define ASSERT_ALWAYS(variable,value) \ always @(variable) \ if((variable)!=(value)) \ $messagelog(`"%:S Assertion [ variable == value ] was not met.`", "Error") `define ASSERT_AT_CLK(clk,variable,value) \ always @(clk) \ if((variable)!=(value)) \ $messagelog(`"%:S Assertion [ variable == value @ clk ] was not met.`", "Error")
上記コードを `include して使うことで、 ModelSim 以外でシミュレーションする必要が生じたときにも このマクロ定義だけを書き換えれば済みますので、 便利な機能を安心して使えます。
ASSERT マクロ群†
本来ならこういったことをするのには OVL のような、 標準的な検証ライブラリを使うべきなのでしょうけれど、 使い方を覚えるのが面倒なのに加えて、 記述量を減らしたいという欲求もあって、 上記のようなマクロを書いて使ってしまっています。
ASSERT や ASSERT_ALWAYS を書くのには SystemVerilog の構文を使っています。 従って、vlog のオプションに -sv を加えておかないと、コンパイルエラーが生じます。 注意して下さい。
やり方は、
- ISE Project Navigator の Processes ペインで Simulate Behavioral Model を右クリック
- Process Properties
- Category で Simulation Properties が選択されているはず
- Other VLOG Command Line Options に -sv を追加
です。
同じ所に "+define+VERBOSE=1" を書くことで、 VERBOSE マクロを書き換えて `INFO マクロによる出力を無効化することもできます。
上記のようにわざわざ SystemVerilog の構文を使っているのは、 与えた条件からエラーメッセージを自動生成するためです。 この自動生成のおかげでアサーションを記述するのがとても楽になっています。
上記 FIFO の例であれば、
LANG:verilog always @(posedge iclk_int) if (full & we) $messagelog("%:S Write strobe was asserted while fifo is full.", "Error")
の代わりに
LANG:verilog `ASSERT_AT_CLK(posedge iclk_int, full && we, 0);
とするだけで、
# ** Error: Assertion [ full && we == 0 @ posedge iclk_int ] was not met.
という分かりやすいメッセージが出力されます。
※ 本当はメッセージ中の full && we を括弧でくくらないと間違いなのですが、 それはそれで見難いので、そのままになっています。 条件式の方は括弧で括ってあるので大丈夫です。 (full && we == 0 だと full && (we == 0) の意味になってしまいます )
メッセージにはエラーの発生箇所が含まれていませんが、 上記の通り発生時刻は Wave Window で分かりますし、 コード中での位置は Message Viewer を使えば調べられます。
エラーメッセージはあまり長くない方が見やすいようです。
検証コードをソースに埋め込む†
上記のような検証コードはテストベンチに記述しても良いのですが、 ロジックを記述したソースコードに直接埋め込んでおくのも便利です。
FIFO のソースコード中に
LANG:verilog `include "simulation.inc" ... module fifo ( ... ); ... // synthesis translate_off `ASSERT_AT_CLK(posedge iclk_int, full && we, 0); `ASSERT_AT_CLK(posedge oclk_int, empty && re, 0); // synthesis translate_on endmodule
のように、synthesis translate_off / synthesis translate_on で挟んで検証コードを埋め込んでおくと、 インプリメント時にはこの部分が無視されるので、 ソースコードを変更することなく検証と合成のどちらも行えます。
注意†
恐らく、上記のようにソースに埋め込んだの検証コードは Behavior レベルのテストでしか効果がありませんので、 合成結果に対するテストでは別途テストベンチに検証コードを記述する必要があるのだと思います。