もう何回目になるのかわからないが、そろそろUVMを覚えなければならないのでVivado Simulatorを使ってUVMに入門してみよう。
今回はVivado 2023.2を使っている。
参考にしているのは例によって以下のウェブサイトだ:
sites.google.com
今回はUVMのスティミュラス(テストパタン)を追加する、ということになりそうだ。
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
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()
を追加する。
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_if(input logic clk, rstz);
logic write;
logic valid;
logic [7:0] addr, data;
endinterface
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
なるほど。以下は、共通クラスとしての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
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
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
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
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);
forever begin
seq_item_port.get_next_item(req);
@(posedge vif.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