uvm_sequence_libraryの使い方

uvm_sequence_libaryはuvm_sequenceのサブクラスとなっており、基本的な使い方は同じです。さらに、uvm_sequence_libraryにuvm_sequenceを登録しておくと、それを複数回ランダムな順番に実行してくれます。

手順の概要は以下の通りになります。
1. uvm_sequence_libraryにuvm_sequenceを登録する。これには3種類の方法があります
2. min_random_count, max_roundom_count等のプロパティーにランダマイズ用のパラメターを設定する
3. randomize()を呼ぶ
4. (uvm_sequenceと同様に)start()でシーケンスライブラリをスタートさせる

例えば以下の例ではseq_a, seq_bを登録して、それぞれの最小実行回数を10に設定してシーケンスライブラリをスタートさせています。結果としてseq_aとseq_bがランダムな順番で最低10回ずつ実行されます。

```verilog
seq_lib.add_sequence(seq_a::get_type());
seq_lib.add_sequence(seq_b::get_type());
seq_lib.min_random_count = 10;
seq_lib.randomize();
seq_lib.start();
```

# uvm_sequenceの登録方法
3通りの方法があります。
## uvm_sequence_library.add_sequence()を使用する

引数はuvm_sequenceのtypeになります。

```verilog
uvm_sequence_library_h = uvm_sequence_library#(uvm_sequence_item)::type_id::create("uvm_sequence_library");
uvm_sequence_library_h.add_sequence(uvm_sequence::get_type());
```
## uvm_sequence_library::add_typewide_sequence()を使用する
こちらはクラスメソッドになります。引数はtypeです。
クラスメソッドなので、対象のクラス全てにuvm_sequenceが登録されます。登録した後にcreate()して使います。

```verilog
uvm_sequence_library::add_typewide_sequence(uvm_sequence::get_type());
uvm_sequence_library_h = uvm_sequence_library::type_id::create("my_sequence_library");
```

## uvm_add_to_seq_libマクロを使用する
このマクロは登録したいシーケンスの中で使います。
第一引数が自分自身のクラス名、第二引数が登録したいシーケンスライブラリのクラス名です。

```verilog
class my_sequence extends uvm_sequence #(my_sequence_item);
  `uvm_object_utils(my_sequence)
  `uvm_add_to_seq_lib(my_sequence, my_sequence_library)
  function new(string name = "my_sequence");
    super.new(name);
  endfunction
  ...
endclass
```

# サンプルプログラム
サンプルプログラムです。上記それぞれ3つの方法でシーケンスライブラリを作り、実行します。全てmin_random_countとmax_random_countを2に設定していますので、シーケンス実行回数は2回に固定されます。
以下がサンプルプログラム全文です。EDA Playgroundでは以下から実行できます。
https://www.edaplayground.com/x/5EZC

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

