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がサポートする集合:
| 指定子 | 意味 |
|---|---|
%b | 2進 |
%d | 10進(signalがsignedならsigned) |
%hまたは%x | 16進 |
%o | 8進 |
%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つの出力に折りたたまれる。
aとbが両方時間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);。