LoginSignup
13
8

UVMのお気軽導入

Last updated at Posted at 2023-01-04

■ はじめに

UVMに興味があって使ってはみたいけど、いきなりガッツリ導入するには腰が引けると言う人向けの記事です。
UVMを使ってみたいと思った人の中には、巷にある教科書を見て内容が膨大過ぎてやる気が失せた人も多いのではないでしょうか。しかし安心してください。UVMには柔軟性があり、何もいきなり全ての機能を使う必要もなく、その極一部の機能から少しずつ使い始める事ができます。簡単なテストの場合にはそれだけで実際の業務に十分使用できると思います。この記事では既存のテストベンチに段階的にUVMを導入してみる例を紹介するので是非参考にしてみてください。


■ 0. 既存のmoduleベースのテストベンチ

まずはUVMじゃない通常のテストベンチはこんな感じだと思います。

TB0 : moduleベース
test_bench.sv
module test_bench;
  bit       clk, rst_n;
  bit       param_a, param_b, param_c;  // Input Settings
  bit [0:2] sig;                        // Input Signals
  logic     x, y, z;                    // Output

  initial forever #(100/2) clk = !clk;

  initial begin
    $display("Start of Test !!!!");
    {param_a, param_b, param_c} = 'b110;
    repeat(10) @(posedge clk);
    #(100/2)    rst_n = 1;
    $display("Reset Is Released!!!");
    @(posedge clk) sig = 'b1_1_1;
    @(posedge clk) sig = 'b0_1_1;
    @(posedge clk) sig = 'b0_0_1;
    @(posedge clk) sig = 'b0_0_0;
    $finish();
 end

  dut i_dut (.clk, .rst_n,
     .param_a, .param_b, .param_c,
     .sig,
     .x , .y, .z);

  int i;
  bit[2:0] exp_xyz[100];
  always@(posedge clk) begin
    if ({x,y,z} !== exp_xyz[i])
      $display("ERROR !!! xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]);
    else
      $display("OK        xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]);
    i++;
  end

endmodule

これを実行するとこんな感じにログが出ます。

log
Start of Test !!!!
OK        xyz = 000, expected 000
OK        xyz = 000, expected 000
OK        xyz = 000, expected 000
OK        xyz = 000, expected 000
OK        xyz = 000, expected 000
OK        xyz = 000, expected 000
OK        xyz = 000, expected 000
OK        xyz = 000, expected 000
OK        xyz = 000, expected 000
OK        xyz = 000, expected 000
Reset Is Released!!!
OK        xyz = 000, expected 000
ERROR !!! xyz = 110, expected 000
ERROR !!! xyz = 010, expected 000
$finish called at time : 1350 ns : File "<FILE PATH>/test_bench.sv" Line 19

これに少しずつ段階的にUVMを適用して行ってみようと思います。

■ 0.5. UVMのメッセージプリントを使用

取り敢えずUVMのメッセージプリントだけ使用してみます。

TB0.5 : UVMメッセージマクロ

そのためにはUVM package(クラスライブラリ)を使用できるようにするのとUVMで定義しているマクロを使えるようにします。
テストベンチに
`include "uvm_macros.svh"
import uvm_pkg::*;
を記述する必要があります。
uvm_pkgを含んだファイルはコンパイルリストに追加して事前にコンパイルされるようにしておくか、テストベンチmoduleのファイルにincludeして一緒にコンパイルされるようにしておきます。シミュレータによってはオプション付加で自動でUVMを使えるようになっていたりします。
$dispay()でのメッセージプリンティングを全部UVMのメッセージプリンティングマクロの`uvm_info()及び`uvm_error()に変えます。

test_bench.sv
`include "uvm_macros.svh"//<=========== To use UVM macros
//`include "uvm_pkg.sv"  //<=========== To compile file of UVM.
module test_bench;
  import uvm_pkg::*;     //<=========== To use UVM class libraries
  bit       clk, rst_n;
  bit       param_a, param_b, param_c;  // Input Settings
  bit [0:2] sig;                        // Input Signals
  logic     x, y, z;                    // Output

  initial forever #(100/2) clk = !clk;

  initial begin
    `uvm_info("test_bench", "Start of Test !!!!", UVM_MEDIUM)
    {param_a, param_b, param_c} = 'b110;
    repeat(10) @(posedge clk);
    #(100/2)    rst_n = 1;
    `uvm_info("test_bench", "Reset Is Released!!!", UVM_MEDIUM)
    @(posedge clk) sig = 'b1_1_1;
    @(posedge clk) sig = 'b0_1_1;
    @(posedge clk) sig = 'b0_0_1;
    @(posedge clk) sig = 'b0_0_0;
    $finish();
 end

  dut i_dut (.clk, .rst_n,
     .param_a, .param_b, .param_c,
     .sig,
     .x , .y, .z);

  int i;
  bit[2:0] exp_xyz[100];
  always@(posedge clk) begin
    if ({x,y,z} !== exp_xyz[i])
      `uvm_error("test_bench", $sformatf("ERROR !!! xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]))
    else
      `uvm_info ("test_bench", $sformatf("OK        xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]), UVM_MEDIUM)
    i++;
  end

endmodule

以下の様なログになると思います。

log
UVM_INFO <FILE PATH>/test_bench.sv(13) @ 0: reporter [test_bench] Start of Test !!!!
UVM_INFO <FILE PATH>/test_bench.sv(36) @ 50000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(36) @ 150000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(36) @ 250000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(36) @ 350000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(36) @ 450000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(36) @ 550000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(36) @ 650000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(36) @ 750000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(36) @ 850000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(36) @ 950000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(17) @ 1000000: reporter [test_bench] Reset Is Released!!!
UVM_INFO <FILE PATH>/test_bench.sv(36) @ 1050000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_ERROR <FILE PATH>/test_bench.sv(34) @ 1150000: reporter [test_bench] ERROR !!! xyz = 110, expected 000
UVM_ERROR <FILE PATH>/test_bench.sv(34) @ 1250000: reporter [test_bench] ERROR !!! xyz = 010, expected 000
$finish called at time : 1350 ns : File "<FILE PATH>/test_bench.sv" Line 22

UVMのメッセージ形式であるUVM_INFO/ERRORで表示されています。メッセージ表示箇所のファイル名そのライン数、シミュレーション時間、指定したメッセージIDなんかが自動で表示されています。

`uvm_info(), `uvm_error()の書式は以下の通りです。
`uvm_info (<Message ID>, <Message>, <Message Verbosity>)
`uvm_error(<Message ID>, <Message>)
マクロなので行末に; は不要です。時々つけてしまっている人を見かけます。`uvm_info(), `uvm_error()の他にも
`uvm_warning(<Message ID>, <Message>)
`uvm_fatal(<Message ID>, <Message>)
があります。`uvm_fatal()はその時点でSimulationが終了します。

<Message ID>は何の文字列を指定しても良いですが、ここでは"test_bench"としました。UVMのclass内の場合には大概get_type_name()get_name()を使用して、そのclassのタイプ名やインスタンス名の文字列を指定する事が多いです。ここの例ではmoduleでget_type_name()get_name()は使えないので"test_bench"と言う文字列を指定しました。

<Message Verbosity>はシミュレーション実行時にそのメッセージを表示させるか、させないかのコントロールに使います。UVM_MEDIUMを指定すればデフォルトで表示されます。なので通常はUVM_MEDIUMで問題無いです。他にはUVM_NONE, UVM_LOW, UVM_HIGH, UVM_FULL, UVM_DEBUGの指定ができます。UVM_HIGH, UVM_FULL, UVM_DEBUGを指定すると、通常ではそのメッセージは表示されません。シミュレーション実行時に+UVM_VERBOSITY=UVM_HIGH/FULL/DEBUGを指定した時のみ表示されるようになります。時々勘違いしている人がいますが、メッセージのseverity(重要度)ではなくてverbosity(冗長性)なので注意。メッセージの冗長性が”高い”シミュレーションの時に表示させたい、重要度の低いメッセージにUVM_HIGHを指定します。逆に+UVM_VERBOSITY=UVM_LOWのメッセージ冗長度の低いシミュレーションでは通常のUVM_MEDIUMで指定したメッセージは表示されなくなります。

■ 1. UVM Test classの使用

次にUVM Test Classを使ってみます。

TB1 : UVM Test Class

test_bench module内でuvm_test classをextendsしたmy_testを定義します。定義したこのmy_testをrun_test("my_test")でコールして実行します。

test_bench.sv
`include "uvm_macros.svh"//<=========== To use UVM macros
//`include "uvm_pkg.sv"  //<=========== To compile file of UVM.
module test_bench;
  import uvm_pkg::*;     //<=========== To use UVM class libraries
  bit       clk, rst_n;
  bit       param_a, param_b, param_c;  // Input Settings
  bit [0:2] sig;                        // Input Signals
  logic     x, y, z;                    // Output Signals

  initial forever #(100/2) clk = !clk;

  //############################################
  class my_test extends uvm_test;
        `uvm_component_utils(my_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual task run_phase (uvm_phase phase);
        `uvm_info( get_type_name(), "############ Hello! This is an UVM message. ################", UVM_MEDIUM)
        phase.raise_objection(this); // <============ To prevent from finishing sim
    endtask

  endclass
  //############################################

  initial uvm_pkg::run_test("my_test"); // <============ Start UVM Test

  initial begin
    `uvm_info("test_bench", "Start of Test !!!!", UVM_MEDIUM)
    {param_a, param_b, param_c} = 'b110;
    repeat(10) @(posedge clk);
    #(100/2)    rst_n = 1;
    `uvm_info("test_bench", "Reset Is Released!!!", UVM_MEDIUM)
    @(posedge clk) sig = 'b1_1_1;
    @(posedge clk) sig = 'b0_1_1;
    @(posedge clk) sig = 'b0_0_1;
    @(posedge clk) sig = 'b0_0_0;
    $finish();
  end

  dut i_dut (.clk, .rst_n,
     .param_a, .param_b, .param_c,
     .sig,
     .x , .y, .z);

  int i;
  bit[2:0] exp_xyz[100];
  always@(posedge clk) begin
    if ({x,y,z} !== exp_xyz[i])
      `uvm_error("test_bench", $sformatf("ERROR !!! xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]))
    else
      `uvm_info ("test_bench", $sformatf("OK        xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]), UVM_MEDIUM)
    i++;
  end

endmodule

これで晴れてUVM testが実行できるようになりました。以下ログの例

log
UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO <FILE PATH>/test_bench.sv(31) @ 0: reporter [test_bench] Start of Test !!!!
UVM_INFO /tools/Xilinx/Vivado/2022.2/data/system_verilog/uvm_1.2/xlnx_uvm_package.sv(20867) @ 0: reporter [UVM/COMP/NAMECHECK] This implementation of the component name checks requires DPI to be enabled
UVM_INFO <FILE PATH>/test_bench.sv(21) @ 0: uvm_test_top [my_test] ############ Hello! This is an UVM message. ################
UVM_INFO <FILE PATH>/test_bench.sv(54) @ 50000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(54) @ 150000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(54) @ 250000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(54) @ 350000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(54) @ 450000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(54) @ 550000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(54) @ 650000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(54) @ 750000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(54) @ 850000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(54) @ 950000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(35) @ 1000000: reporter [test_bench] Reset Is Released!!!
UVM_INFO <FILE PATH>/test_bench.sv(54) @ 1050000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_ERROR <FILE PATH>/test_bench.sv(52) @ 1150000: reporter [test_bench] ERROR !!! xyz = 110, expected 000
UVM_ERROR <FILE PATH>/test_bench.sv(52) @ 1250000: reporter [test_bench] ERROR !!! xyz = 010, expected 000
$finish called at time : 1350 ns : File "<FILE PATH>/test_bench.sv" Line 40

きちんと最初に挿入したメッセージが表示されています。

UVMではTB module内でrun_test()が呼ばれるとすべてが始まり、指定したtest classが自動でインスタンス化されて、そのメンバーのrun_phase()と言うタスクが自動で実行されます。今回はその中でメッセージプリンティングが書かれてているのでそれが実行されています。
メッセージを表示した後、何もしないと'run_phase()'が終了し、それ以降のUVMの規定の処理functionが実行され、シミュレーション自体が勝手に終了していまいます。なのでphase.raise_objection(this)run_phase()の終了を防いでいます。

メッセージ表示を使っただけだとちょっと寂しいので以降少しずつ改造していきます。

■ 2. UVMでのテスト開始・終了の制御

テストの開始と終了の制御をUVM Test Classで行うようにします。

TB2 : テスト開始とDUTへの値設定

既存のテストのinitalで、メッセージ表示とDUTにparam_a/b/cの値を設定していた部分をmy_test classのstart_of_simulation_phase()に移動します。又、元有った所にはuvm_wait_for_nba_region()を挿入します。
あと、
"############ Hello! This is an UVM message. ################"
のメッセージ表示の、run_phase()からstart_of_simulation_phase()への移動と、
新たにfinal_phase()を追加して、シミュレーションの終了を示す
"############ Bye! This is the end of an UVM test. ################"
のメッセージ表示を挿入します。
run_phase()内ではphase.raise_objection(this);の後に@(test_done_evt);およびphase.drop_objection(this);を追加します。

test_bench.sv
`include "uvm_macros.svh"
//`include "uvm_pkg.sv"
module test_bench;
    import uvm_pkg::*;
    bit         clk, rst_n;
    bit         param_a, param_b, param_c;  // Input Settings
    bit [0:2]   sig;                        // Input Signals
    logic       x, y, z;                    // Output

    initial forever #(100/2) clk = !clk;

    event test_done_evt;

  //############################################
  class my_test extends uvm_test;
    `uvm_component_utils(my_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual function void set_params();
        {param_a, param_b, param_c} = 'b110;
    endfunction

    virtual function void start_of_simulation_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Hello! This is an UVM message. ################", UVM_MEDIUM)
        `uvm_info(get_type_name(), "Start of Test !!!!", UVM_MEDIUM)
        set_params();
        `uvm_info(get_type_name(), $sformatf("param_a = %b, param_b = %b, param_c =%b", param_a, param_b, param_c), UVM_MEDIUM)
    endfunction

    virtual task run_phase (uvm_phase phase);
        phase.raise_objection(this);
        @(test_done_evt);
        phase.drop_objection(this);
    endtask

    virtual function void final_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Bye! This is the end of an UVM test. ################", UVM_MEDIUM)
    endfunction

  endclass
  //############################################

  initial uvm_pkg::run_test("my_test");

  initial begin
  //`uvm_info("test_bench", "Start of Test !!!!", UVM_MEDIUM) // ===========> Moved to start_of_simulation_phase()

  //{param_a, param_b, param_c} = 'b110;                      // ===========> Moved to start_of_simulation_phase()
    uvm_pkg::uvm_wait_for_nba_region();                       // <=========== Wait until the start of run_phase()
    `uvm_info("test_bench", "Waited until the start of run_phase.", UVM_MEDIUM)
    repeat(10) @(posedge clk);
    #(100/2)    rst_n = 1;
    `uvm_info("test_bench", "Reset Is Released!!!", UVM_MEDIUM)
    @(posedge clk) sig = 'b1_1_1;
    @(posedge clk) sig = 'b0_1_1;
    @(posedge clk) sig = 'b0_0_1;
    @(posedge clk) sig = 'b0_0_0;
    -> test_done_evt;  //$finish();
  end

  dut i_dut (.clk, .rst_n,
     .param_a, .param_b, .param_c,
     .sig,
     .x , .y, .z);

  int i;
  bit[2:0] exp_xyz[100];
  always@(posedge clk) begin
    if ({x,y,z} !== exp_xyz[i])
      `uvm_error("test_bench", $sformatf("ERROR !!! xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]))
    else
      `uvm_info ("test_bench", $sformatf("OK        xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]), UVM_MEDIUM)
    i++;
  end

endmodule

以下のログメッセージの様にちゃんと時刻0でのメッセージ表示とparam_a/b/cへの値設定を待っています。また、最後の方でで新たに挿入したメッセージも表示されています。
そして今回、シミュレーション終了前に、メッセージのサマリー表示がされるようになりました。

log
UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO /tools/Xilinx/Vivado/2022.2/data/system_verilog/uvm_1.2/xlnx_uvm_package.sv(20867) @ 0: reporter [UVM/COMP/NAMECHECK] This implementation of the component name checks requires DPI to be enabled
UVM_INFO <FILE PATH>/test_bench.sv(27) @ 0: uvm_test_top [my_test] ############ Hello! This is an UVM message. ################
UVM_INFO <FILE PATH>/test_bench.sv(28) @ 0: uvm_test_top [my_test] Start of Test !!!!
UVM_INFO <FILE PATH>/test_bench.sv(30) @ 0: uvm_test_top [my_test] param_a = 1, param_b = 1, param_c =0
UVM_INFO <FILE PATH>/test_bench.sv(52) @ 0: reporter [test_bench] Waited until the start of run_phase.
UVM_INFO <FILE PATH>/test_bench.sv(74) @ 50000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(74) @ 150000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(74) @ 250000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(74) @ 350000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(74) @ 450000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(74) @ 550000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(74) @ 650000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(74) @ 750000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(74) @ 850000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(74) @ 950000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(55) @ 1000000: reporter [test_bench] Reset Is Released!!!
UVM_INFO <FILE PATH>/test_bench.sv(74) @ 1050000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_ERROR <FILE PATH>/test_bench.sv(72) @ 1150000: reporter [test_bench] ERROR !!! xyz = 110, expected 000
UVM_ERROR <FILE PATH>/test_bench.sv(72) @ 1250000: reporter [test_bench] ERROR !!! xyz = 010, expected 000
UVM_INFO <FILE PATH>/test_bench.sv(74) @ 1350000: reporter [test_bench] OK        xyz = 000, expected 000
UVM_INFO /tools/Xilinx/Vivado/2022.2/data/system_verilog/uvm_1.2/xlnx_uvm_package.sv(19968) @ 1350000: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO <FILE PATH>/test_bench.sv(40) @ 1350000: uvm_test_top [my_test] ############ Bye! This is the end of an UVM test. ################
UVM_INFO /tools/Xilinx/Vivado/2022.2/data/system_verilog/uvm_1.2/xlnx_uvm_package.sv(13673) @ 1350000: reporter [UVM/REPORT/SERVER] [test_bench]    16
[my_test]     4
[UVM/RELNOTES]     1
[UVM/COMP/NAMECHECK]     1
[TEST_DONE]     1
[RNTST]     1
** Report counts by id
UVM_FATAL :    0
UVM_ERROR :    2
UVM_WARNING :    0
UVM_INFO :   22
** Report counts by severity

--- UVM Report Summary ---

uvm_wait_for_nba_region()はUVMで用意されているタスクで、initialの後にコールすれば、simulation時刻0でUVMが自動で順番に実行する以下の各ファンクション
build_phase()
end_of_elaboration_phase()
connect_phase()
start_of_simulation_phase()
の終了と
run_phase()
の開始後の時刻0の時点までを待ちます。UVMでは前述のrun_phase()タスクの自動実行の前にこれらのファンクションも自動的に実行されます。
nba_regionはNon Blocking Assignment Regionの事です。なのでinitialの直後に実行すればシミュレーション時刻0の上記の所までを待ちます。
今回の例では、my_test classのstart_of_simulation_phase()でのメッセージをプリントとprams_a/b/cへの値設定を待ちます。
uvm_wait_for_nba_region()の紹介している記事はあまりないのですが、今回の例のように既存のmoduleのテストベンチでinitialを使用していて、UVMの各phaseの実行と同期させる場合に必要になるので是非覚えておいてもらいたいです。

テストの終了は@(test_done_evt)でイベントを待つようにしています。イベント通知を受けたらphase.drop_objection(this)を実行してrun_phase()の終了を許可するようにしています。
UVMではrun_phase()の終了後にも以下のファンクションが順番に自動で実行されテスト終了時の後処理を行います。
extract_phase()
check_phase()
report_phase()
final_phase()
なので、今回final_phase()に追加したメッセージが最後に表示されました。

今回、TB module内の$finish()ではなく、UVMでのテスト終了処理をするようにした為、ログの最後にメッセージのサマリー表示がされます。どういうメッセージが何回表示されて、UVM_FATAL/ERROR/WARNING/INFOがいくつであったかの情報が自動で追加されています。
UVMではこのように、メッセージにデバッグ情報表示や集計を自動でやってくれます。また前述のようにUVM_DEBUG/HIGH/MEDIUM/LOW/NONE等の指定でシミュレーション実行時にメッセージを表示するかしないかのコントロールをする仕組みが備わっています。UVMのこのレポーティング機能を使うだけでもUVMを使う価値が十分にあります。
尚、UVMのメッセージ表示は改造可能です。改造したい場合は以下の記事を参考にすると良いでしょう。

因みに私はファイルのパスがフルパスで表示されるのが長たらしくて嫌いなので短くして表示させています。

■ 3. UVM Testのrun_phase()からのテストの実行

UVM Test Classから直接テスト開始タスクをコールするようにします。

TB3 : テストの開始と終了の制御

これまでuvm_wait_for_nba_region()やeventを使ってUVMとmodule内の実際のテストの開始や終了のタイミングの同期を取っていましたが、次に完全にUVM test classからテストの開始と終了をコントロールするようにします。その為にinitial文の中の信号のDUTの信号をドライブしている部分をtet_sequenceと言うtaskにくくりだしてmy_test classのrun_phase()からコールするようします。これによりテストの開始と終了が完全にUVMで管理されるようになり、uvm_wait_for_nba_region()やtest_done_evtのイベント待ちは要らなくなります。

test_bench.sv
`include "uvm_macros.svh"
//`include "uvm_pkg.sv"
module test_bench;
    import uvm_pkg::*;
    bit         clk, rst_n;
    bit         param_a, param_b, param_c;  // Input Settings
    bit [0:2]   sig;                        // Input Signals
    logic       x, y, z;                    // Output

    initial forever #(100/2) clk = !clk;

  //@(test_doe_evt);                    // ========> Not necessary now

  //############################################
  class my_test extends uvm_test;
    `uvm_component_utils(my_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual function void set_params();
        {param_a, param_b, param_c} = 'b110;
    endfunction

    virtual function void start_of_simulation_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Hello! This is an UVM message. ################", UVM_MEDIUM)
        `uvm_info( get_type_name(), "Start of Test !!!!", UVM_MEDIUM)
        set_params();
        `uvm_info(get_type_name(), $sformatf("param_a = %b, param_b = %b, param_c =%b", param_a, param_b, param_c), UVM_MEDIUM)
    endfunction

    virtual task run_phase (uvm_phase phase);
        phase.raise_objection(this);
        test_sequence();                // <======= Call from UVM test class
      //@(test_doe_evt);                // ========> Not necessary now
        phase.drop_objection(this);
    endtask

    virtual function void final_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Bye! This is the end of an UVM test. ################", UVM_MEDIUM)
    endfunction

  endclass
  //############################################

  initial uvm_pkg::run_test("my_test");

//initial begin
  task test_sequence();
  //{param_a, param_b, param_c} = 'b110;
  //uvm_pkg::uvm_wait_for_nba_region(); // =========> Not necessary now
  //`uvm_info("test_bench", "Waited until the start of run_phase.", UVM_MEDIUM)
    repeat(10) @(posedge clk);
    #(100/2)    rst_n = 1;
    `uvm_info("test_bench.test_sequence()", "Reset Is Released!!!", UVM_MEDIUM)
    @(posedge clk) sig = 'b1_1_1;
    @(posedge clk) sig = 'b0_1_1;
    @(posedge clk) sig = 'b0_0_1;
    @(posedge clk) sig = 'b0_0_0;
  //-> test_done_evt;  //$finish();     // =========> Not necessary now
  endtask
//end

  dut i_dut (.clk, .rst_n,
     .param_a, .param_b, .param_c,
     .sig,
     .x , .y, .z);

  int i;
  bit[2:0] exp_xyz[100];
  always@(posedge clk) begin
    if ({x,y,z} !== exp_xyz[i])
      `uvm_error("test_bench", $sformatf("ERROR !!! xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]))
    else
      `uvm_info ("test_bench", $sformatf("OK        xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]), UVM_MEDIUM)
    i++;
  end

endmodule

この様にmodule内に定義されたclassからはその同じmodule内のtask/functionをコールする事が可能です。

ログほぼ同じなので省略。

■ 4. 別テストの追加

ここで新たに別のテストを追加してみます。

TB4 : テスト追加と実行テストの指定

my_testを継承したrandom_testと言うクラスを同じmodule内に定義します。このテストではparam_a/b/cにランダムな値を設定します。

test_bench.sv
`include "uvm_macros.svh"
//`include "uvm_pkg.sv"
module test_bench;
    import uvm_pkg::*;
    bit         clk, rst_n;
    bit         param_a, param_b, param_c;  // Input Settings
    bit [0:2]   sig;                        // Input Signals
    logic       x, y, z;                    // Output

    initial forever #(100/2) clk = !clk;

  //############################################
  class my_test extends uvm_test;
    `uvm_component_utils(my_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual function void set_params();
        {param_a, param_b, param_c} = 3'b110;
    endfunction

    virtual function void start_of_simulation_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Hello! This is an UVM message. ################", UVM_MEDIUM)
        `uvm_info( get_type_name(), "Start of Test !!!!", UVM_MEDIUM)
        set_params();
        `uvm_info(get_type_name(), $sformatf("param_a = %b, param_b = %b, param_c =%b", param_a, param_b, param_c), UVM_MEDIUM)
    endfunction

    virtual task run_phase(uvm_phase phase);
        phase.raise_objection(this);
        test_sequence();
        phase.drop_objection(this);
    endtask

    virtual function void final_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Bye! This is the end of an UVM test. ################", UVM_MEDIUM)
    endfunction

  endclass
  //############################################

  //############################################
  class random_test extends my_test;         // <========== Added
    `uvm_component_utils(random_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    rand bit[2:0] param_abc = 3'b110;

    virtual function void set_params();
        this.randomize();
        {param_a, param_b, param_c} = this.param_abc;
    endfunction

  endclass
  //############################################

  initial uvm_pkg::run_test(); //"my_test"); //<============

  task test_sequence();
    repeat(10) @(posedge clk);
    #(100/2)    rst_n = 1;
    `uvm_info("test_bench.test_sequence()", "Reset Is Released!!!", UVM_MEDIUM)
    @(posedge clk) sig = 'b1_1_1;
    @(posedge clk) sig = 'b0_1_1;
    @(posedge clk) sig = 'b0_0_1;
    @(posedge clk) sig = 'b0_0_0;

  endtask

  dut i_dut (.clk, .rst_n,
     .param_a, .param_b, .param_c,
     .sig,
     .x , .y, .z);

  int i;
  bit[2:0] exp_xyz[100];
  always@(posedge clk) begin
    if ({x,y,z} !== exp_xyz[i])
      `uvm_error("test_bench", $sformatf("ERROR !!! xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]))
    else
      `uvm_info ("test_bench", $sformatf("OK        xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]), UVM_MEDIUM)
    i++;
  end

endmodule

このrandom_testを実行するには、以下のようにシミュレータの実行時にUVM_TESTNAMEを\$test$plusargsによる引数で指定します。

実行テストの指定
<Simulator Run Command> +UVM_TESTAME=random_test

因みに一般的なシミュレータでは上記の様にシミュレータの実行コマンドにに+UVM_TESTNAMEを追加すれば良いですが、Vivadoのxsimでは何故か--testplusarg "UVM_TESTNAME=random_test"の様にわざわざ--testplusargオプションを付ける必要があります。

この様に通常UVMでは全てのテストケースを同時にコンパイルしてテストの実行時に+UVM_TESTNAMEで実行するテストを指定します。これによりテストの切り替えの為だけの再コンパイル時間の消費を無くしています。UVMを知っている人でも意外にもこの事を理解していない人も結構いる様なので是非知っておいてください。

又、以前はmoduleの中でrun_test("my_test")で実行するテストを指定していましたが、run_test()と実行するテストの指定を無くしました。+UVM_TESTNAMEでの指定が優先されるのでrun_test("my_test")での指定は残しても問題無かったですが、一般的には指定をしないのでここでは指定を無くしました。

このテストを実行すると以下の様にparam_a/b/cはがランダムな値になり、メッセージIDも、get_type_name()でtest class名を指定しているので、自動的に[random_test]に変わっています。。

log
UVM_INFO <FILE PATH>/test_bench.sv(25) @ 0: uvm_test_top [random_test] Start of Test !!!!
UVM_INFO <FILE PATH>/test_bench.sv(27) @ 0: uvm_test_top [random_test] param_a = 1, param_b = 1, param_c =1

因みに生成されるランダム値の制御はrandom seedの値によって変わります。random seedの値が同じであれば同じランダム値になります。seed値の指定は各シミュレータによって違うのでシミュレーターのオプションを調べてみてください。

■ 5. チェッカーもtest classからコール

今までDUTへのstimulus側をUVM test classからの制御にしてきましたが、DUTのoutputのチェック側もUVM test classからの制御にします。

TB5 : チェッカーの開始

まずalwaysで勝手に開始していたチェックを、開始の制御ができるようにする為に、chek_resut()と言う名前のタスクにくくり出します。タスク内では無限ループにalwaysは使えないので、代わりにforeverを使います。そしてそのタスクをmy_testのrun_phase()内のfork join_none内でコールしてバックグラウンドで開始します。

test_bench.sv
`include "uvm_macros.svh"
//`include "uvm_pkg.sv"
module test_bench;
  import uvm_pkg::*;
  bit           clk, rst_n;
  bit           param_a, param_b, param_c;  // Input Settings
  bit [0:2]     sig;                        // Input Signals
  logic         x, y, z;                    // Output

  initial forever #(100/2) clk = !clk;

  //############################################
  class my_test extends uvm_test;
    `uvm_component_utils(my_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual function void set_params();
        {param_a, param_b, param_c} = 3'b110;
    endfunction

    virtual function void start_of_simulation_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Hello! This is an UVM message. ################", UVM_MEDIUM)
        `uvm_info( get_type_name(), "Start of Test !!!!", UVM_MEDIUM)
        set_params();
        `uvm_info(get_type_name(), $sformatf("param_a = %b, param_b = %b, param_c =%b", param_a, param_b, param_c), UVM_MEDIUM)
    endfunction

    virtual task run_phase(uvm_phase phase);
        phase.raise_objection(this);
        fork                    // <======= Background run
            check_result();     // <======= check_result task call
        join_none               // <======= Background run
        test_sequence();
        phase.drop_objection(this);
    endtask

    virtual function void final_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Bye! This is the end of an UVM test. ################", UVM_MEDIUM)
    endfunction

  endclass
  //############################################

  //############################################
  class random_test extends my_test;
    `uvm_component_utils(random_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    rand bit[2:0] param_abc = 3'b110;

    virtual function void set_params();
        this.randomize();
        {param_a, param_b, param_c} = this.param_abc;
    endfunction

  endclass
  //############################################

  initial uvm_pkg::run_test(); //"my_test");

//initial begin
  task test_sequence();
    repeat(10) @(posedge clk);
    #(100/2)    rst_n = 1;
    `uvm_info("test_bench.test_sequence()", "Reset Is Released!!!", UVM_MEDIUM)
    @(posedge clk) sig = 'b1_1_1;
    @(posedge clk) sig = 'b0_1_1;
    @(posedge clk) sig = 'b0_0_1;
    @(posedge clk) sig = 'b0_0_0;
  endtask

  dut i_dut (.clk, .rst_n,
     .param_a, .param_b, .param_c,
     .sig,
     .x , .y, .z);

  task check_result();          // <======== to task from always
    int i;
    bit[2:0] exp_xyz[100];
  //always@(posedge clk) begin  // =========>
    forever @(posedge clk) begin
        if ({x,y,z} !== exp_xyz[i])
          `uvm_error("test_bench.check_result()", $sformatf("ERROR !!! xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]))
        else
          `uvm_info ("test_bench.check_result()", $sformatf("OK        xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]), UVM_MEDIUM)
        i++;
    end
  endtask

endmodule

ログは同様なので省略。

■ 6. 既存のBus Functional Modelがある場合

ここで既存のmoduleベースのBFM(Bus Functional Model)がある場合を考えます。

TB6 : BFMへのアクセス

test classからBFM内のfunction/taskへのアクセスもこれまでと同様に可能です。ここではBFM内にdrive_sig()と言うsigをドライブするタスクを想定します。my_test内にtest_sequence_start()と言うタスクを作り、BFMのdrive_sig()をコールします。その際i_bfm.drive_sig()と階層参照をします。resetのコントロール部分は別にしてreset_release()と言うタスクとしてtest_bench内に残します。それぞれrun_phase()からコールします。

test_bench.sv
`include "uvm_macros.svh"
//`include "uvm_pkg.sv"
module test_bench;
  import uvm_pkg::*;
  bit           clk, rst_n;
  bit           param_a, param_b, param_c;  // Input Settings
  bit [0:2]     sig;                        // Input Signals
  logic         x, y, z;                    // Output

  initial forever #(100/2) clk = !clk;

  //############################################
  class my_test extends uvm_test;
    `uvm_component_utils(my_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual function void set_params();
        {param_a, param_b, param_c} = 3'b110;
    endfunction

    virtual function void start_of_simulation_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Hello! This is an UVM message. ################", UVM_MEDIUM)
        `uvm_info( get_type_name(), "Start of Test !!!!", UVM_MEDIUM)
        set_params();
        `uvm_info(get_type_name(), $sformatf("param_a = %b, param_b = %b, param_c =%b", param_a, param_b, param_c), UVM_MEDIUM)
    endfunction

    virtual task run_phase(uvm_phase phase);
        phase.raise_objection(this);
        fork
            check_result();
        join_none
      //test_sequence();             // =========>
        reset_release();             // <=========
        this.test_sequence_start();  // <=========
        phase.drop_objection(this);
    endtask

    virtual function void final_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Bye! This is the end of an UVM test. ################", UVM_MEDIUM)
    endfunction

    virtual task test_sequence_start();  // <======== Added
        $display("Start signal driving!!!");
        i_bfm.drive_sig('b1_1_1);        // <======== BFM task access
        i_bfm.drive_sig('b0_1_1);        // <======== BFM task access
        i_bfm.drive_sig('b0_0_1);        // <======== BFM task access
        i_bfm.drive_sig('b0_0_0);        // <======== BFM task access
    endtask

  endclass
  //############################################

  //############################################
  class random_test extends my_test;
    `uvm_component_utils(random_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    rand bit[2:0] param_abc = 3'b110;

    virtual function void set_params();
        this.randomize();
        {param_a, param_b, param_c} = this.param_abc;
    endfunction

  endclass
  //############################################

  initial uvm_pkg::run_test(); //"my_test");

//test_sequence();      
  task reset_release();  // <=========
    repeat(10) @(posedge clk);
    #(100/2)    rst_n = 1;
    `uvm_info("test_bench.test_sequence()", "Reset Is Released!!!", UVM_MEDIUM)
//  @(posedge clk) sig = 'b1_1_1;           // =========> To BFM
//  @(posedge clk) sig = 'b0_1_1;           // =========> To BFM
//  @(posedge clk) sig = 'b0_0_1;           // =========> To BFM
//  @(posedge clk) sig = 'b0_0_0;           // =========> To BFM
  endtask

  bfm i_bfm(.clk, .sig);                    // <========= BFM

  dut i_dut (.clk, .rst_n,
     .param_a, .param_b, .param_c,
     .sig,
     .x , .y, .z);

  task check_result();
    int i;
    bit[2:0] exp_xyz[100];
    forever @(posedge clk) begin
        if ({x,y,z} !== exp_xyz[i])
          `uvm_error("test_bench.check_result()", $sformatf("ERROR !!! xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]))
        else
          `uvm_info ("test_bench.check_result()", $sformatf("OK        xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]), UVM_MEDIUM)
        i++;
    end
  endtask

endmodule
bfm.sv
module bfm( input clk, output logic [0:2]  sig);

    task drive_sig(bit[0:2] val);
        @(negedge clk) sig = val;
    endtask

endmodule

■ 7. Test classの別packageへの分離

ここでtest classをmoduleから外に出して別packageに分離してみます。

TB7 : testの分離

packageからはmodule内の信号やtask/functionに直接アクセスできません。なのでtaskやfunctionはinterface経由でコールします。その際packageにはvirtual interfaceとしてinterfaceのハンドルを渡します。ここではtest_ifとbfm_ifと言うinterfaceを定義して、それらを経由してtest_benchやbfm内のtaskをコールします。test_ifはtest_benchに直接インスタンス化、bfm_ifはbfmにbindする形でbfm内にインスタンス化します。
逆にmoduleからはpackage内の信号をimportや::で参照する事ができます。なのでmoduleからはpackageに移動したparam_a/b/cを参照してDUTに値を設定しています。

test_bench.sv
`include "uvm_macros.svh"
//`include "uvm_pkg.sv"
module test_bench;
  import uvm_pkg::*;
  import test_lib_pkg::*;                   // <========= Added
  bit           clk, rst_n;
//bit           param_a, param_b, param_c;  // Input Settings =========> Moved to test_lib_pkg
  bit [0:2]     sig;                        // Input Signals
  logic         x, y, z;                    // Output

  initial forever #(100/2) clk = !clk;

  //############## ==========> Classes were moved into test_lib_pkg
  //############## ==========> Classes were moved into test_lib_pkg

  initial begin
    test_lib_pkg::vif     = test_bench.i_test_if;    // <========= interface handle passing
    test_lib_pkg::bfm_vif = i_bfm.i_probe;           // <========= interface handle passing
    uvm_pkg::run_test(); //"my_test");
  end

  task reset_release();
    repeat(10) @(posedge clk);
    #(100/2)    rst_n = 1;
    `uvm_info("test_bench.test_sequence()", "Reset Is Released!!!", UVM_MEDIUM)
  endtask

  test_if i_test_if();              // <================ Added test_if
  bind bfm bfm_if i_probe();        // <================ Bind bfm_if to the BFM
  
  bfm i_bfm(.clk, .sig);

  dut i_dut (.clk, .rst_n,
     .param_a(param_a), .param_b(param_b), .param_c(param_c),
     .sig,
     .x , .y, .z);

  task check_result();
    int i;
    bit[2:0] exp_xyz[100];
    forever @(posedge clk) begin
        if ({x,y,z} !== exp_xyz[i])
          `uvm_error("test_bench.check_result()", $sformatf("ERROR !!! xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]))
        else
          `uvm_info ("test_bench.check_result()", $sformatf("OK        xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]), UVM_MEDIUM)
        i++;
    end
  endtask

endmodule
bfm.sv
module bfm( input clk, output logic [0:2]  sig);

    task drive_sig(bit[0:2] val);
        @(negedge clk) sig = val;
    endtask

endmodule
test_if.sv
interface test_if();

    task reset_release();
        test_bench.reset_release();
    endtask

    task check_result();
        test_bench.check_result();
    endtask

endinterface
bfm_if.sv
interface bfm_if();

    task drive_sig(bit[0:2] val);
        i_bfm.drive_sig(val);
    endtask

endinterface
test_lib_pkg.sv
`include "uvm_macros.svh"
package test_lib_pkg;
  import uvm_pkg::*;

  bit param_a, param_b, param_c;

  virtual test_if vif;      //<==== Virtual Interface
  virtual bfm_if  bfm_vif;  //<==== Virtual Interface

  class my_test extends uvm_test;
    `uvm_component_utils(my_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual function void set_params();
        {param_a, param_b, param_c} = 3'b110;
    endfunction

    virtual function void start_of_simulation_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Hello! This is an UVM message. ################", UVM_MEDIUM)
        `uvm_info( get_type_name(), "Start of Test !!!!", UVM_MEDIUM)
        set_params();
        `uvm_info(get_type_name(), $sformatf("param_a = %b, param_b = %b, param_c =%b", param_a, param_b, param_c), UVM_MEDIUM)
    endfunction

    virtual task run_phase(uvm_phase phase);
        phase.raise_objection(this);
        fork
            vif.check_result();
        join_none
        vif.reset_release();
        this.test_sequence_start();
        phase.drop_objection(this);
    endtask

    virtual function void final_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Bye! This is the end of an UVM test. ################", UVM_MEDIUM)
    endfunction

   virtual task test_sequence_start();
        $display("Start signal driving!!!");
        bfm_vif.drive_sig('b1_1_1); // <========
        bfm_vif.drive_sig('b0_1_1); // <========
        bfm_vif.drive_sig('b0_0_1); // <========
        bfm_vif.drive_sig('b0_0_0); // <========
    endtask

  endclass

  class random_test extends my_test;
    `uvm_component_utils(random_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    rand bit[2:0] param_abc = 3'b110;

    virtual function void set_params();
        this.randomize();
        {param_a, param_b, param_c} = this.param_abc;
    endfunction

  endclass

endpackage

package内のclassからのtest_bench内にある既存のBFM module内のtask/functionへのアクセスに関しては以下を参照。

UVMでDUTへのアクセス方法をまとめる

これではvirtual classの使用でのやり方紹介しているが、virtual classを使わずに今回の様にinterfaceをbindする事でvirtual interfaceを使う事も可能。
interfaceのbindに関しては以下も参照。

moduleおよびinterfaceのbindと信号の参照方法

■ 8. drive/check taskをTest Classへ移動

次にdrive_sig()やcheck_result()をmy_test classに移動します。

TB8 : drive/check taskの移動

drive/checkのタスクの移動に伴いそれらがドライブ/参照しているsig,x,y,zもparam_a/b/cと同様にtest_benchからpackageに移動します。UVMの教科書的にはDUTの入出力信号はinterfaceを介してclassとやり取りするように書かれていますが、簡単なDUTの場合にはこのようにinterfaceを使わずにpackage直下に入出力信号を置いてそれをtest_bench moduleから参照してDUTに接続しても特に何の問題はありません。同じ種類のI/Fが複数あるDUTの場合にはそれではそれぞれの信号を識別できないのでその場合にはinterfaceを使って信号を違うインスタンスに分ける必要があります。
drive_sig()はBFMからmy_test classに移動になったので、ここではBFMはまた無くします。それに伴いbfm_ifも要らないので無くします。test_ifはにdut_ifに名前を変えてreset_release()だけ残します。reset_release()の本体もtest_benchから移動してdut_ifに入れておきます。dut_ifはtest_benchの中で通常通りにinstantiateされてリセット信号が参照されてます。
test_benchも大分寂しくなりました。

test_bench.sv
`include "uvm_macros.svh"
//`include "uvm_pkg.sv"
module test_bench;
  import uvm_pkg::*;
  import test_lib_pkg::*;
  bit           clk; //,rst_n;                                  ======> Moved to test_lib_pkg

//bit [0:2]     sig;                        // Input Signals    ======> Moved to test_lib_pkg
//logic         x, y, z;                    // Output           ======> Moved to test_lib_pkg

  initial forever #(100/2) clk = !clk;

//bfm i_bfm(.clk, .sig);    // ========>

  dut_if i_dut_if(clk);     // <======= Instanciated here

  initial begin
    test_lib_pkg::vif = i_dut_if;
    uvm_pkg::run_test(); //"my_test");
  end

  dut i_dut (.clk, .rst_n(i_dut_if.rst_n),     // <======
     .param_a(param_a), .param_b(param_b), .param_c(param_c),
     .sig(sig),                                // <======
     .x(x) , .y(y), .z(z));                    // <======

endmodule
dut_if.sv
`include "uvm_macros.svh"
interface dut_if(input clk);
    import uvm_pkg::*;

    bit rst_n;

    task reset_release();
        repeat(10) @(posedge clk);
        #(100/2)    rst_n = 1;
        `uvm_info("reset_release()", "Reset Is Released!!!", UVM_MEDIUM)
    endtask

endinterface
test_lib_pkg.sv
`include "uvm_macros.svh"
package test_lib_pkg;
  import uvm_pkg::*;

  bit       param_a, param_b, param_c;
  bit [0:2] sig;                        // Input Signals
  logic     x, y, z;                    // Output

  virtual dut_if vif;

  class my_test extends uvm_test;
    `uvm_component_utils(my_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual function void set_params();
        {param_a, param_b, param_c} = 3'b110;
    endfunction

    virtual function void start_of_simulation_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Hello! This is an UVM message. ################", UVM_MEDIUM)
        `uvm_info( get_type_name(), "Start of Test !!!!", UVM_MEDIUM)
        set_params();
        `uvm_info(get_type_name(), $sformatf("param_a = %b, param_b = %b, param_c =%b", param_a, param_b, param_c), UVM_MEDIUM)
    endfunction

    virtual task run_phase(uvm_phase phase);
        phase.raise_objection(this);
        fork
            this.check_result();        // <========
        join_none
        vif.reset_release();
        this.test_sequence_start();
        phase.drop_objection(this);
    endtask

    virtual function void final_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Bye! This is the end of an UVM test. ################", UVM_MEDIUM)
    endfunction

    virtual task test_sequence_start();
        `uvm_info(get_type_name(), "Start signal driving!!!", UVM_MEDIUM)
        this.drive_sig('b1_1_1); // <===========
        this.drive_sig('b0_1_1); // <===========
        this.drive_sig('b0_0_1); // <===========
        this.drive_sig('b0_0_0); // <===========
    endtask

    virtual task drive_sig(bit[0:2] val); // <========== Added
        @(negedge vif.clk) sig = val;     // <==========
    endtask                               // <==========

    virtual task check_result();        // <======== Added
        int i;
        bit[2:0] exp_xyz[100];
        forever @(posedge vif.clk) begin
          if ({x,y,z} !== exp_xyz[i])
            `uvm_error(get_type_name(), $sformatf("ERROR !!! xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]))
          else
            `uvm_info (get_type_name(), $sformatf("OK        xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]), UVM_MEDIUM)
          i++;
        end

    endtask

  endclass

  class random_test extends my_test;
    `uvm_component_utils(random_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    rand bit[2:0] param_abc = 3'b110;

    virtual function void set_params();
        this.randomize();
        {param_a, param_b, param_c} = this.param_abc;
    endfunction

  endclass

endpackage

■ 9. drive/check taskをcomponent classへ移動

drive_sig()およびcheck_resultをmy_test classに入れましたが別の独立component classに移動します。

TB9 : Driver/Monitor Component Class

それぞれuvm_driver classとuvm_monitor classと言うベースクラスを継承したmy_driver/my_monitorと言うクラスのメンバータスクにします。
UVMではこのようにテストベンチの各機能毎に別々のコンポーネントクラスに分けて機能の独立性を高めて再利用化を促進しています。
各コンポーネントはbuild_phase()内で::type_id::create()でインスタンス化されます。通常のnew()でインスタンス化せず、create()を使うのはFactoryと言う仕組みを使う場合があるからです。ここではFactoryは説明しません。
また各コンポーネントにもtest class同様にそれぞれbuild_phase()やrun_phase()があります。my_monitorではrun_phase()でcheck_result()の内容が自動で実行されるようにしてあります。それに伴い、my_testのrun_phase()内でfork join_noneでバックグラウンドで実行していたcheck_result()は無くしました。それに対しmy_driverのdrive_sig()は依然my_testのrun_phase()から明示的に実行されています。

test_bench.sv
`include "uvm_macros.svh"
//`include "uvm_pkg.sv"
module test_bench;
  import uvm_pkg::*;
  import test_lib_pkg::*;
  bit           clk;

  initial forever #(100/2) clk = !clk;

  dut_if  i_dut_if (clk);

  initial begin
    test_lib_pkg::vif  = i_dut_if;
    uvm_pkg::run_test(); //"my_test");
  end

  dut i_dut (.clk, .rst_n(i_dut_if.rst_n),
     .param_a(param_a), .param_b(param_b), .param_c(param_c),
     .sig(sig),
     .x(x) , .y(y), .z(z));

endmodule
dut_if.sv
`include "uvm_macros.svh"
interface dut_if(input clk);
    import uvm_pkg::*;

    bit rst_n;

    task reset_release();
        repeat(10) @(posedge clk);
        #(100/2)    rst_n = 1;
        `uvm_info("reset_release()", "Reset Is Released!!!", UVM_MEDIUM)
    endtask

endinterface
test_lib_pkg.sv
`include "uvm_macros.svh"
package test_lib_pkg;
  import uvm_pkg::*;

  bit       param_a, param_b, param_c;
  bit [0:2] sig;                        // Input Signals
  logic     x, y, z;                    // Output

  virtual dut_if vif;

  class my_driver extends uvm_driver;   // <======================= added
    `uvm_component_utils(my_driver)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual task drive_sig(bit[0:2] val);
        @(negedge vif.clk) sig = val;
    endtask

  endclass

  class my_monitor extends uvm_monitor; // <======================= added
    `uvm_component_utils(my_monitor)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    int i;
    bit[2:0] exp_xyz[100];
  //virtual task check_result();
    virtual task run_phase(uvm_phase phase);
        forever @(posedge vif.clk) begin
          if ({x,y,z} !== exp_xyz[i])
            `uvm_error(get_type_name(), $sformatf("ERROR !!! xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]))
          else
            `uvm_info (get_type_name(), $sformatf("OK        xyz = %b%b%b, expected %3b",x,y,z, exp_xyz[i]), UVM_MEDIUM)
          i++;
        end
    endtask

  endclass

  class my_test extends uvm_test;
    `uvm_component_utils(my_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual function void set_params();
        {param_a, param_b, param_c} = 3'b110;
    endfunction

    my_driver  m_drv;                                       // <==============
    my_monitor m_mon;                                       // <==============
    virtual function void build_phase(uvm_phase phase);     // <==============
        `uvm_info( get_type_name(), "############ Hello! This is an UVM message. ################", UVM_MEDIUM)
        m_drv = my_driver ::type_id::create("m_drv", this); // <==============
        m_mon = my_monitor::type_id::create("m_mon", this); // <==============
    endfunction                                             // <==============

    virtual function void start_of_simulation_phase(uvm_phase phase);
        `uvm_info(get_type_name(), "Start of Test !!!!", UVM_MEDIUM)
        set_params();
        `uvm_info(get_type_name(), $sformatf("param_a = %b, param_b = %b, param_c =%b", param_a, param_b, param_c), UVM_MEDIUM)
    endfunction

    virtual task run_phase(uvm_phase phase);
        phase.raise_objection(this);
      //fork                        // ============>
      //    this.check_result();    // ============>
      //join_none                   // ============>
        vif.reset_release();
        this.test_sequence_start();
        phase.drop_objection(this);
    endtask

    virtual function void final_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Bye! This is the end of an UVM test. ################", UVM_MEDIUM)
    endfunction

    virtual task test_sequence_start();
        `uvm_info(get_type_name(), "Start sending items!!!", UVM_MEDIUM);
        m_drv.drive_sig('b1_1_1); // <=========== Driver Access
        m_drv.drive_sig('b0_1_1); // <=========== Driver Access
        m_drv.drive_sig('b0_0_1); // <=========== Driver Access
        m_drv.drive_sig('b0_0_0); // <=========== Driver Access
    endtask

  endclass

  class random_test extends my_test;
    `uvm_component_utils(random_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    rand bit[2:0] param_abc = 3'b110;

    virtual function void set_params();
        this.randomize();
        {param_a, param_b, param_c} = this.param_abc;
    endfunction

  endclass

endpackage

ログのメッセージIDも[my_driver], [my_monitor]に変わっています。

log
UVM_INFO <FILE PATH>/test_lib_pkg.sv(69) @ 0: uvm_test_top [my_test] Start of Test !!!!
UVM_INFO <FILE PATH>/test_lib_pkg.sv(71) @ 0: uvm_test_top [my_test] param_a = 1, param_b = 1, param_c =0
UVM_INFO <FILE PATH>/test_lib_pkg.sv(76) @ 0: uvm_test_top [my_test] Hello! This is an UVM message.
UVM_INFO <FILE PATH>/test_lib_pkg.sv(43) @ 50000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_lib_pkg.sv(43) @ 150000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_lib_pkg.sv(43) @ 250000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_lib_pkg.sv(43) @ 350000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_lib_pkg.sv(43) @ 450000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_lib_pkg.sv(43) @ 550000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_lib_pkg.sv(43) @ 650000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_lib_pkg.sv(43) @ 750000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_lib_pkg.sv(43) @ 850000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_lib_pkg.sv(43) @ 950000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/dut_if.sv(10) @ 1000000: reporter [reset_release()] Reset Is Released!!!
UVM_INFO <FILE PATH>/test_lib_pkg.sv(19) @ 1000000: uvm_test_top.m_drv [my_driver] BFM start driving!!!
UVM_INFO <FILE PATH>/test_lib_pkg.sv(43) @ 1050000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/test_lib_pkg.sv(43) @ 1150000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_ERROR <FILE PATH>/test_lib_pkg.sv(41) @ 1250000: uvm_test_top.m_mon [my_monitor] ERROR !!! xyz = 110, expected 000
UVM_ERROR <FILE PATH>/test_lib_pkg.sv(41) @ 1350000: uvm_test_top.m_mon [my_monitor] ERROR !!! xyz = 010, expected 000

■ 10. driver/monitor componentを別packageへ移動

UVM component classとして作成したmy_driver, my_monitorをtest_lib_pkgとはそれぞれ別のpackageに移動します。

TB10 : Componentのpackage化

ここではそれぞれsig_agent_pkg, xyx_agent_pkgと言うpackageに移動します。その際に、ついでにsig, x, y, zの信号もsig_if, xyz_ifと言うinterfaceをそれぞれ新たに作ってそこに移動します。これまでparam_a/b/cはtest_lib_pkgにいたけれど、これらもせっかくなのでdut_ifに移動します。これでDUTの信号はに全て各種類毎にinterface経由にまとめられてUVMの教科書的になりました。
UVMでは機能コンポーネント毎にpackageに分ける事により、それぞれの独立性を高めて再利用化をさらに促進しています。
また、ここではmy_testで `uvm_component_utilsの代わりに `uvm_component_utils_begin/endを使ってみました。その中で `uvm_field_intを設定した事により、UVMの規定の形式でメンバー変数の値を表示するsprint()が使えるようになりました。param_a/b/cの値を表示するのにsprint()を使って表示するようにしてみました。
それから、今までtest classから、my_driverのdrive_sig()に信号値を引数として与えてコールして信号をドライブしていましたが、それは止めて、新たに定義したmy_itemと言うクラスをインスタンス化して、そのメンバ変数に値を設定し、my_driverに新たに作ったstart_item()と言うタスクの引数として与えてコールするようにしました。UVMではmy_itemの様に変数の塊を持つクラスを定義してそれをコンポーネント間でやりとりします。my_itemの様な変数の集まりを有したクラスはトランザクションとかシーケンスアイテムとか呼ばれます。そしてそれをコンポーネント間でやり取りする構成をTLM(Transaction Level Modeling)と言います。トランザクションレベルは信号の塊を表現しているので、個々の信号レベルより抽象度が一段高いと言えます。UVMではこの様にコンポーネント間通信はTLMを基本としています。抽象度が高いという事は様々な環境に適応でき、それだけ再利用もしやすいという事です。my_itemも `uvm_object_utilsの代わりに `uvm_object_utils_begin/endを使用し、print()でその内容を表示するようにしました。start_item()がコールされる度に表示されます。
更に、新たにfull_random_testと言うテストを追加してみました。このテストではmy_item内に設定する変数を固定値ではなく、毎回ランダム値を設定してmy_driverに送るようにしました。

test_bench.sv
`include "uvm_macros.svh"
//`include "uvm_pkg.sv"
module test_bench;
  import uvm_pkg::*;
  import test_lib_pkg::*;
  bit           clk;

  initial forever #(100/2) clk = !clk;

  dut_if i_dut_if(.clk);
  sig_if i_sig_if(.clk, .rst_n(i_dut_if.rst_n) );   // <========
  xyz_if i_xyz_if(.clk, .rst_n(i_dut_if.rst_n) );   // <========

  initial begin
    test_lib_pkg ::vif = i_dut_if;
    sig_agent_pkg::vif = i_sig_if;                 // <========
    xyz_agent_pkg::vif = i_xyz_if;                 // <========
    uvm_pkg::run_test(); //"my_test");
  end

  dut i_dut (.clk, .rst_n(i_dut_if.rst_n),
     .param_a(i_dut_if.param_a), .param_b(i_dut_if.param_b), .param_c(i_dut_if.param_c),    // <========
     .sig(i_sig_if.sig),                                                                    // <========
     .x(i_xyz_if.x) , .y(i_xyz_if.y), .z(i_xyz_if.z));                                      // <========

endmodule
dut_if.sv
`include "uvm_macros.svh"
interface dut_if(input clk);
    import uvm_pkg::*;

    bit       param_a, param_b, param_c;    // <=======

    bit rst_n;

    task reset_release();
        repeat(10) @(posedge clk);
        #(100/2)    rst_n = 1;
        `uvm_info("reset_release()", "Reset Is Released!!!", UVM_MEDIUM)
    endtask

endinterface
test_lib_pkg.sv
`include "uvm_macros.svh"
package test_lib_pkg;
  import uvm_pkg::*;

//bit       param_a, param_b, param_c;                          // =======> to dut_if
//bit [0:2] sig;                        // Input Signals        // =======> to sig_if
//logic     x, y, z;                    // Output               // =======> to xyz_if

  virtual dut_if vif;

  class my_test extends uvm_test;
  //`uvm_component_utils(my_test)
    `uvm_component_utils_begin(my_test)                         // <=======
        `uvm_field_int(param_abc, UVM_PRINT | UVM_BIN)          // <=======
    `uvm_component_utils_end                                    // <=======

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    rand bit[2:0] param_abc = 3'b110;

    virtual function void set_params();
        {vif.param_a, vif.param_b, vif.param_c} = param_abc;                // <=======
    endfunction

    sig_agent_pkg::my_driver  m_drv;                                        // <========
    xyz_agent_pkg::my_monitor m_mon;                                        // <========
    virtual function void build_phase(uvm_phase phase);                     // <========
        `uvm_info( get_type_name(), "############ Hello! This is an UVM message. ################", UVM_MEDIUM)
        m_drv = sig_agent_pkg::my_driver ::type_id::create("m_drv", this);  // <========
        m_mon = xyz_agent_pkg::my_monitor::type_id::create("m_mon", this);  // <========
    endfunction

    virtual function void start_of_simulation_phase(uvm_phase phase);
        `uvm_info(get_type_name(), "Start of Test !!!!", UVM_MEDIUM)
        set_params();
      //`uvm_info(get_type_name(), $sformatf("param_a = %b, param_b = %b, param_c =%b", param_a, param_b, param_c), UVM_MEDIUM)
        `uvm_info(get_type_name(), {"\n",this.sprint()}, UVM_MEDIUM)        // <===========
    endfunction

    virtual task run_phase(uvm_phase phase);
        phase.raise_objection(this);
        vif.reset_release();
        this.test_sequence_start();
        phase.drop_objection(this);
    endtask

    virtual function void final_phase(uvm_phase phase);
        `uvm_info( get_type_name(), "############ Bye! This is the end of an UVM test. ################", UVM_MEDIUM)
    endfunction

    my_item m_item;
    virtual task test_sequence_start();
        `uvm_info(get_type_name(), "Start sending items!!!", UVM_MEDIUM)
        m_item = my_item::type_id::create("m_item");   // <===========
        m_item.sig ='b1_1_1; m_drv.start_item(m_item); // <===========
        m_item.sig ='b0_1_1; m_drv.start_item(m_item); // <===========
        m_item.sig ='b0_0_1; m_drv.start_item(m_item); // <===========
        m_item.sig ='b0_0_0; m_drv.start_item(m_item); // <===========
    endtask

endclass

class random_test extends my_test;
    `uvm_component_utils(random_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual function void set_params();
        this.randomize();
        super.set_params();
    endfunction

  endclass

  class full_random_test extends random_test;            // <========== Added
    `uvm_component_utils(full_random_test)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual task test_sequence_start();
        `uvm_info(get_type_name(), "Start sending random items!!!", UVM_MEDIUM)
        m_item = my_item::type_id::create("m_item");
        repeat(10) begin                                  // <===========
          if (!m_item.randomize())                        // <===========
            `uvm_fatal(get_name(), "Randomize Failed!!!") // <===========
          m_drv.start_item(m_item);                       // <===========
        end                                               // <===========
    endtask

  endclass

endpackage
sig_if.sv
interface sig_if(input clk, rst_n);
    bit [0:2]   sig;
endinterface
sig_agent_pkg.sv
`include "uvm_macros.svh"
package sig_agent_pkg;
  import uvm_pkg::*;

  virtual sig_if vif;   //<==== Virtual Interface

  class my_item extends uvm_sequence_item;        // <======= Added
  //`uvm_object_utils(my_item)
    `uvm_object_utils_begin(my_item)
        `uvm_field_int(sig, UVM_PRINT | UVM_BIN)
    `uvm_object_utils_end
  
    function new(string name = "");
      super.new(name);
    endfunction

    rand bit[0:2] sig;

  endclass


  class my_driver extends uvm_driver;
    `uvm_component_utils(my_driver)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual task start_item(my_item tr);  // <======= Added
      tr.print();                         // <======= Added
      this.drive_sig(tr.sig);             // <======= Added
    endtask                               // <======= Added

    virtual task drive_sig(bit[0:2] val);
        @(negedge vif.clk) vif.sig = val;
    endtask

  endclass

endpackage
xyz_if.sv
interface sig_if(input clk, rst_n);
    bit [0:2]   sig;
endinterface
xyz_agent_pkg.sv
`include "uvm_macros.svh"
package xyz_agent_pkg;
  import uvm_pkg::*;

  virtual xyz_if vif;   //<==== Virtual Interface

  class my_monitor extends uvm_monitor;
    `uvm_component_utils(my_monitor)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    int i;
    bit[2:0] exp_xyz[100];
    virtual task run_phase(uvm_phase phase);
        forever @(posedge vif.clk) begin
          if ({vif.x,vif.y,vif.z} !== exp_xyz[i])
            `uvm_error(get_type_name(), $sformatf("ERROR !!! xyz = %b%b%b, expected %3b",vif.x,vif.y,vif.z, exp_xyz[i]))
          else
            `uvm_info (get_type_name(), $sformatf("OK        xyz = %b%b%b, expected %3b",vif.x,vif.y,vif.z, exp_xyz[i]), UVM_MEDIUM)
          i++;
        end
    endtask

  endclass

endpackage

ログに以下の様にテーブル形式でmy_test内のコンポーネントと設定した変数param_abcが表示されるようにりました。表示内容としては、コンポーネントクラスでは、それぞれのコンポーネントのインスタンス名、タイプ名(クラス名)やインスタンス化した際のシミュレータ内での識別値の情報が表示されています。変数の場合には変数名、タイプ名、ビット幅、値の情報が表示されています。
そして同様にトランザクションの内容もログに表示されるようになりました。

log
UVM_INFO <FILE PATH>/test_lib_pkg.sv(35) @ 0: uvm_test_top [my_test] Start of Test !!!!
UVM_INFO <FILE PATH>/test_lib_pkg.sv(38) @ 0: uvm_test_top [my_test]
------------------------------------------------------
Name               Type                    Size  Value
------------------------------------------------------
uvm_test_top       my_test                 -     @336
  m_drv            my_driver               -     @349
    rsp_port       uvm_analysis_port       -     @368
    seq_item_port  uvm_seq_item_pull_port  -     @358
  m_mon            my_monitor              -     @378
  param_abc        integral                3     'b110
------------------------------------------------------

UVM_INFO <FILE PATH>/TB10/test_lib_pkg.sv(43) @ 0: uvm_test_top [my_test] Hello! This is an UVM message.
UVM_INFO <FILE PATH>/TB10/scoreboard_pkg.sv(21) @ 50000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/TB10/scoreboard_pkg.sv(21) @ 150000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/TB10/scoreboard_pkg.sv(21) @ 250000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/TB10/scoreboard_pkg.sv(21) @ 350000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/TB10/scoreboard_pkg.sv(21) @ 450000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/TB10/scoreboard_pkg.sv(21) @ 550000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/TB10/scoreboard_pkg.sv(21) @ 650000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/TB10/scoreboard_pkg.sv(21) @ 750000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/TB10/scoreboard_pkg.sv(21) @ 850000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/TB10/scoreboard_pkg.sv(21) @ 950000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
UVM_INFO <FILE PATH>/TB10/dut_if.sv(12) @ 1000000: reporter [reset_release()] Reset Is Released!!!
UVM_INFO <FILE PATH>/TB10/agent_pkg.sv(15) @ 1000000: uvm_test_top [my_test] Start sending items!!!
UVM_INFO <FILE PATH>/TB10/xyz_agent/xyz_agent_pkg.sv(21) @ 1050000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
-----------------------------
Name    Type      Size  Value
-----------------------------
m_item  my_item   -     @440 
  sig   integral  3     'b111
-----------------------------
UVM_INFO <FILE PATH>/TB10/xyz_agent/xyz_agent_pkg.sv(21) @ 1150000: uvm_test_top.m_mon [my_monitor] OK        xyz = 000, expected 000
-----------------------------
Name    Type      Size  Value
-----------------------------
m_item  my_item   -     @440 
  sig   integral  3     'b11 
-----------------------------
UVM_INFO <FILE PATH>/TB10/xyz_agent/xyz_agent_pkg.sv(21) @ 1250000: uvm_test_top.m_mon [my_monitor] ERROR !!! xyz = 110, expected 000
-----------------------------
Name    Type      Size  Value
-----------------------------
m_item  my_item   -     @440 
  sig   integral  3     'b0  
-----------------------------
UVM_INFO <FILE PATH>/TB10/xyz_agent/xyz_agent_pkg.sv(21) @ 1350000: uvm_test_top.m_mon [my_monitor] ERROR !!! xyz = 010, expected 000
-----------------------------
Name    Type      Size  Value
-----------------------------
m_item  my_item   -     @440 
  sig   integral  3     'b0  
-----------------------------

■ おわりに

以上、既存のmoduleベースのテストベンチに少しずつUVMの特にTest class辺りからトップダウンで導入していく例を紹介してみました。巷の教科書ではフルのUVM環境の構築を前提に、Driver, Monitor, Sequener, Agent辺りからボトムアップで説明しているのが多いのではないでしょうか?使う側からしたら、ボトムアップで説明されるより、今回の様に、既存のテストベンチからトップダウンで徐々に導入して行く方が分かり易いのではと思います。
この記事ではDriver, Monitorもどきを作ってパッケージ化しTLMもどき通信をするぐらいまでやりました。もう少し進めるのであればConfig DBを使ってVirtual Interfaceを接続したり、Sequencer/Sequenceの概念を取り入れたり、ちゃんとしたTLMでコンポーネント間通信をするAgentとしてまとめたり、期待値比較を行う部分はScorebaordにしたり、Factory Overrideを取り入れたり、とまだまだいろいろありますが、お気軽導入なのでこれくらいにしておきます。簡単なテストベンチではここで紹介した位の導入で十分な場合が殆どなのではないか思います。
UVMは元々は各EDAベンダーが各社のVerification IPをより多くの顧客に売りたいが為に結託して、統一的手法としてまとめられた物です。Verification IPはAgentとして提供されますが、その統一的な使用方法としてTLM(Transaction Level Modeling)でのコンポーネント間通信やSequence開始でのテストの実行等が定められています。画一化された手法によるテストベンチの一貫性、再利用性、利便性と言った点ではユーザー側にもメリットがあります。この点においてはベンダー側とユーザー側とで利害が一致しています。なので、ユーザーとしてはその利点のみありがたく享受させてもらえば良いのです。逆にユーザーとして利点が無いのであれば、敢えて無理してUVMで規定しているやり方に固執する必要はないと言う事です。したがって、社内IPとして他の製品で再利用したりする予定でも無い限り、わざわざ苦労してまでAgentなどを作るメリットもないと言う事です。他の製品で再利用予定があるのであれば一貫性と言う観点からきちんとUVMで規定している方法に従った方が良いです。


■ サンプルテストベンチ

ここで紹介した例の実行可能な実際のソースを以下GitHubで公開しておきます。

以下ダウンロードと各TBの実行方法

Easy_UVM_Examples
% git clone https://github.com/AlphaLyrae0/Easy_UVM_Examples.git
% cd Easy_UVM_Examples
% cd [TB dir]
% make

MakefileはフリーのシミュレータのVivado Simulator(xsim)を想定しています。各自実行する場合には自身の使用するシミュレータのパスやコマンドに編集してください。

UVMをフリーのシミュレーターで実行できるようになるなんて良い時代になりました。


■ 参考記事

  • Vivado Simulator(xsim)でUVMやってみた

「UVMって何?」という状態からつまみ食いしつつ、正味一週間も弄ってないレベルで「同じ段階の方の手助けになればよいな」という趣旨で書かれた記事。なのでこれからUVMを導入してみようかと思っている人は是非一読する事をお勧めします。


  • UVMことはじめ

UVMを使ってとりあえず定番の"Hello, Word."を表示させてみる例を載せている。DUTは最初は接続していない。その後続編でDUTを入れて、やはり同じように徐々にUVMのコンポーネントを入れて行くが、トップダウンではなく、逆にDriver辺りからやはりボトムアップで構築して行っている。


  • UVMチュートリアル(送信編)

単純なカウンターをUVMを使って検証してみる例。簡単なDUTに対してフルでのUVM検証環境を構築している為、やはり冗長性を感じている様。正にこういう単純なDUTの場合にはトップダウンで少しだけUVMを導入するのが良いであろう。


  • 初心者の為のUVM概説

アートグラフィックス社の初心者向けのUVMの概説資料。巷の他教科書と同様にトランザクション辺りからボトムアップ的に説明しているので取っ付きにくい部分もあるが、UVMの概略を知るには良い資料。
尚、シーケンスのスタート方法として、シーケンサーにdefault_sequenceを指定する方法を紹介しているが、これは現在のUVMでは推奨されていない。この資料に限らずアートグラフィックスの資料ではdefault_sequenceの設定でのシーケンススタートに固執している様なので要注意。テストシーケンスのスタート方法については『UVM Sequence Start』も是非参照してみて下さい。


  • UVMの環境構築!(1) 解説編

UVMの概要を掴んでいてブロック図も示していてイメージしやすく分かり易い記事。但しやはり他の教科書的記事と同様ボトムアップでの順番になっているので(1)解説編の後は(11) Topモジュール (テストベンチ)から(2) TransactionとInterfaceへと逆順に見ていくと良いかも。あと、この記事でも現在では推奨されていないdefault_sequenceを使っているので注意が必要。おそらく筆者はアートグラフィックス社の資料を参考にしているのではないかと思う。UVMの正式なDocumentではAgentの中にはCollectorと言うコンポーネントは無いのだが、それを含めている辺りでもその事がうかがえる。
近年はこのような分かり易い日本語のUVMの記事を書いてくれる人も大分増えてきて大変ありがたい事です。隔世の感があります。


  • Siemens EDA Forum - UVM導入はじめの一歩

Siemens EDA(旧Mentor Graphycs)の無料ウェビナー。UVMの導入を考えている人向けに既存のmoduleベースのテストベンチをUVM環境に移行する例を使って解説している。今までMentorはこのような切り口で解説したりしてこなかったので、本ページに感化されてこのような説明をしだしたのではないかと疑っている。

13
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
8