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

またまた、便利でしょう!