Vengineerの妄想(準備期間)

人生は短いけど、長いです。人生を楽しみましょう!

DPI-CとBFM、その2

Verification Engineerの戯言

今回は、前回(http://blogs.yahoo.co.jp/verification_engineer/12162656.html)に続いて、
DPI-CとBFMについてです。

2つのポイント、

A)、SystemVerilog側からC言語側を呼び出すとき、
B)、C言語の関数からBFMの外部バスにアクセスするとき、

のB)について、本日は説明します。

C言語の関数からBFMの外部バスにアクセスするには、SystemVerilog側のtaskにて行います。
外部バスへのアクセスをSystemVerilogのtaskは次のようになっているとします。

task bus_read( input int unsigned addr, output int unsigned data );
bus.read( addr, data );
endtask : bus_read

task bus_write( input int unsigned addr, input int unsigned data );
bus.write( addr, data );
endtask : bus_write

bus_readタスクは外部バスへのリードアクセス、bus_writeタスクは外部バスへのライトアクセスに
なります。実際の外部バスのアクセスは、busインターフェース内のbus_read/bus_writeタスクに
任せます。

この2つのタスク(bus_read/bus_write)をC言語側から使えるように次のようにします。

export "DPI-C" task bus_read;
export "DPI-C" task bus_write;

importのときと違って、タスクに対する引数の宣言は必要ありません。
ただし、C言語側での関数のプロトタイプでは、次のように引数の型は必要です。

int bus_read( unsigned int addr, unsigned int *data );
int bus_write( unsigned int addr, unsigned int data );

SystemVerilog側のtaskの引数がinputの場合は値渡しで、outputの場合はポインタ渡しになります。
また、SystemVerilogのタスクをC言語側で使うときは、戻り値は必ずint型になります。
通常の戻り値は"0"です。SystemVerilog側のtaskがdisableされたときは戻り値が"1"です。
(disableは、Verilog HDLからあるもので、disable bus_read; のように使います)。

これでC言語側からSystemVerilog側のtaskが使えるようになりました。

c_main関数内では、つぎのようになります。

int c_main( void )
{
unsigned int addr, data, exp;

addr = 0x00004000;
data = 0xabcdef01;

bus_write( addr, data );
bus_read( addr, &exp );

if( data != exp )
printf("<<ERR>> : mismatch data(0x%x) != exp(0x%x)\n",
data, exp );
return 0;
}

アドレス(0x000040000)にデータ(0xabcdef01)をライト、リードし、
ライトした値とリードした値を比較します。

c_main関数の戻り値は、bus_read/bus_writeと同じようにdisableされたときは"1"になります。
通常の戻り値は"0"です。

c_main関数を実行すると、シミュレーション時間が進むのは、bus_read/bus_writeを実行した部分です。
それ以外の部分ではシミュレーション時間は進みません。
でも、外部バスへのアクセスすること無しにシミュレーション時間を進めたい場合は、
SystemVerilog側で時間を進めるタスクを呼び出せばいいのです。そのためのタスクは
次のようなものでいいでしょう!

export "DPI-C" task bus_nop;

task bus_nop( input int n );
for( int i=0 ; i<n ; i++ )
@(posedge CLK);
endtask : bus_nop

このタスクでは、指定したクロック数だけシミュレーション時間を進めます。
このタスクを使って、C言語側からアクセスします。

int bus_nop( int n );

int c_main( void )
{
unsigned int addr, data, exp;

addr = 0x00004000;
data = 0xabcdef01;

bus_write( addr, data );
bus_nop( 10 );
bus_read( addr, &exp );

if( data != exp )
printf("<<ERR>> : mismatch data(0x%x) != exp(0x%x)\n",
data, exp );
return 0;
}

bus_writeとbus_readの間に10クロック分、シミュレーション時間を進めます。

ここまで説明した内容でとりあえず、BFMでDPI-Cを利用できるようになります。