Menu

Verilog initial block:シミュレーション開始時のセットアップコード

initialブロックがalwaysとどう異なるか、シミュレーションでしか存在しない理由、そしてそれが使われる一般的なパターン(刺激、波形セットアップ、ログヘッダ)。

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

1回限りの手続きブロック

initial begin ... endalwaysの兄弟です。違いは、initialはちょうど1回、時刻0から始まり、その後終了します。感応リストもループもありません。

存在する理由は1つ:testbenchのセットアップ。初期値の設定、刺激の発進、ログファイルのオープン、固定シミュレーション時間後の$finish呼び出し、つまり実行開始時に1度、既知の順序で起こって欲しいものすべてです。

シミュレータがやることを順に見てみましょう:

  1. 時間が0に進む。
  2. initialブロックが開始。最初の$displayが出力。
  3. data8'hA5にセット。
  4. #10がシミュレーション時間を10単位進める。
  5. 2番目の$displayが出力。
  6. $finishがシミュレーションを終了。

ブロックは1回実行された。2回目のパスはありません。

定番のtestbench骨格

書くことになるほぼすべてのtestbenchが、このようなinitialブロックを使います:

形を見てください - 毎回手が伸びるtestbenchテンプレートです:

  1. VCDダンプファイルを開く$dumpfile$dumpvars) - Dumpfile and VCDで扱います。
  2. 数サイクル分の 初期値を設定し、resetを保持 する。
  3. #遅延を挟んだ変更の連続で 刺激を駆動 する。
  4. シミュレーションをきれいに終了する $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から並行に走り、シミュレータは時間が進むにつれて文をインターリーブします。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める