UVMの勉強をしている。UVMを使ったもう少し踏み込んだデザインについて勉強中。以下のページを参考にしている。
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
を更新する
sequence
とsequencer
を実装すれば、次はそれをモデルに渡すドライバだ。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