Verilogブロッキングおよびノンブロッキングシミュレーションおよび合成
リファレンスClifford E. Cummings、Sunburst Design、Inc.「Verilog合成でのノンブロッキング割り当て、コーディングスタイルが強制終了する!」
少し前に、ブロッキングとノンブロッキングを調査するための簡単なテストを実施しました。 Sunburstがこの論文を発見するまで(この機関は実際にはnbであり、多くのVerilogであり、SVの古典的なチュートリアルはそこからのものです)、ブロックと非ブロックの原則を数えています。
基本的な概念
RHS(右側):方程式の右側の式または変数(RHS式またはRHS変数)を参照します
LHS(左側):方程式の左側の式または変数(RHS式またはRHS変数)を参照します
競合条件:Verilog競合条件は、2つ以上のステートメントが同じシミュレーションタイムステップで実行されるようにスケジュールされている場合に発生します。ステートメントの実行順序が変わると、異なる結果が生成されます。
ブロッキング割り当て:他のVerilog記述が「ブロッキング割り当て」を中断できない場合、操作はRHSの値を見積もり、割り当てを完了します。「ブロック」とは、現在の割り当てが完了する前に、他のタイプの割り当てタスクをブロックすることを意味します。
- 「割り当てのブロック」は1ステップのプロセスと見なすことができます。割り当てを中断できる説明が他にない場合は、方程式の右(RHS)の指を推定し、左(LHS)に割り当てます。同じ常にブロックでは、割り当ての結果は、割り当てが終了するまで続きます。
非ブロッキング割り当て:タイムステップの開始時のRHS式の値を推定し、LHSをタイムステップの終了時の方程式の右側の値の更新で置き換えます。RHS式の推定とLHS式の更新の中間期間に、LHS式への他の非ブロッキング割り当てを実行できます。つまり、「ノンブロッキング割り当て」は、他のVerilog記述の実装によるRHSの推定を妨げるものではありません。
- 「非ブロッキング割り当て」は、1ステップのプロセスと見なすことができます。
-
- タイムステップでRHSを推定します。
-
- タイムステップの最後にLHSを更新します。
-
Verilog層別イベントキュー(層別イベントキュー)
層別イベントキュー(層別イベントキュー)は概念モデルであり、シミュレータごとに実装が異なります。「層状イベント列」は、現在のシミュレーション時間と将来のシミュレーション時間のために4つの異なるキューに論理的に分割されます。
[アクティブイベント]列(アクティブイベント)(実行されるほとんどのVerilogイベント)には、次のものが含まれます。
- ブロック割り当て
- 継続的な割り当て
- $表示コマンド
- 「ノンブロッキング割り当て」のRHS式を推定します。
- プリミティブ要素の入力と変更インスタンスの出力を計算します
「非ブロッキング割り当て」LHSは「アクティブイベント列」では更新されないことに注意してください。
非アクティブなイベント:#0遅延ブロック割り当て。
非ブロッキング割り当て更新イベント列(非ブロッキング割り当て更新イベントキュー)は、「非ブロッキング割り当て」LHS式がこれらのイベントの割り当てを更新するようにスケジュールされています。シミュレーションタイムステップの開始時に、「RHS式の推定」およびその他のアクティブ化されたイベントが任意の順序で実行されます。
モニターイベント列は、配置された「\(ストロボ」および「\」モニター」表示コマンドによって表示されます。$ストローブと$モニターは、シミュレーションタイムステップの最後に変数の更新された値を表示するために使用されます(この時点では、シミュレーションタイムステップのすべての割り当てが完了しています)。
注:イベントは、任意のイベント列(IEEE標準で必須)に追加できますが、「アクティブイベント列」からのみ削除できます。他のイベント列のイベントは常に「アクティブイベント」になります(または「アクティブイベント」に昇格します)。
例:自己トリガーの常にブロック
---- Non-Self Triggered ------
module osc1 (clk);
output clk;
reg clk;
initial #10 clk = 0;
always @(clk) #10 clk = ~clk;
endmodule
----------------------------------------
------- Self Triggered ------
module osc2 (clk);
output clk;
reg clk;
initial #10 clk = 0;
always @(clk) #10 clk <= ~clk;
endmodule
----------------------------------------
- 最初の書き込み方法では、ブロック割り当てを使用します。この場合、RHS評価とLHS割り当ては中断されることなく実行され、他のステートメントへの干渉は現時点では許可されません。ブロック割り当ては、@(clk)エッジトリガーの到着時間より前に完了する必要があります。つまり、エッジイベントの前に、clkの割り当てが完了しています。したがって、alwaysブロックでトリガーイベントをトリガーする「トリガーイベント」(@(clk))はありません。したがって、それ自体ではトリガーできません。
- 2番目の書き込み方法では、ノンブロッキング割り当てを使用します。最初の@(clk)がトリガーされた後、イベントのシーケンスは次のようにソートできます。
- トリガーを常に10nsでブロックし、ステートメントを常に実行する
- 非ブロッキング割り当てRHS式が評価され、LHS値が「非ブロッキング割り当て更新」イベント列に送信されます。
- LHSの評価が完了し、LHSの続行を待機しています
- 非ブロッキングLHSの値が更新されると同時に、@(clk)トリガーステートメントが検出され、alwaysブロックはclkの値の変化に再び反応します。@(clk)が再びトリガーされます。
- 非ブロッキングに割り当てられたRHSは再度評価されます...
そのため、自己トリガーすることができます。
ブロッキングとノンブロッキングの包括的な問題
一般的に、組み合わせロジックを説明するにはブロッキングロジックを使用し、シーケンシャルロジックを説明するには非ブロッキングロジックを使用することをお勧めします。
しかし、割り当てをブロックすることは、シーケンシャルロジックを記述してはなりませんか?答えは「いいえ」です。ブロッキング割り当ての順序を注意深く調整すると、順序回路の記述も実行でき、シミュレーションおよび合成(または合成)できます。
例として3ステージのパイプラインを取り上げます。
まず、ノンブロッキング割り当てを使用するまで、シミュレーションと合成は問題ありません。では、ブロック割り当てを使用するとどうなるでしょうか。
always @(posedge clk) begin
q1 = d;
q2 = q1;
q3 = q2;
end
上記の方法を使用しても、私たちが望む結果は得られませんが、合成されるレジスタは1つだけです。これは明らかに実現不可能です。
そして、あなたが順序を変えるならば:
always @(posedge clk) begin
q3 = d2;
q2 = q1;
q1 = q;
end
シミュレーションがレジスターのように正しく機能できるように、ブロッキング割り当ては注意深く順序付けられています。この記述は、シミュレーションでも合成でも正しいですが、お勧めできません。
さらに、alwaysブロックを3つの書き込みに分割すると、次のようになります。
always @(posedge clk) q1=d;
always @(posedge clk) q2=q1;
always @(posedge clk) q3=q2;
このように、Verilog標準では、常に3つのブロックを任意の順序で実行できますが、Verilogの競合状態が発生するため、パイプラインシミュレーションの結果が間違っている可能性があります。異なるalwaysブロックの実行順序は、異なる結果になります。これにもかかわらず、その包括的な結果は正しいでしょう!これは、包括的な事前シミュレーションおよび合成後のシミュレーションが一致しません。
また、組み合わせロジックでは、ノンブロッキング代入で記述できますか?
答えは「はい」ですが、いくつかの工夫も必要です。例を見てみましょう。
always @(a or b or c or d) begin
tmp1 <= a & b;
tmp2 <= c & d;
y <= tmp1 | tmp2;
end
上記の組み合わせロジックは、ノンブロッキング割り当てで説明されています。abcdが変更されると、yによって割り当てられたtmpとRHSが同時に評価されるため、yのLHSが古いときに更新されるため、yの値を正しく更新できないことがわかります。 tmp、不正な関数になります。この問題を解決することは面倒ではありません:
always @(a or b or c or d or tmp1 or tmp2) begin
tmp1 <= a & b;
tmp2 <= c & d;
y <= tmp1 | tmp2;
end
「ノンブロッキング割り当て更新イベントキュー」では、ノンブロッキング割り当てがLHS変数を更新すると、常にブロックが「自己トリガー」し、最新のtmp1およびtmp2を使用してy出力を更新します。yの出力値は正しくなりましたが、価格はalwaysブロック全体で2つのパスが追加されたためです。alwaysブロック全体で使用するパスが多すぎると、エミュレータのパフォーマンスが低下します。したがって、正しい機能は得られますが、お勧めできません。
ノンブロッキング割り当てと$表示
非表示の割り当ては$ displayコマンドの後に更新されるため、非表示の割り当ての直後にある表示は無効です。
initial $monitor("\$monitor: a = %b", a);
initial begin
$strobe ("\$strobe : a = %b", a);
a = 0;
a <= 1;
$display ("\$display: a = %b", a);
#1 $finish;
end
出力:
\(ディスプレイ:a = 0 \)モニター:a = 1
$ストロボ:a = 1
0遅延
階層化されたイベントの列で、#0の遅延に特別な非アクティブイベント列があることがわかります。したがって、ゼロの遅延#0は、「非アクティブなイベント列」に割り当てイベントを作成します。つまり、非ブロッキング割り当てLHS更新の前です。
initial begin
a = 0;
b = 1;
a <= b;
b <= a;
$monitor ("%0dns: \$monitor: a=%b b=%b", $stime, a, b);
$display ("%0dns: \$display: a=%b b=%b", $stime, a, b);
$strobe ("%0dns: \$strobe : a=%b b=%b\n", $stime, a, b);
#0 $display ("%0dns: #0 : a=%b b=%b", $stime, a, b);
end
結果:
0ns:$表示:a = 0 b = 1
0ns:#0:a = 0 b = 1
0ns:$モニター:a = 1 b = 0
0ns:$ストロボ:a = 1 b = 0
上記からわかるように、#0遅延を使用した後、displayステートメントは非アクティブイベントに入り、非ブロッキング割り当ての前に実行されます。
提案する
- 時相論理をモデル化するときは、「ノンブロッキング割り当て」を使用します。
- ラッチをモデリングする場合は、「ノンブロッキング割り当て」を使用してください。
- 常にブロックを使用して組み合わせロジックをモデル化する場合は、「ブロック割り当て」を使用します
- 同じ常時ブロックで組み合わせロジックと順次ロジックの両方をモデリングする場合は、「非ブロッキング割り当て」を使用します。
- 「ブロックの割り当て」と「非ブロックの割り当て」を同じ常にブロックに混在させないでください。
- 2つ以上のalwaysブロックで同じ変数を割り当てないでください。
- $ストローブを使用して、「ノンブロッキング割り当て」されている値を表示します。
- #0の遅延割り当てを使用しないでください。