[UVM] Factory overrideの方法を整理する


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()で取得することができます。この部分は次節でもう少し説明します。

```verilog
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のほうは以下のように取得するときにパラメータも指定するので、このようなあいまいさが発生しません。

```verilog
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()しないで使用できます。以下のような感じで使用します。

```verilog
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()と全く同じ動作になります。

```verilog
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からは使用できません。

```verilog
set_type_override_by_type(original_class::get_type(), override_class::get_type());
```

このメソッドはUVMでは以下のように定義されていて、後で説明するfactory.set_type_override_by_type()を呼んでいるだけです。

```verilog
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()が呼ばれます。

```verilog
original_class::type_id::set_type_override(override_class::get_type());
```

このメソッドはUVM-1.1dでは以下のように定義されていて、同様に後で説明するfactory.set_type_override_by_type()を呼んでいるだけです。

```verilog
  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のメソッドを使用する方法です。

```verilog
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からは使用できません。

```verilog
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()を呼んでいるだけです。

```verilog
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番目の引数の起点となるオブジェクトへのハンドルを指定します。

```verilog

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()を呼んでいるだけです。

```verilog
  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のメソッドを使用する方法です。

```verilog
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からは使用できません。

```verilog
set_type_override("original_class_name", "override_class_name");
```

二つ目はuvm_factoryのメソッドを使用する方法です。

```verilog
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からは使用できません。


```verilog
set_inst_override("agent0.*", // Relative path from current path
                  "original_type_name",
                  "override_type_name");
```


二つ目はuvm_factoryのメソッドを使用する方法です。


```verilog
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.