uvm_componentの階層構造について

uvm_componentはenvの下にagent, agentの下にdriver等の階層構造を持ちますが、どのように階層構造がつくられるか、この階層構造がどのような時に使用されるのか等をここでまとめます。
実際の検証環境ではuvm_component間の接続やDUTとの接続なども必要ですがここでは触れません。純粋にuvm_componentの階層のみに着目します。

# uvm_componentの名前に関する用語
最初にuvm_componentの名前に関わる用語をまとめます。似ている名前で混同しやすいですが、それぞれ異なるものなので注意が必要です。いずれもstring型になります。

<dl>
<dt>type_name</dt>
<dd>
各uvm_componentの中で宣言された`uvm_component_util< "name">の引数のnameの部分になります。この値はclass名と一致させるので、実質的にclass名と同じものだと考えて問題ありません。
そのクラスに対するget_type_name()で取得することができます。
</dd>

<dt>name</dt>
<dd>
そのuvm_componentの名前で、RTLのインスタンス名に相当するものになります。そのuvm_componentをcreateした時の第一引数の文字列になります。そのクラスに対するget_name()で取得することができます。
</dd>

<dt>full_name</dt>
<dd>
そのuvm_componentのフルパス名です。RTLのフルパスでのインスタンス名に相当します。最上位階層の名前は必ずuvm_test_topになります。そのクラスに対するget_full_name()で取得することができます。
</dd>
</dl>


# uvm_componentの階層構造の作り方
build_phaseで下位に生成したいuvm_componentのcreate()を呼びます。以下にサンプルを示します。my_env_cクラスでその下にmy_agent_cを生成するために、build_phaseでmy_agentをcreateしています。

```verilog
import uvm_pkg::*;
`include "uvm_macros.svh"

class my_agent_c extends uvm_agent;
  `uvm_component_utils(my_agent_c)
  
  function new(string name, uvm_component parent);
    super.new(name, parent);
  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
```

my_agenct_cは目的のcomponentのクラス名です。my_env_cの中で定義しているmy_agent_cへのオブジェクトハンドル名(本例ではmy_agent)は何でも構いません。type_idは常に必要な文で、意味を気にする必要はありません。
createの第一引数my_agentが作成するコンポーネントの名前になります。この名前は任意の文字列です。通常はクラス名に近い名前にしますが、仕様上はなんでも構いません。第二引数のthisはあまり深く考えず常に記述するものだと思って良いです(簡単に説明すると、下位階層を作る起点のオブジェクトハンドルを渡します。通常は自分のオブジェクトの下に階層を作るのでthisになります。(通常はやりませんが)他のオブジェクトハンドルをわたせば、そのオブジェクトの下に新たなコンポーネントが作成されます)。

# 階層名が使用される
階層名は一例として以下のような場面で使用されます。対象のオブジェクトが生成される前、従ってオブジェクトハンドルがまだない状況で使用することができます。これはオブジェクトハンドルをたどっていく方法では実現できません。
それぞれの詳しい使用方法についてはいずれあらためて解説したいと思います。

## インスタンスをオーバーライドしてcomponentを差し替える
uvm_testからその下のenv.agentにあるdriver_aをdriver_bに入れ替えるには、自分からの相対パスenv.agentを階層名に指定します

```verilog
set_inst_override_by_type("env.agent", driver_a::get_type(), driver_b::get_type());
```

## configで特定componentの値を設定する
uvm_testからその下のenv.agentのint型コンフィグプロパティーに1を設定するには以下のように記述します。上記と同様自分からの相対パスのenv.agentを指定します。

```verilog
set_config_int("env.agent", "num", 1);
```

または

```verilog
uvm_config_db#(uvm_bitstream_t)::set(this, "env.agent", "num", 1);
```

uvm_config_dbにint型を登録するときの型パラメータはintではなくuvm_bitstream_tを渡します。


# 階層名の指定方法
上記のようにcomponentのパス名を指定していますが、これはglobとして評価されます。ただし/expression/のように/で囲うとexpressionが正規表現と解釈されます。正規表現については[UVMでの正規表現](https://qiita.com/triggerfish/items/27fbfb406f14bbc570ba)もご覧ください。いずれにせよ一つの記述で複数のuvm_componentがマッチすることがあります。
注意する点は、階層の区切り文字"."もただの文字として扱われることです。UNIX系のシェルでは\*等のglobの解釈はディレクトリの階層毎に閉じており/で区切られますが、UVMの場合は全ての階層に対して適用されます。
UNIXではecho \*を実行すると、カレントディレクトリのファイルのみが表示されます。一つ下のディレクトリまで表示させる場合はecho \*/\*になります。
一方UVMの場合は\*は全階層のuvm_componentにマッチします。例えば階層が以下のようになっている場合

```plaintext
a b1 c1
     c2
  b2 c1
     c2
