■ はじめに
UVMに興味があって使ってはみたいけど、いきなりガッツリ導入するには腰が引けると言う人向けの記事です。
UVMを使ってみたいと思った人の中には、巷にある教科書を見て、その膨大な内容にやる気が失せた人も多いのではないでしょうか。しかし安心してください。何もいきなり全ての機能を使う必要はありません。UVMには柔軟性があり、その極一部の機能から少しずつ使い始める事ができます。この記事では既存のモジュールベースのテストベンチに段階的にUVMを導入して行く例を紹介するので是非参考にしてみてください。簡単なテストの場合にはここで紹介した程度の適用で十分実際の業務に役立てる事ができると思います。
■ 0. 既存のmoduleベースのテストベンチ
まずはUVMじゃない通常のテストベンチはこんな感じだと思います。
TB0 : moduleベース
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
これを実行するとこんな感じにログが出ます。
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()
に変えます。
`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
以下の様なログになると思います。
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")
でコールして実行します。
`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が実行できるようになりました。以下ログの例
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);
を追加します。
`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)
this.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への値設定を待っています。また、最後の方でで新たに挿入したメッセージも表示されています。
そして今回、シミュレーション終了前に、メッセージのサマリー表示がされるようになりました。
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 Classからのテストの実行
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のイベント待ちは要らなくなります。
`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)
this.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にランダムな値を設定します。
`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)
this.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]に変わっています。。
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. チェッカーもUVM 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内でコールしてバックグラウンドで開始します。
`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)
this.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()からコールします。
`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)
this.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
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に値を設定しています。
`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
module bfm( input clk, output logic [0:2] sig);
task drive_sig(bit[0:2] val);
@(negedge clk) sig = val;
endtask
endmodule
interface test_if();
task reset_release();
test_bench.reset_release();
endtask
task check_result();
test_bench.check_result();
endtask
endinterface
interface bfm_if();
task drive_sig(bit[0:2] val);
i_bfm.drive_sig(val);
endtask
endinterface
`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)
this.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へのアクセスに関しては以下を参照。
これではvirtual classの使用でのやり方紹介しているが、virtual classを使わずに今回の様にinterfaceをbindする事でvirtual interfaceを使う事も可能。
interfaceのbindに関しては以下も参照。
■ 8. DriveおよびCheckを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も大分寂しくなりました。
`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
`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
`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)
this.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()から明示的に実行されています。
`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
`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
`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)
this.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]に変わっています。
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に送るようにしました。
`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
`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
`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)
this.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
interface sig_if(input clk, rst_n);
bit [0:2] sig;
endinterface
`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
interface xyz_if(input clk, rst_n);
logic x, y, z;
endinterface
`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が表示されるようにりました。表示内容としては、コンポーネントクラスでは、それぞれのコンポーネントのインスタンス名、タイプ名(クラス名)やインスタンス化した際のシミュレータ内での識別値の情報が表示されています。変数の場合には変数名、タイプ名、ビット幅、値の情報が表示されています。
そして同様にトランザクションの内容もログに表示されるようになりました。
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導入なので、これくらいにしておきます。これくらいでも簡単なテストベンチなら十分実用に値するのではないか思います。特に既存のテストベンチにUVM Test Classのみを適用すると言うのはおススメです。
UVMは元々は各EDAベンダーが各社のVerification IPをより多くの顧客に売りたいが為に結託して、統一的手法としてまとめられた物です。Verification IPはAgentとして提供されますが、その統一的な使用方法としてTLM(Transaction Level Modeling)でのコンポーネント間通信やSequence開始でのテストの実行等が定められています。画一化された手法によるテストベンチの一貫性、再利用性、利便性と言った点ではユーザー側にもメリットがあります。この点においてはベンダー側とユーザー側とで利害が一致しています。なので、ユーザーとしてはその利点のみありがたく享受させてもらえば良いのです。逆にユーザーとして利点が無いのであれば、敢えて無理してUVMで規定しているやり方に固執する必要はないと言う事です。したがって、社内IPとして他の製品で再利用したりする予定でも無い限り、わざわざ苦労してまでAgentなどを作るメリットもないと言う事です。他の製品で再利用予定があるのであれば一貫性と言う観点からきちんとUVMで規定している方法に従った方が良いです。
■ サンプルテストベンチ
ここで紹介した例の実行可能な実際のソースを以下GitHubで公開しておきます。
以下ダウンロードと各TBの実行方法
% git clone https://github.com/AlphaLyrae0/Easy_UVM_Examples.git
% cd Easy_UVM_Examples
% cd [TB dir]
% make
MakefileはフリーのシミュレータのMetrics DSim DesktopおよびAMD 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はこのような切り口で解説したりしてこなかったので、本ページに感化されてこのような説明をしだしたのではないかと疑っている。