Uvm_config_dbのdefault sequenceによるシーケンスの実行


UVMでuvm_sequenceを開始させるには以下の2通りの方法があります。
1. uvm_config_dbのdefault_sequeneを使う
2. [uvm_seuqnceのstart()メソッドを使う](https://qiita.com/triggerfish/items/1c0cb8992468d6bde3bd)

本稿では前者について説明します。

---
ここで紹介するのは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を登録
以下の書式になります。

```verilog
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されていますので、

```verilog
typedef uvm_config_db#(uvm_object_wrapper) uvm_config_wrapper;
```

替わりにuvm_config_wrapperを使うこともできます。

```verilog
uvm_config_wrapper::set(this,
    ...
```

## オブジェクトを登録
以下の書式になります。

```verilog
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に登録してありますので、[ここ](https://www.edaplayground.com/x/5JGx)で実行することができます。

```verilog
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に渡り、その文字列が表示されていることがわかります。

```plaintext
# 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をメンバとして持たせるのみにしました。

```verilog
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で行えます。

```verilog
  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()と同様です。

```verilog
  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()が返す文字列です。

```verilog
  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進めています。

```verilog
  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をインスタンス化して接続しています。

```verilog
  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)以降のコメントを外してください。

```verilog
  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](http://www.accellera.org/images//downloads/standards/uvm/uvm_users_guide_1.2.pdf). pp.49-50.