もう何回目になるのかわからないが、そろそろUVMを覚えなければならないのでVivado Simulatorを使ってUVMに入門してみよう。
今回はVivado 2023.2を使っている。
参考にしているのは例によって以下のウェブサイトだ:
今回は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
driver
とsequencer
をつなぐ:sample_driver.sv
とsample_sequencer.sv
をわずかに変更する。上記で作成した構造体を、driverとsequencerに渡しているもんだと思う。
class sample_driver extends uvm_driver #(sample_seq_item);
class sample_sequencer extends uvm_sequencer #(sample_seq_item);
driver
とsequencer
をつなぐ。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.sv
に virtual 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_seq
はvirtual
なので、そのままインスタンス化してはいけないのだと思う。
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