Verilog手続き型インターフェース( VPI ) は、元々はPLI 2.0と呼ばれていましたが、主にC言語向けに設計されたインターフェースです。VPI を使用すると、動作指定のVerilogコードから C 関数を呼び出したり、C 関数から標準 Verilog システム タスクを呼び出したりできます。VPI は、IEEE 1364プログラミング言語インターフェース規格の一部であり、この規格の最新版は 2005 年版です。VPI は、廃止されたProgram Language Interface (PLI)に取って代わるものであるため、PLI 2 と呼ばれることもあります。
PLI 1はVPI(別名PLI 2)に置き換えられて非推奨となりましたが、多くのVerilogリファレンスブックで説明されているtf_put、tf_get関数インターフェースがはるかに広く文書化されているため、PLI 1はVPIよりも依然として一般的に使用されています。
C++は、C++コンパイラに組み込まれている「extern C/C++」キーワードを使用することで、VPI(PLI 2.0)およびPLI 1.0と統合可能です。
例として、次の Verilog コード断片を考えてみましょう。
val = 41; $increment(val); $display("$increment 後、val=%d", val); システムタスクが最初のパラメータを1つ増やすと仮定しますincrement。C言語とVPIメカニズムを使用すると、incrementタスクは次のように実装できます。
// インクリメント システム タスクを実装しますstatic PLI_INT32 increment ( PLI_BYTE8 * userdata ) { vpiHandle systfref , args_iter , argh ; s_vpi_value argval ; int value ;// 引数リストへのハンドルを取得しますsystfref = vpi_handle ( vpiSysTfCall , NULL ); args_iter = vpi_iterate ( vpiArgument , systfref );// 最初の引数の値を取得しますargh = vpi_scan ( args_iter ); argval . format = vpiIntVal ; vpi_get_value ( argh , & argval ); value = argval . value . integer ; vpi_printf ( "VPI ルーチンを受信しました %d \n " , value );// 値をインクリメントして、最初の引数として戻します。argval.value.integer = value + 1 ; vpi_put_value ( argh , & argval , NULL , vpiNoDelay );// クリーンアップしてvpi_free_object ( args_iter );を返すreturn 0 ; }また、このシステムタスクを登録する関数も必要です。この関数は、外部からアクセス可能なvlog_startup_routines[]配列に配置された際に、参照の処理や解決の前に呼び出されます。
// インクリメント システム タスクを登録しますvoid register_increment ( void ) { s_vpi_systf_data data = { /* type */ vpiSysTask , /* sysfunctype */ vpiSysTask , /* tfname */ "$increment" , /* calltf */ increment , /* compiletf */ NULL , /* sizetf */ NULL , /* user_data */ NULL };vpi_register_systf ( & data ); }// 起動時に呼び出す必要がある関数のゼロ終端リストが含まれていますvoid ( * vlog_startup_routines [])( void ) = { register_increment , NULL };Cコードは、Verilogシミュレータで使用される共有オブジェクトにコンパイルされます。前述のVerilogコードの断片をシミュレーションすると、次の出力が得られます。
VPIルーチンは41を受信しました $increment 後、val=42