02. 簡単な仕様のモデルの作り方と実行
構成を理解する
構成を理解する
BFM(バスファンクショナルモデル)の、UVMでのclass構成を示します。
以下に示したclassをextendsして構成します。
- class uvm_env
- class uvm_agent
- class uvm_driver
- class uvm_sequencer
- class uvm_monitor
- class uvm_agent
他に
- class uvm_sequence_item
が必要になりますが、これだけ別だしなのは、uvm_env以下にインスタンスして使わないからです。
個々のclassを簡単に説明します。
- uvm_driver
- バスの動きを表現するclassになります。DUT(論理回路)の信号をドライブする唯一のclassです。DUTとは、interface経由で接続します。
- uvm_sequencer
- uvm_sequence(テストベクタ、テストシナリオ)をuvm_driverに渡す役目をします。通常の構成では、「決め打ち」記述ですみます。
- uvm_sequence_item
- 「トランザクション」情報を、uvm_sequenceからuvm_driverへ渡すのに使います。
- uvm_monitor
- バスモニタです。
- uvm_env
- モデルの最上位になります。この下にuvm_agentをインスタンスします。
シンプルなモデル
シンプルなモデル
- 以下の様な仕様のモデルを作ってみたいと思います。
rstz ____|~~~~~~~~~~~~~~~~~~~~~~~~~~~~
clk ~~~~~|___|~~~|___|~~~|___|~~~|___
valid _________|~~~~~~~|_______________
addr XXXXXXXXXX addr XXXXXXXXXXXXXXXX
data XXXXXXXXXX data XXXXXXXXXXXXXXXX
各classのサンプル記述
各classのサンプル記述
- 記述のひな形は、UVMクラスライブラリのexamples/integrated 以下にありますが、そこから肉を削ぎ落として書き換えたものを示します。
uvm_sequence_item
uvm_sequence_item
class sample_seq_item extends uvm_sequence_item;
logic [7:0] addr, data;
`uvm_object_utils_begin(sample_seq_item)
`uvm_field_int
(addr, UVM_DEFAULT)
`uvm_field_int
(data, UVM_DEFAULT)
`uvm_object_utils_end
function new (string name = "sample_seq_item_inst");
super.new(name);
endfunction : new
endclass
- uvm_sequence_itemには、「driverを制御するのに使う」メンバを定義します。
- 今回の場合は、validはaddrとdataをドライブするときに 1 にする仕様なので、validは不要です。
- validをプロトコル違反させたいときは、validを加えてもいいです。
uvm_sequencer
uvm_sequencer
class sample_sequencer extends uvm_sequencer #(sample_seq_item);
`uvm_component_utils(sample_sequencer)
`uvm_new_func
endclass
- `uvm_component_utilsマクロは、「UVMファクトリーにこのclassを登録する」と覚えてください。
- `uvm_new_funcは、以下のコードです。
- 1行目の「sample_seq_item」は、トランザクション情報を持つuvm_sequence_itemをextendsしたclassです。
----- ./src/macros/uvm_object_defines.svh, line=396
`define uvm_new_func \
function new (string name, uvm_component parent); \
super.new(name, parent); \
endfunction
uvm_driver
uvm_driver
class sample_driver extends uvm_driver #(sample_seq_item);
`uvm_component_utils(sample_driver)
virtual sample_if vif;
function new (string name="sample_driver", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual sample_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
endfunction: build_phase
task run_phase(uvm_phase phase);
sample_seq_item trans_item;
// while reset
vif.valid = 1'b0;
@(posedge vif.rstz);// wait negate reset
forever begin
seq_item_port.get_next_item(trans_item);
// get several value from trans_item and drive
// signals, receive signals via virtual interface
@(posedge vif.clk);
vif.valid <= 1'b1;
vif.addr <= trans_item.addr;
vif.data <= trans_item.data;
@(posedge vif.clk) vif.valid <= 1'b0;
seq_item_port.item_done();
end
endtask
endclass
- virtual sample_if vif; は、interface定義です。「virtual」をつけます。
interface sample_if(input logic clk, rstz);
logic [7:0] addr, data;
logic valid;
endinterface
- build_phaseがややこしい記述になっていますが、これは「ほぼコピペ」でいけます。変えるところは、"vif" です。後述する、module定義のテストベンチトップと関係があります。
- task run_phaseは、Simulation実行時に自動で呼び出されます。中身は
- valid信号値をリセットして
- リセット解除を待って
- 無限ループを作って(forever)
- seq_item_port.get_next_itemで、sequence(テストベクタ・テストシナリオ)からの命令を「待ちます」
- 受け取ったら、クロック同期させて、vifに値をセットします。
- validを 0 にしてプロトコルを完了させたら、
- seq_item_port.item_done(); を実行します。これを実行するまでは、テストベクタ・テストシナリオは待たされます。
uvm_monitor
uvm_monitor
class sample_monitor extends uvm_monitor;
`uvm_component_utils(sample_monitor)
virtual sample_if vif;
function new (string name="sample_monitor", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual sample_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"});
endfunction: build_phase
task run_phase(uvm_phase phase);
fork
//check_clock;
check_trans;
join
endtask
task check_trans;
forever begin
@(posedge vif.valid) uvm_report_info("MON", $sformatf("addr=%02Xh, data=%02Xh", vif.addr, vif.data));
end
endtask
task check_clock;
forever begin
wait(vif.clk===1'b0);
uvm_report_info("MON", "fall clock");
wait(vif.clk===1'b1);
uvm_report_info("MON", "rise clock");
end
endtask
endclass
- virtualでinterfaceをインスタンスするのは、driverと同じです。
- build_phaseもdriverと同じです。
- task run_phaseの中で、モニタの動作を記述します。
uvm_agent
uvm_agent
class sample_agent extends uvm_agent;
`uvm_component_utils(sample_agent)
`uvm_new_func
sample_driver driver;
sample_sequencer sequencer;
sample_monitor monitor;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
`build_component(sample_driver,driver)
`build_component(sample_sequencer,sequencer)
`build_component(sample_monitor,monitor)
endfunction
function void connect_phase(uvm_phase phase);
driver.seq_item_port.connect(sequencer.seq_item_export);
endfunction
endclass
- driver, sequencer, monitorをインスタンスします。
- build_phaseで、`build_componentマクロを実行して、各コンポーネントを生成します。このマクロはぼくのオリジナルです。
`define build_component(NAME,INST) \
INST = NAME::type_id::create(`"INST`", this);
- connect_phaseで、sequencerとdriverをつなぎます。
uvm_env
uvm_env
class sample_env extends uvm_env;
`uvm_component_utils(sample_env)
`uvm_new_func
sample_agent agent;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
`build_component(sample_agent,agent)
endfunction
endclass
- agentをインスタンスします。
- build_phaseでagentを生成します。
uvm_test
uvm_test
class test extends uvm_test;
`uvm_component_utils(test)
`uvm_new_func
sample_env env;
virtual function void build_phase(uvm_phase phase);
uvm_config_db#(uvm_object_wrapper)::set(this,
"env.agent.sequencer.run_phase","default_sequence",
issue_one_trans_seq::type_id::get());
`build_component(sample_env,env)
super.build_phase(phase);
endfunction
endclass
- envをインスタンスします。
- build_phaseで、
- sequencerの「default_sequence」に、動かしたいsequence(テストベクタ・テストシナリオ)を指定します。
- envを生成します。
sequence(テストベクタ・テストシナリオ)
sequence(テストベクタ・テストシナリオ)
virtual class sample_base_sequence extends uvm_sequence #(sample_seq_item);
function new(string name="sample_base_seq");
super.new(name);
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 issue_one_trans_seq extends sample_base_sequence;
`seq_head(issue_one_trans_seq)
virtual task body();
sample_seq_item trans_item;
$display("I am issue_one_trans_seq");
`uvm_create(trans_item)
trans_item.addr = 8'h00;
trans_item.data = 8'h10;
`uvm_send(trans_item)
#1000ns;
endtask
endclass
- sample_base_sequenceを定義し、pre_body、post_bodyにオブジェクションの記述を入れておきます。
- sample_base_sequenceをextendsし、
- `seq_headを記述します。このマクロはぼくのオリジナルです。
`define seq_head(NAME) \
function new(string name=`"NAME`"); \
super.new(name); \
endfunction \
\
`uvm_object_utils(NAME)
- task bodyを定義します。uvm_testクラスの、default_sequence定義のところに指定されたsequenceは、Simulation実行時にbodyが呼び出されます。
- このサイトでは、無料のmodelsimを使っているため、uvm_sequence_itemを`uvm_createし、その中に値を書き込んで`uvm_sendしています。
テストベンチトップ
テストベンチトップ
`timescale 1ps/1ps
module tb_top;
// UVM ////////////////////////////////////////////////
`include "./uvm-1.1d/src/uvm_macros.svh"
import uvm_pkg::*;
// model
`include "sample.svh"
`include "test.sv"
//////////////////////////////////////////////////////
logic rstz, clk;
//
// rstz _________|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// clk ^^^^^^^^^^^^^^|__|^^|__|^^|__|^^|__|^^|__|^^
//
sample_if sif(clk, rstz);// interface
initial begin
fork
begin
clk = 1'b1;
#100ns;
forever #50ns clk = ~clk;
end
begin
rstz = 1'b0;
#100ns;
rstz = 1'b1;
end
join
end
initial begin
uvm_config_db#(virtual sample_if)::set(uvm_root::get(), "*", "vif", sif);
run_test();
end
endmodule
- `include "sample.svh"は、上記UVMモデルの各ファイルを`includeしてまとめたものです。
- initial文の中で、
- uvm_config_dbで、このmoduleにインスタンスしているinterfaceの情報を書き込みます。uvm_driverとuvm_monitorは、build_phase内のuvm_config_dbで「書き込まれた情報を取得」します。取得 に失敗すると、Simulation実行時にError表示されます。コンパイル、エラボレーションは通ってしまいます。
- run_test()で、UVMテストベンチを走らせます。
コンパイル(modelsim)
コンパイル(modelsim)
- 例:vlog -sv tb_top.sv +incdir+./uvm-1.1d/src
- module tb_top内でincludeするファイルのあるパスを、適時「+incdir+」などで付け加えます。
実行(modelsim)
実行(modelsim)
- vsim -c tb_top +UVM_TESTNAME=test -do "run -all;quit"
- 「+UVM_TESTNAME」というコマンドラインオプションを使って、uvm_testのクラス名を指定します。