Factory overrideには似たようなメソッドがたくさんあって、それぞれどのように違うのか、どう使い分ければいいのかわかりづらいです。そこで全種類を網羅し、どのような違いがあるのかをまとめてみることにしました。
本解説で引用するソースコードのUVMのバージョンは1.2ではなく、1.1dになります。これは1.1dのほうがソースが単純で説明しやすいからです。本解説で扱う範囲では本質的には1.2も1.1dも同じ構造になっていますので、同様に取り扱い可能です。
前知識
最初にFactory overrideの周辺部分の説明です。
typeとtype_name
Factory overrideでのオリジナルクラスとオーバライドクラスの指定は、type_nameかtypeいずれかで行います。type_nameの型はstringでtypeの型はuvm_object_wrapperになります。uvm_object_wrapperが何者かを正確に説明するのはとても難しいので、ここでは簡単に、factory overrideを簡単にするためにUVMで定義されたクラスと考えて下さい。
各クラスのtype, type_nameは以下のマクロで定義されます。引数typeを文字列(string)に変換したものがそのクラスのtype_nameになります。
- `uvm_object_utils(type)
- `uvm_object_utils_begin(type)
- `uvm_object_param_utils(type)
- `uvm_object_param_utils_begin(type)
- `uvm_component_utils(type)
- `uvm_component_utils_begin(type)
- `uvm_component_param_utils(type)
- `uvm_component_param_utils_begin(type)
以下のコードの場合、type_nameは"my_env_c"という文字列になります。またこのクラスに対応するuvm_object_wrapperが定義され、そのオブジェクトはmy_env_c::get_type()またはmy_env_c::type_id::get()で取得することができます。この部分は次節でもう少し説明します。
class my_env_c extends uvm_env;
`uvm_component_utils(my_env_c)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
endclass
`uvm_object_param_utils等のパラメタライズクラス用のマクロでは定義されるのはtypeだけでtype_nameが定義されません。パラメータが異なる同一クラス名のクラスはSystemVerilogでは異なるクラスとして扱うので、それらのtype_nameが一緒になってしまうと都合が悪いからです (type_nameにparameterをstringに変換したものを付け加えられれば良いのですが、マクロで実現するのは不可能でしょう)。typeのほうは以下のように取得するときにパラメータも指定するので、このようなあいまいさが発生しません。
uvm_object_wrapper ctype;
ctype = my_param_env_c#(param)::get_type();
uvm_driverの派生クラスはuvm_sequence_itemのサブクラスをパラメータとしたparameterized classですので、`uvm_component_param_utils()を使わなければならないはずですが、そのような例はとてもまれでほとんどが`uvm_component_utils()を使用しています。通常uvm_driverは各クラス一種類のパラメータだけ受け取るように設計されるため、複数種類のパラメータへの対応を気にする必要がないためです。
get_type()とtype_id::get()
あるクラスのtypeを取得するのに、2種類の方法が使われます。一つはClass::get_type()で、もう一つはClass::type_id::get()です。両方ともスタティックメソッドなので、new()/create()しないで使用できます。以下のような感じで使用します。
set_type_override_by_type(Original_class::get_type(), Override_class::get_type());
set_type_override_by_type(Original_class::type_id::get(), Override_class::type_id::get());
両者に違いはなく、同じ値を返します。
class::get_type()は以下のように定義されているので、class::type_id::get()と全く同じ動作になります。
static function type_id get_type();
return type_id::get();
endfunction
本記事ではこれ以降class::get_type()を使う例に統一します。
Type_override_by_type
特定のtypeのクラスを全て新しいtypeで置き換える方法です。3つの書式があります。
一つ目はuvm_componentで定義されているメソッドを使用する方法です。従ってuvm_objectからは使用できません。
set_type_override_by_type(original_class::get_type(), override_class::get_type());
このメソッドはUVMでは以下のように定義されていて、後で説明するfactory.set_type_override_by_type()を呼んでいるだけです。
function void uvm_component::set_type_override_by_type (uvm_object_wrapper original_type,
uvm_object_wrapper override_type,
bit replace=1);
factory.set_type_override_by_type(original_type, override_type, replace);
endfunction
次が対象クラスのメソッドを呼び出してオーバーライドする方法です。uvm_component_registery #(T, Tname)::set_type_override()が呼ばれます。
original_class::type_id::set_type_override(override_class::get_type());
このメソッドはUVM-1.1dでは以下のように定義されていて、同様に後で説明するfactory.set_type_override_by_type()を呼んでいるだけです。
static function void set_type_override (uvm_object_wrapper override_type,
bit replace=1);
factory.set_type_override_by_type(get(),override_type,replace);
endfunction
最後にuvm_factoryのメソッドを使用する方法です。
factory.set_type_override_by_type(original_class::get_type(),
override_class::get_type());
要するに、どの方法を使っても最後にはfactory.set_type_override_by_type()が呼び出されるわけです。
Inst_override_by_type
指定されたクラスのうち、特定のインスタンスのクラスを新しいtypeで置き換える方法です。インスタンスには正規表現が適用され、複数のインスタンスを一度にオーバーライドすることも可能です。3つの書式があります。
一つ目はuvm_componentで定義されているメソッドを使用する方法です。従ってuvm_objectからは使用できません。
set_inst_override_by_type("agent0.*, // Relative path from current path
original_class::get_type(),
override_class::get_type());
このメソッドはUVMでは以下のように定義されていて、パス名を絶対パスに変換して後で説明するfactory.set_inst_override_by_type()を呼んでいるだけです。
function void uvm_component::set_inst_override_by_type (string relative_inst_path,
uvm_object_wrapper original_type,
uvm_object_wrapper override_type);
string full_inst_path;
if (relative_inst_path == "")
full_inst_path = get_full_name();
else
full_inst_path = {get_full_name(), ".", relative_inst_path};
factory.set_inst_override_by_type(original_type, override_type, full_inst_path);
endfunction
次が対象クラスのメソッドを呼び出してオーバーライドする方法です。3番目の引数は2番目の引数の起点となるオブジェクトへのハンドルを指定します。
original_class::type_id::set_inst_override(override_class::get_type(),
"agent0.*", // Relative path from parent
parent); // Class handle to base path
このメソッドはUVMでは以下のように定義されていて、後で説明するfactory.set_inst_override_by_type()を呼んでいるだけです。
static function void set_inst_override(uvm_object_wrapper override_type,
string inst_path,
uvm_component parent=null);
string full_inst_path;
if (parent != null) begin
if (inst_path == "")
inst_path = parent.get_full_name();
else
inst_path = {parent.get_full_name(),".",inst_path};
end
factory.set_inst_override_by_type(get(),override_type,inst_path);
endfunction
最後にuvm_factoryのメソッドを使用する方法です。
factory.set_inst_override_by_type(original_class::get_type(),
override_class::get_type(),
"uvm_test_top.agent0.*"); // Full path
Type_override_by_name
overrideするクラスの指定をtypeではなくtype_nameで行う方法です。Deprecatedではないですが推奨されていません。クラスに対して`uvm_*_param_utilsを使った場合はtype_nameが設定されないため、この方法が使用できないためです。
一つ目はuvm_componentで定義されているメソッドを使用する方法です。従ってuvm_objectからは使用できません。
set_type_override("original_class_name", "override_class_name");
二つ目はuvm_factoryのメソッドを使用する方法です。
factory.set_type_override_by_name("original_class_name", "override_class_name");
Inst_override_by_name
Type_override_by_nameと同じですが、オーバーライドの範囲を引数で限定したUVM_componentの階層に絞ります。同様にDeprecatedではないですが推奨されていません。
一つ目はuvm_componentで定義されているメソッドを使用する方法です。従ってuvm_objectからは使用できません。
set_inst_override("agent0.*", // Relative path from current path
"original_type_name",
"override_type_name");
二つ目はuvm_factoryのメソッドを使用する方法です。
factory.set_inst_override_by_name("uvm_test_top.agent0.*", // Full path
"original_type_name",
"override_type_name");
参考文献
Accellera, "8.2 UVM factory", [Universal Verification Methodology (UVM) 1.2 Class Reference]
(https://www.accellera.org/images/downloads/standards/uvm/UVM_Class_Reference_Manual_1.2.pdf), pp.110-124.