Verilogブロッキング代入とノンブロッキング代入:違いを徹底解説
ブロッキング代入
ブロッキング代入文は=を使用し、手続きブロック内で順次実行されます。ただし、並行ブロックで実行される文の実行を妨げることはありません。
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a = 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
b = 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c = 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
d = 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
e = 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
2つのinitialブロックがあり、シミュレーション開始時に並行して実行されることに注意してください。各ブロック内では文が順次実行され、両ブロックとも時間0nsで終了します。具体的には、変数aが最初に代入され、続いてdisplay文、その後すべての文が実行されます。これは出力からも明らかで、最初の$displayでは変数bとcが8'hxxになっています。これは最初の$displayが呼び出された時点で、変数bとcへの代入がまだ実行されていないためです。
ncsim> run [0] a=0xda b=0xx c=0xx [0] a=0xda b=0xf1 c=0xx [0] a=0xda b=0xf1 c=0x30 [0] d=0xaa e=0xx [0] d=0xaa e=0x55 ncsim: *W,RNQUIE: Simulation is complete.
次に、同じ文にいくつか遅延を追加して動作を見てみましょう。
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a = 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
#10 b = 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c = 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
#5 d = 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
#5 e = 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
ncsim> run [0] a=0xda b=0xx c=0xx [5] d=0xaa e=0xx [10] a=0xda b=0xf1 c=0xx [10] a=0xda b=0xf1 c=0x30 [10] d=0xaa e=0x55 ncsim: *W,RNQUIE: Simulation is complete.
ノンブロッキング代入
ノンブロッキング代入は、後続文の実行をブロックせずに代入をスケジュールし、<=記号で指定します。同じ記号が式では関係演算子として、ノンブロッキング代入の文脈では代入演算子として使用されるのは興味深い点です。上記の最初の例で、すべての=記号をノンブロッキング代入演算子<=に置き換えると、出力に違いが現れます。
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a <= 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
b <= 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c <= 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
d <= 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
e <= 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
すべての$display文が'h'xを出力していることがわかります。この動作の理由は、ノンブロッキング代入の実行方法にあります。特定のタイムステップにおけるすべてのノンブロッキング文のRHSが取得され、次の文に進みます。取得されたRHSの値は、タイムステップの終了時にLHS変数に代入されます。
ncsim> run [0] a=0xx b=0xx c=0xx [0] a=0xx b=0xx c=0xx [0] a=0xx b=0xx c=0xx [0] d=0xx e=0xx [0] d=0xx e=0xx ncsim: *W,RNQUIE: Simulation is complete.
上記例の実行フローを分解すると以下のようになります。
|__ ブロック1を生成: initial | |___ 時間 #0ns: a <= 8'DA、ノンブロッキングなのでRHSの値(8'hDA)を記録し次へ | |___ 時間 #0ns: $display()はブロッキングなので実行: しかしaは新しい値をまだ受け取っていないためa=8'hx | |___ 時間 #0ns: b <= 8'F1、ノンブロッキングなのでRHSの値(8'hF1)を記録し次へ | |___ 時間 #0ns: $display()はブロッキングなので実行。しかしbは新しい値をまだ受け取っていないためb=8'hx | |___ 時間 #0ns: c <= 8'30、ノンブロッキングなのでRHSの値(8'h30)を記録し次へ | |___ 時間 #0ns: $display()はブロッキングなので実行。しかしcは新しい値をまだ受け取っていないためc=8'hx | |___ タイムステップ終了とinitialブロック終了、取得した値を変数a, b, cに代入 |__ ブロック2を生成: initial | |___ 時間 #0ns: d <= 8'AA、ノンブロッキングなのでRHSの値(8'hAA)を記録し次へ | |___ 時間 #0ns: $display()はブロッキングなので実行: しかしdは新しい値をまだ受け取っていないためd=8'hx | |___ 時間 #0ns: e <= 8'55、ノンブロッキングなのでRHSの値(8'h55)を記録し次へ | |___ 時間 #0ns: $display()はブロッキングなので実行。しかしeは新しい値をまだ受け取っていないためe=8'hx | |___ タイムステップ終了とinitialブロック終了、取得した値を変数d, eに代入 |__ シミュレーション終了 #0ns
次に、2番目の例を使用し、すべてのブロッキング文をノンブロッキングに置き換えてみます。
module tb;
reg [7:0] a, b, c, d, e;
initial begin
a <= 8'hDA;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
#10 b <= 8'hF1;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
c <= 8'h30;
$display ("[%0t] a=0x%0h b=0x%0h c=0x%0h", $time, a, b, c);
end
initial begin
#5 d <= 8'hAA;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
#5 e <= 8'h55;
$display ("[%0t] d=0x%0h e=0x%0h", $time, d, e);
end
endmodule
再び、以前とは異なる出力が得られます。
ncsim> run [0] a=0xx b=0xx c=0xx [5] d=0xx e=0xx [10] a=0xda b=0xx c=0xx [10] a=0xda b=0xx c=0xx [10] d=0xaa e=0xx ncsim: *W,RNQUIE: Simulation is complete.
実行フローを分解すると以下のようになります。
|__ #0nsでブロック1を生成: initial | |___ 時間 #0ns: a <= 8'DA、ノンブロッキングなのでRHSの値(8'hDA)を記録し次へ | |___ 時間 #0ns: $display()はブロッキングなので実行: しかしaは新しい値をまだ受け取っていないためa=8'hx | |___ タイムステップ終了: 取得した値を変数aに代入、aは8'hDAになる | |___ 時間が10タイムユニット進むまで待機 (#10ns) | | |___ 時間 #10ns: b <= 8'F1、ノンブロッキングなのでRHSの値(8'hF1)を記録し次へ | |___ 時間 #10ns: $display()はブロッキングなので実行。しかしbは新しい値をまだ受け取っていないためb=8'hx | |___ 時間 #10ns: c <= 8'30、ノンブロッキングなのでRHSの値(8'h30)を記録し次へ | |___ 時間 #10ns: $display()はブロッキングなので実行。しかしcは新しい値をまだ受け取っていないためc=8'hx | |___ タイムステップ終了とinitialブロック終了、取得した値を変数b, cに代入 |__ #0nsでブロック2を生成: initial | |___ 時間が5タイムユニット進むまで待機 (#5ns) | | |___ 時間 #5ns: d <= 8'AA、ノンブロッキングなのでRHSの値(8'hAA)を記録し次へ | |___ 時間 #5ns: $display()はブロッキングなので実行: しかしdは新しい値をまだ受け取っていないためd=8'hx | |___ タイムステップ終了: 取得した値を変数dに代入、dは8'hAAになる | |___ 時間が5タイムユニット進むまで待機 (#5ns) | | |___ 時間 #10ns: e <= 8'55、ノンブロッキングなのでRHSの値(8'h55)を記録し次へ | |___ 時間 #10ns: $display()はブロッキングなので実行。しかしeは新しい値をまだ受け取っていないためe=8'hx | |___ タイムステップ終了とinitialブロック終了、取得した値を変数eに代入、eは8'h55になる |__ シミュレーション終了 #10ns
Verilog
- Verilogマスター:デジタル回路設計のための完全チュートリアル
- Verilog連結マスター:実践ガイド
- Verilog代入文の理解:種類、構文、ベストプラクティス
- Verilog関数のマスター:効率的なRTL設計のために
- Verilogタスクの理解:関数との違い、構文、使用法
- Verilogクロックジェネレータ:設計と実装ガイド
- Verilog数学関数のマスター:効率的なハードウェア設計のために
- Verilog $timeformatのマスター:精度と表示制御
- 任意のスコープでのVerilog timescale理解と表示
- VerilogファイルI/Oの完全ガイド:オープン、読み取り、書き込み、クローズ
- Verilog入門:Hello World例で基本をマスター