FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Vivado Simulatorを使ってUVMに入門する (3. テストのシーケンスを作る)

もう何回目になるのかわからないが、そろそろUVMを覚えなければならないのでVivado Simulatorを使ってUVMに入門してみよう。

今回はVivado 2023.2を使っている。

参考にしているのは例によって以下のウェブサイトだ:

sites.google.com

今回はUVMのスティミュラス(テストパタン)を追加する、ということになりそうだ。

  • sequenceから
    • sequence_item
    • sequencerに渡す
  • sequencerが受け取って、
  • driverに渡す

  • sequence_itemについて:トランザクションの発行に必要な情報を格納する構造体を示す。

  • model/sample_seq_item.sv

class sample_seq_item extends uvm_sequence_item;
  rand byte addr, data;
  bit  write;
  `uvm_object_utils(sample_seq_item)

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

endclass // sample_seq_item
  • driversequencerをつなぐ:sample_driver.svsample_sequencer.svをわずかに変更する。上記で作成した構造体を、driverとsequencerに渡しているもんだと思う。
class sample_driver extends uvm_driver #(sample_seq_item);
class sample_sequencer extends uvm_sequencer #(sample_seq_item);

driversequencerをつなぐ。uvm_agentを修正して、connect_phase()を追加する。

  • model/sample_agent.sv
class sample_agent extends uvm_agent;
/* ... 途中省略 ... */
  function void connect_phase(uvm_phase phase);
    if(get_is_active() == UVM_ACTIVE)begin
      driver.seq_item_port.connect(sequencer.seq_item_export);
    end
  endfunction
  • interfaceの記述:とりあえず、上記のsample_seq_itemで定義した信号と同じようなものを定義する。

  • sample_if.sv

interface sample_if(input logic clk, rstz);

  logic write;  // 1:write, 0:read                                                                                                                                                                                                                                                                                                                                                        
  logic valid;
  logic [7:0] addr, data;

endinterface // sample_if                                                                                                                                                                                                                                                                                                                                                                 
  • virtual interfaceとのリンク:クラスの中に、仮想的にinterfaceを置く。この辺は良く分からない。データベースへの仮想interfaceの登録ということになる。
initial begin
  uvm_config_db#(virtual sample_if)::set(uvm_root::get(), "*.env.*", "vif", vif);
  run_test();
end

uvm_config_dbについて:指定したオブジェクトの、指定した範囲の、指定したフィールド名(変数、メンバ)に、このmoduleに組み込んだ「vif」をセットする、ということらしい(?)

  • uvm_root::get() : uvm_root::get() でuvm_testをゲットする?
  • "*.env.*" : uvm_testの中に定義しているsample_env envの中身をリンクする、という意味になる
  • "vif" : 第2引数の中で引きだしたインスタンスの各階層でvifというフィールド名を探す。
  • vif : 第3引数のフィールドに対してvifを渡す

model/sample_driver.svvirtual sample_ifを追加する。また、build_phase()も追加してuvm_config_dbを追加する。

class sample_driver extends uvm_driver #(sample_seq_item);
  virtual sample_if vif;
/* ... 以下省略 ... */
  function void build_phase(uvm_phase phase);
    bit status;
    super.build_phase(phase);
    status = uvm_config_db#(virtual sample_if)::get(this, "", "vif", vif);
    if (status==1'b0)
      uvm_report_fatal("NOVIF", {"virtual interface must be set for: ",get_full_name(),".vif"});
  endfunction // build_phase                                                                                                                                                                                                                                                                                                                                                              
  • sequenceつまり、テストベクトルを作成する。その基本的な流れは、

  • baseとなるsequenceクラスを定義して、そこに「共通記述」を埋め込む

  • sequence毎に異なる記述を、1番をextendsしたクラスの中に定義することで、記述量の削減を行う

なるほど。以下は、共通クラスとしてのsample_base_seqとそれを継承したwrite_seqの記述だ。sample_base_seqvirtualなので、そのままインスタンス化してはいけないのだと思う。

virtual class sample_base_seq extends uvm_sequence #(sample_seq_item);

class write_seq extends sample_base_seq;
  • function new() : do_no_randomizeは削除してもよい。
  • virtual task pre_body(), post_body() ここでは、raise_objection()を実行しているのと、drop_objection()を実行している。これは、実行すべきテストが存在していることを示すのだと思われる。

  • write_seqクラスでは、body()タスクを作成して、実際にシーケンスを作成している。

virtual class sample_base_seq extends uvm_sequence #(sample_seq_item);
  function new(string name="sample_base_seq");
    super.new(name);
    do_not_randomize = 1;
  endfunction // new                                                                                                                                                                                                                                                                                                                                                                      

  virtual task pre_body();
    if (starting_phase!=null) begin
       `uvm_info(get_type_name(),
                 $sformatf("%s pre_body() raising %s objection",
                           get_sequence_path(),
                           starting_phase.get_name()), UVM_MEDIUM);
       starting_phase.raise_objection(this);
    end
  endtask // pre_body                                                                                                                                                                                                                                                                                                                                                                     

  // Drop the objection in the post_body so the objection is removed when                                                                                                                                                                                                                                                                                                                 
  // the root sequence is complete.                                                                                                                                                                                                                                                                                                                                                       
  virtual task post_body();
    if (starting_phase!=null) begin
       `uvm_info(get_type_name(),
                 $sformatf("%s post_body() dropping %s objection",
                           get_sequence_path(),
                           starting_phase.get_name()), UVM_MEDIUM);
      starting_phase.drop_objection(this);
    end
  endtask
endclass

//------------------------------------------------------------------------                                                                                                                                                                                                                                                                                                                
class write_seq extends sample_base_seq;
  `uvm_object_utils(write_seq)

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

  virtual task body();
    $display("Hello SEQ");
    `uvm_create(req)
    req.write <= 1'b1;
    req.addr  <= 8'h10;
    req.data  <= 8'h55;
    `uvm_send(req)
    #1000;
  endtask // body                                                                                                                                                                                                                                                                                                                                                                         

endclass // write_seq                                                                                                                                                                                                                                                                                                                                                                     
  • sample_driver.svを更新し、vifを使って実際にデータを駆動し、seq_item_portから次のリクエストを受け取る記述を実装する。
  task run_phase(uvm_phase phase);
    uvm_report_info("DRIVER", "Hi");
    vif.valid <= 1'b0;
    @(posedge vif.rstz);  // wait reset negate                                                                                                                                                                                                                                                                                                                                            
    forever begin
      seq_item_port.get_next_item(req);  // wait seq_item from sequence (via sequencer)                                                                                                                                                                                                                                                                                                   
      @(posedge vif.clk); // sync clk                                                                                                                                                                                                                                                                                                                                                     
      vif.valid <= 1'b1;
      vif.write <= req.write;
      vif.addr  <= req.addr;
      vif.data  <= req.data;
      @(posedge vif.clk);
      vif.valid <= 1'b0;
      seq_item_port.item_done(rsp);
    end
  endtask