1回限りの手続きブロック
initial begin ... endはalwaysの兄弟です。違いは、initialはちょうど1回、時刻0から始まり、その後終了します。感応リストもループもありません。
存在する理由は1つ:testbenchのセットアップ。初期値の設定、刺激の発進、ログファイルのオープン、固定シミュレーション時間後の$finish呼び出し、つまり実行開始時に1度、既知の順序で起こって欲しいものすべてです。
シミュレータがやることを順に見てみましょう:
- 時間が0に進む。
initialブロックが開始。最初の$displayが出力。dataが8'hA5にセット。#10がシミュレーション時間を10単位進める。- 2番目の
$displayが出力。 $finishがシミュレーションを終了。
ブロックは1回実行された。2回目のパスはありません。
定番のtestbench骨格
書くことになるほぼすべてのtestbenchが、このようなinitialブロックを使います:
形を見てください - 毎回手が伸びるtestbenchテンプレートです:
- VCDダンプファイルを開く(
$dumpfile、$dumpvars) - Dumpfile and VCDで扱います。 - 数サイクル分の 初期値を設定し、resetを保持 する。
#遅延を挟んだ変更の連続で 刺激を駆動 する。- シミュレーションをきれいに終了する
$finish。
それだけです。ベンダーのサンプルを含め、目にするすべてのVerilogテストがこのパターンに従います。
複数のinitialブロックは並列
同じmodule内の複数のinitialブロックはすべて時刻0で 同時に 始まります。各々が自分の文を自分のタイムスライスで実行します:
シミュレータは両ブロックを時刻0で開始します。BブロックのE最初の$displayがすぐに実行されます。Aブロックは5単位待ってから出力します。Bブロックは時刻2で再び出力。Aブロックの2番目の出力は時刻15で発火。Bブロックの$finishは時刻22でシミュレーションを終了します。
セットアップ、クロック生成、刺激を別々のinitialブロックに分割するのは一般的なスタイルです。各ブロックは短く、1つのことをします。
宣言時のインライン初期化
一般的な簡潔形式:宣言時にregを初期化する。ほとんどのシミュレータはこれをinitialと同じに扱います:
reg clk = 0;
reg reset = 1;
reg [7:0] count = 0;
これは以下と等価です:
reg clk;
reg reset;
reg [7:0] count;
initial begin
clk = 0;
reset = 1;
count = 0;
end
簡潔な形式がtestbenchで目にするものです。初期値を宣言の隣に置けるので、スキャンしやすくなります。
initialはシミュレーション専用
合成ツールはinitialブロックを無視します。実ハードウェアにはセットアップコードが走る「瞬間0」がなく、電源投入、reset signal、それらのイベントから来る設定があるだけです。実ハードウェアでregisterを既知のstateで開始させる必要があれば、always @(posedge clk)内のreset signalで駆動します:
always @(posedge clk) begin
if (reset) state <= IDLE;
else state <= next_state;
end
if (reset)分岐がinitial state = IDLEの合成可能版です。Resetが「実ハードウェアでregisterを初期化するには?」への答えです。
一部のFPGAフロー(例えばXilinxツール)は、registerのreset値のための制限されたinitialを受け入れますが、それはベンダーごとの拡張です。ポータブルなコードでは頼らないでください。
initial内に登場するもの
標準のtestbench骨格を超える、いくつかのよくあるパターン:
時間遅延終了
initial begin
#1000 $finish; // セーフティネット: 1000時間単位後にシミュレーションを終了
end
シミュレーションの厳しい上限を置くだけの仕事をする別のinitial。メイン刺激ブロックが来ないsignalを待ってデッドロックしても、これが発火して実行を終了します。
波形セットアップ
initial begin
$dumpfile("dump.vcd");
$dumpvars(0, test); // testスコープ下のすべてをダンプ
end
この2行は、シミュレータにtestスコープの全signalを含むVCDファイルを書くよう伝えます。これがないと波形は得られません。
初期メモリイメージ
reg [7:0] memory [0:255];
initial begin
$readmemh("image.hex", memory);
end
$readmemhは16進フォーマットのファイルを配列にロードします。CPUのtestbenchで命令メモリを事前ロードするのに使います。シミュレーション専用。
よくある間違い
合成可能ロジックでinitialを使う。 合成されません。代わりにreset signalを使ってください。
$finishを忘れる。 これがないと、シミュレータは別のもの(デフォルトの時間制限、手動割り込みなど)が止めるまで実行されます。クイックテストならそれで構いませんが、リグレッションスクリプトでは常に$finishしてください。
刺激代入の間に#delayを忘れる。 a = 0; b = 1;を遅延なしで書くと、両方が同じシミュレーション時間に起こり、DUTは別々のイベントではなく同時に見るかもしれません。明確な刺激イベントの間に#1以上を挟んでください。
initialからwireを駆動しようとする。 alwaysと同じルール:regだけが合法ターゲットです。
次に読むもの
これで2つの手続きブロックフレーバーを見ました。次のドキュメントは初心者Verilogで最も混同される話題、Blocking vs Non-blocking代入を扱います。=と<=をいつ使うかを知ることが、動くflip-flopと競合状態の混乱の違いです。
よくある質問
Verilogのinitial blockとは?
initial begin ... endはシミュレーション開始時、時刻0でちょうど1回実行される手続きブロックです。testbench stateのセットアップに使う標準の場所です:signalの初期化、ログファイルのオープン、$dumpfile/$dumpvarsの呼び出し、刺激の駆動、$finishでのシミュレーション終了。1つのmodule内に複数のinitialブロックが共存でき、すべて時刻0で並列に開始します。
Verilogのinitialとalwaysの違いは?
initialは時刻0で1回実行されて終了します。alwaysは永遠に再実行され、感応リストを持ち、リストされたsignalが変わるたびに目覚めます。initialはほぼ独占的にtestbenchで使われます。alwaysはtestbenchと合成可能RTLの両方の主役です。
initial blockは合成可能ですか?
プレーンVerilogでは違います。合成ツールはinitialブロックを無視します。実ハードウェアにはセットアップコードが走る「時刻0」の瞬間がないからです。一部のFPGAツールチェーンはregisterのreset値を設定するための制限された形式を受け入れますが、一般的なケースはシミュレーション専用です。initialブロックはtestbenchに留め、合成可能なロジックの初期化にはreset signalを使ってください。
Verilog moduleに複数のinitial blockを持てますか?
はい。各initialブロックは時刻0で開始し、独立に完了まで実行されます。セットアップを複数のブロックに分割するのは一般的なtestbenchパターンです。1つはクロック生成用、1つは刺激用、1つは波形ダンプ用。時刻0から並行に走り、シミュレータは時間が進むにつれて文をインターリーブします。