For Good FPGA Design

UVMの環境構築!(11) Topモジュール (テストベンチ)

UVMの環境構築第11回では、Topモジュールの定義(作成)方法について解説していきます。

なお、ソースコードはGitHubに公開しています。

目次

シリーズ目次

UVMの環境構築!シリーズの目次は、第1回 解説編の一番下をご覧ください。

Topモジュールの概要

検証におけるTopモジュールはテストベンチとも呼ばれます。テストベンチは一般に、DUTと検証機能をインスタンスとして保有します。

UVMのTopモジュールでは主に次のことを行います。

  1. Interfaceのインスタンスを作成
  2. DUTのインスタンスを作成
  3. DUTのポートにinterfaceを接続
  4. クロックを生成
  5. (オプション) uvm_config_dbやfactoryによりコンフィグレーションを変更
  6. (オプション) テストコンポーネントのインスタンスを作成
  7. run_test()を呼び、UVMのシミュレーションを開始

上記6.については、コマンドライン引数でテストコンポーネント(テストケース)を指定することで、Topモジュールでインスタンスの作成は不要となります。コマンドライン引数でテストコンポーネントを指定したほうが、Topモジュールの再利用性が高まります。

図1 UVMにおけるTopモジュール(テストベンチ)

Topモジュールの定義方法

では早速ソースコードを見ながら、Topモジュールの定義方法について説明します。

top.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
`include "uvm_macros.svh"
`include "my_definitions.svh"
`include "my_if.sv"
`include "pkg.sv"
 
module top();
import uvm_pkg::*;
import pkg::*;
 
bit     clk;
my_if   sif (clk);
//my_test1 test1;
 
/* Clock generation */
initial forever #10 clk = ~clk;
 
/* DUT */
MC_MULT #(
    .P_BW_A     ( `BW_A         ),    /* Bit width of A_IN. P_BW_A clocks needed for calculation */
    .P_BW_B     ( `BW_B         )     /* Bit width of B_IN */
) MC_MULT (
    .CLK        ( sif.clk       ),
    .RST        ( sif.rst       ),    /* Active high */
    .A_IN       ( sif.a_in      ),
    .B_IN       ( sif.b_in      ),
    .START      ( sif.start     ),    /* Caluculation start */
    .MULT_OUT   ( sif.mult_out  ),    /* Multiply result. This value is valid when VALID==1'b1 */
    .VALID      ( sif.valid     )     /* Result valid. 1 clock pulse */
);
 
/* Main */
initial begin
    /* my_monitor to my_monitor_coverage */
    uvm_factory factory = uvm_factory::get();
    factory.set_type_override_by_type(my_monitor::get_type(), my_monitor_coverage::get_type());
 
    /* If create test instance, UVM runs this test */
    //test1 = my_test1::type_id::create("test1", null);
 
    /* Set interface */
    uvm_config_db #(virtual my_if)::set(null, "*env0*", "vif", sif);
 
    /* Run */
    run_test();
end
 
endmodule
  • 1行目:uvm_macros.svhをインクルードします。これは必須です。
  • 2行目:DUTのポートのビット幅などをmy_definitions.svhに定義したので、my_definitions.svhをインクルードします。my_definitions.svhの詳細はこちらの記事をご覧ください。
  • 3行目:interfaceの定義my_if.svをインクルードします。my_if.svの詳細はこちらの記事をご覧ください。
  • 4行目:UVM環境構築で定義したclassをpkg.svにまとめています。下記に示します。これをインクルードします。
  • 6行目:topを定義します。シミュレーションのTopモジュールなので、入出力ポートはありません。
  • 7~8行目:パッケージをインポートします。
  • 10行目:クロックを定義します。
  • 11行目:interfaceのインスタンスを作成します。入力としてクロックポートを持っていますので、10行目で定義したclkを接続します。
  • 15行目:クロックの生成を行います。bitで定義した信号の初期値は0になります。したがって、周期/2の時間ごとに反転させる記述で、クロックの生成ができます。
  • 17~29行目:DUTのインスタンスを作成し、interfaceを接続します。
  • 34~35行目:Factoryメソッドにより、my_monitorをmy_monitor_coverageに置き換えています。my_monitorはエージェントでインスタンスしています。このようにFactoryメソッドを使うことで、コンポーネントに変更を加えることなく、テストの環境や条件を変更できます。
  • 41行目:virtual intervaceであるvifに、interfaceのsifをセットします。この記述と、ドライバーおよびコレクター内のuvm_config_dbの記述により、ドライバーとコレクターのvifがDUTのポートに接続されることになります。
  • 44行目:run_test()を呼び、UVMのシミュレーションを開始します。

