OVMトライアル
このページの内容は、ともすけ趣味のツブヤキさんの成果をまとめたモノです(掲載了承済み)。
0、はじめに
ModelSim AEでOVMを実際に使用して、検証環境を構築した例(ARM社のAPB)です。
1、ファイル構成
添付ファイル(apb.tar.gz)を展開すると、
apb_bfmディレクトリの下に
svディレクトリ
exampleディレクトリ
が展開されます。
svディレクトリは、APBに関するモデルを格納しています。
exampleディレクトリは、APBに関するモデルの使用例です。
実際のシミュレーションは、exampleディレクトリにて行います。
2、APBモデル
svディレクトリにあるAPBモデルは、つぎのようなモノです。
APBシーケンス・アイテム (apb_seq_item.sv)
APBマスター・ドライバ (apb_master_driver.sv)
APBマスター・シーケンサ (apb_master_sequencer.sv)
APBマスター・エージェント (apb_master_agent.sv)
APBスレーブ・ドライバ (apb_slave_driver.sv)
APBスレーブ・シーケンサ (apb_slave_sequencer.sv)
APBスレーブ・エージェント (apb_slave_agent.sv)
env (apb_env.sv)
これらのファイルは、apb.svh内でまとめて`includeされています。
3、使用例
exampleディレクトリに使用例があります。
svディレクトリに格納されているファイルは、次のようなモノです。
APBインターフェース ( apb_if.sv)
APBマスター・シーケンス ( apb_master_seq.svh,
apb_master_seq_lib_base.sv,
abp_master_single_seq.sv)
APBスレーブ・シーケンス ( apb_slave_seq.svh,
abp_slave_normal_seq.sv)
test ( apb_sample_test.sv)
検証モデルのトップ記述 ( tb_sve.sv)
トップテストベンチ ( tb_top.sv)
ローカルに使用するマクロ定義 ( local_macro.svh )
4、ModelSim AEで動作確認
exampleディレクトリでつぎのコマンドを実行します。
なお、ModelSim AE対応版OVM 2.0.2 は、../../ovm-2.0.2にインストールされていることとします。
4.1、コンパイル
% vlib work
(workライブラリを作ります)
% vlog sv/apb_if.sv
(apbインターフェースをコンパイルする)
% vlog +incdir+../../ovm-2.0.2/src+../sv+sv sv/tb_top.sv
(トップテストベンチ:sv/tb_top.svをコンパイルする)
4.2、シミュレーション
% vsim tb_top +OVM_TESTNAME=apb_sample_test -do vsim.do \
+OVM_VERBOSITY=OVM_FULL
(テストとしてapb_sample_testを指定して、シミュレーションを実行する)
4.3、シミュレーション結果
シミュレーション結果は、つぎのようにtranscriptに出力されます。
# vsim -c +OVM_TESTNAME=apb_sample_test +OVM_VERBOSITY=OVM_FULL -do vsim.do tb_top
# Loading sv_std.std
# Loading work.tb_top
# Loading work.apb_if
# ----------------------------------------------------------------
# OVM-2.0.2
# (C) 2007-2009 Mentor Graphics Corporation
# (C) 2007-2009 Cadence Design Systems, Inc.
# ----------------------------------------------------------------
# do vsim.do
# resume
# OVM_INFO @ 0: reporter [RNTST] Running test apb_sample_test...
# [0] hier=ovm_test_top: running ovm_test
# ----------------------------------------------------------------------
# Name Type Size Value
# ----------------------------------------------------------------------
# <unnamed> ovm_root - @0
# ovm_test_top apb_sample_test - ovm_test_top@1
# apb_sve tb_sve - apb_sve@3
# apb0 apb_env - apb0@5
# masters[0] apb_master_agent - masters[0]@7
# driver apb_master_driver - driver@41
# rsp_port ovm_analysis_port - rsp_port@45
# sqr_pull_po+ ovm_seq_item_pull_+ - sqr_pull_port@43
# master_id integral 32 'h0
# sequencer apb_master_sequenc+ - sequencer@11
# rsp_export ovm_analysis_export - rsp_export@13
# seq_item_ex+ ovm_seq_item_pull_+ - seq_item_export@37
# master_id integral 32 'h0
# default_seq+ string 21 apb_master_single_s+
# count integral 32 -1
# max_random_+ integral 32 'd10
# sequences array 4 -
# [0] string 19 ovm_random_sequence
# [1] string 23 ovm_exhaustive_sequ+
# [2] string 19 ovm_simple_sequence
# [3] string 21 apb_master_single_s+
# max_random_+ integral 32 'd4
# num_last_re+ integral 32 'd1
# num_last_rs+ integral 32 'd1
# slaves[0] apb_slave_agent - slaves[0]@9
# driver apb_slave_driver - driver@77
# rsp_port ovm_analysis_port - rsp_port@81
# sqr_pull_po+ ovm_seq_item_pull_+ - sqr_pull_port@79
# slave_id integral 32 'h0
# sequencer apb_slave_sequencer - sequencer@47
# rsp_export ovm_analysis_export - rsp_export@49
# seq_item_ex+ ovm_seq_item_pull_+ - seq_item_export@73
# slave_id integral 32 'h0
# default_seq+ string 20 apb_slave_normal_seq
# count integral 32 -1
# max_random_+ integral 32 'd10
# sequences array 4 -
# [0] string 19 ovm_random_sequence
# [1] string 23 ovm_exhaustive_sequ+
# [2] string 19 ovm_simple_sequence
# [3] string 20 apb_slave_normal_seq
# max_random_+ integral 32 'd4
# num_last_re+ integral 32 'd1
# num_last_rs+ integral 32 'd1
# num_masters integral 32 'h1
# num_slaves integral 32 'h1
# ----------------------------------------------------------------------
# [0] hier=__global__: boku ha slave seq dayo
#
# [0] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: Wait sequence item
# [0] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: Wait sequence item
# [0] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: Debug : Command Issued
# [0] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: clock ha ugoiteru?
# [0] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: Debug : Command Issued
# [200] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PSEL
# [200] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: un, daijobu
# [200] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PENABLE
# [300] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PREADY latency
# [400] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: assert PREADY
# [500] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: Wait sequence item
# [500] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: negate PREADY
# [500] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: Wait sequence item
# [500] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: Debug : Command Issued
# [500] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: clock ha ugoiteru?
# [500] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: Debug : Command Issued
# [600] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PSEL
# [600] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: un, daijobu
# [600] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PENABLE
# [700] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PREADY latency
# [800] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: assert PREADY
# [900] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: Wait sequence item
# [900] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: negate PREADY
# [900] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: Wait sequence item
# [900] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: Debug : Command Issued
# [900] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: clock ha ugoiteru?
# [900] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: Debug : Command Issued
# [1000] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PSEL
# [1000] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: un, daijobu
# [1000] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PENABLE
# [1100] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PREADY latency
# [1200] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: assert PREADY
# [1300] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: Wait sequence item
# [1300] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: negate PREADY
# [1300] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: Wait sequence item
# [1300] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: Debug : Command Issued
# [1300] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: clock ha ugoiteru?
# [1300] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: Debug : Command Issued
# [1400] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PSEL
# [1400] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: un, daijobu
# [1400] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PENABLE
# [1500] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PREADY latency
# [1600] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: assert PREADY
# [1700] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: Wait sequence item
# [1700] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: negate PREADY
# [1700] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: Wait sequence item
# [1700] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: Debug : Command Issued
# [1700] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: clock ha ugoiteru?
# [1700] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: Debug : Command Issued
# [1800] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PSEL
# [1800] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: un, daijobu
# [1800] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PENABLE
# [1900] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: wait PREADY latency
# [2000] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: assert PREADY
# [2100] hier=ovm_test_top.apb_sve.apb0.masters[0].driver: Wait sequence item
# [2100] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: negate PREADY
# [2100] hier=ovm_test_top.apb_sve.apb0.slaves[0].driver: Wait sequence item
#
# --- OVM Report Summary ---
#
# ** Report counts by severity
# OVM_INFO : 60
# OVM_WARNING : 0
# OVM_ERROR : 0
# OVM_FATAL : 0
# ** Report counts by id
# [DEBUG] 48
# [RNTST] 1
# [STOP] 11
# ** Note: $finish : ../../ovm-2.0.2/src/base/ovm_root.svh(488)
# Time: 2100 ps Iteration: 6 Instance: /tb_top/ovm_root::run_test
4.4、テスト内容
examples/sv/apb_sample_test.svにテストapb_sample_testが次のように定義しています。
シーケンサ( apb_sve.apb0.masters[0].sequence )には、apb_master_single_seq を
シーケンサ( apb_sve.apb0.slaves[0].sequencer )には、apb_slave_normal_seq を設定しています。
class apb_sample_test extends ovm_test;
`ovm_component_utils(apb_sample_test)
tb_sve apb_sve;
function new(string name = "apb_sample_test", ovm_component parent=null);
super.new(name,parent);
endfunction : new
virtual function void build();
set_config_string("apb_sve.apb0.masters[0].sequencer",
"default_sequence", "apb_master_single_seq");
set_config_string("apb_sve.apb0.slaves[0].sequencer",
"default_sequence", "apb_slave_normal_seq");
super.build();
apb_sve = tb_sve::type_id::create("apb_sve", this);
endfunction : build
task run;
`message(OVM_NONE, ("running ovm_test"))
ovm_top.print();
endtask
endclass
シーケンス( apb_master_single_seq )は、sv/examples/apb_master_single_seq.svで次のように定義しています。
apb_master_seq_lib_base クラスを継承しています。
body タスクで、5回(loop変数)、apb_write タスクを実行します。
その後、シーケンサ( p_sequencer )に対して、global_stop_request を実行します。
class apb_master_single_seq extends apb_master_seq_lib_base;
function new(string name="apb_master_single_seq");
super.new(name);
endfunction : new
`ovm_sequence_utils(apb_master_single_seq, apb_master_sequencer)
virtual task body();
int loop = 5;
for (byte i=0; i<loop; i++) begin
apb_write(32'h0000_1000 + i*4, i);
end
p_sequencer.global_stop_request();
endtask
endclass
apb_master_seq_lib_base クラスは、次のように定義しています。
apb_seq_item クラスをシーケンス・アイテムとしています。
また、apb_write タスクを定義しています。このタスクは、APBに対して、ライトトランザクションを生成します。
トランザクションは、通常`ovm_doマクロで行いますが、ModelSim AEでは、randomize関数をサポートされていないので、
下記のように、`ovm_createマクロと`ovm_sendマクロを使っています。
この apb_write タスクは、apb_master_seq_lib_base クラスを継承するクラスで利用できます。
class apb_master_seq_lib_base extends ovm_sequence #(apb_seq_item);
function new(string name="apb_master_seq_lib_base");
super.new(name);
endfunction : new
task apb_write (bit [31:0] addr, data);
`ovm_create(req)
req.cmd_kind = 0;
req.PADDR = addr;
req.PWDATA = data;
req.PWRITE = 1;
`ovm_send(req)
endtask
endclass
apb_slave_normal_seq クラスは、次のように定義しています。
apb_seq_item クラスをシーケンス・アイテムとしています。
class apb_slave_normal_seq extends ovm_sequence #(apb_seq_item);
function new(string name="apb_slave_normal_seq");
super.new(name);
endfunction : new
`ovm_sequence_utils(apb_slave_normal_seq, apb_slave_sequencer)
virtual task body();
`msg(OVM_NONE, ("boku ha slave seq dayo\n"))
forever begin
`ovm_create(req)
req.latency = 1;
`ovm_send(req)
end
endtask
endclass
4.5、新しいテストを定義する
新しいテストを定義するときは、下記の abp_sample_test の部分を新しいテスト名に変え、
apb_master_single_seq と apb_slave_normal_seq を適当な値にすればいいのです。
class 新しいテストのクラス名 extends ovm_test;
`ovm_component_utils( 新しいテストのクラス名 )
tb_sve apb_sve;
function new(string name = " 新しいテストのクラス名 ", ovm_component parent=null);
super.new(name,parent);
endfunction : new
virtual function void build();
set_config_string("apb_sve.apb0.masters[0].sequencer",
"default_sequence", "apbマスターのシーケンス名");
set_config_string("apb_sve.apb0.slaves[0].sequencer",
"default_sequence", "apbスレーブのシーケンス名");
super.build();
apb_sve = tb_sve::type_id::create("apb_sve", this);
endfunction : build
task run;
`message(OVM_NONE, ("running ovm_test"))
ovm_top.print();
endtask
endclass
変更するのが5箇所だけです。そこで、次のようなマクロを定義します。
下記に添付してあります( apb_test_macro.svh )。
`define MY_APB_TEST( apb_test, apb_master, apb_slave ) \
class apb_test extends ovm_test; \
\
`ovm_component_utils( apb_test ); \
\
tb_sve apb_sve; \
\
function new(string name = `"apb_test`", ovm_component parent=null); \
super.new(name,parent); \
endfunction : new \
\
virtual function void build(); \
set_config_string("apb_sve.apb0.masters[0].sequencer", \
"default_sequence", `"apb_master`" ); \
set_config_string("apb_sve.apb0.slaves[0].sequencer", \
"default_sequence", `"apb_slave`" ); \
super.build(); \
apb_sve = tb_sve::type_id::create("apb_sve", this); \
endfunction : build \
\
task run; \
`message(OVM_NONE, ("running ovm_test")) \
ovm_top.print(); \
endtask \
\
endclass
このマクロを使えば、テストは次のように簡単に記述できます。
`MY_APB_TEST( apb_sample_test, apb_master_single_seq, apb_slave_normal_seq)
便利でしょう!
4.6、新しいシーケンスを定義する
テストとどうようにシーケンスもマクロ定義してみます。
下記に添付してあります( apb_master_seq_macro.svh )。
`define MY_APB_MASTER_SEQ_BEGIN( apb_m_seq ) \
class apb_m_seq extends apb_master_seq_lib_base; \
\
function new(string name=`"apb_m_seq`"); \
super.new(name); \
endfunction : new \
\
`ovm_sequence_utils(apb_m_seq, apb_master_sequencer) \
\
virtual task body();
`define MY_APB_MASTER_SEQ_END \
p_sequencer.global_stop_request(); \
endtask \
\
endclass
このマクロを使えば、シーケンスは次のように簡単に記述できます。
`MY_APB_MASTER_SEQ_BEGIN( apb_master_single_seq )
int loop = 5;
for (byte i=0; i<loop; i++) begin
apb_write(32'h0000_1000 + i*4, i);
end
‘MY_APB_MASTER_SEQ_END
またまた、便利でしょう!