UVMでuvm_sequenceを開始させるには以下の2通りの方法があります。
- uvm_config_dbのdefault_sequeneを使う
- uvm_seuqnceのstart()メソッドを使う
本稿では前者について説明します。
ここで紹介するのはuvm_config_dbを介して実行するuvm_sequenceを選択する方法です。なお本方法はUVM 1.2以降でDeprecated(非推奨)とされています。本解説の内容は旧コードの解析やメンテナンスの為とし、新規作成のコードでは使用しないことをお勧めします。
ちなみにUVMではDeprecatedな機能を使用すると通常はwarningが出力されるのですが、本機能についてはwarningを出すコードに問題があるようで出力されません。
使い方
uvm_componentのbuild_phase()内でuvm_sequenceの登録を行います。一つのuvm_sequencerに登録できる最大uvm_sequenceの数は一つです。typeを登録する方法と、対象のuvm_sequenceを生成してそのオブジェクト自体を登録する方法の2種類があります。
typeを登録
以下の書式になります。
uvm_config_db#(uvm_object_wrapper)::set(this,
"Hierarchal_path_name_to_the_uvm_sequencer",
"default_sequence",
sequence_name::type_id::get());
uvm_config_dbに渡すパラメータタイプはuvm_object_wrapperになります。これはuvm_config_dbにtypeを登録する場合の型です。
第一引数は通常はthisです。
第二引数は対象となるシーケンサのUVMの階層名+フェーズ名になります。フェーズ名は使用するphaseメカニズムの選択により"main_phase"か"run_phase"のどちらかです。
第三引数は"default_sequence"です。
第四引数は登録したいシーケンスのタイプです。動作をさせたくない(シーケンスを登録したくない)場合はnullを指定します。
uvm_config_db#(uvm_object_wrapper)はuvm_config_db.svh内で下記のようにtypedefされていますので、
typedef uvm_config_db#(uvm_object_wrapper) uvm_config_wrapper;
替わりにuvm_config_wrapperを使うこともできます。
uvm_config_wrapper::set(this,
...
オブジェクトを登録
以下の書式になります。
my_sequence = my_sequence_c::type_id::create("my_sequence_c");
uvm_config_db#(uvm_sequence_base)::set(this,
"Hierarchal_path_name_to_the_uvm_sequencer",
"default_sequence",
my_sequence);
uvm_config_dbに渡すパラメータタイプはuvm_sequence_baseです。
第一引数は通常はthisです。
第二引数は対象となるシーケンサのUVMの階層名+フェーズ名になります。フェーズ名は使用するphaseメカニズムの選択により"main_phase"か"run_phase"のどちらかです。
第三引数は"default_sequence"です。
第四引数は登録したいシーケンスのオブジェクトです。動作をさせたくない(シーケンスを登録したくない)場合はnullを指定します。
注意点
本方式でシーケンスを実行する場合、そのシーケンスのpre_body()でオブジェクションを上げ、post_body()でオブジェクションを下げる必要があります。これをしないと、シミュレーションが時間0で終了してしまい、目的のシーケンスが最後まで実行されなくなってしまいます。
この部分はUVM 1.1とUVM 1.2では少し書き方が異なります。具体的な例についてはこの後説明します。
プログラム例
サンプルプログラムです。EDA Playgroundに登録してありますので、ここで実行することができます。
import uvm_pkg::*;
`include "uvm_macros.svh"
class my_sequence_item_c extends uvm_sequence_item;
string str;
`uvm_object_utils(my_sequence_item_c)
function new(string name = "my_sequence_item_c");
super.new(name);
endfunction
endclass
class my_sequence_c extends uvm_sequence #(my_sequence_item_c);
`uvm_object_utils(my_sequence_c)
function new(string name = "my_sequence_c");
super.new(name);
endfunction
task pre_body();
`ifdef UVM_POST_VERSION_1_1
uvm_phase starting_phase = get_starting_phase();
`endif
if (starting_phase != null) begin
starting_phase.raise_objection(this, get_type_name());
end
endtask
task post_body();
`ifdef UVM_POST_VERSION_1_1
uvm_phase starting_phase = get_starting_phase();
`endif
if (starting_phase != null) begin
starting_phase.drop_objection(this, get_type_name());
end
endtask
virtual task body();
req = my_sequence_item_c::type_id::create("req");
start_item(req);
req.str = get_type_name();
finish_item(req);
start_item(req);
req.str = get_full_name();
finish_item(req);
endtask
endclass
class my_driver_c extends uvm_driver #(my_sequence_item_c);
`uvm_component_utils(my_driver_c)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
forever begin
seq_item_port.get_next_item(req);
#10ns;
`uvm_info(get_type_name(), $sformatf("Passed string = %s", req.str), UVM_NONE);
seq_item_port.item_done();
end
endtask
endclass
class my_agent_c extends uvm_component;
my_driver_c my_driver;
uvm_sequencer #(my_sequence_item_c) my_sequencer;
`uvm_component_utils(my_agent_c)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
my_sequencer = uvm_sequencer#(my_sequence_item_c)::type_id::create("my_sequencer", this);
my_driver = my_driver_c::type_id::create("my_driver", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
my_driver.seq_item_port.connect(my_sequencer.seq_item_export);
endfunction
endclass
class my_env_c extends uvm_env;
my_agent_c my_agent;
`uvm_component_utils(my_env_c)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
my_agent = my_agent_c::type_id::create("my_agent", this);
endfunction
endclass
class my_test_c extends uvm_test;
`uvm_component_utils(my_test_c)
my_env_c my_env;
my_sequence_c my_sequence;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
my_env = my_env_c::type_id::create("my_env", this);
//// Set sequence by configuration mechanism - Type_id version
//// Uncomment lines below to make it effective.
////
// uvm_config_db#(uvm_object_wrapper)::set(this,
// "my_env.my_agent.my_sequencer.run_phase",
// "default_sequence",
// my_sequence_c::type_id::get());
//// Set sequence by configuration mechanism - Object handle version
//// Uncomment lines below to make it effective.
////
my_sequence = my_sequence_c::type_id::create("my_sequence");
uvm_config_db#(uvm_sequence_base)::set(this,
"my_env.my_agent.my_sequencer.run_phase",
"default_sequence",
my_sequence);
endfunction
endclass
module top();
initial begin
run_test("my_test_c");
end
endmodule
実行結果です。my_sequence_cでuvm_sequenceの生成したuvm_sequence_itemがmy_driver_cに渡り、その文字列が表示されていることがわかります。
# KERNEL: UVM_INFO @ 0: reporter [RNTST] Running test my_test_c...
# KERNEL: UVM_INFO /home/runner/testbench.sv(67) @ 10: uvm_test_top.my_env.my_agent.my_driver [my_driver_c] Passed string = my_sequence_c
# KERNEL: UVM_INFO /home/runner/testbench.sv(67) @ 20: uvm_test_top.my_env.my_agent.my_driver [my_driver_c] Passed string = uvm_test_top.my_env.my_agent.my_sequencer.my_sequence
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 20: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_report_server.svh(856) @ 20: reporter [UVM/REPORT/SERVER]
# KERNEL: --- UVM Report Summary ---
# KERNEL:
# KERNEL: ** Report counts by severity
# KERNEL: UVM_INFO : 5
# KERNEL: UVM_WARNING : 0
# KERNEL: UVM_ERROR : 0
# KERNEL: UVM_FATAL : 0
# KERNEL: ** Report counts by id
# KERNEL: [RNTST] 1
# KERNEL: [TEST_DONE] 1
# KERNEL: [UVM/RELNOTES] 1
# KERNEL: [my_driver_c] 2
# KERNEL:
# RUNTIME: Info: RUNTIME_0068 uvm_root.svh (521): $finish called.
# KERNEL: Time: 20 ns, Iteration: 61, Instance: /top, Process: @INITIAL#147_0@.
# KERNEL: stopped at time: 20 ns
# VSIM: Simulation has finished. There are no more test vectors to simulate.
exit
# VSIM: Simulation has finished.
以下各クラスの要点を説明します。
my_sequence_item_c
uvm_sequenceからuvm_sequencerを介してuvm_driverに渡されるデータを格納するクラスです。今回の例ではDUTはありませんので、簡単にstringをメンバとして持たせるのみにしました。
class my_sequence_item_c extends uvm_sequence_item;
`uvm_object_utils(my_sequence_item_c)
string str;
endclass
my_sequence_c
pre_body()ではオブジェクションを上げています。前述の通りこれをしないとシミュレーションが時間0で終了してしまいます。UVM 1.2の場合はstarting_phaseがuvm_sequenceのデータメンバーに含まれていないので、明示的に宣言しなければなりません。UVM 1.2以降とそれ以前のバージョンの切り分けはUVM_POST_VERSION_1_1で行えます。
task pre_body();
`ifdef UVM_POST_VERSION_1_1
uvm_phase starting_phase = get_starting_phase();
`endif
if (starting_phase != null) begin
starting_phase.raise_objection(this, get_type_name());
end
endtask
post_body()でオブジェクションを下げています。前述の通りこれをしないとシミュレーションが終了しません。UVM 1.2ではstarting_phaseを宣言する必要があるのはpre_body()と同様です。
task post_body();
`ifdef UVM_POST_VERSION_1_1
uvm_phase starting_phase = get_starting_phase();
`endif
if (starting_phase != null) begin
starting_phase.drop_objection(this, get_type_name());
end
endtask
body()がシーケンスの本体です。ここではシーケンスアイテムを2回渡しています。1回目はget_type_name()が返す文字列、2回目はget_full_name()が返す文字列です。
virtual task body();
req = my_sequence_item_c::type_id::create("req");
start_item(req);
req.str = get_type_name();
finish_item(req);
start_item(req);
req.str = get_full_name();
finish_item(req);
endtask
my_driver_c
uvm_sequenceからuvm_sequencerを介してuvm_sequence_itemを受け取ります。重要なのはrun_phaseタスクのみです。本例ではDUTを駆動せず、uvm_sequenceのデータメンバーをログに出力するのみです。オブジェクションの効果を見るためにデータを受け取ってから、時間を10ns進めています。
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
forever begin
seq_item_port.get_next_item(req);
#10ns;
`uvm_info(get_type_name(), $sformatf("Passed string = %s", req.str), UVM_NONE);
seq_item_port.item_done();
end
endtask
my_agent_c
my_driver_cとuvm_sequencerをインスタンス化して接続しています。
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
my_sequencer = uvm_sequencer#(my_sequence_item_c)::type_id::create("my_sequencer", this);
my_driver = my_driver_c::type_id::create("my_driver", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
my_driver.seq_item_port.connect(my_sequencer.seq_item_export);
endfunction
my_env_c
my_agenct_cをインスタンス化しています。特に解説するところはありません。
my_test_c
build_phaseでmy_env_cをインスタンス化しています。また、uvm_sequencerへのsequenceの登録を行っています。uvm_config_dbにオブジェクトを登録する方法を使用しています。typeを登録する方法はこの部分をコメントアウトして、uvm_confi_db#(uvm_object_wrapper)以降のコメントを外してください。
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
my_env = my_env_c::type_id::create("my_env", this);
//// Set sequence by configuration mechanism - Type_id version
//// Uncomment lines below to make it effective.
////
// uvm_config_db#(uvm_object_wrapper)::set(this,
// "my_env.my_agent.my_sequencer.run_phase",
// "default_sequence",
// my_sequence_c::type_id::get());
//// Set sequence by configuration mechanism - Object handle version
//// Uncomment lines below to make it effective.
////
my_sequence = my_sequence_c::type_id::create("my_sequence");
uvm_config_db#(uvm_sequence_base)::set(this,
"my_env.my_agent.my_sequencer.run_phase",
"default_sequence",
my_sequence);
endfunction
参考文献
"3.10.3 Starting a Sequence on a Sequencer". Universal Verification Methodology(UVM) 1.2 User's Guide. pp.49-50.