Menu

Verilog testbenchの基礎:moduleを検証する方法

Verilog testbenchの書き方。クロック生成、resetシーケンス、刺激、観察、そして実行するすべてのシミュレーションを駆動する標準骨格。

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

testbenchが実際に何か

testbenchは単なるVerilog moduleです。合成可能moduleとの違いは、中身です:

  • ポートを持たない - シミュレーション階層の頂点。
  • DUT(design under test)を インスタンス化 する。
  • initialalwaysブロックからDUTの入力を 駆動 する。
  • $display$monitor、またはVCDダンプでDUTの出力を 観察 する。
  • $finishでシミュレーションを 終了 する。

それが仕事の全部です。特別な「testbench」キーワードはなく、何かがtestbenchだと分かるのは、それが何をするかを読むことによってです。

標準骨格

これが全パターンです。5つのセクション、コピペしやすく、適応しやすい。

組み合わせDUT(このadderのような)にはクロックは要りません。順序DUTには必要で、それが次のレイヤーです。

順序DUTのクロック生成

DUTがclk入力を持つとき、testbenchがそれを生成しなければなりません。標準のワンライナー:

reg clk = 0;
always #5 clk = ~clk;

これはclkを5時間単位ごとにトグルします。各トグルはクロック周期の半分で、完全周期は10単位(1サイクル = 5の高 + 5の低)です。タイムスケールが1ns / 1ps(ほとんどのシミュレータのデフォルト)なら、100MHzクロックです。

モデル化したいものに基づいて半周期を選んでください。同じタイムスケールで10MHzクロックなら#50。200MHzなら#2.5(またはタイムスケールを変える)。

resetシーケンス

同期設計は、時刻0後数サイクル間高で保持され、その後解除されるresetが必要です。慣例的な形:

reg reset = 1;     // アサート状態で開始

initial begin
    // 数クロック分resetを保持。
    #20 reset = 0;
end

これは時間20までresetを高に保持し、その後落とします。resetが落ちる頃には、クロックがいくつかのサイクルを終え、すべてのflip-flopがreset値を捕捉し、設計は既知のstateにあります。

active-low reset(resetではなくreset_n)には、初期値を反転:

reg reset_n = 0;   // active-low: 0はアサートを意味する

initial begin
    #20 reset_n = 1;
end

完全な順序testbench

クロック、reset、刺激を組み合わせ:

これが4ビットカウンタの完全なtestbenchです。Runを押すと、カウンタがresetから出て、イネーブル中カウントアップし、enableが落ちると一時停止し、上がると再開し、その後停止する様子が見えます。

構造について3つの注目点:

  1. 3つの異なる活動の場:クロックジェネレータ(1つのalways)、刺激(1つのinitial)、モニタ(別のalways)。各々が1つの仕事を持つ。
  2. 刺激ブロックの#遅延は時間単位で測られる - クロックサイクルではありません。クロック周期が10単位なら、#10はちょうど1サイクルです。代わりに@(posedge clk)を使うtestbenchもあり、周期に関わらず1サイクル進めます。
  3. モニタはalways @(posedge clk)ブロックから$displayを使う。 毎サイクル出力します。より洗練された出力には$monitorに切り替えます(次のドキュメントで扱う)。

サイクルベースの刺激

時には「N単位待つ」より「N サイクル待つ」の方が読みやすいことがあります:

initial begin
    @(posedge clk);     // 次のクロックエッジを待つ
    reset = 0;

    repeat (5) @(posedge clk);   // さらに5サイクル待つ
    enable = 1;

    repeat (8) @(posedge clk);
    enable = 0;

    repeat (10) @(posedge clk);
    $finish;
end

@(posedge clk)は次のクロック立ち上がりエッジまでブロックします。repeat (N) @(posedge clk);はNサイクル待ちます。このスタイルはクロック周期に依存しません。クロック周波数を変えても、刺激はサイクル単位で同じことをします。

初期のtestbenchでは#遅延がシンプルです。複数のクロック速度で実行される可能性のあるプロダクションtestbenchでは、サイクルベースの方が安全なスタイルです。

自己チェックテスト

これまでのtestbenchは何が起こったかを出力し、あなたに出力を読ませる作りです。自己チェックtestbenchは代わりに出力を チェック し、合否をレポートします:

initial begin
    #1;
    a = 10; b = 20;
    #1;
    if (sum !== 30) begin
        $display("FAIL: 10 + 20 = %0d (expected 30)", sum);
        $finish;
    end
    a = 250; b = 5;
    #1;
    if (sum !== 255) begin
        $display("FAIL: 250 + 5 = %0d (expected 255)", sum);
        $finish;
    end

    $display("PASS");
    $finish;
end

安全のために!==(ケース不等価演算子)を使ってください。オペランドが未知ビットを持つときにxを返しません。パターンは:入力を駆動し、安定するのを待ち、期待と比較し、合否をレポートする。

自己チェックはリグレッションスイートで貴重です。数千のテストが無人で走り、失敗だけが注目を必要とします。

次に読むもの

これで任意のmoduleに十分なtestbench形状が揃いました。次のドキュメントはtestbench内に入るツールを扱います。よりリッチなテキスト出力のDisplay and Monitor、グラフィカルな波形デバッグのDumpfile and VCD、そしてシミュレーション時間が壁時計時間とどう関係するかを正確に制御するTimescale and Delaysです。

よくある質問

Verilogのtestbenchとは?

testbenchは、通常ポートを持たないVerilog moduleで、その目的はDUT(design under test)を駆動することだけです。DUTをインスタンス化し、クロックを生成し、initialalwaysブロックで刺激を駆動し、$display/$monitorで出力を観察し、オプションでVCD波形をダンプし、$finishでシミュレーションを終了します。

Verilog testbenchでクロックを生成するには?

標準パターンは1行:always #5 clk = ~clk;reg clk = 0;を事前に宣言)。これはclkを5シミュレーション時間単位ごとにトグルし、10単位の周期(タイムスケールがナノ秒なら100MHzクロック)を与えます。#5は半周期で、半分はclkが高、半分は低です。

VerilogのDUTとは?

DUTはDesign Under Testの略で、testbenchが駆動しているmoduleです。慣例として、testbench内のDUTインスタンスはdutまたはu_dutと名付けます:my_module dut(.clk(clk), .reset(reset), .in(in), .out(out));。名前は単なるラベルで、重要なのはインスタンス化されてポートがtestbenchのsignalに接続され、testbenchがそれらのsignalを駆動することです。

Verilogシミュレーションはどれくらいの長さ実行すべき?

検証したいすべてを実行するのに十分長く、その後$finishします。ほとんどのtestbenchは明示的な時間制限(#1000 $finish)を設定して、決して来ないイベントを待ってシミュレーションがハングしないようにします。そのウィンドウ内で、刺激を駆動し、DUTを安定させ、理想的には出力が期待と一致しない場合にFAILを表示する自己チェックのif文を含めます。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める