```

* \*は全ての階層にマッチします。すなわちa, a.b1, a.b1.c1, a.b1.c2, a.b2, a.b2.c1, a.b2.c2の7つになります

* a.\*はa. (aではなくてa.であることに注意)。で始まる全ての階層にマッチします。 a.b1, a.b1.c1, a.b1.c2, a.b2, a.b2.c1, a.b2.c2の6つになります

* \*c1はc1で終わる全ての階層にマッチします。a.b1.c1とa.b2.c1の2つになります

* a.b1\*c1はa.b1ではじまりc1で終わる全ての階層にマッチします。a.b1.c1の1つだけになります


# top階層について
top階層となるクラスのtype_nameはuvm_rootです。このクラスはUVMのbase classとしてデフォルトで定義されています。このクラスは自動的に生成され、ユーザが生成する必要はありません。詳細についてはUVM Class Reference ManualのBase Classesの下にあるuvm_rootを参照してください。

このuvm_rootのオブジェクトハンドルはuvm_topという名前になります。全てのuvm_componentは自動的にこのオブジェクトハンドルを持ちます。このため、全てのuvm_componentはuvm_top._method()_という形でuvm_topのメソッドを呼ぶことが可能です。メソッドのいくつかはこの後に紹介します。
このクラスの下にuvm_test_topという名前でuvm_testが生成されます。全てのuvm_componentはuvm_test_topから始まる階層名 (full_name)を持つことになります。

混乱しそうなので、重複しますが以下にもう一度まとめます。
<dl>
<dt>uvm_root</dt> <dd>uvm_componentの階層のルートとなるクラスの名前 (type_name)</dd>
<dt>uvm_top</dt> <dd>uvm_rootへのオブジェクトハンドル。すべてのuvm_componentはデフォルトでこのオブジェクトハンドルを持っておりuvm_top._method()_の形でメソッドを呼ぶことが可能</dd>
<dt>uvm_test_top</dt> <dd>uvm\_rootが生成したuvm\_testの階層名 (name)。トップ階層となる。</dd>
</dl>

## uvm_rootのメソッド
以下に階層構造に関係するuvm_rootのメソッドを説明します。

<dl>
<dt>print_topology()</dt>
<dd>
uvm_test_topの下にある全てのuvm_componentのリストを表示します。
</dd>
<dt>find_all(string comp_match, ref uvm_component comps[$], input uvm_component comp = null) </dt>
<dd>
comp_matchで指定された階層名にマッチするuvm_componentのオブジェクトハンドルの配列を返します。階層の起点はcompで渡されるuvm_comonentのオブジェクトハンドルとなります。nullの場合はフルパスに対して検索を行います。
</dd>
<dt>find(string comp_match)</dt>
<dd>
comp_matchで指定された階層名にマッチするuvm_componentのオブジェクトハンドルを返します。
*等のメタ文字も使えるのですが、複数のuvm_componentにマッチした場合の動作が定義されていません。
恐らく最初に見つけたuvm_componentのオブジェクトハンドルを返すと思います。
複数のオブジェクトにマッチする可能性がある場合にはメタ文字は避けた方が良いです。
</dd>
</dl>



# サンプルプログラム
サンプルプログラムを示します。uvm_componentの階層生成後、その階層構造を表示して終わります。uvm_component間の接続等は行わず生成するだけです。

```verilog
import uvm_pkg::*;
`include "uvm_macros.svh"

class my_driver_c extends uvm_driver;
  `uvm_component_utils(my_driver_c)

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


class my_agent_c extends uvm_component;
  my_driver_c my_driver[2];

  `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_driver[0] = my_driver_c::type_id::create("my_driver_0", this);
    my_driver[1] = my_driver_c::type_id::create("my_driver_1", this);
  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;

  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);
  endfunction

  virtual function void end_of_elaboration_phase(uvm_phase phase);
    super.end_of_elaboration_phase(phase);
    uvm_top.print_topology();
  endfunction

  virtual task run_phase(uvm_phase phase);
    string s[] = {"*", "*_0", "*driver_?.*", "/^u.+t$/"};

    foreach (s[i]) begin
      uvm_component components[$];
      `uvm_info(get_type_name(), $sformatf("Loop %d, str=\"%s\"", i, s[i]), UVM_NONE)
      uvm_top.find_all(s[i], components);
      foreach (components[j]) begin
        `uvm_info(get_type_name(), $sformatf("type_name = %25s, name = %14s, full_name = %s",
                                             components[j].get_type_name(),
                                             components[j].get_name(),
                                             components[j].get_full_name()), UVM_NONE)
      end
    end
  endtask
endclass


program top();
  initial begin
    run_test("my_test_c");
  end
endprogram
```

