Verilogでの「時間」の意味
Verilogには秒の組み込み概念がありません。シミュレータは「時間単位」(任意の整数ティック)を進め、コードは#Nを使ってそのN個を待ちます。`timescaleディレクティブが、それらのティックを実時間にマップするものです。
`timescale 1ns / 1ps
module test;
initial begin
$display("t = %0t", $time); // 0
#5;
$display("t = %0t", $time); // 5 ns
#1.5;
$display("t = %0t", $time); // 6500 ps (または 6.5 ns)
$finish;
end
endmodule
2つのパラメータ:
- 単位(最初の数字):
#1の意味。1nsは1ティックが1ナノ秒と言う。 - 精度(2番目の数字):その単位内で時間をどれだけ細かく追跡するか。
1psは分数遅延がピコ秒に丸められると言う。
精度は単位より粗くできません(1ps / 1nsは違法)。一般的な選択:
1ns / 1ps- 事実上の標準。すべてナノ秒で、gate遅延のためにサブナノ秒精度。1ps / 1ps- 極めて速い回路をモデル化するときや、すべての遅延がサブナノ秒のとき。1us / 1ns- 遅い組み込みスケールのシミュレーション(UARTバイト時間、遅いプロトコル)。
どこに置くか
`timescaleは コンパイラディレクティブ で、module レベルの構造ではありません。ファイルの一番上、moduleの前に置きます:
`timescale 1ns / 1ps
module foo(...);
// ...
endmodule
スコープは「この時点からコンパイル順序で前進、次の`timescaleまたはコンパイル終了まで」です。微妙な含意があります。明示的な`timescaleのないファイルは、コンパイラがファイルを読む順序に依存して、以前にコンパイルされたファイルが宣言したものを継承します。驚きを避けるために、すべてのファイルの先頭に`timescaleを置いてください。
このドキュメントのブラウザエディタはデフォルトのtimescale(通常1ns / 1ps)を設定するので、前のドキュメントの例は宣言なしで動きました。実プロジェクトでは明示するでしょう。
実践での#delay
`timescale 1ns / 1psの後:
#5 // 5 ns待つ
#100 // 100 ns待つ
#1.5 // 1.5 ns待つ (精度が許す)
#0.001 // 1 ps待つ (精度のちょうど上)
#0 // 遅延0; 同時イベントの順序付けに有用
initialまたはalwaysブロック内では、#Nは手続き的フローをN時間単位ブロックします。シミュレータは この ブロックを一時停止し(他の並行ブロックは走り続ける)、遅延後に再開します。
代入に遅延を前置できます:
#10 a = 1; // 10 ns待ってから代入
data <= #2 new_value; // 今から2 ns後に非blocking代入をスケジュール
最初の形がtestbenchの定番です。2番目(遅延付き非blocking)はgateレベルシミュレーションで伝播遅延をモデル化するのに使います。
クロックの生成
古典的なパターン:
`timescale 1ns / 1ps
module test;
reg clk = 0;
always #5 clk = ~clk;
// ...
endmodule
timescale 1ns / 1psで、#5は5 nsです。クロックは5 nsごとにトグルし、10 ns周期 - 100 MHz クロックを与えます。周波数を変えるには遅延を変えます:
| 半周期 | 周期 | 周波数 |
|---|---|---|
#1 | 2 ns | 500 MHz |
#2.5 | 5 ns | 200 MHz |
#5 | 10 ns | 100 MHz |
#10 | 20 ns | 50 MHz |
#25 | 50 ns | 20 MHz |
#50 | 100 ns | 10 MHz |
分数の半周期(#2.5)が欲しいなら、精度がサポートする必要があります。1ps精度は0.001 nsまで処理するので、任意の合理的な周波数で問題ありません。
ファイル間のtimescale混在
実際の設計は多くのファイルを持ち、異なるtimescaleを宣言できます。シミュレータは 各ファイル自身の timescaleを使って、そのファイル内の遅延を解釈します。module_a.vが`timescale 1ns / 1psを言って#5を使うと、それは5 nsです。module_b.vが`timescale 1us / 1nsを言って#5を使うと、それは5 usです。
これはシミュレータが関わらずグローバルな時間軸を提示するので、ほぼ見えませんが、同じ#Nが2つのファイルでまったく異なるものを意味することを意味します。修正:1つのtimescale(業界標準は1ns / 1ps)を選び、すべてのファイルの先頭に置いてください。混在させないこと。
遅延は合成可能ではない
重要な点:#delayはシミュレーションのためにだけ存在します。合成ツールが読むのは:
// 合成可能RTL内 - 間違い
always @(posedge clk) begin
out <= #2 in;
end
…そして、#2を無視する(ほとんどのツール)か、構造を拒否する(より厳格なlinter)かのどちらかです。実ハードウェアのタイミングはクロックとgate伝播遅延によって決まり、どちらもソースコードから見えません。
ルール:#はtestbenchだけで使ってください。合成可能RTLには#遅延はありません。合成可能コードで遅延が欲しくなったら、実際はクロックでカウントダウンするカウンタが欲しいのです。実ハードウェアがどう「待つ」かはそうです。
$time vs $realtime
現在のシミュレーション時間を読む2つの方法:
$timeは現在のtimescaleの 単位 で64ビット整数を返します。$realtimeは同じ単位のrealを返しますが、完全な精度です。
testbenchロギングには$timeがほぼ常に十分です。print文でサブティック精度が必要なときだけ$realtimeに手を伸ばします。
実用上のヒント
- すべてのファイルの先頭に常に
`timescaleを宣言する。1ns / 1psが安全なデフォルト。 #delayはtestbenchだけで使う。 合成可能コードでの不在を静的ルールとして扱う。- クロック周期をターゲット周波数に一致させる。 50 MHz設計をシミュレートしているなら、20 ns周期を使う。不一致の周期はタイミング感応のバグを覆い隠すことがあります。
- サイクルカウントの刺激には、
#の代わりに@(posedge clk)を使う。 クロック周期の変更に対して頑健。
次に読むもの
これでこれらのVerilogチュートリアルのすべてのドキュメントを見ました。言語の基礎(wire vs reg、module、operator)から、手続きブロックと制御フローを通り、同期設計と状態機械に入り、最後にすべてが動くことを証明するtestbenchツールまで。何かを作る時間です。これらのドキュメントの隣のプレイグラウンドは、これまで使ってきたものと同じシミュレータで、スケッチした任意のmoduleを待っています。
よくある質問
Verilogの`timescale 1ns / 1psの意味は?
シミュレータに「このファイルの1時間単位は1ナノ秒で、時間はピコ秒精度で追跡される」と伝えます。そのディレクティブの後、#5は5 ns待ち、#1.5は1.5 ns(ピコ秒に丸められる)待ち、$timeはナノ秒で報告されます。最初の数字が単位で、2番目が精度です。
すべてのVerilogファイルに`timescaleが必要ですか?
ベストプラクティス:はい。ディレクティブのスコープは次の\timescaleまたはコンパイル終了で終わるので、ディレクティブのないファイルは以前コンパイルされたファイルが宣言したものを継承します。これによりビルド間でタイミングが非決定的になります。すべてのソースファイルの先頭に`timescale 1ns / 1ps`を置いてください(最も一般的な慣習)、そうすれば驚きはありません。
Verilogの#5の意味は?
#5はシミュレーション時間を5時間単位進めます。単位はアクティブな\timescaleディレクティブから来ます。`timescale 1ns / 1psでは#5は5ナノ秒です。`timescale 1us / 1nsでは#5は5マイクロ秒です。精度が単位より細かければ、数値は分数(#1.5`)でも構いません。
Verilogの#delayは合成可能ですか?
いいえ。#delayはシミュレーションにのみ影響します。合成ツールは無視するか拒否します。実ハードウェアのタイミングは#文ではなく、クロックsignalとgateの伝播遅延から来ます。testbenchでは#を自由に使い、合成可能RTLには決して書かないでください。