0
0

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_config_dbのdefault sequenceによるシーケンスの実行

Last updated at Posted at 2018-12-10

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

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

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


ここで紹介するのは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を登録

以下の書式になります。

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

typedef uvm_config_db#(uvm_object_wrapper) uvm_config_wrapper;

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

uvm_config_wrapper::set(this,
    ...

オブジェクトを登録

以下の書式になります。

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に登録してありますので、ここで実行することができます。

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

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

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

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

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

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

  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

build_phaseでmy_env_cをインスタンス化しています。また、uvm_sequencerへのsequenceの登録を行っています。uvm_config_dbにオブジェクトを登録する方法を使用しています。typeを登録する方法はこの部分をコメントアウトして、uvm_confi_db#(uvm_object_wrapper)以降のコメントを外してください。

  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. pp.49-50.

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?