my_test_cのend_of_elaboration_phaseでuvm_top.print_topology()を呼んでいます。これによりuvm_componentの全階層構造が表示されます。今回は以下のように出力されました。rsp_portとseq_item_portはプログラムの中には現れませんが、これはuvm_driverの中で生成されているコンポーネントです。uvm_driverを継承したクラスには常に生成されます。

```plaintext
----------------------------------------------------------
Name                   Type                    Size  Value
----------------------------------------------------------
uvm_test_top           my_test_c               -     @335 
  my_env               my_env_c                -     @348 
    my_agent           my_agent_c              -     @357 
      my_driver_0      my_driver_c             -     @366 
        rsp_port       uvm_analysis_port       -     @385 
        seq_item_port  uvm_seq_item_pull_port  -     @375 
      my_driver_1      my_driver_c             -     @395 
        rsp_port       uvm_analysis_port       -     @414 
        seq_item_port  uvm_seq_item_pull_port  -     @404 
----------------------------------------------------------
```

my_test_cのrun_phaseではuvm_top.find_all()で全uvm_componentのオブジェクトハンドルを取得し、それぞれに対してget_type_name(), get_name(), get_full_name()を呼んでその戻り値を表示させています。"\*", "*_0", "*driver\_?.\*", "/^u.+t$/"の4つの文字列に対して実行しています。最後の文字列は/囲われているので正規表現と解釈されます。


```plaintext
UVM_INFO testbench.sv(70) @ 0: uvm_test_top [my_test_c] Loop           0, str="*"
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =         uvm_analysis_port, name =       rsp_port, full_name = uvm_test_top.my_env.my_agent.my_driver_0.rsp_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =    uvm_seq_item_pull_port, name =  seq_item_port, full_name = uvm_test_top.my_env.my_agent.my_driver_0.seq_item_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =               my_driver_c, name =    my_driver_0, full_name = uvm_test_top.my_env.my_agent.my_driver_0
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =         uvm_analysis_port, name =       rsp_port, full_name = uvm_test_top.my_env.my_agent.my_driver_1.rsp_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =    uvm_seq_item_pull_port, name =  seq_item_port, full_name = uvm_test_top.my_env.my_agent.my_driver_1.seq_item_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =               my_driver_c, name =    my_driver_1, full_name = uvm_test_top.my_env.my_agent.my_driver_1
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =                my_agent_c, name =       my_agent, full_name = uvm_test_top.my_env.my_agent
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =                  my_env_c, name =         my_env, full_name = uvm_test_top.my_env
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =                 my_test_c, name =   uvm_test_top, full_name = uvm_test_top
UVM_INFO testbench.sv(70) @ 0: uvm_test_top [my_test_c] Loop           1, str="*_0"
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =               my_driver_c, name =    my_driver_0, full_name = uvm_test_top.my_env.my_agent.my_driver_0
UVM_INFO testbench.sv(70) @ 0: uvm_test_top [my_test_c] Loop           2, str="*driver_?.*"
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =         uvm_analysis_port, name =       rsp_port, full_name = uvm_test_top.my_env.my_agent.my_driver_0.rsp_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =    uvm_seq_item_pull_port, name =  seq_item_port, full_name = uvm_test_top.my_env.my_agent.my_driver_0.seq_item_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =         uvm_analysis_port, name =       rsp_port, full_name = uvm_test_top.my_env.my_agent.my_driver_1.rsp_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =    uvm_seq_item_pull_port, name =  seq_item_port, full_name = uvm_test_top.my_env.my_agent.my_driver_1.seq_item_port
UVM_INFO testbench.sv(70) @ 0: uvm_test_top [my_test_c] Loop           3, str="/^u.+t$/"
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =         uvm_analysis_port, name =       rsp_port, full_name = uvm_test_top.my_env.my_agent.my_driver_0.rsp_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =    uvm_seq_item_pull_port, name =  seq_item_port, full_name = uvm_test_top.my_env.my_agent.my_driver_0.seq_item_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =         uvm_analysis_port, name =       rsp_port, full_name = uvm_test_top.my_env.my_agent.my_driver_1.rsp_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =    uvm_seq_item_pull_port, name =  seq_item_port, full_name = uvm_test_top.my_env.my_agent.my_driver_1.seq_item_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =                my_agent_c, name =       my_agent, full_name = uvm_test_top.my_env.my_agent
```

最後に全logを載せておきます。

