Menu

Verilog $displayと$monitor:testbenchから出力する

$display$write$monitorの動作。使うフォーマット指定子、それらの違い、どれがどんな場面で正しいツールか。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

VerilogのC printfファミリ

3つのシステムタスクがシミュレータのstdoutに出力できます:

  • $display - 1回出力、改行を付加。
  • $write - 1回出力、改行なし。
  • $monitor - 監視signalが変わるたびに自動的に出力。

3つすべてがprintfと同様にフォーマット文字列と引数のリストを取ります。フォーマット指定子は類似ですがVerilog固有です。

$display:デフォルト

$displayは主役です:

このような出力が見えます:

Hello, world.
byte_val  = ab
byte_val  = 10101011
byte_val  = 171
byte_val  = 171 (no padding)
multi: nibble=1010 count=42

注意点:

  • %dはオペランドサイズに基づくデフォルト幅にパディングします。8ビットregなら3文字(255の余地)。先頭スペースは表形式の出力で見苦しくなります - 抑制するには%0dを使ってください。
  • %h%b%oはデフォルトで同様のパディングを持ちます。多くのtestbenchコードはアライメントが有用でないときは%0バリアントを使います。
  • 末尾の改行は自動です。$displayフォーマット文字列の末尾に\nを付けないでください - 空行になります。

フォーマット指定子

Verilogがサポートする集合:

指定子意味
%b2進
%d10進(signalがsignedならsigned)
%hまたは%x16進
%o8進
%c単一ASCII文字(下位8ビット)
%s文字列
%tシミュレーション時間
%m現在スコープの階層名
%%リテラル%
%0X先頭パディングなし、%b%dなどのいずれにも

%b%d%h%oが95%の時間使う4つです。%tが次によく使われます。タイムスタンプ付きログ行が欲しいときはいつでも。

$write:改行なし

$writeは改行を付加しないこと以外$displayと同じです:

出力:

abc
done

ループ本体から1行を組み立てるのに有用:

$write("[");
for (integer i = 0; i < 8; i = i + 1) $write("%h ", arr[i]);
$display("]");

$monitor:変化時に自動出力

$monitorはウォッチリストを登録します。フォーマット文字列内のsignal いずれかが 変わるたびにシミュレータが再評価して出力します:

入力の各変化に対して1行ずつ、3行が見えます。すべての刺激変更後に手動で$displayを呼ぶ必要はありません - $monitorがやります。

2つの制限:

  • 1つの$monitorしかアクティブにできない。 再度呼び出すと前のウォッチリストを置き換えます。$monitoroff$monitoronで一時的に抑制・再有効化できます。
  • 同じtime step内の変化は1つの出力に折りたたまれる。 abが両方時間5で変わると、monitorは両方の新しい値で1回発火し、2回ではありません。

いつどれを使うか

  • $display:ほとんどのtestbench出力。刺激後、重要なstate遷移後、またはサンプリングalways @(posedge clk)ブロック内で明示的に呼びます。
  • $write:ループや複数の小さなチャンクから1行を組み立てたいとき。
  • $monitor:小さなsignalセットを連続的に追跡し、変化したときだけ出力を見たいとき。最初のデバッグに有用ですが、出力が総行数で決定的でないので、リグレッションスクリプトで使うのは難しいです。

ほとんどのワークフローでは、$displayがすべてをカバーします。連続的な変化駆動の出力が本当に欲しいときだけ$monitorに手を伸ばします。

時間との作業

$timeは現在のシミュレーション時間を64ビット整数として返します。%0tとペアにします:

$display("at %0t: signal flipped", $time);

出力はat 25: signal flippedのように見えます(単位はtimescaleに依存)。

サブティック精度が必要なら(稀)、代わりに$realtimeを使ってください - realを返します。

%tはシミュレータが選ぶデフォルト幅で時間を自動的にフォーマットします。%0tはパディングを取り除きます。

クロックエッジでサンプリング

順序設計を監視するきれいな慣用句:1サイクルに1回出力する別のalways @(posedge clk)ブロック:

このサンプリングパターンはクロックごとに1ログ行を保証します - 出力にパターンマッチするリグレッションテストに最適です。

ファイルへのログ

$fopenでファイルを開き、$fdisplayでログします($displayのように動作しますがファイルハンドルに書き込みます):

integer fd;
initial begin
    fd = $fopen("results.txt", "w");
    $fdisplay(fd, "test=%s status=%s", test_name, status);
    $fclose(fd);
end

$fopenは32ビットハンドルを返します。$fdisplay$fwrite$fstrobeなどの最初の引数として渡します。関数はそれ以外はコンソール出力の兄弟分と同じです。

次に読むもの

$displayとその仲間はテキストログを与えます。視覚的デバッグ(電圧として時間に渡ってsignalを見る)にはVCD波形が欲しいでしょう。次のドキュメントDumpfile and VCDは、$dumpfile$dumpvars、シミュレーションをスクラブできるグラフィカル波形に変える2つの呼び出しを扱います。

よくある質問

Verilogの$displayと$monitorの違いは?

$displayは実行されたとき1回、即座に出力します。Cのprintfのようなものです。$monitorはウォッチリストを登録します。リスト内のsignalが変わるたびに、フォーマットされたメッセージが自動的に出力されます。一度に1つの$monitorしかアクティブにできず、再度呼び出すと前のウォッチリストを置き換えます。

Verilogの$displayがサポートするフォーマット指定子は?

一般的なもの:%b(2進)、%d(10進)、%h(16進)、%o(8進)、%c(下位バイトからの1文字)、%s(文字列)、%t(シミュレーション時間)、%m(階層instance名)。先頭ゼロ詰めを落とすには%0d形式を使ってください。%dはデフォルト幅にパディングし、%0dはパディングなしです。

Verilogの$writeとは?

$write$displayのようですが、改行を付加しません。複数の呼び出しから1行の出力を構築したいときに有用です。行末の$display(引数なしまたは末尾改行付き)が行を終了します。

Verilogでシミュレーション時間を出力するには?

%tフォーマット指定子と$time(またはサブティック分解能には$realtime)を使います:$display("at time %t: ...", $time);。デフォルトパディングを抑制するには%0tを使います。時間単位の単なる整数カウントには、%0d$timeも動きます:$display("t=%0d", $time);

Coddy programming languages illustration

Coddyでコードを学ぼう

始める