FPGA開発日記

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

Vivado Simulatorを使ってUVMに入門する (3. sequence / sequence_item / driver)

UVMの勉強をしている。UVMを使ったもう少し踏み込んだデザインについて勉強中。以下のページを参考にしている。

sites.google.com

UVMのテストパタンがどのようにテストターゲットに向けて流されるのかという話だが、とりあえずは、

  • sequnceというユニットがあり、この中ではsequence_itemというユニットが入っておりここでテストを生成する。
  • sequence_itemで生成したテストをsequencerに渡す。
  • sequencerはテストをdriverに渡す。

という流れになっているようだ。それぞれについてよく見ていくことにする。

sequence_itemについて

sequence_itemクラスによってテストを生成するらしい。uvm_sequence_itemクラスを派生してクラスを作成する。

class sample_seq_item extends uvm_sequence_item;
  byte addr, data;
  bit  write;
  `uvm_object_utils(sample_seq_item)
  function new (string name="sample_seq_item_inst");
    super.new(name);
  endfunction
endclass

sequenceを記述する

sequence内にテストベクタを記述するということになる。

virtual class sample_base_seq extends uvm_sequence #(sample_seq_item);
    // sample_bas_seqのコンストラクタみたいなもの
    // do_not_randomize は上記参考サイトで使っているModelSimの制約
    // VivadoSimだときっとうまくいく
    function new(string name="sample_base_seq");
        super.new(name);
        do_not_randomize = 1;
    endfunction
 virtual task pre_body();
        // pre_body()はbodyの前に呼び出されるメソッド
        // pre_body() --> body() --> post_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);
            // これの意味はよくわからないが、objectionをriseさせるというのは、
            // Queueに何かを設定するくらいの意味合いで良い気がしてきた。
            // このQueueが空になったら、きっとUVMは終了する。
            starting_phase.raise_objection(this);
        end
    endtask
 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);
            // body()の実行が終わったら呼び出されるメソッド
            // objectionからdropさせて、テストを終わらせる、ということだと思われる。
            starting_phase.drop_objection(this);
        end
    endtask

そして、上記のsample_base_seqを継承してwrite_seqクラスを作成する。ここにbody()を記述することで、いろんなテストでpre_body(), post_body()を共有する、ということか。

// このクラスは上記のsample_base_seqを派生させたもの
// sample_base_seqはuvm_sequenceの派生なので、結果的にこれもuvm_sequenceになる
class write_seq extends sample_base_seq;
    `uvm_object_utils(write_seq)
    function new (string name="write_seq");
        super.new(name);
    endfunctio
    // ここに本物のbody()が置かれる
    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
endclass

driverを更新する

sequencesequencerを実装すれば、次はそれをモデルに渡すドライバだ。sample_driverを更新する。

seq_item_port.get_next_item()で取得した情報をここでドライバに設定する。s

 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;
            // reqで取得した情報をここで設定する
            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
f:id:msyksphinz:20210412231829p:plain