```plaintext
UVM_INFO @ 0: reporter [RNTST] Running test my_test_c...
UVM_INFO /apps/vcsmx/etc/uvm-1.2/src/base/uvm_root.svh(589) @ 0: reporter [UVMTOP] UVM testbench topology:
----------------------------------------------------------
Name                   Type                    Size  Value
----------------------------------------------------------
uvm_test_top           my_test_c               -     @335 
  my_env               my_env_c                -     @348 
    my_agent           my_agent_c              -     @357 
      my_driver_0      my_driver_c             -     @366 
        rsp_port       uvm_analysis_port       -     @385 
        seq_item_port  uvm_seq_item_pull_port  -     @375 
      my_driver_1      my_driver_c             -     @395 
        rsp_port       uvm_analysis_port       -     @414 
        seq_item_port  uvm_seq_item_pull_port  -     @404 
----------------------------------------------------------

UVM_INFO testbench.sv(70) @ 0: uvm_test_top [my_test_c] Loop           0, str="*"
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =         uvm_analysis_port, name =       rsp_port, full_name = uvm_test_top.my_env.my_agent.my_driver_0.rsp_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =    uvm_seq_item_pull_port, name =  seq_item_port, full_name = uvm_test_top.my_env.my_agent.my_driver_0.seq_item_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =               my_driver_c, name =    my_driver_0, full_name = uvm_test_top.my_env.my_agent.my_driver_0
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =         uvm_analysis_port, name =       rsp_port, full_name = uvm_test_top.my_env.my_agent.my_driver_1.rsp_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =    uvm_seq_item_pull_port, name =  seq_item_port, full_name = uvm_test_top.my_env.my_agent.my_driver_1.seq_item_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =               my_driver_c, name =    my_driver_1, full_name = uvm_test_top.my_env.my_agent.my_driver_1
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =                my_agent_c, name =       my_agent, full_name = uvm_test_top.my_env.my_agent
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =                  my_env_c, name =         my_env, full_name = uvm_test_top.my_env
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =                 my_test_c, name =   uvm_test_top, full_name = uvm_test_top
UVM_INFO testbench.sv(70) @ 0: uvm_test_top [my_test_c] Loop           1, str="*_0"
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =               my_driver_c, name =    my_driver_0, full_name = uvm_test_top.my_env.my_agent.my_driver_0
UVM_INFO testbench.sv(70) @ 0: uvm_test_top [my_test_c] Loop           2, str="*driver_?.*"
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =         uvm_analysis_port, name =       rsp_port, full_name = uvm_test_top.my_env.my_agent.my_driver_0.rsp_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =    uvm_seq_item_pull_port, name =  seq_item_port, full_name = uvm_test_top.my_env.my_agent.my_driver_0.seq_item_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =         uvm_analysis_port, name =       rsp_port, full_name = uvm_test_top.my_env.my_agent.my_driver_1.rsp_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =    uvm_seq_item_pull_port, name =  seq_item_port, full_name = uvm_test_top.my_env.my_agent.my_driver_1.seq_item_port
UVM_INFO testbench.sv(70) @ 0: uvm_test_top [my_test_c] Loop           3, str="/^u.+t$/"
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =         uvm_analysis_port, name =       rsp_port, full_name = uvm_test_top.my_env.my_agent.my_driver_0.rsp_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =    uvm_seq_item_pull_port, name =  seq_item_port, full_name = uvm_test_top.my_env.my_agent.my_driver_0.seq_item_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =         uvm_analysis_port, name =       rsp_port, full_name = uvm_test_top.my_env.my_agent.my_driver_1.rsp_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =    uvm_seq_item_pull_port, name =  seq_item_port, full_name = uvm_test_top.my_env.my_agent.my_driver_1.seq_item_port
UVM_INFO testbench.sv(73) @ 0: uvm_test_top [my_test_c] type_name =                my_agent_c, name =       my_agent, full_name = uvm_test_top.my_env.my_agent
UVM_INFO /apps/vcsmx/etc/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

** Report counts by severity
UVM_INFO :   26
UVM_WARNING :    0
UVM_ERROR :    0
UVM_FATAL :    0
** Report counts by id
[RNTST]     1
[UVM/RELNOTES]     1
[UVMTOP]     1
[my_test_c]    23

$finish called from file "/apps/vcsmx/etc/uvm-1.2/src/base/uvm_root.svh", line 527.
$finish at simulation time                    0
           V C S   S i m u l a t i o n   R e p o r t 
Time: 0 ns
CPU Time:      0.320 seconds;       Data structure size:   0.1Mb
Tue Jul 17 13:26:06 2018
```

# 参考文献
accellra, "uvm_root" and "Components", Universal Verification Methodology (UVM) 1.2 Class Reference, p. 34-37. and p. 331-373.