工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Industrial programming >> Verilog

Verilog遅延制御の理解:タイミング技術とベストプラクティス

Verilogには遅延とイベント式の2種類のタイミング制御があります。遅延制御は、シミュレータが文に遭遇した時点と実際に実行する時点の間に遅延を追加する方法です。イベント式は、ネットや変数の値の変化(暗黙的イベント)や、別の手続きでトリガーされる明示的に名前付けられたイベントなど、何らかのシミュレーションイベントの発生まで文を遅延させます。

シミュレーション時間は以下の方法のいずれかで進められます。

内部遅延を持つようにモデル化されたゲートとネットもシミュレーション時間を進めます。

遅延制御

遅延式が未知値またはハイインピーダンス値と評価される場合、ゼロ遅延として解釈されます。負の値と評価される場合、time変数と同じサイズの2の補数無符号整数として解釈されます。

`timescale 1ns/1ps
module tb;
  reg [3:0] a, b;
  initial begin
    {a, b} <= 0;
    $display ("T=%0t a=%0d b=%0d", $realtime, a, b);
    #10; a <= $random;
    $display ("T=%0t a=%0d b=%0d", $realtime, a, b);
    #10 b <= $random;
    $display ("T=%0t a=%0d b=%0d", $realtime, a, b);
    #(a) $display ("T=%0t After a delay of a=%0d units", $realtime, a);
    #(a+b) $display ("T=%0t After a delay of a=%0d + b=%0d = %0d units", $realtime, a, b, a+b);
    #((a+b)*10ps) $display ("T=%0t After a delay of %0d * 10ps", $realtime, a+b);
    #(b-a) $display ("T=%0t Expr evaluates to a negative delay", $realtime);
    #('h10) $display ("T=%0t Delay in hex", $realtime);
    a = 'hX; #(a) $display ("T=%0t Delay is unknown, taken as zero a=%h", $realtime, a);
    a = 'hZ; #(a) $display ("T=%0t Delay is in high impedance, taken as zero a=%h", $realtime, a);
    #1ps $display ("T=%0t Delay of 10ps", $realtime);
  end
endmodule

timescaleの精度が1psであるため、遅延式(a+b)*10psの文では精度値を表示するために$realtimeが必要です。

xcelium> run
T=0 a=x b=x
T=10000 a=0 b=0
T=20000 a=4 b=0
T=24000 After a delay of a=4 units
T=29000 After a delay of a=4 + b=1 = 5 units
T=29050 After a delay of 5 * 10ps
T=42050 Expr evaluates to a negative delay
T=58050 Delay in hex
T=58050 Delay is unknown, taken as zero a=x
T=58050 Delay is in high impedance, taken as zero a=z
T=58051 Delay of 10ps
xmsim: *W,RNQUIE: Simulation is complete.

イベント制御

ネットと変数の値変化を、他の手続き文の実行をトリガーする同期イベントとして使用でき、これは暗黙的イベントです。イベントは、0への変化方向(negedge)や1への変化方向(posedge)に基づくこともできます。

同じ状態から同じ状態への遷移はエッジとは見なされません。エッジイベント(posedgeやnegedge)は、ベクタ信号や変数のLSBでのみ検出できます。式が同じ結果と評価される場合、イベントとは見なされません。

module tb;
  reg a, b;
  initial begin
    a <= 0;
    #10 a <= 1;
    #10 b <= 1;
    #10 a <= 0;
    #15 a <= 1;
  end
  
  // 上記手続きブロックで行われる信号更新を待つ別の手続きブロック
  initial begin
    @(posedge a);
    $display ("T=%0t Posedge of a detected for 0->1", $time);
    @(posedge b);
    $display ("T=%0t Posedge of b detected for X->1", $time);
  end
  
  initial begin
    @(posedge (a + b))
    $display ("T=%0t Posedge of a+b", $time);
    @(a)
    $display ("T=%0t Change in a found", $time);
  end
endmodule
ncsim> run
T=10 Posedge of a detected for 0->1
T=20 Posedge of b detected for X->1
T=30 Posedge of a+b
T=45 Change in a found
ncsim: *W,RNQUIE: Simulation is complete.

名前付きイベント

キーワードeventを使用して名前付きイベントを宣言でき、これは明示的にトリガーできます。eventはデータを保持せず、時間の長さを持たず、任意の特定の時間に発生させられます。名前付きイベントは、->演算子を名前付きイベントハンドルの前に付けることでトリガーされます。名前付きイベントは、上述の@演算子を使用して待機できます。

module tb;
  event a_event;
  event b_event[5];
  
  initial begin
    #20 -> a_event;
    #30; ->a_event;
    #50 ->a_event;
    #10 ->b_event[3];
  end
  
  always @ (a_event)
    $display ("T=%0t [always] a_event is triggered", $time);
  
  initial begin
    #25; @(a_event)
    $display ("T=%0t [initial] a_event is triggered", $time);
    #10 @(b_event[3])
    $display ("T=%0t [initial] b_event is triggered", $time);
  end
endmodule

名前付きイベントは、2つ以上の並行実行プロセスを同期するために使用できます。例えば、alwaysブロックと2番目のinitialブロックはa_eventで同期されます。イベントは配列として宣言でき、b_eventのようにサイズ5の配列で、インデックス3がトリガーと待機に使用されます。

ncsim> run
T=20 [always] a_event is triggered
T=50 [always] a_event is triggered
T=50 [initial] a_event is triggered
T=100 [always] a_event is triggered
T=110 [initial] b_event is triggered
ncsim: *W,RNQUIE: Simulation is complete.

イベントor演算子

or演算子を使用して、リストされたイベントのいずれかが式でトリガーされるまで待機できます。カンマ,もor演算子の代わりに使用できます。

module tb;
  reg a, b;
  initial begin
    $monitor ("T=%0t a=%0d b=%0d", $time, a, b);
    {a, b} <= 0;
    #10 a <= 1;
    #5 b <= 1;
    #5 b <= 0;
  end
  
  // イベント間で "or" を使用
  always @ (posedge a or posedge b)
    $display ("T=%0t posedge of a or b found", $time);
  
  // カンマを使用
  always @ (posedge a, negedge b)
    $display ("T=%0t posedge of a or negedge of b found", $time);
  
  always @ (a, b)
    $display ("T=%0t Any change on a or b", $time);
endmodule
ncsim> run
T=0 posedge of a or negedge of b found
T=0 Any change on a or b
T=0 a=0 b=0
T=10 posedge of a or b found
T=10 posedge of a or negedge of b found
T=10 Any change on a or b
T=10 a=1 b=0
T=15 posedge of a or b found
T=15 Any change on a or b
T=15 a=1 b=1
T=20 posedge of a or negedge of b found
T=20 Any change on a or b
T=20 a=1 b=0
ncsim: *W,RNQUIE: Simulation is complete.

暗黙的イベント式リスト

感度リストまたはイベント式リストは、RTLで多くの機能エラーの一般的な原因です。これは、ユーザーが手続きブロックに新しい信号を導入した後、感度リストを更新し忘れるためです。

module tb;
  reg a, b, c, d;
  reg x, y;
  
  // @演算子の後の()内のすべての信号で
  // イベント式/感度リストが形成され、この場合はa, b, c, d
  always @ (a, b, c, d) begin
    x = a | b;
    y = c ^ d;
  end
  
  initial begin
    $monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b x=%0b y=%0b", $time, a, b, c, d, x, y);
    {a, b, c, d} <= 0;
    #10 {a, b, c, d} <= $random;
    #10 {a, b, c, d} <= $random;
    #10 {a, b, c, d} <= $random;
  end
endmodule
ncsim> run
T=0 a=0 b=0 c=0 d=0 x=0 y=0
T=10 a=0 b=1 c=0 d=0 x=1 y=0
T=20 a=0 b=0 c=0 d=1 x=0 y=1
T=30 a=1 b=0 c=0 d=1 x=1 y=1
ncsim: *W,RNQUIE: Simulation is complete.

ユーザーが新しい信号eを追加し、その逆をzに取得する場合、eも感度リストに追加するよう特別な注意が必要です。

module tb;
  reg a, b, c, d, e;
  reg x, y, z;
  
  // 感度リストに "e" も追加
  always @ (a, b, c, d, e) begin
    x = a | b;
    y = c ^ d;
    z = ~e;
  end
  
  initial begin
    $monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b e=%0b x=%0b y=%0b z=%0b", $time, a, b, c, d, e, x, y, z);
    {a, b, c, d, e} <= 0;
    #10 {a, b, c, d, e} <= $random;
    #10 {a, b, c, d, e} <= $random;
    #10 {a, b, c, d, e} <= $random;
  end
endmodule
ncsim> run
T=0 a=0 b=0 c=0 d=0 e=0 x=0 y=0 z=1
T=10 a=0 b=0 c=1 d=0 e=0 x=0 y=1 z=1
T=20 a=0 b=0 c=0 d=0 e=1 x=0 y=0 z=0
T=30 a=0 b=1 c=0 d=0 e=1 x=1 y=0 z=0
ncsim: *W,RNQUIE: Simulation is complete.

Verilogでは感度リストを*で置き換えることができるようになり、文によって読み取られるすべてのネットと変数を自動的に追加する便利なショートハンドで、これらの問題を解消できます。

module tb;
  reg a, b, c, d, e;
  reg x, y, z;
  
  // @* または @(*) を使用
  always @ * begin
    x = a | b;
    y = c ^ d;
    z = ~e;
  end
  
  initial begin
    $monitor ("T=%0t a=%0b b=%0b c=%0b d=%0b e=%0b x=%0b y=%0b z=%0b", $time, a, b, c, d, e, x, y, z);
    {a, b, c, d, e} <= 0;
    #10 {a, b, c, d, e} <= $random;
    #10 {a, b, c, d, e} <= $random;
    #10 {a, b, c, d, e} <= $random;
  end
endmodule
ncsim> run
T=0 a=0 b=0 c=0 d=0 e=0 x=0 y=0 z=1
T=10 a=0 b=0 c=1 d=0 e=0 x=0 y=1 z=1
T=20 a=0 b=0 c=0 d=0 e=1 x=0 y=0 z=0
T=30 a=0 b=1 c=0 d=0 e=1 x=1 y=0 z=0
ncsim: *W,RNQUIE: Simulation is complete.

レベルセンシティブ イベント制御

手続き文の実行は、条件が真になるまで遅延させることもでき、これはwaitキーワードで実現され、レベルセンシティブな制御です。

wait文は条件を評価し、偽の場合、その後の手続き文は条件が真になるまでブロックされたままになります。

module tb;
  reg [3:0] ctr;
  reg clk;
  
  initial begin
    {ctr, clk} <= 0;
    wait (ctr);
    $display ("T=%0t Counter reached non-zero value 0x%0h", $time, ctr);
    wait (ctr == 4)
    $display ("T=%0t Counter reached 0x%0h", $time, ctr);
    $finish;
  end
  
  always #10 clk = ~clk;
  always @ (posedge clk)
    ctr <= ctr + 1;
endmodule
ncsim> run
T=10 Counter reached non-zero value 0x1
T=70 Counter reached 0x4
T=90 Counter reached 0x5
T=170 Counter reached 0x9
Simulation complete via $finish(1) at time 170 NS + 1

Verilog

  1. Verilogマスター:デジタル回路設計のための完全チュートリアル
  2. Verilog連結マスター:実践ガイド
  3. Verilog代入文の理解:種類、構文、ベストプラクティス
  4. Verilogブロッキング代入とノンブロッキング代入:違いを徹底解説
  5. Verilog制御フローのマスター:if-else、case、ループ
  6. Verilog関数のマスター:効率的なRTL設計のために
  7. Verilogタスクの理解:関数との違い、構文、使用法
  8. Verilogインターバル遅延とイントラ代入遅延の理解
  9. Verilogゲート遅延の理解:信頼性の高いデジタル設計のための正確なタイミング
  10. Verilogクロックジェネレータ:設計と実装ガイド
  11. Verilog数学関数のマスター:効率的なハードウェア設計のために