UVMでC/C++のテストシーケンスを走らせる場合のアイディアを紹介しておこうと思う。
Dynamic Objectであるclass内ではDPI-CでC/C++をimportする事ができない。なので、UVM sequence class内ではC/C++のプログラムを直接importして使う事はできない。ではどうすれば良いのかと言うと、staticな領域であるpackage内でC/C++のfunctionをimportしてそれを呼ぶようにする。その際に直接呼ぶのではなく、そのpackageに別途SVのtaskを用意して、そのtaskからC/C++のfunctionをコールするようにする。そのSVのtaskの引数としてシーケンサーと親シーケンスのハンドルを指定して、それらをそのpackageに渡すようにする。C側から呼ぶSVのレジスタRead/WriteのSVタスクもpackage内に用意し、その中でではAgentのRead/WriteのAPIシーケンスをスタートさせる。その際に、先ほどのpackageに渡ったシーケンサーと親シーケンスのハンドルを引数にして.start()を実行する。
package dpi_seq_pkg;
import "DPI-C" context task C_Program();
export "DPI-C" task sv_write;
export "DPI-C" task sv_read ;
import uvm_pkg::*;
uvm_sequencer_base m_sqr;
uvm_sequence_base parent_seq;
// Called by a UVM sequence
task start(uvm_sequencer_base sequencer, uvm_sequence_base parent_sequence);
m_sqr = sequencer;
parent_seq = parent_sequence;
C_Program(); // C/C++ Program Call
endtask
// Called from C side
task sv_write (input int addr, data);
agent_pkg::write_sequence api_wr_seq = agent_pkg::write_sequence::type_id::create("api_wr_seq");
api_wr_seq.Val_A = addr;
api_wr_seq.Val_D = data;
api_wr_seq.start( .sequencer(m_sequencer), .parent_seuence(parent_seq) ); // API Sequence Start with Sequencer Handle and Parent Sequencer Handle
endtask : sv_write
// Called from C side
task sv_read (input int addr, output int data);
agent_pkg::read_sequence api_rd_seq = agent_pkg::read_sequence::type_id::create("api_rd_seq");
api_rd_seq.Val_A = addr;
api_rd_seq.start( .sequencer(m_sequencer), .parent_sequence(parent_seq) ); // API Sequence Start with Sequencer Handle and Parent Sequencer Handle
data = api_rd_seq.Val_D;
endtask : sv_read
endpackage
task C_sequence::body()
// C Program
dpi_seq_pkg::start( .sequencer(m_sequencer), .parent_sequence(this) );
バリエーションとしてstart() taskの代わりにdpi_seq_pkgの中にUVM Sequenceを定義してそれをstatさせるでもよい。その場合にはbody() taskの中でstart() taskと同様にsequnecerとparent sequenceのハンドルをpackageに渡すのとC_program()をコールするようにする。
package dpi_seq_pkg;
import "DPI-C" context task C_Program();
export "DPI-C" task sv_write;
export "DPI-C" task sv_read ;
import uvm_pkg::*;
uvm_sequencer_base m_sqr;
uvm_sequence_base parent_seq;
// UVM Sequence Class instead of start task
class dpi_c_sequence extends uvm_sequence;
`uvm_object_utils(dpi_c_sequence)
function new (string name="");
super.new(name);
endfunction
virtual task body();
m_sqr = this.get_sequencer(); // Pass sequencer handle
parent_seq = this.get_parent_sequence(); // Pass parent sequence handle
C_Program(); // C/C++ Program Call
endtask
endclass
// Called from C side
task sv_write (input int addr, data);
agent_pkg::write_sequence api_wr_seq = agent_pkg::write_sequence::type_id::create("api_wr_seq");
api_wr_seq.Val_A = addr;
api_wr_seq.Val_D = data;
api_wr_seq.start( .sequencer(m_sqr), .parent_seuence(parent_seq) ); // API Sequence Start with Sequencer Handle and Parent Sequencer Handle
endtask : sv_write
// Called from C side
task sv_read (input int addr, output int data);
agent_pkg::read_sequence api_rd_seq = agent_pkg::read_sequence::type_id::create("api_rd_seq");
api_rd_seq.Val_A = addr;
api_rd_seq.start( .sequencer(m_sqr), .parent_sequence(parent_seq) ); // API Sequence Start with Sequencer Handle and Parent Sequencer Handle
data = api_rd_seq.Val_D;
endtask : sv_read
endpackage
task C_sequence::body()
// Start DPI C Sequence
dpi_seq_pkg::dpi_c_sequence dpi_c_seq = dpi_seq_pkg::dpi_c_sequence::type_id::create("dpi_c_seq");
dpi_c_seeq.start(this.m_sequencer, this);
上記の例ではdpi_seq_pkgとして別packageを用意したが、もちろんテストsequenceが入っているpackageや他のテストコンポーネントが入っているpackageに同様の記述をしても良い。Agentのpackageをいじれるならこの記述自体をAgentのpackageに入れてC/C++を走らせるAgent API Sequenceとしてしまっても良いかもしれない。
package内でDPI-Cを使うことは許されているが、以前とある商用シミュレータでは実行時にオプションを設定しないとできなかった。うまくできない場合は各ベンダーのサポートに聞いてみよう。
また、Verification Academyでは以下のようなRAL(Register Abstraction Layer)を使った方法が提案されているが、ここまで複雑な事をせずとも、殆どの場合上記の様な方法で事足りるのではないだろうか。特にこの為だけにわざわざRAL(Register Abstraction Layer)を作るなんていうのは馬鹿げてる。
C-Based Stimulus for UVM (On Demand Seminars)
CBasedStimulus (Cookbook)
更に同じくVerification Academy内に以下の様なpapaerも見つけた。
UVM and C Tests - Perfect Together
Technical Paper: UVM and C Tests - Perfect Together
この例ではpackageではなく同じくstaticであるinterfaceを介してDPI-CでC/C++をコールしている。そしてinterface内で、Agent API Sequenceを介してではなく、Agentのsequencerを直接参照してsequenceを流しているようである。"the simplest approach"とか自称しているが、わざわざsequencerをいじったりしなくてはならず、とても"the simplest"には思えない。
因みにVerification Academyの情報は確かに有用ではあるのだが、時々トンチンカンな事を言ったりしているのも見受けられるので、少し注意が必要である。何でもそうなのだが、自分で何も考えずに妄信したりしてはいけない。
以下、今回紹介した方法の実際のサンプル環境をGit Hub公開したので参考にしてみてください。
フリーでUVMが動くDSim Desktop及びVivado Simulator (xsim)で動作するようにしてあります。
> git clone https://github.com/AlphaLyrae0/UVM_DPI_Example.git
でダウンロードできます。多分。
> cd UVM_DPI_Example
> make all
で全てコンパイルされて
- simple_test
- DPI-Cを使わない普通のUVMシーケンスが走るテスト
- dpi_test1
- DPI-Cを使ったC/C++のシーケンスが走るテスト
- dpi_test2
- DPI-Cを使ってC/C++から値を取って来て普通のUVMシーケンスが走るテスト
- unite_test
- 上記3つのシーケンスが連続で続けて走るテスト
の4つテストが全て実行されます。
個別のテストを実行したい場合には以下でで実行されます。
> make <Test Name>
ファイルをいじればテスト実行時に必要なコンパイルがされるので是非自分でいろいろいじって実行してみてください。C/C++ファイルだけいじればC/C++のコンパイルだけがされます。