09日目~再利用の仕方

9日目になりました。UVMという題材で扱っていないものはいろいろありますが、再利用の仕組みについて説明したいと思います。今回扱う「再利用」は、単にクラスの拡張(extends)の話だけでなく、実業務 で発生する一歩先の問題について取り扱います。

取り扱うのは、以下のケースです。

  • 記述済みの特定クラスにおいて、あるプロジェクトで多少変更する必要が出てきた

こんなとき、どうしますか?

せっかくクラスを使っているので、記述済みで変更不要なところは「再利用」したいですよね。例えば、sample_master_monitorを変えたいとします。

class sample_master_monitor extends uvm_monitor;

が元の記述だっとして、いまこの中にはスコアボード書き込みの記述があります。

  task scrbd_write;
    sample_scrbd_item item;
    forever begin
      @scrbd_e;
      item = new();
      item.addr = vif.addr;
      if(vif.write===1'b1)begin
        item.data = vif.wdata; // write側のanalysis_port
        item.`_GP_SCOREBOARD_MODE2_MARK = vif.addr;
        ap_write.write(item);
      end else if(vif.write===1'b0)begin
        item.data = vif.rdata;
        item.`_GP_SCOREBOARD_MODE2_MARK = vif.addr;
        ap_read.write(item);   // read側のanalysis_port
      end
    end
  endtask

では、このtaskの中身を書き換えたクラスを作ってみましょう。クラス名命名のセンスの無さは我慢して下さい。

class sample_master_chg_monitor extends sample_master_monitor;

extendsして記述すると、クラス名が変わりますね。そうすると、変更したクラス名に合わせて、agentも変えなければなりません。

  • インスタンス記述
  • build_phase内のcreate部分

…と、このように、クラスを再利用すると、関連するすべてのコードを書き換えなければなりません。非常に面倒です。

もちろん、そんな面倒なことをする必要はなく、適切な手法が用意されています。あたかも、新しいクラスを元のクラスとして見えるようにすることができます。

それには2種類あり、

  1. set_inst_override
  2. set_type_override

となります。

1番は、インスタンスに対して適用する、つまり複数インスタンスして使うようなコンポーネントだけど、部分変更を適用するのは1つだけ、または個々のインスタンス毎にコンポーネントの処理内容を変えた いとき、に使用する文法です。

2番は、クラス自体を置き換える方法で、変更後のコンポーネントをそのプロジェクト全体で使用する場合には、こちらを選択します。

さて、置き換えるクラスを以下のように定義してみました。内容としては、task scrbd_write の内容を、汎用比較器のmode2用からmode0用に戻しています。ここで、最初の2行に typedef class があります。こ れまではクラスの読み込み順序を「意識」して書いてきましたが、今回の「拡張記述」を読み込み順序を意識してincludeするには他のファイルを変える必要があります。具体的には ./model/sample_model.svh に手を入れることになりますが、この model/ のコードをVIPとして捉えるならば、直接変更するのは適切ではありません。そこで、typedef class 記述の登場です。これにより、クラスの順序をエラボレーション 段階で解決してくれます。このへんは、「HDLでは順序に依存しなくても大丈夫」というのと異なるので、気をつけてください。

--- tb/sample_master_chg_monitor.sv

typedef class sample_master_monitor;
typedef class sample_scrbd_item;
class sample_master_chg_monitor extends sample_master_monitor;
  `uvm_component_utils(sample_master_chg_monitor)
  function new (string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  task scrbd_write;
    sample_scrbd_item item;

$display("me me me"); //これはdebugで入れた文字

    forever begin
      @scrbd_e;
      item = new();
      item.addr = vif.addr;
      if(vif.write===1'b1)begin
        item.data = vif.wdata; // write側のanalysis_port
        item.addr = vif.addr;
        ap_write.write(item);
      end else if(vif.write===1'b0)begin
        item.data = vif.rdata;
        item.addr = vif.addr;
        ap_read.write(item);   // read側のanalysis_port
      end
    end
  endtask
endclass

さて、元のモニタを上記モニタに「置き換える」やり方ですが、./tb/tb_env.sv に記述を加えます。

--- ./tb/tb_env.sv の抜粋

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    // override ------------------------------------------
    //sample_master_monitor::type_id::set_type_override(sample_master_chg_monitor::get_type());
    sample_master_monitor::type_id::set_inst_override(sample_master_chg_monitor::get_type(),
        "uvm_test_top.tb.sample_model.master.monitor");
    // ---------------------------------------------------
    uvm_config_db#(int)::set(this, "sample_scrbd", "mode", 2);

はい、この太字のところになります。まず1つ目。

sample_master_monitor::type_id::set_type_override(sample_master_chg_monitor::get_type());

① ②

これですが、①が「置き換え前のクラス名」、②が「置き換えたいクラス名」になります。set_type_overrideですので、このプロジェクトで「sample_master_monitor」を使用するすべての箇所で、chg_monitor が使われます。

つぎ、2つ目。

sample_master_monitor::type_id::set_inst_override(sample_master_chg_monitor::get_type(),

① "uvm_test_top.tb.sample_model.master.monitor");

これも、①が「置き換え前のクラス名」、②が「置き換えたいクラス名」になります。set_inst_overrideですので、「指定したインスタンスパス」のみ対象となります。

なお、大事な点を忘れていましたが、今回書き換える処理は task scrbd_write でした。この場合、元のmonitorのtaskにvirtualをつける必要があります。

virtual task scrbd_write;

これをつけていないと、overrideする前のtask処理が実行されてしまいます。virtual つけていない task / function のoverrideですが、virtual がついていないからといって、InfoもWarningも出ません。(modelsim- ase)ので、十分注意してください。上記で示した、tb_env.sv 内のoverride記述は、コメントアウトした方の記述も適切な記述です。inst_overrideかtype_overrideか、ケースバイケースで使い分けましょう。

なお、このoverrideが正常に行われると、Simulationログに

# UVM_INFO @ 3600: reporter [MISCMP] Miscompare for sample_scrbd_item.addr: lhs = 'hd7 : rhs = 'h3b

# UVM_INFO @ 3600: reporter [MISCMP] 1 Miscompare(s) for object sample_scrbd_item@982 vs. sample_scrbd_item@1008

のような比較エラーを示すメッセージが表示されます。これはoverrideによって、汎用比較器の比較mode2の仕様に合わない、スコアボードへの書き込みが行われることで不一致となっています。これは期待し た結果です。

このような手法を用いて、UVMではコンポーネントの再利用をしやすくしています。