06日目~モデルの追加
5日目までは、とりあえず小さな構成のモデル基本構造と、それらを構成するコードを説明してきました。
今回は、UVMのテストベクタ・テストシナリオ構築に関するちょっと細かい説明と、サンプルの追加を通してそれを理解することをしたいと思っていましたが、その話をする下準備をここでは行います。スレー ブモデルを追加して、2つのモデル間でハンドシェイクして動くようにします。
スレーブモデルを追加する
ファイルのコピーと変更
現状、なんちゃってマスター動作するモデルしか作っていませんので、マスター動作に応答するスレーブモデルを作りましょう。以下の作業をします。
- コピー
- sample_agent.sv
- → sample_master_agent.sv, sample_slave_agent.sv
- sample_driver.sv
- → sample_master_driver.sv, sample_slave_driver.sv
- sample_monitor.sv
- → sample_master_monitor.sv, sample_slave_monitor.sv
- sample_sequencer.sv
- → sample_master_sequencer.sv, sample_slave_sequencer.sv
- sample_seq_lib.sv
- → sample_master_seq_lib.sv, sample_slave_seq_lib.sv
- 削除
- sample_agent.sv, sample_driver.sv, sample_monitor.sv, sample_sequencer.sv, sample_seq_lib.sv
- 修正1
- まず、master系4つ、slave系4つ、計8つのファイルを修正します。
- class sample_agent... → class sample_master_agent... という修正を各ファイルに
- `uvm_component_utilsの引数名も変える
- agentファイルでは、3つのインスタンス元クラス名を変える
- sample_driver driver → sample_master_driver driver; のように
- build_phaseの中も修正します。
- 次に、sample_model.svhを修正します。
- agent, driver, monitor, sequencer の各ファイル名を、masterのに変えましょう。
- slaveのは、まだ入れないでおきましょう。
- sample_env.sv を修正します。
- sample_agent をインスタンスしているので、sample_master_agent に変えます。インスタンス名は master に変えます。
- build_phase の中も修正するのを忘れずに。
- sample_test.sv を修正します。
- build_phaseのところで、default_sequenceを指定しているところがありますので、ここを直します。
- まず、master系4つ、slave系4つ、計8つのファイルを修正します。
- 修正2
- スレーブモデルを追加するにあたり、valid - ready のハンドシェイクを行うようにします。driver と monitor は、このハンドシェイクに対応した修正を行います。
説明抜けなどありそうなのと、わかりやすいように、各コードを貼り付けておきます。太字が「変更箇所」です。
sample_seq_item.sv
スレーブ追加分をカバーするため、修正します。
class sample_seq_item extends uvm_sequence_item;
logic [7:0] addr, wdata, rdata;
bit write;
bit [3:0] wait_cycle;
`uvm_object_utils(sample_seq_item)
function new (string name="sample_seq_item_inst");
super.new(name);
endfunction
endclass
sample_master_agent.sv
class sample_master_agent extends uvm_agent;
`uvm_component_utils(sample_master_agent)
sample_master_driver driver;
sample_master_monitor monitor;
sample_master_sequencer sequencer;
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
driver = sample_master_driver::type_id::create("driver", this);
monitor = sample_master_monitor::type_id::create("monitor", this);
sequencer = sample_master_sequencer::type_id::create("sequencer", this);
endfunction
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
task run_phase(uvm_phase phase);
uvm_report_info("AGENT", "Hi");
endtask
endclass
sample_master_driver.sv
class sample_master_driver extends uvm_driver #(sample_seq_item);
virtual sample_if vif;
`uvm_component_utils(sample_master_driver)
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction
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
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;
if(req.write==1'b1) vif.wdata <= req.wdata;
@(posedge vif.ready);
if(req.write==1'b0) req.rdata = vif.rdata; //reqにリードデータを入れておいて
@(posedge vif.clk);
vif.valid <= 1'b0;
seq_item_port.item_done(req); // item_doneでreqを返すと、sequenceでリードデータを取得できる
end
endtask
endclass
sample_master_sequencer.sv
class sample_master_sequencer extends uvm_sequencer #(sample_seq_item);
`uvm_component_utils(sample_master_sequencer)
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
uvm_report_info("SEQR", "Hi");
super.run_phase(phase);
endtask
endclass
sample_master_monitor.sv
class sample_master_monitor extends uvm_monitor;
virtual sample_if vif;
`uvm_component_utils(sample_master_monitor)
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction
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
task run_phase(uvm_phase phase);
string s_trans_kind;
uvm_report_info("MONITOR", "Hi");
forever begin
@(posedge vif.valid);
wait(vif.ready===1'b1);
if(vif.write===1'b1)
uvm_report_info("MON", $sformatf("write addr=%02xh wdata=%02xh", vif.addr, vif.wdata));
else if(vif.write===1'b0)
uvm_report_info("MON", $sformatf("read addr=%02xh rdata=%02xh", vif.addr, vif.rdata));
else
uvm_report_info("MON", $sformatf("signal write is unknown..."));
end
endtask
endclass
ところで、上記monitorですが、vif.validの立ち上がりを待った後、wait文でvif.readyが1になるのを待っています。さて、何故だと思いますか?
sample_master_seq_lib.sv
class名をmasterに揃えて、それに付随する箇所を変更します。また、write_seqのクラスは、暫定的にリードを追加し、書いて読む動作にしています。
virtual class sample_master_base_seq extends uvm_sequence #(sample_seq_item);
function new(string name="sample_master_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
// 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_master_base_seq;
`uvm_object_utils(write_seq)
function new (string name="write_seq");
super.new(name);
endfunction
virtual task body();
`uvm_create(req)
req.write <= 1'b1;
req.addr <= 8'h10;
req.wdata <= 8'h55;
`uvm_send(req)
`uvm_create(req)
req.write <= 1'b0;
req.addr <= 8'h10;
`uvm_send(req)
uvm_report_info("SEQ", $sformatf("read data is %02xh", req.rdata));
#1000;
endtask
endclass
スレーブモデル
slaveモデルの方を記述していきます。
sample_slave_agent.sv
master側のときと同じように修正していきます。ただし今回は、monitor をコメントアウトします。理由は、今回のサンプルにおいてmaster のmonitorとslave のmonitor が同じところをモニ ターするため。
sample_slave_sequencer.sv
class名と、uvm_component_utilsの引数名を直します。
sample_slave_driver.sv
このファイルは、スレーブ側動作をさせるので、動きを定義していきます。せっかくマスターとスレーブを用意したので、まずはバスインターフェースの信号「ready」を追加します。ま た、8bit定義のdataを、wdataとrdataの2つに分けます。
→ 修正ファイル sample_if.sv
動きですが、以下のような仕様にしてみたいと思います。
「validが1になったら、一定サイクル後にreadyを返す。ready=1のときのアドレス、ライト・リード信号、データを有効とする」
この動きを実現するためのslave_driverのコードは以下のようにしました。ポイントは、sequenceでslave_driverの挙動を変えられるようにすること。
class sample_slave_driver extends uvm_driver #(sample_seq_item);
virtual sample_if vif;
logic [7:0] memory[bit [7:0]]; //aa(連想配列):入れる値は4値(logic)、indexは4値を扱えないので2値で
`uvm_component_utils(sample_slave_driver)
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction
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
task run_phase(uvm_phase phase);
logic [7:0] rdata;
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.valid);
repeat(req.wait_cycle) @(posedge vif.clk); //sequenceでwaitを制御できるようにする
vif.ready <= 1'b1;
if(vif.write === 1'b1)begin
mem_write(vif.addr, vif.wdata);
end else if(vif.write === 1'b0)begin
vif.rdata <= mem_read(vif.addr);
end
@(posedge vif.clk) vif.ready <= 1'b0;
seq_item_port.item_done(req);
end
endtask
function void mem_write(bit [7:0] addr, logic [7:0] data);
//uvm_report_info("SLAVE", $sformatf("write access accept. addr=%02xh, data=%02xh", addr,data));
memory[addr] = data;
endfunction
function logic [7:0] mem_read(bit [7:0] addr);
//uvm_report_info("SLAVE", $sformatf("read access accept. addr=%02xh", addr));
if(memory.exists(addr))begin
//uvm_report_info("SLAVE", $sformatf("read data is =%02xh", memory[addr]));
return memory[addr];
end else
return 8'hXX;
endfunction
endclass
sample_slave_seq_lib.sv
とりあえず、簡単な動作シーケンスを定義しておきます。
virtual class sample_slave_base_seq extends uvm_sequence #(sample_seq_item);
function new(string name="sample_slave_base_seq");
super.new(name);
do_not_randomize = 1;
endfunction
endclass
//------------------------------------------------------------------------
class normal_response_seq extends sample_slave_base_seq;
`uvm_object_utils(normal_response_seq)
function new (string name="normal_response_seq");
super.new(name);
endfunction
virtual task body();
forever begin
`uvm_create(req)
req.wait_cycle <= 1'b1;
`uvm_send(req)
end
endtask
endclass
sample_env.sv
masterのagent名が変わったことを反映し、slave_agentを追加します。
class sample_env extends uvm_env;
`uvm_component_utils(sample_env)
sample_master_agent master;
sample_slave_agent slave;
function new (string name, uvm_component parent);
super.new(name,parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
master = sample_master_agent::type_id::create("master", this);
slave = sample_slave_agent::type_id::create("slave", this);
endfunction
task run_phase(uvm_phase phase);
uvm_report_info("ENV", "Hello ENV");
endtask
endclass
sample_model.svh
slave系のファイルを追加します。
`include "sample_seq_item.sv"
`include "sample_master_seq_lib.sv"
`include "sample_slave_seq_lib.sv"
`include "sample_master_driver.sv"
`include "sample_master_monitor.sv"
`include "sample_master_sequencer.sv"
`include "sample_master_agent.sv"
`include "sample_slave_driver.sv"
`include "sample_slave_sequencer.sv"
`include "sample_slave_agent.sv"
`include "sample_env.sv"
uvm_testの修正
slaveモデル側に、使用するsequenceをセットしましょう。そして、agentのインスタンス名を変えたので、黄色で塗られたところに注意しましょう。
class sample_test extends uvm_test;
`uvm_component_utils(sample_test)
sample_env env;
function new (string name="sample_test", uvm_component parent=null);
super.new(name,parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_object_wrapper)::set(this,
"env.master.sequencer.run_phase", "default_sequence",
write_seq::type_id::get());
uvm_config_db#(uvm_object_wrapper)::set(this,
"env.slave.sequencer.run_phase", "default_sequence",
normal_response_seq::type_id::get());
env = sample_env::type_id::create("env", this);
endfunction
task run_phase(uvm_phase phase);
uvm_report_info("TEST", "Hello World");
uvm_top.print_topology();
endtask
endclass
コンパイル
vlog -sv model/sample_if.sv
vlog -sv tb_top.sv +incdir+./+model/+../uvm-1.1d/src
実行
vsim -c tb_top +UVM_TESTNAME=sample_test -do "run -all; quit"
ログ
# run -all
# ----------------------------------------------------------------
# UVM-1.1d
# (C) 2007-2013 Mentor Graphics Corporation
# (C) 2007-2013 Cadence Design Systems, Inc.
# (C) 2006-2013 Synopsys, Inc.
# (C) 2011-2013 Cypress Semiconductor Corp.
# ----------------------------------------------------------------
# UVM_INFO ../uvm-1.1d/src/base/uvm_root.svh(370) @ 0: reporter [NO_DPI_TSTNAME] UVM_NO_DPI defined--getting UVM_TESTNAME directly, without DPI
# UVM_INFO @ 0: reporter [RNTST] Running test sample_test...
# UVM_INFO @ 0: uvm_test_top [TEST] Hello World
# UVM_INFO @ 0: reporter [UVMTOP] UVM testbench topology:
# ---------------------------------------------------------------
# Name Type Size Value
# ---------------------------------------------------------------
# uvm_test_top sample_test - @448
# env sample_env - @461
# master sample_master_agent - @469
# driver sample_master_driver - @486
# rsp_port uvm_analysis_port - @503
# seq_item_port uvm_seq_item_pull_port - @494
# monitor sample_master_monitor - @512
# sequencer sample_master_sequencer - @520
# rsp_export uvm_analysis_export - @528
# seq_item_export uvm_seq_item_pull_imp - @634
# arbitration_queue array 0 -
# lock_queue array 0 -
# num_last_reqs integral 32 'd1
# num_last_rsps integral 32 'd1
# slave sample_slave_agent - @477
# driver sample_slave_driver - @646
# rsp_port uvm_analysis_port - @663
# seq_item_port uvm_seq_item_pull_port - @654
# sequencer sample_slave_sequencer - @672
# rsp_export uvm_analysis_export - @680
# seq_item_export uvm_seq_item_pull_imp - @786
# arbitration_queue array 0 -
# lock_queue array 0 -
# num_last_reqs integral 32 'd1
# num_last_rsps integral 32 'd1
# ---------------------------------------------------------------
#
# UVM_INFO @ 0: uvm_test_top.env [ENV] Hello ENV
# UVM_INFO @ 0: uvm_test_top.env.slave [AGENT] Hi
# UVM_INFO @ 0: uvm_test_top.env.slave.sequencer [SEQR] Hi
# UVM_INFO @ 0: uvm_test_top.env.slave.driver [DRIVER] Hi
# UVM_INFO @ 0: uvm_test_top.env.master [AGENT] Hi
# UVM_INFO @ 0: uvm_test_top.env.master.sequencer [SEQR] Hi
# UVM_INFO @ 0: uvm_test_top.env.master.monitor [MONITOR] Hi
# UVM_INFO @ 0: uvm_test_top.env.master.driver [DRIVER] Hi
# UVM_INFO model//sample_master_seq_lib.sv(13) @ 0: uvm_test_top.env.master.sequencer@@write_seq [write_seq] write_seq pre_body() raising run objection
# UVM_INFO @ 300: uvm_test_top.env.master.monitor [MON] write addr=10h wdata=55h
# UVM_INFO @ 600: uvm_test_top.env.master.monitor [MON] read addr=10h rdata=55h
# UVM_INFO @ 700: uvm_test_top.env.master.sequencer@@write_seq [SEQ] read data is 55h
# UVM_INFO model//sample_master_seq_lib.sv(25) @ 1700: uvm_test_top.env.master.sequencer@@write_seq [write_seq] write_seq post_body() dropping run objection