pkg.sv

UVM環境構築で定義したclassをpkg.svにまとめます。そして、topでこのpkg.svをインクルードします。このようにすることで、すっきりした記述になります。

pkg.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package pkg;
    `include "uvm_macros.svh"
    import uvm_pkg::*;
 
    /* Transaction */
    `include "my_item_base.sv"
    `include "my_item.sv"
 
    /* Typedef */
    typedef virtual my_if VIF_DRIVER;
    typedef virtual my_if VIF_COLLECTOR;
 
    /* Include classes */
    `include "my_driver_base.sv"
    `include "my_driver.sv"
    `include "my_sequencer.sv"
    `include "my_sequence_base.sv"
    `include "my_sequence1.sv"
    `include "my_collector_base.sv"
    `include "my_collector.sv"
    `include "my_monitor.sv"
    `include "my_monitor_coverage.sv"
    `include "my_agent.sv"
    `include "my_scoreboard.sv"
    `include "my_env.sv"
    `include "my_test_base.sv"
    `include "my_test1.sv"
 
endpackage

コマンドライン引数でテストケースを指定

通常、コマンドライン引数でテストケースを指定します。一般的には次のように書きます。

+UVM_TESTNAME=my_test1

今回はXilinxのVivado環境でシミュレーションを行いました。この場合はVivadoのTools→Settingsと行って、図2のところに以下のように書きます。

-testplusarg UVM_TESTNAME=my_test1
図2 Vivadoでのコマンドライン引数の設定

なお、コマンドライン引数でテストケースを指定せず、topでテストコンポーネントのインスタンスも作成しない場合は、エラーとなってシミュレーションは進みません。これは、テストケースが何も指定されていない状態になるので、当然といえば当然です。

実行結果

以上でUVMの環境構築ができました!実行すると、次のように出力されます。なお、DUTについては第1回 解説編で説明しています。


@ 450000: a_in = 85, b_in = 3, mult_out = 255
@ 670000: a_in = 66, b_in = 10, mult_out = 660
…略…
@ 3530000: a_in = 68, b_in = 6, mult_out = 408
@ 3750000: a_in = 170, b_in = 4, mult_out = 680
…略…
UVM_INFO my_test_base.sv(37) @ 3950000: uvm_test_top [my_test1] [PASS] Trial = 16, Error = 0


DUTにコーディングミスがあった場合

DUTのこちらの行を、

b_lat   <=  b_lat << 1;       /* This is correct */

ミスにより、下記のように書いてしまった場合の実行結果を示します。

b_lat   <=  b_lat >> 1;       /* This is miss cording */

以下のように、不具合を検出しています。


@ 450000: a_in = 85, b_in = 3, mult_out = 3
UVM_ERROR my_scoreboard.sv(34) @ 450000: uvm_test_top.env0.scoreboard0 [my_scoreboard] @
450000: a_in = 85, b_in = 3, mult_out = 3, mult_exp = 255

@ 670000: a_in = 66, b_in = 10, mult_out = 5
UVM_ERROR my_scoreboard.sv(34) @ 670000: uvm_test_top.env0.scoreboard0 [my_scoreboard] @
670000: a_in = 66, b_in = 10, mult_out = 5, mult_exp = 660

…略…
UVM_INFO my_test_base.sv(37) @ 3950000: uvm_test_top [my_test1] [FAIL] Trial = 16, Error = 16
…略…
UVM_FATAL : 0
UVM_ERROR : 16
UVM_WARNING : 0
UVM_INFO : 6


まとめ

全11シリーズにてUVMの環境構築方法について解説しました。UVMはIEEE Std 1800.2-2017規格となっており、ハードウェア設計者および検証技術者にとっては必須の知識になってきています。今回の記事が技術習得の一助となれば幸いです。

長いシリーズになりましたが、ご覧いただきありがとうございました。

アバター画像
この記事を書いた人
ジーノ。大手電機メーカーで、基板設計の全般と、FPGAの設計に従事した経験を活かし、FPGAについて情報発信中。
RTL設計、シミュレーション、タイミング・クロージャ、FPGAまわりのハードウェア開発まで、幅広く取り扱っております。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA