3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

uvm_sequenceのstart()メソッドでシーケンススタート

Last updated at Posted at 2019-01-20

UVMでuvm_sequenceを開始させるには以下の2通りの方法があります。

  1. uvm_config_dbのdefault_sequeneを使う
  2. uvm_seuqnceのstart()メソッドを使う

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


本稿では直接的にuvm_sequenceを実行する方法を説明します。UVMでは本方法が主流で、もう一つの方法であるuvm_config_dbを使用したものと比べて簡単かつ柔軟性が高いのですが、WEB上の解説記事ではあまり触れられてないように思います。UVMの前身であるOVM時代にはサポートされていなかったことが影響しているのだと思います。

使い方

(UVMに不慣れな方は、以下でuvm_sequenceとuvm_sequencerの2つが登場することに注意してください。似ている名前ですが異なるものです) uvm_sequenceのstart()メソッドで、目的のuvm_sequencerのオブジェクトを引数に指定するだけです。一つのuvm_sequencer上で複数のuvm_sequenceを走らせることも可能です。この時の調停方法は対象のuvm_sequencerの設定に依存します。デフォルトでは早い者勝ちです。詳細はいずれ機会があれば説明したいと思います。

uvm_sequence.start(uvm_sequencer);

start()の説明はこれだけです。では早速プログラム例を見ていきましょう。

プログラム例

サンプルプログラムです。EDA Playgroundに登録してありますので、以下から実行することができます。
https://www.edaplayground.com/x/3mMj


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

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

    start_item(req);
    req.str = {get_full_name(), " --- 1st call"};
    finish_item(req);

    start_item(req);
    req.str = {get_full_name(), " --- 2nd call"};
    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;

  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 task run_phase(uvm_phase phase);
    uvm_root uvm_root_hdl = uvm_root::get();
    uvm_sequencer #(my_sequence_item_c) my_sequencer;

    my_sequence_c my_sequence_1 = my_sequence_c::type_id::create("my_sequence_1");
    my_sequence_c my_sequence_2 = my_sequence_c::type_id::create("my_sequence_2");
    my_sequence_c my_sequence_3 = my_sequence_c::type_id::create("my_sequence_3");

    $cast(my_sequencer, uvm_root_hdl.find("uvm_test_top.my_env.my_agent.my_sequencer"));
    if (my_sequencer == null) begin
      `uvm_error(get_type_name(), "The sequencer was not found")
    end
 
    phase.raise_objection(this);
    fork
      my_sequence_1.start(my_sequencer);
      my_sequence_2.start(my_sequencer);
    join
    my_sequence_3.start(my_sequencer);
    phase.drop_objection(this);
  endtask
endclass


module top();
  initial begin
    run_test("my_test_c");
  end
endmodule

重要なのはmy_test_c::run_phase()のみです。my_sequence_cを3つ、異なるオブジェクトとしてインスタンス化しています。オブジェクションを上げてからmy_sequence_1とmy_sequence_2はfork joinで同時にスタートさせます。この2つはuvm_sequencerにより調停されて順番に処理されます。両方のシーケンス実行が終了すると最後にmy_sequence_3が実行されます。最後にオブジェクションを落としてテストを終了させます。

以下が実行結果です。my_sequence_1とmy_sequence_2が並列に処理され、両方が終了した後にsequence_3が実行されていることが読み取れます。

# KERNEL: UVM_INFO @ 0: reporter [RNTST] Running test my_test_c...
# KERNEL: UVM_INFO /home/runner/testbench.sv(48) @ 10: 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_1 --- 1st call
# KERNEL: UVM_INFO /home/runner/testbench.sv(48) @ 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_2 --- 1st call
# KERNEL: UVM_INFO /home/runner/testbench.sv(48) @ 30: 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_1 --- 2nd call
# KERNEL: UVM_INFO /home/runner/testbench.sv(48) @ 40: 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_2 --- 2nd call
# KERNEL: UVM_INFO /home/runner/testbench.sv(48) @ 50: 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_3 --- 1st call
# KERNEL: UVM_INFO /home/runner/testbench.sv(48) @ 60: 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_3 --- 2nd call
# 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] 

以下各クラスの要点を説明します。

my_sequence_item_c

uvm_sequenceからuvm_sequencerを介してuvm_driverに渡されるデータを格納するクラスです。今回の例ではDUTはありませんので、簡単にstringをメンバとして持たせるのみにしました。

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

my_sequence_c

body()がシーケンスの本体です。ここではシーケンスアイテムを2回渡しています。両方ともget_full_name()に呼び出し回数を示す文字列を連結したものです。
ちなみにuvm_config_dbを使用したスタートの時のようなobjectionの制御は通常はここでは行いません。本シーケンスをstart()する側で行ったほうがシンプルになるからです。

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

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

    start_item(req);
    req.str = {get_full_name(), " --- 1st call"};
    finish_item(req);

    start_item(req);
    req.str = {get_full_name(), " --- 2nd call"};
    finish_item(req);
  endtask
endclass

my_driver_c

uvm_sequenceからuvm_sequencerを介してuvm_sequence_itemを受け取ります。重要なのはrun_phaseタスクのみです。本例ではDUTを駆動せず、uvm_sequenceのデータメンバーをログに出力するのみです。オブジェクションの効果を見るためにデータを受け取ってから、時間を10ns進めています。

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

  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

前述した通りです。
重要なのはmy_test_c::run_phase()のみです。my_sequence_cを3つ、異なるオブジェクトとしてインスタンス化しています。オブジェクションを上げてからmy_sequence_1とmy_sequence_2はfork joinで同時にスタートさせます。この2つはuvm_sequencerにより調停されて順番に処理されます。両方のシーケンス実行が終了すると最後にmy_sequence_3が実行されます。最後にオブジェクションを落としてテストを終了させます。

  virtual task run_phase(uvm_phase phase);
    uvm_root uvm_root_hdl = uvm_root::get();
    uvm_sequencer #(my_sequence_item_c) my_sequencer;

    my_sequence_c my_sequence_1 = my_sequence_c::type_id::create("my_sequence_1");
    my_sequence_c my_sequence_2 = my_sequence_c::type_id::create("my_sequence_2");
    my_sequence_c my_sequence_3 = my_sequence_c::type_id::create("my_sequence_3");

    $cast(my_sequencer, uvm_root_hdl.find("uvm_test_top.my_env.my_agent.my_sequencer"));
    if (my_sequencer == null) begin
      `uvm_error(get_type_name(), "The sequencer was not found")
    end
 
    phase.raise_objection(this);
    fork
      my_sequence_1.start(my_sequencer);
      my_sequence_2.start(my_sequencer);
    join
    my_sequence_3.start(my_sequencer);
    phase.drop_objection(this);
  endtask

参考文献

"3.10.3 Starting a Sequence on a Sequencer". Universal Verification Methodology(UVM) 1.2 User's Guide. pp.48-50.

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?