組み込みテスト

組み込みテストは #[test(test_name)] アトリビュートでマークすることができます。マークされたブロックはテストとして認識され、 veryl test コマンドによって実行されます。

組み込みテストの記述方法は3種類あります。

  • ネイティブテスト
  • SystemVerilogテスト
  • cocotb テスト

ネイティブテストはVerylの組み込みシミュレータを使用し、SystemVerilogテストとcocotbテストは外部のRTLシミュレータを使用します。veryl test で使用される外部RTLシミュレータについては シミュレータ を参照してください。すべてのテスト種別で、--wave オプションを指定すると波形を生成できます。

#[ignore] 属性を追加することでデフォルトでは無視するテストを指定できます。無視されたテストは --ignored オプションで実行できます。--include-ignored オプションを使うと通常のテストと無視されたテストの両方を実行します。

#[test(test_ignored)]
#[ignore]
module test_ignored {
    inst clk: $tb::clock_gen;
    inst rst: $tb::reset_gen;

    initial {
        rst.assert(clk);
        $finish   ();
    }
}

ネイティブテスト

ネイティブテストでは、SystemVerilogの埋め込みや外部フレームワークを使わずに、Verylで直接テストベンチを記述できます。#[test(test_name)] 属性が付けられたモジュール(embed 宣言なし)がネイティブテストとして扱われます。

以下のテストベンチコンポーネントが利用可能です。

  • $tb::clock_gen — クロック信号生成器(オプションで #(period: N) パラメータを指定可能)
  • $tb::reset_gen — リセット信号生成器(オプションで #(cycles: N) パラメータを指定可能)

また、以下のシステム関数が initial ブロック内で使用できます。

  • $assert(condition) または $assert(condition, message) — シミュレーション中にアサーションを検証
  • $finish() — シミュレーションを終了

基本的な例

module Counter (
    clk: input  clock    ,
    rst: input  reset    ,
    cnt: output logic<32>,
) {
    always_ff {
        if_reset {
            cnt = 0;
        } else {
            cnt += 1;
        }
    }
}

#[test(test_counter)]
module test_counter {
    inst clk: $tb::clock_gen;
    inst rst: $tb::reset_gen;

    var cnt: logic<32>;

    inst dut: Counter (
        clk: clk,
        rst: rst,
        cnt: cnt,
    );

    initial {
        rst.assert(clk);
        clk.next  (10);
        $assert   (cnt == 32'd10);
        $finish   ();
    }
}

テストベンチメソッド

clock_gen はクロックサイクルを進める next メソッドを提供します。

  • clk.next() — クロックを1サイクル進める
  • clk.next(N) — クロックを N サイクル進める

reset_gen はリセットをアサートする assert メソッドを提供します。

  • rst.assert(clk) — クロックに同期してリセットをアサート
  • rst.assert(clk, duration) — 指定された期間リセットをアサート

リセット期間はインスタンス化時にも設定できます。

#[test(test_reset_cycles_param)]
module test_reset_cycles_param {
    inst clk: $tb::clock_gen;
    inst rst: $tb::reset_gen #( cycles: 5 );

    // ...

    initial {
        rst.assert(clk);
        // ...
    }
}

テストベンチ内の関数呼び出し

clk.next などのテストベンチメソッドはユーザー定義関数から呼び出すことができます。

#[test(test_function_call)]
module test_function_call {
    inst clk: $tb::clock_gen;
    inst rst: $tb::reset_gen;

    var cnt: logic<32>;

    // inst dut: Counter (clk, rst, cnt);

    function step_n (
        n: input logic<32>,
    ) {
        clk.next(n);
    }

    initial {
        rst.assert(clk);
        step_n    (5);
        step_n    (5);
        $assert   (cnt == 32'd10);
        $finish   ();
    }
}

SystemVerilogテスト

SystemVerilog テストは inline 指定子で記述することができます。ブロックのトップレベルモジュールはテスト名と同じでなければなりません。

$info$warning$error$fatal システム関数によるメッセージは Veryl コンパイラにより実行ログとして表示されます。$error$fatal の呼び出しはテストの失敗として扱われます。

以下の例では SystemVerilog のソースコードを embed 宣言で埋め込み、テストとしてマークしています。

#[test(test1)]
embed (inline) sv{{{
    module test1;
        initial begin
            assert (0) else $error("error");
        end
    endmodule
}}}

cocotb テスト

cocotb テストは cocotb 指定子で記述することができます。テスト対象のモジュール名は #[test] アトリビュートの第二引数で指定します。

#[test(test1, ModuleA)]
embed (cocotb) py{{{
    # cocotb code
}}}