class my_sequence_item extends uvm_sequence_item;
  string str;
  `uvm_object_utils(my_sequence_item)

  function new(string name = "my_sequence_item");
    super.new(name);
  endfunction
endclass


typedef class my_sequence_library_b;


class my_sequence extends uvm_sequence #(my_sequence_item);
  `uvm_object_utils(my_sequence)

  `uvm_add_to_seq_lib(my_sequence, my_sequence_library_b)

  function new(string name = "my_sequence");
    super.new(name);
  endfunction

  virtual task body();
    req = my_sequence_item::type_id::create("req");

    start_item(req);
    req.str = get_full_name();
    finish_item(req);
  endtask
endclass


class my_sequence_library_a extends uvm_sequence_library #(my_sequence_item);
  `uvm_object_utils(my_sequence_library_a)
  `uvm_sequence_library_utils(my_sequence_library_a)

  function new(string name = "my_sequence_library_a");
    super.new(name);
    init_sequence_library();
  endfunction
endclass


class my_sequence_library_b extends uvm_sequence_library #(my_sequence_item);
  `uvm_object_utils(my_sequence_library_b)
  `uvm_sequence_library_utils(my_sequence_library_b)

  function new(string name = "my_sequence_library_b");
    super.new(name);
    init_sequence_library();
  endfunction
endclass


class my_driver extends uvm_driver #(my_sequence_item);
  `uvm_component_utils(my_driver)

  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 extends uvm_component;
  my_driver my_driver_h;
  uvm_sequencer #(my_sequence_item) my_sequencer_h;

  `uvm_component_utils(my_agent)

  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_h = uvm_sequencer#(my_sequence_item)::type_id::create("my_sequencer", this);
    my_driver_h = my_driver::type_id::create("my_driver", this);
  endfunction

  virtual function void connect_phase(uvm_phase phase);
    my_driver_h.seq_item_port.connect(my_sequencer_h.seq_item_export);
  endfunction
endclass


class my_env extends uvm_env;
  my_agent my_agent_h;

  `uvm_component_utils(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_agent_h = my_agent::type_id::create("my_agent", this);
  endfunction
endclass


class my_test extends uvm_test;
  `uvm_component_utils(my_test)

  my_env my_env_h;

  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_h = my_env::type_id::create("my_env", this);
  endfunction

  virtual task run_phase(uvm_phase phase);
    uvm_root uvm_root_h = uvm_root::get();
    uvm_sequencer #(my_sequence_item) my_sequencer_h;

    uvm_sequence_library#(my_sequence_item) uvm_sequence_library_h;
    my_sequence_library_a my_sequence_library_a_h;
    my_sequence_library_b my_sequence_library_b_h;

    // Option 1. Use add_sequence method
    uvm_sequence_library_h = uvm_sequence_library#(my_sequence_item)::type_id::create("uvm_sequence_library");
    uvm_sequence_library_h.add_sequence(my_sequence::get_type());
    uvm_sequence_library_h.min_random_count = 2;
    uvm_sequence_library_h.max_random_count = 2;
    if (!uvm_sequence_library_h.randomize()) begin
      `uvm_error(get_type_name(), "randomization error")
    end

    // Option 2. Use add_typewide_sequence class method
    my_sequence_library_a::add_typewide_sequence(my_sequence::get_type());
    my_sequence_library_a_h = my_sequence_library_a::type_id::create("my_sequence_library_a");
    my_sequence_library_a_h.min_random_count = 2;
    my_sequence_library_a_h.max_random_count = 2;
    if (!my_sequence_library_a_h.randomize()) begin
      `uvm_error(get_type_name(), "randomization error")
    end

    // Option 3. Use uvm_add_to_seq_lib macro
    my_sequence_library_b_h = my_sequence_library_b::type_id::create("my_sequence_library_b");
    my_sequence_library_b_h.min_random_count = 2;
    my_sequence_library_b_h.max_random_count = 2;
    if (!my_sequence_library_b_h.randomize()) begin
      `uvm_error(get_type_name(), "randomization error")
    end

    // Obtain handle to uvm_sequencer
    $cast(my_sequencer_h, uvm_root_h.find("uvm_test_top.my_env.my_agent.my_sequencer"));
    if (my_sequencer_h == null) begin
      `uvm_error(get_type_name(), "The sequencer was not found")
    end

    // Start sequences
    phase.raise_objection(this);
    uvm_sequence_library_h.start(my_sequencer_h);
    my_sequence_library_a_h.start(my_sequencer_h);
    my_sequence_library_b_h.start(my_sequencer_h);
    phase.drop_objection(this);
  endtask
endclass

module top();
  initial begin
    run_test("my_test");
  end
endmodule
```

## 解説

uvm_sequenceは自分のfull_nameをuvm_sequene_itemに登録して、uvm_driverに送ります。

```verilog
    start_item(req);
    req.str = get_full_name();
    finish_item(req);
```

uvm_driverはuvm_sequenceからuvm_sequence_itemを受け取ると、以下でその内容を表示します。

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


## 実行結果
実行結果です。

```plaintext
# KERNEL: UVM_INFO @ 0: reporter [RNTST] Running test my_test...
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/seq/uvm_sequence_library.svh(663) @ 0: uvm_test_top.my_env.my_agent.my_sequencer@@uvm_sequence_library [SEQLIB/START] Starting sequence library uvm_sequence_library #(REQ,RSP) in unknown phase: 2 iterations in mode UVM_SEQ_LIB_RAND
# KERNEL: UVM_INFO /home/runner/testbench.sv(70) @ 10: uvm_test_top.my_env.my_agent.my_driver [my_driver] Passed string = uvm_test_top.my_env.my_agent.my_sequencer.uvm_sequence_library.my_sequence:1
# KERNEL: UVM_INFO /home/runner/testbench.sv(70) @ 20: uvm_test_top.my_env.my_agent.my_driver [my_driver] Passed string = uvm_test_top.my_env.my_agent.my_sequencer.uvm_sequence_library.my_sequence:2
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/seq/uvm_sequence_library.svh(737) @ 20: uvm_test_top.my_env.my_agent.my_sequencer@@uvm_sequence_library [SEQLIB/END] Ending sequence library in phase unknown
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/seq/uvm_sequence_library.svh(663) @ 20: uvm_test_top.my_env.my_agent.my_sequencer@@my_sequence_library_a [SEQLIB/START] Starting sequence library my_sequence_library_a in unknown phase: 2 iterations in mode UVM_SEQ_LIB_RAND
# KERNEL: UVM_INFO /home/runner/testbench.sv(70) @ 30: uvm_test_top.my_env.my_agent.my_driver [my_driver] Passed string = uvm_test_top.my_env.my_agent.my_sequencer.my_sequence_library_a.my_sequence:1
# KERNEL: UVM_INFO /home/runner/testbench.sv(70) @ 40: uvm_test_top.my_env.my_agent.my_driver [my_driver] Passed string = uvm_test_top.my_env.my_agent.my_sequencer.my_sequence_library_a.my_sequence:2
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/seq/uvm_sequence_library.svh(737) @ 40: uvm_test_top.my_env.my_agent.my_sequencer@@my_sequence_library_a [SEQLIB/END] Ending sequence library in phase unknown
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/seq/uvm_sequence_library.svh(663) @ 40: uvm_test_top.my_env.my_agent.my_sequencer@@my_sequence_library_b [SEQLIB/START] Starting sequence library my_sequence_library_b in unknown phase: 2 iterations in mode UVM_SEQ_LIB_RAND
# KERNEL: UVM_INFO /home/runner/testbench.sv(70) @ 50: uvm_test_top.my_env.my_agent.my_driver [my_driver] Passed string = uvm_test_top.my_env.my_agent.my_sequencer.my_sequence_library_b.my_sequence:1
# KERNEL: UVM_INFO /home/runner/testbench.sv(70) @ 60: uvm_test_top.my_env.my_agent.my_driver [my_driver] Passed string = uvm_test_top.my_env.my_agent.my_sequencer.my_sequence_library_b.my_sequence:2
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/seq/uvm_sequence_library.svh(737) @ 60: uvm_test_top.my_env.my_agent.my_sequencer@@my_sequence_library_b [SEQLIB/END] Ending sequence library in phase unknown
# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 60: 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) @ 60: reporter [UVM/REPORT/SERVER] 
```

uvm_sequence_libraryにはここで説明した以外にも実行するuvm_sequenceをランダムに選ぶ方法のアルゴリズムを指定するプロパティー等があります。詳細は以下のリファレンスマニュアルを参照してください。

# 参考文献
Accellera, "20.4 uvm_sequence_library", [Universal Verification Methodology (UVM) 1.2 Class Reference]
(https://www.accellera.org/images/downloads/standards/uvm/UVM_Class_Reference_Manual_1.2.pdf), pp.431-435.