16.2 DPI
16.2.1. VPI/DPIとの違い

16.2.1.1 SystemVerilot taskを直接呼べる

PLI/VPIとの大きな違いは、CからSVのtask/functionを”直接に”呼べることです。PLI/VPIの場合は、イベントの発生を待ってCALLBACKで呼んでもらう方法しかなく、Cから主体的に呼ぶ方法はありません。ところが、DPIを使うとC側から、SystemVerilogのtaskを直接に呼びます。これにより、時間を消費するDelayやイベントを含むtask を呼べるのでCとの同期通信が、全く簡単になります。


いままでそのようなIFが無いこと自体が不思議と思うかもしれません。そのためには、OBJECTレベルのリンクが不可避で、逆に言うと、いままでは、PLI/VPIでそれを巧妙に避けてきた、ということだと思います。

16.2.1.2 速度重視

2番目の違いは速度重視です。OBJECTレベルのリンクなので、オーバヘッドは0 または、VPIに比べると小さいです。 逆にいうとチェックがありません。Cで配列のアクセスチェックがないのと同様に、定義したOBJECTに対してなんらチェックは働きません。定義して、定義したとおりに実装するのはユーザの責任です。リンクは、CレベルのリンクがLRMで定義されています。しかしCレベルのリンケージというのは、型や引数は定義されていなくて、リンカでチェックは働きません。Cリンケージで外部にエキスポートされたシンボルというのは、変数なのか関数なのかさえ区別がありません。ですから間違うと簡単にクラッシュします。

<DLLの互換性 >
リンカが文句を言うのは、C++のリンクだからです。同一C++コンパイラ内のリンクでは、引き数や型のチェックが働きます。しかし、C++コンパイラ間の互換はありませんし、Cだけではなく、他の言語とのインターフェースの為に、DPIでは、Cリンケージになっています。厳密に言えば、Cリンケージにしても、レジスタ規約や、構造体のアライメントが同じでないと、各Cコンパイラで生成したDLL間の互換性が保証されません。X86については、ほぼ互換性はあると言ってよいと思いますが、X64では、GCC-VC++間のレジスタ規約が違うのでDLL間の互換性はない筈です。

ここまでする背景には、Cとのtransaction level 通信を徹底的に速くしたい、という要求があるのだと思います。

試しにVPIを使った場合に比べてどれ位になるか次のソースでやってみました。

  1. `define USE_SV_FUNCTION//USE_INLINE
  2. module incremental_call_test;
  3.  
  4. `ifdef USE_DPI
  5.         import "DPI-C"  pure function int inc (int n1);
  6. `else
  7.  
  8.         function  int inc(int i1);
  9.                 return i1+1;
  10.         endfunction
  11.  
  12. `endif
  13.  
  14.         int i;
  15.        
  16.         initial begin
  17.                 i=0;
  18.                 repeat(100000000) begin
  19.                         `ifdef USE_INLINE
  20.                                 i++;           
  21.                         `elsif  USE_DPI
  22.                                 i=inc(i);
  23.                         `elsif USE_SV_FUNCTION
  24.                                 i=inc(i);
  25.                         `elsif USE_VPI
  26.                                 i=$Increment(i);       
  27.                         `endif
  28.                        
  29.                 end
  30.                 $display("i=%d",i);
  31.  
  32.         end
  33.  
  34. endmodule

DPI C ソースは、svdpi.h をインクルードし

  1. __declspec(dllexport) int inc (int i)
  2. {
  3.         return i+1;
  4. }

一方、VPIについては、 vpi_user.hをインクルードして次のようなCソースになります。

  1.  
  2. staticint sys_Increment(char*  name)
  3. {
  4.  
  5.  vpiHandle systfref, argsiter, argh;
  6.  
  7.   s_vpi_value value;
  8.   unsignedint num;    
  9.   unsigned i;
  10.  
  11.   systfref = vpi_handle(vpiSysTfCall, NULL); /* get system function that invoked C routine */
  12.   argsiter = vpi_iterate(vpiArgument, systfref);/* get iterator (list) of passed arguments */
  13.                  
  14.         for (i=0;i<1;i++){                     
  15.                 argh = vpi_scan(argsiter);/* get the one argument - add loop for more args */
  16.                 if(!argh){
  17.                                 vpi_printf("$Increment: missing parameter. \n");
  18.                             return 0;
  19.                                
  20.                         }      
  21.                        
  22.                 value.format = vpiIntVal;
  23.                 vpi_get_value(argh ,&value);
  24.                 num=value.value.integer;
  25.         }      
  26.                
  27.                
  28.  
  29.         if(argh) vpi_free_object(argsiter);
  30.                
  31.         value.value.integer =num+1;//インクリメント本体
  32.         value.format = vpiIntVal;/* return the result */
  33.  
  34.         vpi_put_value(systfref ,&value, NULL, vpiNoDelay);
  35.                  
  36.         return(0);
  37.          
  38.  }
  39.  
  40. //32bit 専用
  41. staticint sys_systems_size_tf(PLI_BYTE8* p)//VPI をコンパイラに報告
  42. {
  43.         return 32;
  44. }
  45.  
  46. void sys_inc_vpi_register()//VPI call/callback register routine
  47. {
  48.       s_vpi_systf_data tf_data;
  49.  
  50.           tf_data.type      =vpiSysFunc;
  51.       tf_data.sysfunctype       =vpiIntFunc;
  52.       tf_data.tfname    = "$Increment";
  53.       tf_data.user_data = "$Increment";
  54.       tf_data.calltf    = sys_Increment;//関数本体
  55.       tf_data.compiletf = 0;
  56.       tf_data.sizetf    = sys_systems_size_tf;//サイズ
  57.           vpi_register_systf(&tf_data);
  58.          
  59. }        
  60.          
  61. __declspec(dllexport) void (*vlog_startup_routines[])() = {//VPI スタートアップルーチンを登録
  62.       sys_inc_vpi_register,
  63.        0
  64. }; 

このように、単にインクリメント値を返すだけのファンクションですが、VPIでは、本題に行くまでにかなりのコードを書く必要があります。一方DPIの方は、ダイレクトの記述でOKです。

結果:

VeritakSV ver0.25 Q9550 Vista 32bit ,VC8++ release モードでコンパイル

時間 ifdef 定義
インライン記述 0.26sec USE_INLINE
DPI 0.42sec USE_DPI
VPI 21sec USE_VPI
SV関数 0.8sec USE_SV_FUNCTION

DPIとVPIでは、50倍近い差となりました。この例は、単純にインクリメントだけですので、やや極端な結果になっています。

16.2.1.3 DPI-SC
 C/C++とsystemverilogの通信を掌るのが、"DPI-C" で宣言されたfunctionや、taskです。では、systemc とのやり取りも同じでよいかというと、そこはまた別で、
 "DPI-SC" として宣言します。 これは、ベンダユニークでLRM化されてはいませんが、他のベンダも多分同じです。VeritakSVでは、systemcもシングルカーネルで動きますが、内部スレディングの扱いが違います。そのために必要な宣言です。 なお、"DPI-C"と"DPI-SC" を間違えるとクラッシュします。

16.2.2 インポート

SystemVerilogからCの関数・タスクを呼び出すことをインポートすると言います。
インポートの手順は、

SV側
1)インポート(関数またはタスク)を宣言する

        import "DPI-C"  pure function real Sin (real n1);
   
   
2)インポート(関数またはタスク)を起動する
      
  1.        
  2. `define PAI 3.141529
  3.         initial begin
  4.                
  5.                 for(int i=0;i<=5;i++)
  6.                         $display("sin=%f",Sin(i*`PAI/180));
  7.        
  8.         end
  9.  


C側
3)インポート(関数またはタスク)を定義する

  1. #include <math.h>
  2. #include <svdpi.h>
  3.  
  4. extern "C" __declspec(dllexport) double Sin(double r)
  5. {
  6.         return sin(r);
  7. }


のようになります。

<Cランタイムは直接呼べる>
ところで、上の例の場合、C側の関数でCのランタイムを呼び出しています。ユーザのSin関数は、結局のところCのランタイムを呼び出しているだけで少し冗長ですね。このようなCのランタイム関数呼び出しについては、SystemVerilogから直接呼び出すことが可能です。以下のように インポート定義を書くだけで、DLL作成は必要です。

  1. module incremental_call_test;
  2.  
  3.  
  4.         import "DPI-C"  pure function real cos (real n1);//DLL作成不要 インポート宣言だけでよい
  5.         import "DPI-C"  pure function real sinh (real n2);//DLL作成不要 インポート宣言だけでよい
  6.  
  7. `define PAI 3.14159265358979323846
  8.         initial begin
  9.                
  10.                 for(int i=0;i<=90;i++)
  11.                         $display("cos=%f sinh=%f",cos(i*`PAI/180),sinh(i*`PAI/180));
  12.        
  13.         end
  14.  
  15. endmodule


16.2.3 エクスポート
 SystemVerilogの関数やタスクがCから呼ばれることをエクスポートすると言います。エクスポートするには、まず、インポートすることから始める必要があります。
  

 という順序が必然です。システムスケジューラ(シミュレーションカーネル)は、SystemVerilog側にあるので、必ずこのような形態になります。

SV側: 
1)インポート宣言

        import "DPI-C"  context function void import_func ();//Exportするにはimportが必要

2)インポートファンクション・タスクを起動

  1. initial
  2.   import_func();//Cのインポートファンクションを呼ぶ

3)エクスポート宣言

  1.         export "DPI-C"  function  sv_inc;//Export宣言では、ファンクション名・タスク名しか書かない
     

4)エクスポートファンクション・タスクの中身

  1.         function  int sv_inc(int i1);//Cから呼ばれるExportファンクション
  2.                 $display("私は sv_inc 関数です。");
  3.                 return i1+1;
  4.         endfunction


C側:

5)Cインポートファンクション

  1. extern "C" __declspec(dllexport) void import_func()
  2. {
  3.         int i;
  4.         vpi_printf("私は、C のimport_func()です。\n");
  5.         i=sv_inc(100);//SV Functionを呼ぶ
  6.         vpi_printf("私はCです。 sv_incを呼び出した結果=%dが返ってきました。\n",i);//printfの代わりにvpi_printfを使う
  7.  
  8. }

 printfの代わりにVPIファンクションvpi_printfを使っているのは、SV上のコンソールにSV/C両方の結果が出るからです。


SV側全体です

  1. module export_test;
  2.  
  3.        
  4.         import "DPI-C"  context function void import_func ();//Exportするにはimportが必要
  5.         export "DPI-C"  function  sv_inc;//Export宣言では、ファンクション名・タスク名しか書かない
  6.  
  7.         function  int sv_inc(int i1);//Cから呼ばれるExportファンクション
  8.                 $display("私は sv_inc 関数です。");
  9.                 return i1+1;
  10.         endfunction
  11.  
  12.        
  13.         initial
  14.                 import_func();//Cのインポートファンクションを呼ぶ
  15.  
  16.  
  17.  
  18.  


実行結果です。

***** Veritak SV Engine Version 0.25 Build Oct.23.2009 *****

私は、C のimport_func()です。
私は sv_inc 関数です。
私はCです。 sv_incを呼び出した結果=101が返ってきました。

**** Test Done. Total 3.00[msec] ****

SV-Cを行ったり来たりしていますが、Cのファンクションの中身と結果は、直線的で分かりやすいと思います。 

<functionとtaskの違い>
この例では、SV・C共にfunctionだけを使っていますが、functionからtask を呼べないのは、DPIでも同じです。ディレイや、イベント待ちを処理するのは、verilogと同様にtask しか書けませんので、そういう場合には、taskにします。逆に、ディレイやイベント待ちがないのなら、functionを使うのが速度的に有利です。functionの実装は、多くの実装の場合Cのスタックフレームをそのまま使うと思います。スレッド処理(コンテキスト保存)が不要なためtaskより高速です。 SystemVerilogでは、上の例のように、値を返さない void funtion が使用可能になっているので、 ディレイや、イベント待ちがない場合は、functionを使うとよいでしょう。

<pure と context >
pureは、ここは、オプティマイズが可能だよ、とシミュレータに教えてやるためのものです。結果が引き数だけで決まるものの場合、pureをつけることができます。inoutや、output ポートを持つものは、pureにすることはできません。高度なコンパイラでは意味があるかもしれませんが、現状VeritakSVでは使っていませんのであってもなくても変りません。他のコンパイラならば、影響する可能性はあるかもしれません。安全サイドは、pureなしです。

しかし、contextは、違います。あるべきところにcontextが附加されていないと簡単にクラッシュします。SVコンパイラは、インポートの中身(Cの中身)について知ることはできないので、ユーザサイド側からコンパイラに教えてやるべきことになります。逆に、contextがなくてもよいところにcontextがあるとどうなるかというと、挙動には影響がなく速度低下だけです。ですから安全サイドは、contextをつけることになります。

T.B.D.
<インポートタスクの例>
SV側記述

  1. module export_test;
  2.  
  3.        
  4.         import "DPI-C"  context task import_task();//Exportするにはimportが必要
  5.         export "DPI-C"  function  get_sv_time;//Export宣言では、ファンクション名・タスク名しか書かない
  6.  
  7.  
  8.  
  9.         function longint get_sv_time();
  10.                 return $time;
  11.         endfunction
  12.  
  13.        
  14.         initial begin
  15.                 #10;
  16.                 import_task();//Cのインポートタスクを呼ぶ
  17.         end
  18.  
  19.  
  20. endmodule


C側記述:

  1. extern "C" __declspec(dllexport) void import_task()
  2. {
  3.         __int64  t;
  4.         vpi_printf("私は、C のimport_task()です。\n");
  5.         t=get_sv_time();//SV Functionを呼ぶ
  6.         vpi_printf("私はCです。 現在時刻は%ldです。\n",t);//printfの代わりにvpi_printfを使う
  7.  
  8. }


結果:
***** Veritak SV Engine Version 0.25 Build Oct.23.2009 *****

私は、C のimport_task()です。
私はCです。 現在時刻は10です。

**** Test Done. Total 1.00[msec] ****


今度は、インポートタスク内で、ディレイを作りたいと思います。しかし、C言語では、Verilogのような並列プロセスを記述する機構がありません。そこで、ディレイやイベント類は、SV側で面倒を見てもらうことにします。時間を消費するtaskを呼び出します。


SV側:

  1. `timescale 1ns/1ns
  2. module export_test1;
  3.  
  4.        
  5.         import "DPI-C"  context task import_task1();//Exportするにはimportが必要
  6.  
  7.         export "DPI-C"  function  get_sv_time;//Export宣言では、ファンクション名・タスク名しか書かない
  8.         export "DPI-C"  task    delay_task;
  9.  
  10.  
  11.         function longint get_sv_time();
  12.                 return $time;
  13.         endfunction
  14.  
  15.         task delay_task();
  16.                 #(10);
  17.         endtask
  18.  
  19.         initial begin
  20.                 #1;
  21.                 fork
  22.                         import_task1();//Cのインポートタスクを呼ぶ
  23.                         repeat(6) #2 $display("..............SV実行中です。時刻= %2d",$time);
  24.                 join
  25.                 $display("join しました。");
  26.         end
  27.  
  28.  
  29. endmodule

C側:

  1.  
  2. extern "C" __declspec(dllexport) void import_task1()
  3. {
  4.  
  5.  
  6.  
  7.         vpi_printf("私はCです。 現在時刻は%ldです。これから#10後に戻ってきます。\n",get_sv_time());
  8.  
  9.         delay_task();//時間消費export task を呼ぶ
  10.  
  11.         vpi_printf("私はCです。 戻ってきました。現在時刻は%ldです。\n",get_sv_time());
  12.  
  13.  
  14. }

実行結果です。fork join 内のステートメントは平行に走ります。上のC記述が、SystemVerilogタスクを記述するように書けることに注目してください。


***** Veritak SV Engine Version 0.25 Build Oct.23.2009 *****

私はCです。 現在時刻は1です。これから#10後に戻ってきます。
..............SV実行中です。時刻= 3
..............SV実行中です。時刻= 5
..............SV実行中です。時刻= 7
..............SV実行中です。時刻= 9
私はCです。 戻ってきました。現在時刻は11です。
..............SV実行中です。時刻= 11
..............SV実行中です。時刻= 13
join しました。

**** Test Done. Total 5.00[msec] ****


これにより、SVとC/C++間の同期通信が全く簡単になることがお分かりでしょう。CでSVタスクを呼んだ後でもCのコンテキストは、何事もなかったのごとく保存されていますので、ユーザは、自身の仕事に専心できるでしょう。

下の図は、筆者のイメージするSV-C++モデルです。Mainは、従来どおり、SVスケジューラになります。Initail Import TaskでC++オブジェクトモデルを生成します。この場合のImport Taskは、SIMの全期間を通じて生きるイメージです。ただし、ImportTaskはDynamicに生成され、ImportTaskを抜けたところでDeleteされますので、あくまでDynamicなObjectです。一旦、Import Taskが呼び出されれば、後は、SVとの同期通信は、Export Taskを使用してC++ 側に立って書くことが出来ます。









<インポートとエクスポートの例>

 
下の例では、fork join 間でスレッドを4つ平行に起動しています。内一つは、インポートtask で他の3つのスレッドと平行して動きます。

  1. `timescale 1ns/1ns
  2. module export_test1;
  3.  
  4.        
  5.         import "DPI-C"  context task import_task2();//Exportするにはimportが必要
  6.  
  7.         export "DPI-C"  function  get_sv_time;//Export宣言では、ファンクション名・タスク名しか書かない
  8.         export "DPI-C"  task delay_task_by_parameter;
  9.         export "DPI-C"  task wait_n_clks;
  10.         export "DPI-C"  task wait_trigger;
  11.         export "DPI-C"  task wait_level_high;
  12.  
  13.         event ev;
  14.         reg level=0;   
  15.  
  16.         function longint get_sv_time();
  17.                 return $time;
  18.         endfunction
  19.  
  20. //クロックジェネレーター
  21.         reg clk=0;
  22.         always #1 clk=~clk;
  23.  
  24. //指定時間待つ
  25.         task delay_task_by_parameter( inputlongint d);
  26.                 #(d);
  27.         endtask;
  28.  
  29. //指定クロック数待つ
  30.         task wait_n_clks( inputint n);
  31.                 repeat(n) begin
  32.                         @(negedge clk);
  33.                 end
  34.         endtask
  35.  
  36. //イベント待ち
  37.         task wait_trigger();
  38.                 @(ev);//イベント待ち
  39.         endtask
  40.  
  41. //レベルH待ち
  42.         task wait_level_high();
  43.                 wait(level==1);
  44.         endtask
  45.        
  46.  
  47.         initial begin
  48.                 #1;
  49.                 fork
  50.                         import_task2();//Cのインポートタスクを呼ぶ
  51.                         #40 level=1;
  52.                         repeat(21) #2 $display("..............SV実行中です。時刻= %2d",$time);
  53.                         #30 ->ev;
  54.                 join
  55.                 $display("join しました。 %d",$time);
  56.                 $finish();
  57.         end
  58.  
  59.  
  60. endmodule

このCプログラムです。

  1. extern "C" __declspec(dllexport) void import_task2()
  2. {
  3.  
  4.  
  5.  
  6.         vpi_printf("私はCです。 現在時刻は%ldです。これから#10後に戻ってきます。\n",get_sv_time());
  7.  
  8.         delay_task_by_parameter(10);//時間消費export task を呼ぶ
  9.         vpi_printf("私はCです。 戻ってきました。現在時刻は%ldです。5CLK待ちます。\n",get_sv_time());
  10.  
  11.         wait_n_clks(5);
  12.         vpi_printf("私はCです。 戻ってきました。現在時刻は%ldです。トリガを待ちます。\n",get_sv_time());
  13.        
  14.         wait_trigger();
  15.         vpi_printf("私はCです。 戻ってきました。現在時刻は%ldです。レベルがHighになるのを待ちます。\n",get_sv_time());
  16.         wait_level_high();
  17.         vpi_printf("私はCです。 戻ってきました。現在時刻は%ldです。C importを抜けます。\n",get_sv_time());
  18.        
  19.  
  20. }


結果です。時刻30でイベントをトリガ、時刻40でレベルをHighにしてトリガしています。C上では、それらのイベントにより、次のステップ行にいくのが分かると思います。

この例のようにVerilogは、イベントコントロールが豊富です。これらを駆使することにより、Cモデルとの通信が容易になるのではないかと思います。 


 

 ***** Veritak SV Engine Version 0.25 Build Oct.23.2009 *****

私はCです。 現在時刻は1です。これから#10後に戻ってきます。
..............SV実行中です。時刻= 3
..............SV実行中です。時刻= 5
..............SV実行中です。時刻= 7
..............SV実行中です。時刻= 9
私はCです。 戻ってきました。現在時刻は11です。5CLK待ちます。
..............SV実行中です。時刻= 11
..............SV実行中です。時刻= 13
..............SV実行中です。時刻= 15
..............SV実行中です。時刻= 17
..............SV実行中です。時刻= 19
私はCです。 戻ってきました。現在時刻は20です。トリガを待ちます。
..............SV実行中です。時刻= 21
..............SV実行中です。時刻= 23
..............SV実行中です。時刻= 25
..............SV実行中です。時刻= 27
..............SV実行中です。時刻= 29
..............SV実行中です。時刻= 31
私はCです。 戻ってきました。現在時刻は31です。レベルがHighになるのを待ちます。
..............SV実行中です。時刻= 33
..............SV実行中です。時刻= 35
..............SV実行中です。時刻= 37
..............SV実行中です。時刻= 39
私はCです。 戻ってきました。現在時刻は41です。C importを抜けます。
..............SV実行中です。時刻= 41
..............SV実行中です。時刻= 43
join しました。 43

**** Test Done. Total 12.00[msec] ****


16.2.4 C++モデルとの接続
 <Cポインタは、chandleで受ける>
  Cポインタ専用の型としてchandleが用意されています。 しかし、これは、ポインタを一時的に受けるための便宜的な型であって、その演算の種類は、極めて限定されています。下の例はchandleに関する可能な演算の殆ど全てです。++とか、--とかは、言語仕様的に(意図的に)できないようになっています。 ですので、chandle は、DPIを使うときだけ存在意義があります。

  1. module chandle_test;
  2.  
  3.  
  4.        
  5.         import "DPI-C" function chandle malloc(longint size);
  6.         import "DPI-C" function void free(chandle);
  7.         import "DPI-C" function chandle calloc(longint n,longint size);
  8.  
  9.        
  10.  
  11.         chandle c_handle1,c_handle2;
  12.  
  13.        
  14.  
  15.  
  16.         initial begin// chandleで許されるほぼ全てのオペレーション
  17.                 c_handle1=calloc(10,100);//C-RunTime使用
  18.                 c_handle2=calloc(10,100);
  19.                 if (c_handle1 ==c_handle2) $display("assert(0). なんかおかしい");
  20.                 elseif (c_handle1 !=c_handle2) ;
  21.  
  22.                
  23.                 free(c_handle1);//サブルーチンの使用はOK
  24.                 c_handle1=c_handle2;//c_handle同士の代入はOK
  25.                 if (c_handle1 !=c_handle2) $display("assert(0). なんかおかしい");//比較もOK
  26.                 free(c_handle1);
  27.                 c_handle1=null;//アサインは、nullだけ可能
  28.                 c_handle2=null;        
  29.        
  30.        
  31.         end
  32.  
  33. endmodule

<C++の仮想関数を使う例>

C++側で、テストベンチを組みたいとします。往々にして、C++クラスのメンバー関数を直接呼び出したくなりますが、DPIは、Cリンケージで接続されるためにそれは、できません。 次善の策として、C側でラッパー関数を定義してC++メンバー関数を呼び出しています。functionで呼び出せば、C++に比しても殆どオーバヘッドはなく高速に処理することができます。

結果です。

***** Veritak SV Engine Version 0.25 Build Oct.23.2009 *****

私はベースクラスです。
私は子クラスです。
私は孫クラスです。

私はベースクラスです。
私は子クラスです。
私は孫クラスです。

**** Test Done. Total 2.00[msec] ****


SVコードです。chandleは、C++の型を全く理解していません。単なるホルダーにすぎないことに注意してください。

  1. module cpp_test;
    1.  
    2.         import "DPI-C" function chandle make_base_class();
    3.         import "DPI-C" function chandle make_child_class();
    4.         import "DPI-C" function chandle make_grand_child_class();
    5.         import "DPI-C" function void disp_name( inputchandle );
    6.         import "DPI-C" function void delete_class( inputchandle);
    7.         chandle ptr,c1,c2,c3;
    8.  
    9.  
    10.  
    11.  
    12.         initial begin
    13.                 c1=make_base_class();//インスタンス生成
    14.                 c2=make_child_class();//インスタンス生成
    15.                 c3=make_grand_child_class();//インスタンス生成
    16.  
    17.                
    18.                 disp_name(c1);
    19.                 disp_name(c2);
    20.                 disp_name(c3);
    21.                
    22.                 //仮想関数
    23.                 $display("");
    24.  
    25.                 ptr=c1;
    26.                 disp_name(ptr);
    27.                 delete_class(ptr);//インスタンス消去
    28.  
    29.                 ptr=c2;
    30.                 disp_name(ptr);
    31.                 delete_class(ptr);//インスタンス消去
    32.  
    33.                 ptr=c3;
    34.                 disp_name(ptr);
    35.                 delete_class(ptr);//インスタンス消去
    36.        
    37.  
    38.        
    39.         end
    40.  
    41. endmodule


C++コードです。

    1. class Base_class
    2. {
    3. public:
    4.         virtual void disp_name (){ vpi_printf("私はベースクラスです。\n");}
    5.         virtual ~Base_class (){}
    6.  
    7. };
    8.  
    9. class Child_Class: public Base_class
    10. {
    11. public:
    12.         virtual ~Child_Class (){}
    13.         virtual void disp_name (){ vpi_printf("私は子クラスです。\n");}
    14.        
    15. };
    16. class Grand_Child_Class: public Child_Class
    17. {
    18. public:
    19.         virtual ~Grand_Child_Class (){}
    20.         virtual void disp_name (){ vpi_printf("私は孫クラスです。\n");}
    21.  
    22. };
    23.  
    24. extern "C" __declspec(dllexport) void disp_name(void* chandle1){
    25.         Base_class* base_ptr=reinterpret_cast<Base_class*>(chandle1);
    26.         assert(base_ptr);
    27.         base_ptr->disp_name();
    28. }
    29. extern "C" __declspec(dllexport) void delete_class(void* chandle1){
    30.         Base_class* base_ptr=reinterpret_cast<Base_class*>(chandle1);
    31.         assert(base_ptr);
    32.         delete base_ptr;
    33. }
    34. extern "C" __declspec(dllexport) void* make_base_class()
    35. {
    36.         return reinterpret_cast<void*>(new Base_class());
    37.  
    38. }
    39. extern "C" __declspec(dllexport) void* make_child_class()
    40. {
    41.         return reinterpret_cast<void*>(new Child_Class());
    42.  
    43. }
    44. extern "C" __declspec(dllexport) void* make_grand_child_class()
    45. {
    46.         return reinterpret_cast<void*>(new Grand_Child_Class());
    47.  
    48. }





16.2.5 DPIのコンパイル・リンク手順

 VeritakSVのコンパイル・リンク手順については、こちらをご参照ください。

  ベンダにより、違うと思いますが、概ね下図の手順です。

  1)SVソースを読み込み、SVコンパイラは、ヘッダファイルを生成する。エクスポートがあった場合は、ラッパCファイルを生成する
  2)C++コンパイラによりエクスポートDLLを生成する(エクスポートがない場合は、このステップは不要です。)
  3)C++コンパイラによりインポートDLL(ユーザC++用)を生成する  
  4)シミュレータ本体と上記DLLをリンクしてシミュレーションを実行する


 
 
16.2.6 DPIのデバッグ
DPI周りは、間違えると簡単にクラッシュします。また、一見動いているように見えてもおかしな動作をすることも考えられます。DLLは、シミュレータプロセスにマッピングされ同一プロセスです。従いシミュレータ内のオブジェクトに対して保護は働きません。そこで、VC++デバッガの登場になるのですが、Cのスタックフレームや、Cの呼び出し規約に関する知識が必要になる場合があるので、下で簡単に触れておきます。

16.2.6.1 Cのスタックフレーム

下は、Cの関数の定石です。プロログ、中身、エピログの3つに分けられます。プロログの目的は、関数内で使うローカルスタックの確保及び、ebpの設定、エピログは、確保したスタックの廃棄とebpの値を元に戻すことです。

//プロログコード
push ebp
mov ebp, esp
sub esp, ローカルスタック確保量


~中身

//エピログコード
leave
ret

leave というのは複合命令で、

mov esp,ebp
pop ebp

と等価です。

こうすると n番目の引き数は[ebp+(n+1)*4]でアクセスできることになります。
つまり、Cの関数では、渡された引き数及び、内部のローカル変数は、すべて[ebp+index] でアクセスできることになります。コンパイラは、楽にコード生成できますね。

C言語では、関数の呼び出しがネストしますが、その度に下の新しいスタックフレームが作られます。視覚的には、上方向(アドレスが低くなる方向)に伸びます。関数から戻るときには、下に巻き戻る訳です。デバッガは、このスタックフレームを見て現在の関数がどのようにして呼ばれたか(呼び出し履歴)を表示できます。

上記のコードは、少なくともGCC・VC++共デバッグモードでは、上記のコードを生成するはずです。DPIでのクラッシュ解析は、まず呼ばれたところで、引き数が正しく受け渡しされていることを確認することから始まります。うまく動いてくれれば、上記のような知識は不要です。

.....
ローカル変数2 ebp-4*2
ローカル変数1 ebp-4*1
関数の戻り番地 ebp+0
ebpの前の値 ebp+4
1番目の引き数 ebp+4+4
2番目の引き数 ebp+4+4*2
3番目の引き数 ebp+4+4*3



16.2.6.2 VC++の呼び出し規約
 
まず、C の呼び出し規約は3種類あります。VC++ の名前で言うと __cdecl, __stdcall, __fastcall がありますが、DPIでは、__cdeclのみが使われています。(ちなみにC++のメンバ関数呼び出しでは、 thiscall というのもあります。これは、インスタンスアドレスをECXに入れておいてアクセスします。DPIでは使いません。)

 引数はスタックに push して渡します。32bit 以下の引数は全て 32bit に拡張されてスタックに push されます。push する順番は引数リストの右から左の順番です。戻り値は 32bit に拡張され、eax で返されます。64ビットの場合は、 edx:eax のペアで返されます。浮動小数点変数は 浮動小数レジスタst(0) で返されます。

 たとえば、svlogic は、charで1バイトです。これを入力とする関数は、C表記でも charになりますが、実際にスタックにpushされるのは、32ビット=4バイトでpushされます。64bitの場合は、4バイトx2分pushされます。つまり、全ての変数引渡しは、32ビット単位にアラインされているということが言えます。

レジスター使用規約
EAX, ECX, EDX は作業用レジスタで、 関数の中で破壊してかまいません
EBX, ESI, EDI, EBP は関数呼び出し前後で保存されなければなりません
EFLAGS は、方向フラグが前方方向でなければならないことを除いて、 関数呼び出しの間で破壊されると仮定します
関数呼び出し時には FPU スタックは空になっていること
FPU の制御ワードは関数呼び出し前後で保存されなければなりません
浮動小数点数の返値は FPU スタックで返却される。 使わない場合も呼び出し側でスタックをクリアしなければなりません




16.2.7 引数の渡し方

  SVでは、多くの場面で4値が使われるでしょう。一方Cでは2値しかありませんから、インターフェースのためにマッピングが必要になります。DPIでは、このエンコードを次のように規定しています。
    

SV DPI ENCODE
0 0
1 1
X 3
Z 2

 ですので、1ビット表すのに2ビットが必要になります。ところで、このままでは、複数のビット幅の変換が面倒です。そこでDPIでは多ビットように次の構造体を定義しています。この定義は、VPIでも同じですが、aが上表のLSB,bがMSBを指しています。
  

typedef struct vpi_vecval {
     uint32_t aval;//LRM2005のa は誤り
     uint32_t bval;//LRM2005のb は誤り
} s_vpi_vecval, *p_vpi_vecval;//4値でインターフェースする場合


/* (a chunk of) packed bit array */
typedef uint32_t svBitVecVal;//2値でインターフェースする場合

//これらの定義は、シミュレータベンダが提供する"svdpi.h"に書いてあります。

たとえば、10進数の10は、ZXがないので、b=0;aにそのまま10を入れればよいです。
 a=10; b=0;

32'hzは、
 a=0; b=0xffff_ffff;

16'hfffxは、
 a=0xffff; b=0x000f;

と表現できます。 ビット単位の表現形式をスカラ、複数ビット単位の表現形式をベクタ形式と呼びます。
この構造体で、1ビットから32ビットまでのビット幅を表現できます。33ビット以上のビット幅は、この構造体のアレーになります。使用していないビットは、undetermined になります。ユーザは、マスク・符号拡張を行う必要があります。

16.2.3.1 input 引数
 引数の受け渡しは、原則的に"値渡し"か"ポインタ渡し"のいずれかです。([ ]アレーは例外でハンドル渡し) "値渡し"は、小さいサイズの引数でのみ次のように定義されており、これ以外は、すべて"ポインタ渡し"になります。

 

要するに1ビット(bit/logic) もしくは、2値primitive でのみです。(これらは、C リンケージでは、4バイトもしくは、8バイトで固定サイズです。)
SV タイプとC typeの対応は次の通りです。 

SystemVerilog type C type
byte char
shortint short int
int int
longint long long
real double
shortreal float
chandle void*
string const char*
bit unsigned char
logic unsigned char




16.2.8.1 function戻り値
以下の例は、全て値渡しの例です。inputのパラメータは、スタックに積まれ、function戻り値は、全てレジスタ(EAX/EDX/FLOAT REG)で返ります。これは、DPIの規格という訳ではなく、各Cコンパイラの規約によります。幸いにして、__cstd に関してはGCC/VC/TCC皆同じです。
下の例に stringをプラスしたものが、function戻り値で、定義できる全てです。これ以外は、レジスタで返せるほど小さいオブジェクトではありません。例えばintegerは、32ビットの4値ですが、これは、次の章のinout または、ouput ポートで返します。なお、DPIでは、ref ポートは定義できません。
SV側:

  1. module chandle_test;
  2.  
  3.  
  4.  
  5.  
  6.         import "DPI-C" function bit get_bit( input bit);
  7.         import "DPI-C" function logic get_logic( input logic);
  8.         import "DPI-C" function byte get_byte( input byte );
  9.         import "DPI-C" function shortint get_shortint( input shortint);
  10.         import "DPI-C" function int get_int( input int);
  11.         import "DPI-C" function longint get_longint( input longint);
  12.         import "DPI-C" function shortreal get_shortreal( input shortreal);
  13.         import "DPI-C" function real get_real(input real);
  14.         import "DPI-C" function chandle get_chandle( input chandle);
  15.  
  16.        
  17.  
  18.        
  19.         bit b0;
  20.         logic logic0;
  21.         byte byte0;
  22.         shortint shortint0;
  23.         int int0;
  24.         longint longint0;
  25.         shortreal shortreal0;
  26.         real real0;
  27.         chandle chandle0;
  28.        
  29.         initialbegin
  30.                 b0=get_bit(3'b111);
  31.                 $display("b0=%b",b0);
  32.  
  33.                 logic0=get_logic(3'bxxx);              
  34.                 $display("logic0=%b",logic0);
  35.  
  36.                 byte0=get_byte(-1);
  37.                 $display("byte0=%b",byte0);
  38.  
  39.                 shortint0=get_shortint(-1);
  40.                 $display("shortint0=%b",shortint0);
  41.  
  42.                 int0=get_int(-1);
  43.                 $display("int0=%d",int0);
  44.  
  45.                 longint0=get_longint(-1);
  46.                 $display("longint0=%x",longint0);
  47.        
  48.                 shortreal0=get_shortreal(-1);
  49.                 $display("shortreal0=%f",shortreal0);
  50.  
  51.                 real0=get_real(-1);
  52.                 $display("real0=%f",real0);
  53.  
  54.                 chandle0=get_chandle(chandle0);
  55.  
  56.         end
  57.  
  58. endmodule

C側:
svdpi.h

  1. /* common type for 'bit' and 'logic' scalars. */
  2. typedef uint8_t svScalar;
  3. typedef svScalar svBit; /* scalar */
  4. typedef svScalar svLogic; /* scalar */
    1. extern "C"  __declspec(dllexport) svBit get_bit( svBit i)
    2. {
    3.         return i;
    4. }
    5.  
    6. extern "C"  __declspec(dllexport) svLogic get_logic( svLogic i)
    7. {
    8.         return i;
    9. }
    10.  
    11. extern "C" __declspec(dllexport) char get_byte( char i){
    12.  
    13.         return i;
    14. }
    15. extern "C" __declspec(dllexport) short get_shortint( short i){
    16.  
    17.         return i;
    18. }
    19.  
    20. extern "C" __declspec(dllexport) svBitVecVal get_int( int i){
    21.  
    22.         return i;
    23. }
    24.  
    25. extern "C" __declspec(dllexport) __int64 get_longint( __int64 i){
    26.  
    27.         return i;
    28. }
    29.  
    30. extern "C" __declspec(dllexport) float get_shortreal( float i){
    31.  
    32.         return i;
    33. }
    34.  
    35. extern "C" __declspec(dllexport) double get_real( double i){
    36.         return i;
    37. }
    38.  
    39. extern "C" __declspec(dllexport)  void* get_chandle( const void* i){
    40.         return const_cast<void*>(i);
    41. }


結果です

***** Veritak SV Engine Version 0.25 Build Oct.23.2009 *****

b0=1
logic0=x
byte0=11111111
shortint0=1111111111111111
int0= -1
longint0=ffffffffffffffff
shortreal0=-1.000000
real0=-1.000000

**** Test Done. Total 4.00[msec] ****

16.2.7.2 input/output/inout 引数
 inputは、値渡し、ポインタ渡しと2種類あります。少し面倒ですが、SVコンパイラが出力するヘッダファイルの通りにすればOKです。
 inout/outputは、ポインタ渡しのみです。メモリの確保は、呼び出し側が行います。 

注意するべきは、bit[7:0]は、charではないことです。これは、先のsvBitVecVal構造体のポインタ渡しになります。b[15:0] が short int でないのは同じです。
struct やunion も引数として渡せます。これらは、一次元の等価なpacked vectorとして2値の場合は、svBitVecVal構造体,4値の場合は、s_vpi_vecval構造体/アレーのポインタ渡しで行えます。

  1. module real2_test;
  2.  
  3.  
  4.         import "DPI-C" context task import_task4();
  5.  
  6.         export "DPI-C" function  get_logic1;
  7.         export "DPI-C" function  get_logic2;
  8.         export "DPI-C" function  get_logic3;
  9.  
  10.         export "DPI-C" function  get_bit1;
  11.         export "DPI-C" function  get_bit2;
  12.         export "DPI-C" function  get_bit3;
  13.  
  14.         export "DPI-C" function  get_byte1;
  15.         export "DPI-C" function  get_byte2;
  16.         export "DPI-C" function  get_byte3;
  17.  
  18.         export "DPI-C" function  get_shortint1;
  19.         export "DPI-C" function  get_shortint2;
  20.         export "DPI-C" function  get_shortint3;
  21.  
  22.         export "DPI-C" function  get_int1;
  23.         export "DPI-C" function  get_int2;
  24.         export "DPI-C" function  get_int3;
  25.  
  26.         export "DPI-C" function  get_integer1;
  27.         export "DPI-C" function  get_integer2;
  28.         export "DPI-C" function  get_integer3;
  29.  
  30.         export "DPI-C" function  get_bitvec1;
  31.         export "DPI-C" function  get_bitvec2;
  32.         export "DPI-C" function  get_bitvec3;
  33.  
  34.         export "DPI-C" function  get_logicvec1;
  35.         export "DPI-C" function  get_logicvec2;
  36.         export "DPI-C" function  get_logicvec3;
  37.  
  38.         export "DPI-C" function  get_longint1;
  39.         export "DPI-C" function  get_longint2;
  40.         export "DPI-C" function  get_longint3;
  41.  
  42.         export "DPI-C" function  get_real1;
  43.         export "DPI-C" function  get_real2;
  44.         export "DPI-C" function  get_real3;
  45.  
  46.         export "DPI-C" function  get_shortreal1;
  47.         export "DPI-C" function  get_shortreal2;
  48.         export "DPI-C" function  get_shortreal3;
  49.  
  50. //logic
  51.         function void get_logic1( input logic b0);
  52.                 if (b0 ===1'bz) $display("................ SV logic0=%b",b0);
  53.                 else $display("Fail %b",b0);
  54.                 return ;
  55.         endfunction
  56.  
  57.         function void get_logic2( inout logic b0);
  58.                 if (b0 !==1'bx) $display("Fail %b",b0);
  59.                 else    $display("................ SV logic0=%b",b0);
  60.                 b0=1'b1;
  61.                 return ;
  62.         endfunction
  63.  
  64.         function void get_logic3( output logic b0);
  65.                 b0=1'bx;
  66.                 return ;
  67.         endfunction
  68.  
  69. //bit
  70.         function void get_bit1( input bit b0);
  71.                 if (b0 ===1'b0)         $display("................ SV bit0=%b",b0);
  72.                 else $display("Fail");
  73.                 return ;
  74.         endfunction
  75.  
  76.         function void get_bit2( inout bit b0);
  77.                 if (b0 ===1'b1)         $display("................ SV bit0=%b",b0);
  78.                 else $display("Fail");
  79.                 b0=1'b0;
  80.                 return ;
  81.         endfunction
  82.  
  83.         function void get_bit3( outputbit b0);
  84.                 b0=1'b1;
  85.                 return ;
  86.         endfunction
  87.  
  88. //byte
  89.         function void get_byte1( input byte b0);
  90.                 if (b0 ===8'h7f) $display("................ SV byte0=%x",b0);
  91.                 else $display("Fail");
  92.                 return ;
  93.         endfunction
  94.  
  95.  
  96.         function void get_byte2( inout byte b0);
  97.                 if (b0 !==8'h7f) $display("Fail");
  98.                 b0=1;
  99.                 return ;
  100.         endfunction
  101.        
  102.         function void get_byte3( output byte b0);
  103.                 b0=8'h02;
  104.                 return ;
  105.         endfunction
  106.  
  107. //shortint
  108.         function void get_shortint1( input shortint b0);
  109.                 if (b0 !==16'habcd) $display("Fail1 %x",b0);
  110.                 return ;
  111.         endfunction
  112.  
  113.  
  114.         function void get_shortint2( inoutshortint b0);
  115.                 if (b0 !==16'habcd) $display("Fail");
  116.                 b0=32'hdead1234;
  117.                 return ;
  118.         endfunction
  119.        
  120.         function void get_shortint3( output shortint b0);
  121.                 b0=16'h1122;
  122.                 return ;
  123.         endfunction
  124.  
  125. //int
  126.         function void get_int1( input int b0);
  127.                 if (b0 !==32'hdead_beaf) $display("FailA %x",b0);
  128.                 return ;
  129.         endfunction
  130.  
  131.  
  132.         function void get_int2( inout int b0);
  133.                 if (b0 !==32'hdead_beaf) $display("Fail");
  134.                 b0=32'h1234_5678;
  135.                 return ;
  136.         endfunction
  137.        
  138.         function void get_int3( output int b0);
  139.                 b0=32'haabb1234;
  140.                 return ;
  141.         endfunction
  142.  
  143. //integer
  144.         function void get_integer1( input integer b0);
  145.                 if (b0 !==32'habcd_1234) $display("Fail %x",b0);
  146.                 return ;
  147.         endfunction
  148.  
  149.  
  150.         function void get_integer2( inout integer b0);
  151.                 if (b0 !==32'habcd_1234) $display("Fail");
  152.                 b0=32'hdeadbeaf;
  153.                 return ;
  154.         endfunction
  155.        
  156.         function void get_integer3( output integer b0);
  157.                 b0=32'haabb1234;
  158.                 return ;
  159.         endfunction
  160.  
  161. //longint
  162.         function void get_longint1( input longint b0);
  163.                 if (b0 !==64'habcd_1234_dead_beaf) $display("FailB %x",b0);
  164.                 return ;
  165.         endfunction
  166.  
  167.  
  168.         function void get_longint2( inout longint b0);
  169.                 if (b0 !==64'habcd_1234_dead_beaf)              $display("Fail %x",b0);
  170.                 b0=64'hdeadbeaf_abcd_1234;
  171.                 return ;
  172.         endfunction
  173.        
  174.         function void get_longint3( output longint b0);
  175.                 b0=64'h12345678;
  176.                 return ;
  177.         endfunction
  178.  
  179. //real
  180.         function void get_real1( input real b0);
  181.                 if (b0 !=1.0) $display("Fail %f",b0);
  182.                 return ;
  183.         endfunction
  184.  
  185.  
  186.         function void get_real2( inout real b0);
  187.                 if (b0 !=1.0) $display("Fail %f",b0);
  188.                 b0=-1.0;
  189.                 return ;
  190.         endfunction
  191.        
  192.         function void get_real3( output real b0);
  193.                 b0=-11.0;
  194.                 return ;
  195.         endfunction
  196.  
  197. //shortreal
  198.         function void get_shortreal1( input shortreal b0);
  199.                 if (b0 !=1.0) $display("Fail %f",b0);
  200.                 return ;
  201.         endfunction
  202.  
  203.  
  204.         function void get_shortreal2( inout shortreal b0);
  205.                 if (b0 !=1.0) $display("Fail %f",b0);
  206.                 b0=-1.0;
  207.                 return ;
  208.         endfunction
  209.        
  210.         function void get_shortreal3( output shortreal b0);
  211.                 b0=-21.0;
  212.                 return ;
  213.         endfunction
  214.  
  215.  
  216.  
  217. //bit vec
  218.         function void get_bitvec1(input  bit [7:0] b0);
  219.                 if (b0 !==8'h11) $display("Fail4 %x",b0);
  220.                 return ;
  221.         endfunction
  222.  
  223.  
  224.         function void get_bitvec2( inout bit [7:0] b0);
  225.                 if (b0 !==8'h11) $display("Fail3");
  226.                 b0=32'hdeadbeaf;
  227.                 return ;
  228.         endfunction
  229.        
  230.         function void get_bitvec3(output  bit [7:0] b0);
  231.                 b0=8'haa;
  232.                 return ;
  233.         endfunction
  234.  
  235. //logic vec
  236.         function void get_logicvec1(input  logic [7:0] b0);
  237.                 if (b0 !==8'hzz) $display("Fail2 %b",b0);
  238.                 return ;
  239.         endfunction
  240.  
  241.  
  242.         function void get_logicvec2( inout logic [7:0] b0);
  243.                 if (b0 !==8'hzz) $display("Fail1 %b",b0);
  244.                 b0=32'hdeadbeaf;
  245.                 return ;
  246.         endfunction
  247.        
  248.         function void get_logicvec3(output  logic [7:0] b0);
  249.                 b0=8'haa;
  250.                 return ;
  251.         endfunction
  252.  
  253.  
  254.  
  255.         initial begin
  256.                 import_task4();
  257.         end
  258.  
  259. endmodule


C++側:
ヘッダファイル(シミュレータが自動生成)

  1. /* Copyright 2010 www.sugawara-systems.com
  2. * Note:
  3. *   This file is automatically generated.
  4. *   Please do not edit this file - you will lose your edits.*/
  5.  
  6. #ifndef INCLUDED_DPIHEADER
  7. #define INCLUDED_DPIHEADER
  8. #ifdef __cplusplus
  9. #define DPI_LINK_DECL  extern "C"
  10. #else
  11. #define DPI_LINK_DECL
  12. #endif
  13. #include "svdpi.h"
  14.  
  15. DPI_LINK_DECL DPI_DLLISPEC
  16. void get_bit1(svBit );
  17.  
  18. DPI_LINK_DECL DPI_DLLISPEC
  19. void get_bit2(svBit* );
  20.  
  21. DPI_LINK_DECL DPI_DLLISPEC
  22. void get_bit3(svBit* );
  23.  
  24. DPI_LINK_DECL DPI_DLLISPEC
  25. void get_bitvec1(const svBitVecVal* );
  26.  
  27. DPI_LINK_DECL DPI_DLLISPEC
  28. void get_bitvec2(svBitVecVal* );
  29.  
  30. DPI_LINK_DECL DPI_DLLISPEC
  31. void get_bitvec3(svBitVecVal* );
  32.  
  33. DPI_LINK_DECL DPI_DLLISPEC
  34. void get_byte1(char );
  35.  
  36. DPI_LINK_DECL DPI_DLLISPEC
  37. void get_byte2(char* );
  38.  
  39. DPI_LINK_DECL DPI_DLLISPEC
  40. void get_byte3(char* );
  41.  
  42. DPI_LINK_DECL DPI_DLLISPEC
  43. void get_int1(svBitVecVal );
  44.  
  45. DPI_LINK_DECL DPI_DLLISPEC
  46. void get_int2(svBitVecVal* );
  47.  
  48. DPI_LINK_DECL DPI_DLLISPEC
  49. void get_int3(int * );
  50.  
  51. DPI_LINK_DECL DPI_DLLISPEC
  52. void get_integer1(const svLogicVecVal* );
  53.  
  54. DPI_LINK_DECL DPI_DLLISPEC
  55. void get_integer2(svLogicVecVal* );
  56.  
  57. DPI_LINK_DECL DPI_DLLISPEC
  58. void get_integer3(svLogicVecVal* );
  59.  
  60. DPI_LINK_DECL DPI_DLLISPEC
  61. void get_logic1(svLogic );
  62.  
  63. DPI_LINK_DECL DPI_DLLISPEC
  64. void get_logic2(svLogic* );
  65.  
  66. DPI_LINK_DECL DPI_DLLISPEC
  67. void get_logic3(svLogic* );
  68.  
  69. DPI_LINK_DECL DPI_DLLISPEC
  70. void get_logicvec1(const svLogicVecVal* );
  71.  
  72. DPI_LINK_DECL DPI_DLLISPEC
  73. void get_logicvec2(svLogicVecVal* );
  74.  
  75. DPI_LINK_DECL DPI_DLLISPEC
  76. void get_logicvec3(svLogicVecVal* );
  77.  
  78. DPI_LINK_DECL DPI_DLLISPEC
  79. void get_longint1(int64_t );
  80.  
  81. DPI_LINK_DECL DPI_DLLISPEC
  82. void get_longint2(int64_t* );
  83.  
  84. DPI_LINK_DECL DPI_DLLISPEC
  85. void get_longint3(int64_t* );
  86.  
  87. DPI_LINK_DECL DPI_DLLISPEC
  88. void get_real1(double );
  89.  
  90. DPI_LINK_DECL DPI_DLLISPEC
  91. void get_real2(double* );
  92.  
  93. DPI_LINK_DECL DPI_DLLISPEC
  94. void get_real3(double* );
  95.  
  96. DPI_LINK_DECL DPI_DLLISPEC
  97. void get_shortint1(short );
  98.  
  99. DPI_LINK_DECL DPI_DLLISPEC
  100. void get_shortint2(short* );
  101.  
  102. DPI_LINK_DECL DPI_DLLISPEC
  103. void get_shortint3(short* );
  104.  
  105. DPI_LINK_DECL DPI_DLLISPEC
  106. void get_shortreal1(float );
  107.  
  108. DPI_LINK_DECL DPI_DLLISPEC
  109. void get_shortreal2(float* );
  110.  
  111. DPI_LINK_DECL DPI_DLLISPEC
  112. void get_shortreal3(float* );
  113.  
  114. DPI_LINK_DECL DPI_DLLESPEC
  115. int import_task4();
  116.  
  117. #endif


C++ソースファイル

  1.  
  2.  
  3. extern "C" __declspec(dllexport) void import_task4()
  4. {
  5.         svLogic svlogic0;
  6.        
  7.  
  8. //スカラーlogic
  9.         svlogic0=sv_z;
  10.         get_logic1(svlogic0);
  11.         svlogic0=sv_x;
  12.         get_logic2(&svlogic0);
  13.         vpi_printf("svlogic0=%x \n",svlogic0);
  14.         get_logic3(&svlogic0);
  15.         vpi_printf("svlogic0=%x \n",svlogic0);
  16.  
  17. //スカラーbit
  18.         svBit svbit0=sv_0;
  19.         get_bit1(svbit0);
  20.        
  21.        
  22.         svbit0=sv_1;
  23.         get_bit2(&svbit0);
  24.         vpi_printf("svbit0=%x \n",svbit0);
  25.         get_bit3(&svbit0);
  26.         vpi_printf("svbit0=%x \n",svbit0);
  27.  
  28. //byte
  29.         char byte0=0x7f;
  30.         get_byte1(byte0);
  31.         get_byte2(&byte0);
  32.         vpi_printf("byte0=%x \n",byte0);
  33.         get_byte3(&byte0);
  34.         vpi_printf("byte0=%x \n",byte0);
  35.  
  36. //shortint
  37.         short short0=0xabcd;
  38.         get_shortint1(short0);
  39.         get_shortint2(&short0);
  40.         vpi_printf("short0=%x \n",short0);
  41.         get_shortint3(&short0);
  42.         vpi_printf("short0=%x \n",short0);
  43.  
  44. //int
  45.         int int0=0xdeadbeaf;
  46.         get_int1(int0);
  47.         get_int2(&int0);
  48.         vpi_printf("int0=%x \n",int0);
  49.         get_int3(&int0);
  50.         vpi_printf("int0=%x \n",int0);
  51.  
  52. //integer
  53.         svLogicVecVal logic0={0xabcd1234,0};
  54.         get_integer1(&logic0);
  55.         get_integer2(&logic0);
  56.         vpi_printf("integer={%x,%x}\n",logic0.aval,logic0.bval);
  57.         get_integer3(&logic0);
  58.         vpi_printf("integer={%x,%x}\n",logic0.aval,logic0.bval);
  59.  
  60. //longint
  61.         __int64 longint0=0xabcd1234deadbeafL;
  62.         get_longint1(longint0);
  63.         get_longint2(&longint0);
  64.         vpi_printf("longint0=%x \n",longint0);
  65.         get_longint3(&longint0);
  66.         vpi_printf("longint0=%x \n",longint0);
  67.  
  68. //real
  69.         double real0=1.0;
  70.         get_real1(real0);
  71.         get_real2(&real0);
  72.         vpi_printf("real0=%f \n",real0);
  73.         get_real3(&real0);
  74.         vpi_printf("real0=%f \n",real0);
  75.  
  76. //shortreal
  77.         float shortreal0=1.0;
  78.         get_shortreal1(shortreal0);
  79.         get_shortreal2(&shortreal0);
  80.         vpi_printf("shortreal0=%f \n",shortreal0);
  81.         get_shortreal3(&shortreal0);
  82.         vpi_printf("shortreal0=%f \n",shortreal0);
  83.  
  84. //bitvector
  85.         svBitVecVal bitvec0=0x11;
  86.         get_bitvec1(&bitvec0);
  87.         get_bitvec2(&bitvec0);
  88.         vpi_printf("bitvec0=%x \n",bitvec0);   
  89.         get_bitvec3(&bitvec0);
  90.         vpi_printf("bitvec0=%x \n",bitvec0);
  91.  
  92. //logicvector
  93.         svLogicVecVal logicvec0={0x00,0xff};
  94.         get_logicvec1(&logicvec0);
  95.         get_logicvec2(&logicvec0);
  96.         vpi_printf("logicvec0={%x,%x}\n",logicvec0.aval,logicvec0.bval);
  97.         get_logicvec3(&logicvec0);
  98.         vpi_printf("logicvec0={%x,%x}\n",logicvec0.aval,logicvec0.bval);
  99.        
  100.  
  101.  
  102.  
  103.  
  104.  
  105. }

結果です

***** Veritak SV Engine Version 0.25 Build Oct.23.2009 *****

................ SV logic0=z
................ SV logic0=x
svlogic0=1
svlogic0=3
................ SV bit0=0
................ SV bit0=1
svbit0=0
svbit0=1
................ SV byte0=7f
byte0=1
byte0=2
short0=1234
short0=1122
int0=12345678
int0=aabb1234
integer={deadbeaf,0}
integer={aabb1234,0}
longint0=abcd1234
longint0=12345678
real0=-1.000000
real0=-11.000000
shortreal0=-1.000000
shortreal0=-21.000000
bitvec0=af
bitvec0=aa
logicvec0={af,0}
logicvec0={aa,0}

**** Test Done. Total 5.00[msec] ****



 Notes:
 (LRM 1145p に import "DPI-C" context MyCFunc = function integer MapID(int portID);とありますがintの間違いだと思います。)




16.2.9 配列引渡し
配列引渡しの方法は、2通りあります。一つは、生データのポインタを渡す方法です。この方法は、送り側と受け取り側で、配列の種類、大きさ等のPropertyを知っている必要があります。配列サイズを変更したら、受け取る側でもメンテしなければしなければいけない(再コンパイルが必要)のでメンテナンス性はよくありませんが、高速な受け渡しが可能です。もう一つの方法は、シミュレータの内部オブジェクトハンドルを使う方法です。配列を受け取る側は、配列の大きさや次元数等の情報を問い合わせしてからアクセスすることになるのでメンテナンス性ははよいです。しかしその分遅くなってしまいます。

16.2.9.1packed array

サンプルソースです。

  1. module test;
  2.  
  3. import "DPI-C" function void packet_C( input logic [127:0]);
  4.  
  5.         logic [127:0] packet;
  6.         initial begin
  7.        
  8.                 packet=128'h1234_5678_aaaa_bbbb_cccc_dddd_eeee_ffff;
  9.                 $display("SVからデータを送ります。 %x",packet);
  10.                 packet_C(packet);
  11.        
  12.                        
  13.  
  14.         end
  15.  
  16. endmodule
  17.  

これに対して、自動生成されたCのヘッダファイルが次です。

  1. /* Copyright 2010 www.sugawara-systems.com
  2. * Note:
  3. *   This file is automatically generated.
  4. *   Please do not edit this file - you will lose your edits.*/
  5.  
  6. #ifndef INCLUDED_DPIHEADER
  7. #define INCLUDED_DPIHEADER
  8. #ifdef __cplusplus
  9. #define DPI_LINK_DECL  extern "C"
  10. #else
  11. #define DPI_LINK_DECL
  12. #endif
  13. #include "svdpi.h"
  14.  
  15. DPI_LINK_DECL DPI_DLLESPEC
  16. void packet_C(const svLogicVecVal* );
  17.  
  18. #endif
  19.  

このように配列の大きさの情報が消えてしまいました。ポインタで渡される為です。
設計者は、配列の大きさは、128ビットであることが分かっているので、例えば、次のように書けます。

SV_PACKED_DATA_NELEMSは、sv_dpi.hで定義されているマクロで、ビット幅からチャンク数に変換します。この場合、128ビット幅のVectorを収納可能な、svLogicVecVal構造体の要素数を返します。

  1. void packet_C(const svLogicVecVal* packet)
  2. {
  3.         const unsigned elements=SV_PACKED_DATA_NELEMS(128);//チャンク数を得るマクロ
  4.         svLogicVecVal mem[elements];
  5.        
  6.  
  7.         memcpy(mem,packet,sizeof(mem));//ローカル変数にCOPYコピー
  8.         printf("SVから来たデータを表示します。\n");
  9.         for (unsigned i=0;i< elements;i++){
  10.                 printf("mem[%2d]={%4x,%4x}\n",i,mem[i].aval,mem[i].bval);
  11.  
  12.         }
  13.        
  14. }


結果です。

***** Veritak SV Engine Version 0.27 Build no.27.2009 *****

SVからデータを送ります。 12345678aaaabbbbccccddddeeeeffff
SVから来たデータを表示します。
mem[ 0]={eeeeffff, 0}
mem[ 1]={ccccdddd, 0}
mem[ 2]={aaaabbbb, 0}
mem[ 3]={12345678, 0}

**** Test Done. Total 0.00[msec] ****

<配列を部分的に書き換える>

inout を使って書き換えます。

  1. module test;
  2.  
  3. import "DPI-C" function void packet_C1( inout logic [127:0]);
  4.  
  5.  
  6.         logic [127:0] packet;
  7.         initial begin
  8.        
  9.                 packet=128'h1234_5678_aaaa_bbbb_cccc_dddd_eeee_ffff;
  10.                 $display("SVからデータを送ります。 %x",packet);
  11.                 packet_C1(packet);
  12.                 $display("Cで変更されたデータです。 %x",packet);       
  13.                        
  14.  
  15.         end
  16.  
  17. endmodule
  18.  


C++ソースです。inoutなので、constが付きません。svGetPartselLogicは、sv_dpi.hで定義されるAPIで、packed_arrayに対して、32ビット以下のパートセレクトを読み出しを返します。svPutPartselLogicは、同様にパートセレクト(32ビット以下)が書き込みになります。このAPIの制限事項は、

です。

  1. void packet_C1(svLogicVecVal* packet)
  2. {
  3.         const unsigned elements=SV_PACKED_DATA_NELEMS(128);//チャンク数を得るマクロ
  4.        
  5.         printf("SVから来たデータを表示します。\n");
  6.         for (unsigned i=0;i< elements;i++){
  7.                 printf("packet[%2d]={%4x,%4x}\n",i,packet[i].aval,packet[i].bval);
  8.  
  9.         }
  10.  
  11.         svLogicVecVal data;
  12.         svGetPartselLogic(&data,packet,64,32);//sv_dpi.h
  13.         printf("パートセレクトAPIで packet[64 +:32]={%4x,%4x}\n",data.aval,data.bval);
  14.  
  15.         data.aval=0xdeadbeaf;
  16.         data.bval=0x0;
  17.  
  18.         svPutPartselLogic(packet,data,64,32);//sv_dpi.h
  19.         //戻る
  20.        
  21.  
  22. }

結果です。

***** Veritak SV Engine Version 0.27 Build no.27.2009 *****

SVからデータを送ります。 12345678aaaabbbbccccddddeeeeffff
SVから来たデータを表示します。
packet[ 0]={eeeeffff, 0}
packet[ 1]={ccccdddd, 0}
packet[ 2]={aaaabbbb, 0}
packet[ 3]={12345678, 0}
パートセレクトAPIで packet[64 +:32]={aaaabbbb, 0}
Cで変更されたデータです。 12345678deadbeafccccddddeeeeffff

**** Test Done. Total 4.00[msec] ****

<packed arrayは、1次元のベクタ>
上のサンプルは、4値でしたが、2値にして、packed arrayを2次元にしてみたのが次です。packed arrayは、SV上で多次元でも、1次元のvector として、渡されることに注意してください。[L:R] は、[abs(L-R):0]と正規化された解釈でアクセスされます。

  1. module test;
  2.  
  3. import "DPI-C" function void packet_C2( inout bit [3:0][31:0]);
  4.  
  5.  
  6.         bit [3:0][31:0] packet;
  7.         initial begin
  8.        
  9.                 packet=128'h1234_5678_aaaa_bbbb_cccc_dddd_eeee_ffff;
  10.                 $display("SVからデータを送ります。 %x",packet);
  11.                 packet_C2(packet);
  12.                 $display("Cで変更されたデータです。 %x",packet);       
  13.                        
  14.  
  15.         end
  16.  
  17. endmodule
  18.  


  1. void packet_C2(svBitVecVal* packet)
  2. {
  3.                  
  4.         constunsigned elements=SV_PACKED_DATA_NELEMS(128);//チャンク数を得るマクロ
  5.  
  6.        
  7.         printf("SVから来たデータを表示します。\n");
  8.         for (unsigned i=0;i< elements;i++){
  9.                 printf("packet[%2d]={%4x}\n",i,packet[i]);
  10.  
  11.         }
  12.  
  13.         svBitVecVal data;
  14.         svGetPartselBit(&data,packet,64,32);//sv_dpi.h
  15.         printf("パートセレクトAPIで packet[64 +:32]={%4x}\n",data);
  16.  
  17.         data=0xdeadbeaf;
  18.  
  19.  
  20.         svPutPartselBit(packet,data,64,32);//sv_dpi.h
  21.         //戻る
  22.        
  23.  
  24. }

***** Veritak SV Engine Version 0.27 Build no.27.2009 *****

SVからデータを送ります。 12345678aaaabbbbccccddddeeeeffff
SVから来たデータを表示します。
packet[ 0]={eeeeffff}
packet[ 1]={ccccdddd}
packet[ 2]={aaaabbbb}
packet[ 3]={12345678}
パートセレクトAPIで packet[64 +:32]={aaaabbbb}
Cで変更されたデータです。 12345678deadbeafccccddddeeeeffff

**** Test Done. Total 4.00[msec] ****

16.2.10.1unpacked array
アンパックド配列の例です。

  1. module test;
  2. parameter int MAX_MSB=2;
  3. import "DPI-C" function void packet_C3( inout bit [127:0] mem[0:MAX_MSB]);
  4.  
  5.  
  6.         bit [127:0] packet[0:MAX_MSB];
  7.        
  8.         initial begin
  9.        
  10.                 packet[MAX_MSB]=128'h1234_5678_aaaa_bbbb_cccc_dddd_eeee_ffff;
  11.                
  12.                 $display("SVからデータを送ります。 %x",packet[MAX_MSB]);       
  13.                 packet_C3(packet);
  14.  
  15.                 for (int i=0;i<=MAX_MSB;i++)            $display("Cで変更されたデータです。 %x",packet[i]);    
  16.                        
  17.  
  18.         end
  19.  
  20. endmodule
  21.  


パックドアレイーは、chunk_elements分ビットが連続しています。一方、アンパックド次元では、必ずしも連続にはならないことに注意してください。

  1. void packet_C3(svBitVecVal* packet)
  2. {
  3.                  
  4.         const unsigned chunk_elements=SV_PACKED_DATA_NELEMS(128);//チャンク数を得るマクロ
  5.         const unsigned unpacked_elements=3;    
  6.        
  7.         printf("SVから来たデータを表示します。\n");
  8.         for (unsigned j=0;j<unpacked_elements;j++){
  9.                 for (unsigned i=0;i< chunk_elements;i++){
  10.                         printf("packet[%2d][%2d]={%4x}\n",j,i,packet[j*chunk_elements+i]);
  11.                 }
  12.  
  13.         }
  14.  
  15.         svBitVecVal data;
  16.         svGetPartselBit(&data,&packet[(unpacked_elements-1)*chunk_elements],64,32);//sv_dpi.h
  17.         printf("パートセレクトAPIで packet[%2d][64 +:32]={%4x}\n",unpacked_elements-1,data);
  18.  
  19.        
  20.  
  21.         for (unsigned i=0;i< (unpacked_elements-1)*chunk_elements;i++){
  22.                 packet[i]=i;//書き換え
  23.         }
  24.         data=0xdeadbeaf;
  25.         svPutPartselBit(&packet[(unpacked_elements-1)*chunk_elements],data,64,32);//sv_dpi.h
  26.         //戻る
  27.        
  28.  
  29. }


***** Veritak SV Engine Version 0.27 Build no.27.2009 *****

SVからデータを送ります。 12345678aaaabbbbccccddddeeeeffff
SVから来たデータを表示します。
packet[ 0][ 0]={ 0}
packet[ 0][ 1]={ 0}
packet[ 0][ 2]={ 0}
packet[ 0][ 3]={ 0}
packet[ 1][ 0]={ 0}
packet[ 1][ 1]={ 0}
packet[ 1][ 2]={ 0}
packet[ 1][ 3]={ 0}
packet[ 2][ 0]={eeeeffff}
packet[ 2][ 1]={ccccdddd}
packet[ 2][ 2]={aaaabbbb}
packet[ 2][ 3]={12345678}
パートセレクトAPIで packet[ 2][64 +:32]={aaaabbbb}
Cで変更されたデータです。 00000003000000020000000100000000
Cで変更されたデータです。 00000007000000060000000500000004
Cで変更されたデータです。 12345678deadbeafccccddddeeeeffff

**** Test Done. Total 0.00[msec] ****

<アンパックド次元では、C風に宣言してもよい>

結果は、上と変りません。アンパックド次元では、レンジ宣言で小さいほうが最初に来ます。つまり、SVのレンジが [L:R]のとき、CのIndex0は、 min(L,R)に対応し、SVの max(L,R)が、 Cの Index abs(L-R)に対応します。パックド次元と違うので注意してください。
 

  1. module test;
  2. parameter int MAX_MSB=2;
  3.  
  4. import "DPI-C" function void packet_C3( inout bit [127:0] mem[MAX_MSB+1]);
  5.  
  6.  
  7.         bit [127:0] packet[MAX_MSB+1];
  8.        
  9.         initial begin
  10.        
  11.                 packet[MAX_MSB]=128'h1234_5678_aaaa_bbbb_cccc_dddd_eeee_ffff;
  12.                
  13.                 $display("SVからデータを送ります。 %x",packet[MAX_MSB]);       
  14.                 packet_C3(packet);
  15.  
  16.                 for (int i=0;i<=MAX_MSB;i++)            $display("Cで変更されたデータです。 %x",packet[i]);    
  17.                        
  18.  
  19.         end
  20.  
  21. endmodule

<多次元アンパックド配列>

  1. module test;
  2. parameter int MAX_MSB=2;
  3. parameter int MAX_MSB2=3;
  4.  
  5. import "DPI-C" function void packet_C5( inout bit [127:0] mem[MAX_MSB+1][MAX_MSB2+1]);
  6.  
  7.  
  8.         bit [127:0] packet[MAX_MSB+1][MAX_MSB2+1];
  9.        
  10.         initial begin
  11.        
  12.                 packet[MAX_MSB][MAX_MSB2]=128'h1234_5678_aaaa_bbbb_cccc_dddd_eeee_ffff;
  13.                
  14.                 $display("SVからデータを送ります。 %x",packet[MAX_MSB][MAX_MSB2]);     
  15.                 packet_C5(packet);
  16.  
  17.                
  18.                 for (int i=0;i<=MAX_MSB;i++)   
  19.                         for (int m=0;m<=MAX_MSB2;m++)
  20.                                 $display("Cで変更されたデータです。 %x",packet[i][m]); 
  21.                        
  22.  
  23.         end
  24.  
  25. endmodule
  26.  


多次元でも基本的に同じ考え方です。

  1. void packet_C5(svBitVecVal* packet)
  2. {
  3.                  
  4.         const unsigned chunk_elements=SV_PACKED_DATA_NELEMS(128);//チャンク数を得るマクロ
  5.         const unsigned unpacked_elements=3*4;  
  6.        
  7.         printf("SVから来たデータを表示します。\n");
  8.         for (unsigned j=0;j<unpacked_elements;j++){
  9.                 for (unsigned i=0;i< chunk_elements;i++){
  10.                         printf("packet[%2d][%2d]={%4x}\n",j,i,packet[j*chunk_elements+i]);
  11.                 }
  12.  
  13.         }
  14.  
  15.         svBitVecVal data;
  16.         svGetPartselBit(&data,&packet[(unpacked_elements-1)*chunk_elements],64,32);//sv_dpi.h
  17.         printf("パートセレクトAPIで packet[%2d][64 +:32]={%4x}\n",unpacked_elements-1,data);
  18.  
  19.        
  20.  
  21.         for (unsigned i=0;i< (unpacked_elements-1)*chunk_elements;i++){
  22.                 packet[i]=i;
  23.         }
  24.         data=0xdeadbeaf;
  25.         svPutPartselBit(&packet[(unpacked_elements-1)*chunk_elements],data,64,32);//sv_dpi.h
  26.         //戻る
  27.        
  28.  
  29. }

***** Veritak SV Engine Version 0.27 Build no.27.2009 *****

SVからデータを送ります。 12345678aaaabbbbccccddddeeeeffff
SVから来たデータを表示します。
packet[ 0][ 0]={ 0}
packet[ 0][ 1]={ 0}
packet[ 0][ 2]={ 0}
packet[ 0][ 3]={ 0}
packet[ 1][ 0]={ 0}
packet[ 1][ 1]={ 0}
packet[ 1][ 2]={ 0}
packet[ 1][ 3]={ 0}
packet[ 2][ 0]={ 0}
packet[ 2][ 1]={ 0}
packet[ 2][ 2]={ 0}
packet[ 2][ 3]={ 0}
packet[ 3][ 0]={ 0}
packet[ 3][ 1]={ 0}
packet[ 3][ 2]={ 0}
packet[ 3][ 3]={ 0}
packet[ 4][ 0]={ 0}
packet[ 4][ 1]={ 0}
packet[ 4][ 2]={ 0}
packet[ 4][ 3]={ 0}
packet[ 5][ 0]={ 0}
packet[ 5][ 1]={ 0}
packet[ 5][ 2]={ 0}
packet[ 5][ 3]={ 0}
packet[ 6][ 0]={ 0}
packet[ 6][ 1]={ 0}
packet[ 6][ 2]={ 0}
packet[ 6][ 3]={ 0}
packet[ 7][ 0]={ 0}
packet[ 7][ 1]={ 0}
packet[ 7][ 2]={ 0}
packet[ 7][ 3]={ 0}
packet[ 8][ 0]={ 0}
packet[ 8][ 1]={ 0}
packet[ 8][ 2]={ 0}
packet[ 8][ 3]={ 0}
packet[ 9][ 0]={ 0}
packet[ 9][ 1]={ 0}
packet[ 9][ 2]={ 0}
packet[ 9][ 3]={ 0}
packet[10][ 0]={ 0}
packet[10][ 1]={ 0}
packet[10][ 2]={ 0}
packet[10][ 3]={ 0}
packet[11][ 0]={eeeeffff}
packet[11][ 1]={ccccdddd}
packet[11][ 2]={aaaabbbb}
packet[11][ 3]={12345678}
パートセレクトAPIで packet[11][64 +:32]={aaaabbbb}
Cで変更されたデータです。 00000003000000020000000100000000
Cで変更されたデータです。 00000007000000060000000500000004
Cで変更されたデータです。 0000000b0000000a0000000900000008
Cで変更されたデータです。 0000000f0000000e0000000d0000000c
Cで変更されたデータです。 00000013000000120000001100000010
Cで変更されたデータです。 00000017000000160000001500000014
Cで変更されたデータです。 0000001b0000001a0000001900000018
Cで変更されたデータです。 0000001f0000001e0000001d0000001c
Cで変更されたデータです。 00000023000000220000002100000020
Cで変更されたデータです。 00000027000000260000002500000024
Cで変更されたデータです。 0000002b0000002a0000002900000028
Cで変更されたデータです。 12345678deadbeafccccddddeeeeffff

**** Test Done. Total 0.00[msec] ****

<chandleの配列 >

chandleは、初期0で初期化されています。以下は、chandleのアンパックドアレーにクラス配列のアドレスを設定しています。

  1. module test;
  2.  
  3. parameter int MEM_SIZE=10;
  4. import "DPI-C" function void set_chandles( inout chandle   mem[MEM_SIZE ]);
  5. import "DPI-C" function void disp_data( input chandle);
  6. import "DPI-C" function void delete_chandles( input chandle mem[MEM_SIZE]);
  7.  
  8.         chandle chandles[MEM_SIZE];
  9.         initial begin
  10.                 set_chandles(chandles);//ハンドルのセット
  11.                 for (int i=0;i< MEM_SIZE;i++) disp_data(chandles[i]);//クラス内データの表示
  12.                 delete_chandles(chandles);//ハンドルのリセット
  13.  
  14.         end
  15.  
  16. endmodule
  17.  


C++ソースです。

  1. const unsigned class_handle_size=10;
  2. class Sample_Class
  3. {
  4. public:
  5.         Sample_Class (){
  6.  
  7.         }
  8.         void set_data(unsigned u ){ data=u;}
  9.         void disp_data (){
  10.                 std::cout<< "データは、"<< data<<"です。"<<std::endl;
  11.         }
  12. private:
  13.         unsigned data;
  14. };
  15. void  set_chandles(void** mem){
  16.                
  17.                 Sample_Class*ptr=new Sample_Class[class_handle_size];//クラス配列生成
  18.                 for (unsigned i=0;i< class_handle_size;i++){
  19.                         std::cout << "クラスオブジェクト アドレスは"<< ptr << std::endl;
  20.                         ptr->set_data(i);
  21.                         mem[i]=ptr++;//ハンドルのセット
  22.                 }
  23. }
  24. void disp_data( const void* ptr){
  25.        
  26.         reinterpret_cast<Sample_Class*>(const_cast<void*>(ptr))->disp_data();
  27.        
  28.  
  29. }
  30. void delete_chandles( const void**mem){
  31.         delete [] mem[0];//クラス配列の消去
  32.         for (unsigned i=0;i< class_handle_size;i++){
  33.                 mem[i]=0;//ハンドルのリセット
  34.         }
  35. }

結果です。

***** Veritak SV Engine Version 0.27 Build no.27.2009 *****

クラスオブジェクト アドレスは00336898
クラスオブジェクト アドレスは0033689C
クラスオブジェクト アドレスは003368A0
クラスオブジェクト アドレスは003368A4
クラスオブジェクト アドレスは003368A8
クラスオブジェクト アドレスは003368AC
クラスオブジェクト アドレスは003368B0
クラスオブジェクト アドレスは003368B4
クラスオブジェクト アドレスは003368B8
クラスオブジェクト アドレスは003368BC
データは、0です。
データは、1です。
データは、2です。
データは、3です。
データは、4です。
データは、5です。
データは、6です。
データは、7です。
データは、8です。
データは、9です。

**** Test Done. Total 0.00[msec] ****


<その他の配列>

最初の二つは、スカラー配列で、1ビットがCの1バイトに対応します。ベクタ表現とは、異なる配置になることに注意してください。
また、byte/shortint/int/longint/shortreal/real 配列のメモリ配置は、LRMでは厳密には規定していないのですが、VeritakSVでは、下のように、Cのメモリ配置と同じにしています。恐らく、同じになる実装が大半だと思います。

  1. module test;
  2.  
  3. parameter int MEM_SIZE=10;
  4.  
  5.  
  6. import "DPI-C" function void set_bit_array( inout bit   mem[MEM_SIZE ]);//パックド次元がないbit のアンパック  
  7. import "DPI-C" function void set_logic_array( inout logic   mem[MEM_SIZE ]);//パックド次元がないlogicのアンパック
  8.  
  9.  
  10. import "DPI-C" function void set_byte_array( inout byte   mem[MEM_SIZE ]);
  11. import "DPI-C" function void set_shortint_array( inout shortint   mem[MEM_SIZE ]);
  12. import "DPI-C" function void set_int_array( inout int   mem[MEM_SIZE ]);
  13. import "DPI-C" function void set_integer_array( inout integer   mem[MEM_SIZE ]);
  14. import "DPI-C" function void set_longint_array( inout longint   mem[MEM_SIZE ]);
  15. import "DPI-C" function void set_shortreal_array( inout shortreal   mem[MEM_SIZE ]);
  16. import "DPI-C" function void set_real_array( inout real   mem[MEM_SIZE ]);
  17. import "DPI-C" function void set_time_array( inout time   mem[MEM_SIZE ]);
  18.  
  19.         bit      bit_array[MEM_SIZE];
  20.         logic logic_array[MEM_SIZE];
  21.         byte byte_array[MEM_SIZE];
  22.         shortint shortint_array[MEM_SIZE];
  23.         int     int_array[MEM_SIZE];
  24.         integer integer_array[MEM_SIZE];
  25.         longint longint_array[MEM_SIZE];
  26.         shortreal shortreal_array[MEM_SIZE];
  27.         real real_array[MEM_SIZE];
  28.         time time_array[MEM_SIZE];
  29.  
  30.         initialbegin
  31.                
  32.                 set_bit_array(bit_array);
  33.                 for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。bit_array[%2d]=%b",i,bit_array[i]);
  34.                 $display("");
  35.  
  36.                 set_logic_array(logic_array);
  37.                 for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。logic_array[%2d]=%b",i,logic_array[i]);
  38.                 $display("");
  39.  
  40.                 set_byte_array(byte_array);
  41.                 for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。byte_array[%2d]=%2x",i,byte_array[i]);
  42.                 $display("");
  43.  
  44.                 set_shortint_array(shortint_array);
  45.                 for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。shortint_array[%2d]=%2x",i,shortint_array[i]);
  46.                 $display("");
  47.  
  48.                 set_int_array(int_array);
  49.                 for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。int_array[%2d]=%2x",i,int_array[i]);
  50.                 $display("");
  51.  
  52.                 set_integer_array(integer_array);
  53.                 for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。integer_array[%2d]=%2x",i,integer_array[i]);
  54.                 $display("");
  55.  
  56.                 set_longint_array(longint_array);
  57.                 for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。longint_array[%2d]=%2x",i,longint_array[i]);
  58.                 $display("");
  59.  
  60.                 set_shortreal_array(shortreal_array);
  61.                 for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。shortreal_array[%2d]=%f",i,shortreal_array[i]);
  62.                 $display("");
  63.  
  64.                 set_real_array(real_array);
  65.                 for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。real_array[%2d]=%f",i,real_array[i]);
  66.                 $display("");
  67.  
  68.                 set_time_array(time_array);
  69.                 for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。time_array[%2d]=%4x",i,time_array[i]);
  70.                 $display("");
  71.         end
  72.  
  73. endmodule
  74.  


C++のソースです。

  1. const unsigned array_size=10;
  2. void set_bit_array(svBit* bit_array)
  3. {
  4.         for (unsigned i=0;i<array_size;i++) bit_array[i]=i%2;
  5.  
  6. }
  7. void set_logic_array(svLogic* logic_array)
  8. {
  9.         for (unsigned i=0;i<array_size;i++) logic_array[i]=i%4;
  10.  
  11. }
  12.  
  13. void set_byte_array(char* byte_array)
  14. {
  15.         for (unsigned i=0;i<array_size;i++) byte_array[i]=i;
  16.  
  17. }
  18. void set_shortint_array(short* shortint_array)
  19. {
  20.         for (unsigned i=0;i<array_size;i++) shortint_array[i]=i;
  21.  
  22. }
  23.  
  24. void set_int_array(int* int_array)
  25. {
  26.         for (unsigned i=0;i<array_size;i++) int_array[i]=i;
  27.  
  28. }
  29. void set_integer_array(svLogicVecVal* integer_array)
  30. {
  31.         for (unsigned i=0;i<array_size;i++){
  32.                 integer_array[i].aval=i;
  33.                 integer_array[i].bval=0;
  34.         }
  35. }
  36. void set_longint_array(int64_t* longint_array)
  37. {
  38.         for (unsigned i=0;i<array_size;i++) longint_array[i]=i;
  39.  
  40. }
  41. void set_shortreal_array(float* shortreal_array)
  42. {
  43.         for (unsigned i=0;i<array_size;i++) shortreal_array[i]=static_cast<float>(i);
  44.  
  45. }
  46.  
  47. void set_real_array(double* real_array)
  48. {
  49.         for (unsigned i=0;i<array_size;i++) real_array[i]=static_cast<double>(i);
  50.  
  51. }
  52.  
  53. void set_time_array(svLogicVecVal* time_array)
  54. {
  55.         const unsigned chunk_elements=2;
  56.         for (unsigned i=0;i<array_size;i++){
  57.                 time_array[i*chunk_elements].aval=i;
  58.                 time_array[i*chunk_elements+1].aval=0;
  59.                 time_array[i*chunk_elements].bval=0;
  60.                 time_array[i*chunk_elements+1].bval=0;
  61.         }
  62. }

結果です。

***** Veritak SV Engine Version 0.27 Build no.27.2009 *****

Cで設定されました。bit_array[ 0]=0
Cで設定されました。bit_array[ 1]=1
Cで設定されました。bit_array[ 2]=0
Cで設定されました。bit_array[ 3]=1
Cで設定されました。bit_array[ 4]=0
Cで設定されました。bit_array[ 5]=1
Cで設定されました。bit_array[ 6]=0
Cで設定されました。bit_array[ 7]=1
Cで設定されました。bit_array[ 8]=0
Cで設定されました。bit_array[ 9]=1

Cで設定されました。logic_array[ 0]=0
Cで設定されました。logic_array[ 1]=1
Cで設定されました。logic_array[ 2]=z
Cで設定されました。logic_array[ 3]=x
Cで設定されました。logic_array[ 4]=0
Cで設定されました。logic_array[ 5]=1
Cで設定されました。logic_array[ 6]=z
Cで設定されました。logic_array[ 7]=x
Cで設定されました。logic_array[ 8]=0
Cで設定されました。logic_array[ 9]=1

Cで設定されました。byte_array[ 0]=00
Cで設定されました。byte_array[ 1]=01
Cで設定されました。byte_array[ 2]=02
Cで設定されました。byte_array[ 3]=03
Cで設定されました。byte_array[ 4]=04
Cで設定されました。byte_array[ 5]=05
Cで設定されました。byte_array[ 6]=06
Cで設定されました。byte_array[ 7]=07
Cで設定されました。byte_array[ 8]=08
Cで設定されました。byte_array[ 9]=09

Cで設定されました。shortint_array[ 0]=0000
Cで設定されました。shortint_array[ 1]=0001
Cで設定されました。shortint_array[ 2]=0002
Cで設定されました。shortint_array[ 3]=0003
Cで設定されました。shortint_array[ 4]=0004
Cで設定されました。shortint_array[ 5]=0005
Cで設定されました。shortint_array[ 6]=0006
Cで設定されました。shortint_array[ 7]=0007
Cで設定されました。shortint_array[ 8]=0008
Cで設定されました。shortint_array[ 9]=0009

Cで設定されました。int_array[ 0]=00000000
Cで設定されました。int_array[ 1]=00000001
Cで設定されました。int_array[ 2]=00000002
Cで設定されました。int_array[ 3]=00000003
Cで設定されました。int_array[ 4]=00000004
Cで設定されました。int_array[ 5]=00000005
Cで設定されました。int_array[ 6]=00000006
Cで設定されました。int_array[ 7]=00000007
Cで設定されました。int_array[ 8]=00000008
Cで設定されました。int_array[ 9]=00000009

Cで設定されました。integer_array[ 0]=00000000
Cで設定されました。integer_array[ 1]=00000001
Cで設定されました。integer_array[ 2]=00000002
Cで設定されました。integer_array[ 3]=00000003
Cで設定されました。integer_array[ 4]=00000004
Cで設定されました。integer_array[ 5]=00000005
Cで設定されました。integer_array[ 6]=00000006
Cで設定されました。integer_array[ 7]=00000007
Cで設定されました。integer_array[ 8]=00000008
Cで設定されました。integer_array[ 9]=00000009

Cで設定されました。longint_array[ 0]=0000000000000000
Cで設定されました。longint_array[ 1]=0000000000000001
Cで設定されました。longint_array[ 2]=0000000000000002
Cで設定されました。longint_array[ 3]=0000000000000003
Cで設定されました。longint_array[ 4]=0000000000000004
Cで設定されました。longint_array[ 5]=0000000000000005
Cで設定されました。longint_array[ 6]=0000000000000006
Cで設定されました。longint_array[ 7]=0000000000000007
Cで設定されました。longint_array[ 8]=0000000000000008
Cで設定されました。longint_array[ 9]=0000000000000009

Cで設定されました。shortreal_array[ 0]=0.000000
Cで設定されました。shortreal_array[ 1]=1.000000
Cで設定されました。shortreal_array[ 2]=2.000000
Cで設定されました。shortreal_array[ 3]=3.000000
Cで設定されました。shortreal_array[ 4]=4.000000
Cで設定されました。shortreal_array[ 5]=5.000000
Cで設定されました。shortreal_array[ 6]=6.000000
Cで設定されました。shortreal_array[ 7]=7.000000
Cで設定されました。shortreal_array[ 8]=8.000000
Cで設定されました。shortreal_array[ 9]=9.000000

Cで設定されました。real_array[ 0]=0.000000
Cで設定されました。real_array[ 1]=1.000000
Cで設定されました。real_array[ 2]=2.000000
Cで設定されました。real_array[ 3]=3.000000
Cで設定されました。real_array[ 4]=4.000000
Cで設定されました。real_array[ 5]=5.000000
Cで設定されました。real_array[ 6]=6.000000
Cで設定されました。real_array[ 7]=7.000000
Cで設定されました。real_array[ 8]=8.000000
Cで設定されました。real_array[ 9]=9.000000

Cで設定されました。time_array[ 0]=0000000000000000
Cで設定されました。time_array[ 1]=0000000000000001
Cで設定されました。time_array[ 2]=0000000000000002
Cで設定されました。time_array[ 3]=0000000000000003
Cで設定されました。time_array[ 4]=0000000000000004
Cで設定されました。time_array[ 5]=0000000000000005
Cで設定されました。time_array[ 6]=0000000000000006
Cで設定されました。time_array[ 7]=0000000000000007
Cで設定されました。time_array[ 8]=0000000000000008
Cで設定されました。time_array[ 9]=0000000000000009


**** Test Done. Total 15.00[msec] ****

16.2.11 構造体の引渡し
16.2.11.1 アンパックド構造体
アンパックド構造体は、予約語 packedがついていない構造体です。メンバーの型に制限がないのが、特徴です。メンバーのアライメントは、シミュレータの実装依存です。

SVソースです。

  1.  
  2.  
  3.  
  4. module root_scope;
  5.        
  6.          export "DPI-C" function func;
  7.          import "DPI-C" context task struct_operation();
  8.  
  9.         typedef struct   {
  10.                 byte red;
  11.                 byte green;
  12.                 byte blue;
  13.         } RGB_TYPE;
  14.  
  15.  
  16.         RGB_TYPE A;
  17.  
  18.         function void func(inout RGB_TYPE A);
  19.                 $display("私は、SVファンクションです。Cで設定した値を読みます。 赤=%3d 緑=%3d 青blue=%3d",A.red,A.green,A.blue);
  20.                 A.red=101;
  21.                 A.green=102;
  22.                 A.blue=103;
  23.                 $display("SVで設定し直しました。");
  24.                
  25.         endfunction
  26.  
  27.  
  28.         initial begin
  29.                 struct_operation();
  30.  
  31.         end
  32.  
  33. endmodule
  34.  
  35.  

これのソースを読み込ませて自動生成されたヘッダファイルが次です。

  1. /* Copyright 2010 www.sugawara-systems.com
  2. * Note:
  3. *   This file is automatically generated.
  4. *   Please do not edit this file - you will lose your edits.*/
  5.  
  6. #ifndef INCLUDED_DPIHEADER
  7. #define INCLUDED_DPIHEADER
  8. #ifdef __cplusplus
  9. #define DPI_LINK_DECL  extern "C"
  10. #else
  11. #define DPI_LINK_DECL
  12. #endif
  13. #include "svdpi.h"
  14.  
  15. typedef struct {
  16.         char red;
  17.         char green;
  18.         char blue;
  19. } RGB_TYPE;
  20.  
  21. DPI_LINK_DECL DPI_DLLISPEC
  22. void func(RGB_TYPE*);
  23.  
  24. DPI_LINK_DECL DPI_DLLESPEC
  25. int struct_operation();
  26.  
  27. #endif

このヘッダファイルでRGB_TYPEという構造体のメンバ配置が分かります。次は、C++ソースファイルです。

  1. int struct_operation()
  2. {
  3.                
  4.         RGB_TYPE rgb;
  5.         rgb.red=1;
  6.         rgb.green=2;
  7.         rgb.blue=3;
  8.  
  9.         func(&rgb);//SV functionを呼び出す
  10.  
  11.         printf("SVで設定した値は、赤=%3d 緑=%3d 青=%3dです。",rgb.red,rgb.green,rgb.blue);
  12.        
  13.  
  14.  
  15.         return 0;
  16. }


結果です。予想通りに動きます。

***** Veritak SV Engine Version 0.27 Build no.27.2009 *****

私は、SVファンクションです。Cで設定した値を読みます。 赤=  1 緑=  2 青blue=  3
SVで設定し直しました。
SVで設定した値は、赤=101 緑=102 青=103です。
**** Test Done. Total 5.00[msec] ****

アンパックドの場合のメンバの配置は、シミュレータの実装に依存すると思います。VeritakSVの場合は、VC++のdefault配置に準じているので、出力されるCヘッダファイルのイメージでアクセスすればよいです。

次は、少し複雑な例です。

  1.  
  2.  
  3.  
  4. module root_scope;
  5.        
  6.          export "DPI-C" function func_example;
  7.          import "DPI-C" context task struct_operation();
  8.  
  9.         typedef struct   {
  10.                 byte red;
  11.                 byte green;
  12.                 byte blue;
  13.         } RGB_TYPE;
  14.  
  15.  
  16.         RGB_TYPE A;
  17.  
  18.         typedefstruct {
  19.                 RGB_TYPE RGB;//メンバーが構造体
  20.                 bit b0;//2値スカラー
  21.                 logic b1;//4値スカラー
  22.                 bit [8:0] b2;//2値ベクター
  23.                 logic [8:0] b3;//4値ベクター
  24.                 byte b4;
  25.                 shortint b5;
  26.                 int      b6;
  27.                 integer b7;
  28.                 longint b8;
  29.                 time b9;
  30.                 shortreal b10;
  31.                 real b11;
  32.                 bit [127:0] b12;//パックド配列
  33.                 logic [127:0] b13;//パックド配列
  34.                 logic [127:0] b14 [10][10];//アンパックド配列
  35.                 bit [3:0][31:0] b15 [0:3][0:3];//アンパックド配列
  36.         } EXAMPLE_TYPE;
  37.  
  38.  
  39.         function void func_example(inout EXAMPLE_TYPE A);
  40.  
  41.                 $display("私は、SVファンクションです。Cで設定した値を読みます。");
  42.                 $display("RGB.red=%d",A.RGB.red);
  43.                 $display("RGB.green=%d",A.RGB.green);
  44.                 $display("RGB.blue=%d",A.RGB.blue);
  45.                 $display("b0=%b",A.b0);
  46.                 $display("b1=%b",A.b1);
  47.                 $display("b2=%d",A.b2);
  48.                 $display("b3=%b",A.b3);
  49.                 $display("b4=%d",A.b4);
  50.                 $display("b5=%d",A.b5);
  51.                 $display("b6=%d",A.b6);
  52.                 $display("b7=%d",A.b7);
  53.                 $display("b8=%d",A.b8);
  54.                 $display("b9=%d",A.b9);
  55.                 $display("b10=%f",A.b10);
  56.                 $display("b11=%f",A.b11);
  57.                 $display("b12=%x",A.b12);
  58.                 $display("b13=%x",A.b13);
  59.                 $display("b14=%x",A.b14[9][9]);
  60.                 $display("b15=%x",A.b15[3][3]);
  61.  
  62.                 A.RGB.red=101;
  63.                 A.RGB.green=102;
  64.                 A.RGB.blue=103;
  65.                 A.b0=1'b0;
  66.                 A.b1=1'bx;
  67.                 A.b2=256;
  68.                 A.b3=99;
  69.                 A.b4=4;
  70.                 A.b5=5000;
  71.                 A.b6=6000;
  72.                 A.b7=7000;
  73.                 A.b8=8000;
  74.                 A.b9=9000;
  75.                 A.b10=1000.0;
  76.                 A.b11=1100.0;
  77.                 A.b12=32'h12000;
  78.                 A.b13=32'h13000;
  79.                 A.b14[9][9]=32'h14000;
  80.                 A.b15[3][3]=32'h15000;
  81.                 $display("SVで設定し直しました。");
  82.                
  83.         endfunction
  84.  
  85.  
  86.         initial begin
  87.                 struct_operation();
  88.  
  89.         end
  90.  
  91. endmodule
  92.  
  93.  

これに対するVeritakSVが生成したCヘッダ出力です。

  1. /* Copyright 2010 www.sugawara-systems.com
  2. * Note:
  3. *   This file is automatically generated.
  4. *   Please do not edit this file - you will lose your edits.*/
  5.  
  6. #ifndef INCLUDED_DPIHEADER
  7. #define INCLUDED_DPIHEADER
  8. #ifdef __cplusplus
  9. #define DPI_LINK_DECL  extern "C"
  10. #else
  11. #define DPI_LINK_DECL
  12. #endif
  13. #include "svdpi.h"
  14.  
  15. typedefstruct {
  16.         char red;
  17.         char green;
  18.         char blue;
  19. } RGB_TYPE;
  20.  
  21. typedefstruct {
  22.         RGB_TYPE RGB;
  23.         svBit b0;
  24.         svLogic b1;
  25.         svBitVecVal b2[SV_PACKED_DATA_NELEMS(9)];
  26.         svLogicVecVal b3[SV_PACKED_DATA_NELEMS(9)];
  27.         char b4;
  28.         short b5;
  29.         int b6;
  30.         svLogicVecVal b7;
  31.         int64_t b8;
  32.         svLogicVecVal b9[SV_PACKED_DATA_NELEMS(64)];
  33.         float b10;
  34.         double b11;
  35.         svBitVecVal b12[SV_PACKED_DATA_NELEMS(128)];
  36.         svLogicVecVal b13[SV_PACKED_DATA_NELEMS(128)];
  37.         svLogicVecVal b14[10][10][SV_PACKED_DATA_NELEMS(128)];
  38.         svBitVecVal b15[4][4][SV_PACKED_DATA_NELEMS(128)];
  39. } EXAMPLE_TYPE;
  40.  
  41. DPI_LINK_DECL DPI_DLLISPEC
  42. void func_example(EXAMPLE_TYPE*);
  43.  
  44. DPI_LINK_DECL DPI_DLLESPEC
  45. int struct_operation();
  46.  
  47. #endif



32ビット幅がパックド配列における1チャンク(塊)になります。
SVとCの表現の対応は次の通りです。X86(リトルエンディアン)のアンパック構造体では、記述順にオフセットが足されます。

SV表現 C表現 備考 メモリ占有バイト数(バイト) 構造体先頭アドレスからのオフセットバイト(X86)
bit b0 svBit b0; 2値スカラー 3
logic b1; svLogic b1; 4値スカラー 4
bit [8:0] b2; svBitVecVal b2[SV_PACKED_DATA_NELEMS(9)]; 2値ベクタ 4 8
logic [8:0] b3; svLogicVecVal b3[SV_PACKED_DATA_NELEMS(9)]; 4値ベクタ 8 12
byte b4; char b4; 2値8bit 1 20
shortint b5; short b5; 2値16ビット 2 22
int      b6; int b6; 2値32ビット 4 24
integer b7; svLogicVecVal b7; 4値32ビット 8 28
longint b8; int64_t b8; 2値64ビット(windows) 8 40
time b9; svLogicVecVal b9[SV_PACKED_DATA_NELEMS(64)]; 4値ベクタ 16 48
shortreal b10; float b10; 単精度浮動小数 4 64
real b11; double b11; 倍精度浮動小数 8 72
bit [127:0] b12; svBitVecVal b12[SV_PACKED_DATA_NELEMS(128)]; パックド2値ベクタ 4x4 80
logic [127:0] b13; svLogicVecVal b13[SV_PACKED_DATA_NELEMS(128)]; パックド4値ベクタ 8x4 96
logic [127:0] b14 [10][10]; svLogicVecVal b14[10][10][SV_PACKED_DATA_NELEMS(128)]; アンパックド4値配列 8x4x100 128
bit [3:0][31:0] b15 [0:3][0:3]; svBitVecVal b15[4][4][SV_PACKED_DATA_NELEMS(128)]; アンパックド2値配列 4x4x16 3328

TIPS



C++ソースです。

  1. int struct_operation()
  2. {
  3.         EXAMPLE_TYPE rgb;
  4.  
  5.         rgb.RGB.red=1;
  6.         rgb.RGB.green=2;
  7.         rgb.RGB.blue=3;
  8.  
  9.         rgb.b0=sv_1;
  10.         rgb.b1=sv_z;//sv_z;
  11.         rgb.b2[0]       =20;
  12.  
  13.         rgb.b3[0].aval=0x1ff;
  14.         rgb.b3[0].bval=0x1ff;
  15.        
  16.         rgb.b4=40;
  17.         rgb.b5=500;
  18.         rgb.b6=600;
  19.  
  20.         rgb.b7.aval=700;
  21.         rgb.b7.bval=0;
  22.  
  23.         rgb.b8=800;
  24.  
  25.         rgb.b9[0].aval=900;//Timeは、2チャック
  26.         rgb.b9[0].bval=0;
  27.         rgb.b9[1].aval=0;
  28.         rgb.b9[1].bval=0;
  29.  
  30.         rgb.b10=100.0;//float
  31.         rgb.b11=110.0;//double
  32.  
  33.         memset(&rgb.b12,0, sizeof(svBitVecVal)*SV_PACKED_DATA_NELEMS(128));//zero クリア
  34.         rgb.b12[0]=0x1200;
  35.  
  36.         memset(&rgb.b13,0, sizeof(svLogicVecVal)*SV_PACKED_DATA_NELEMS(128));//zero クリア
  37.         rgb.b13[0].aval=0x1300;
  38.  
  39.         memset(&rgb.b14,0, sizeof(svLogicVecVal)*SV_PACKED_DATA_NELEMS(128)*100);//zero クリア
  40.         rgb.b14[9][9][0].aval=0x1400;
  41.  
  42.         memset(&rgb.b15,0, sizeof(svBitVecVal)*SV_PACKED_DATA_NELEMS(128)*4*4);//zero クリア
  43.         rgb.b15[3][3][0]=0x1500;
  44.  
  45.         func_example(&rgb);//SV functionを呼び出す
  46.  
  47.         printf("SVで設定した値は、赤=%3d 緑=%3d 青=%3dです。\n",rgb.RGB.red,rgb.RGB.green,rgb.RGB.blue);
  48.         printf("SVで設定した値は、b0=%1d\n",rgb.b0);
  49.         printf("SVで設定した値は、b1=%1d\n",rgb.b1);
  50.         printf("SVで設定した値は、b2=%1d\n",rgb.b2[0]& SV_MASK(9));//範囲外はINVALIDな値なのでマスクする
  51.         printf("SVで設定した値は、b3=%3d\n",rgb.b3[0].aval & SV_MASK(9));//範囲外はINVALIDな値なのでマスクする
  52.         printf("SVで設定した値は、b4=%3d\n",rgb.b4);
  53.         printf("SVで設定した値は、b5=%3d\n",rgb.b5);
  54.         printf("SVで設定した値は、b6=%3d\n",rgb.b6);
  55.         printf("SVで設定した値は、b7=%3d\n",rgb.b7.aval );
  56.         printf("SVで設定した値は、b8=%3d\n",rgb.b8);
  57.         printf("SVで設定した値は、b7=%3d\n",rgb.b9[0].aval );
  58.        
  59.         printf("SVで設定した値は、b10=%f\n",rgb.b10);
  60.         printf("SVで設定した値は、b11=%f\n",rgb.b11);
  61.  
  62.         printf("SVで設定した値は、b12=%x\n",rgb.b12[0]);
  63.         printf("SVで設定した値は、b13=%x\n",rgb.b13[0].aval);
  64.         printf("SVで設定した値は、b14=%x\n",rgb.b14[9][9][0].aval);
  65.         printf("SVで設定した値は、b15=%x\n",rgb.b15[3][3][0]);
  66.  
  67.         return 0;
  68. }


結果です。

***** Veritak SV Engine Version 0.27 Build no.27.2009 *****

私は、SVファンクションです。Cで設定した値を読みます。
RGB.red=   1
RGB.green=   2
RGB.blue=   3
b0=1
b1=z
b2= 20
b3=xxxxxxxxx
b4=  40
b5=   500
b6=        600
b7=        700
b8=                  800
b9=                 900
b10=100.000000
b11=110.000000
b12=00000000000000000000000000001200
b13=00000000000000000000000000001300
b14=00000000000000000000000000001400
b15=00000000000000000000000000001500
SVで設定し直しました。
SVで設定した値は、赤=101 緑=102 青=103です。
SVで設定した値は、b0=0
SVで設定した値は、b1=3
SVで設定した値は、b2=256
SVで設定した値は、b3= 99
SVで設定した値は、b4=  4
SVで設定した値は、b5=5000
SVで設定した値は、b6=6000
SVで設定した値は、b7=7000
SVで設定した値は、b8=8000
SVで設定した値は、b7=9000
SVで設定した値は、b10=1000.000000
SVで設定した値は、b11=1100.000000
SVで設定した値は、b12=12000
SVで設定した値は、b13=13000
SVで設定した値は、b14=14000
SVで設定した値は、b15=15000

**** Test Done. Total 6.00[msec] ****


16.2.11.2 パックド構造体
パックド構造体は、予約語 packedがついている構造体です。構造体内ではビット連続が保証されます。またそれ故にメンバーは、ベクタータイプである必要があります。(real/shortrealは、不可) 2値4値を混在させるとメモリ配置的には、4値として生成されてしまうので、混在させないようにします。


 アンパックド構造体最初の例と同じRGB_TYPE構造体です。違うのは、packedがついていることです。メンバーの順序も同じですが、中身は、アンパックドとは違います。packedの場合は、記述の最初がMSB側になり、記述最後がLSB側のビットになります。

  1.  
  2.  
  3.  
  4. module root_scope;
  5.        
  6.          export "DPI-C" function func;
  7.          import "DPI-C" context task struct_operation();
  8.  
  9.         typedef struct  packed {//パックド構造体のビット配置
  10.                 byte red;//MSB
  11.                 byte green;
  12.                 byte blue;//LSB ↑
  13.         } RGB_TYPE;
  14.  
  15.  
  16.  
  17.  
  18.         function void func(inout RGB_TYPE A);
  19.                 $display("私は、SVファンクションです。Cで設定した値を読みます。 赤=%3d 緑=%3d 青=%3d",A.red,A.green,A.blue);
  20.                 A.blue=101;
  21.                 A.green=102;
  22.                 A.red=103;
  23.                 $display("SVで設定し直しました。");
  24.                
  25.         endfunction
  26.  
  27.  
  28.         initial begin
  29.  
  30.        
  31.                 struct_operation();
  32.  
  33.         end
  34.  
  35. endmodule
  36.  
  37.  


生成されたCファイルでは、メンバー情報は消えて単なる2値ベクタになってしまいます。

  1. /* Copyright 2010 www.sugawara-systems.com
  2. * Note:
  3. *   This file is automatically generated.
  4. *   Please do not edit this file - you will lose your edits.*/
  5.  
  6. #ifndef INCLUDED_DPIHEADER
  7. #define INCLUDED_DPIHEADER
  8. #ifdef __cplusplus
  9. #define DPI_LINK_DECL  extern "C"
  10. #else
  11. #define DPI_LINK_DECL
  12. #endif
  13. #include "svdpi.h"
  14.  
  15. DPI_LINK_DECL DPI_DLLISPEC
  16. void func(svBitVecVal* );
  17.  
  18. DPI_LINK_DECL DPI_DLLESPEC
  19. int struct_operation();
  20.  
  21. #endif
  22.  


C++ソースです。
 メンバー位置をSV構造体から読み取りパートセレクトで取り出しています。パックド構造体は、基本的にベクターと同義だからです。

  1.  
  2. int struct_operation()
  3. {
  4.        
  5.         svBitVecVal rgb;
  6.         svPutPartselBit(&rgb,1,0,8);//青
  7.         svPutPartselBit(&rgb,2,8,8);//緑
  8.         svPutPartselBit(&rgb,3,16,8);//赤
  9.  
  10.         func(&rgb);//SV functionを呼び出す
  11.  
  12.         svBitVecVal blue=0;
  13.         svBitVecVal red=0;
  14.         svBitVecVal green=0;
  15.        
  16.  
  17.         svGetPartselBit(&blue,&rgb,0,8);//SVで設定した値を取り出す
  18.         svGetPartselBit(&green,&rgb,8,8);//SVで設定した値を取り出す
  19.         svGetPartselBit(&red,&rgb,16,8);//SVで設定した値を取り出す
  20.  
  21.  
  22.         printf("SVで設定した値は、赤=%3d 緑=%3d 青=%3dです。\n",red,green,blue);
  23.  
  24.         return 0;
  25. }


結果です。

***** Veritak SV Engine Version 0.27 Build no.27.2009 *****

私は、SVファンクションです。Cで設定した値を読みます。 赤=  3 緑=  2 青=  1
SVで設定し直しました。
SVで設定した値は、赤=103 緑=102 青=101です。

**** Test Done. Total 4.00[msec] ****



16.2.12 バインド名

 Cから見える名前空間は、グローバルです。SVでの宣言 sv_import_taskがイコールCでのリンケージ名になります。

  1. module gloval_name_space;
  2.  
  3.         import "DPI-C"  context task sv_import_task ();
  4.  
  5.  
  6. endmodule

全モジュールで、名前は、ユニークである必要があります。Cのリンケージは、グローバルだからです。

そこで、

Cリンケージ名を別名にする方法が下です。それぞれ、SV上の名前は同じですが、C上では、C_task1,C_task2と言う別名でリンクが行われます。
(Cからは、sv_import_taskは、見えません)

  1. module gloval_name_space1;
  2.  
  3.         import "DPI-C"  context C_task1=task sv_import_task ();
  4.  
  5.  
  6. endmodule
  7.  
  8. module gloval_name_space2;
  9.  
  10.         import "DPI-C"  context C_task2=task sv_import_task ();
  11.  
  12.  
  13. endmodule
  14.  
  15.  
  16.  

16.2.10 スコープ
複数のインスタンスを持つ場合は、どうなるのでしょうか?

  1. module multi_export;
  2.  
  3.  
  4.  for (genvar g=0;g<3;g++)       export_test ex1();//3個のインスタンスを生成
  5.  
  6.  
  7.  
  8. endmodule
  9.  
  10.  
  11. module export_test;
  12.  
  13.        
  14.         import "DPI-C"  context function void multi_import_func ();
  15.         export "DPI-C"  multi_export_inc=function  sv_inc;
  16.  
  17.  
  18.         function  int sv_inc(int i1);//Cから呼ばれるExportファンクション
  19.                 $display("........................私は sv_inc 関数です。%m");
  20.                 return i1+1;
  21.         endfunction
  22.        
  23.  
  24.  
  25.        
  26.         initial
  27.                 multi_import_func();//Cのインポートファンクションを呼ぶ
  28.  
  29.  
  30.  
  31. endmodule


C側のソースです。

  1. extern "C" __declspec(dllexport) void multi_import_func()
  2. {
  3.  
  4.         int i;//ローカル変数
  5.        
  6.         vpi_printf("私は、C のimport_func()です。ロケーション=%s\n",svGetNameFromScope(svGetScope()));
  7.        
  8.  
  9.         i=multi_export_inc(100);//SV Functionを呼ぶ
  10.         vpi_printf("私はCです。 multi_export_incを呼び出した結果=%dが返ってきました。\n\n",i);
  11.        
  12.        
  13. }


一個のモジュールについてシミュレータ内部では次のような構成になります。


結果です。CのsvGetScope()関数は、svdpi.hで定義されるAPIで、インポートファンクションmulti_import_func ()が定義されているロケーションを返します。
svGetNameFromScope()は、ロケーション情報から名前に変換するAPIです。SVの普通のfunctionとなんら変わりなく動いていることが分かります。
ロケーションは、呼んでいる場所ではなくて、定義場所になります。普通のtask/functionがそこに定義されたように思えばよいでしょう。


***** Veritak SV Engine Version 0.25 Build Oct.23.2009 *****

私は、C のimport_func()です。ロケーション=multi_export.genblk0[0].ex1
........................私は sv_inc 関数です。multi_export.genblk0[0].ex1.sv_inc
私はCです。 multi_export_incを呼び出した結果=101が返ってきました。

私は、C のimport_func()です。ロケーション=multi_export.genblk0[1].ex1
........................私は sv_inc 関数です。multi_export.genblk0[1].ex1.sv_inc
私はCです。 multi_export_incを呼び出した結果=101が返ってきました。

私は、C のimport_func()です。ロケーション=multi_export.genblk0[2].ex1
........................私は sv_inc 関数です。multi_export.genblk0[2].ex1.sv_inc
私はCです。 multi_export_incを呼び出した結果=101が返ってきました。


**** Test Done. Total 4.00[msec] ****

それでは、インポートtaskでは、どうなるでしょうか?同じように3個のインスタンスを生成してみます。

  1. module multi_export;
  2.  
  3.  
  4.  for (genvar g=0;g<3;g++)       export_test ex1();
  5.  
  6.  
  7.  
  8. endmodule
  9.  
  10.  
  11. module export_test;
  12.  
  13.        
  14.         import "DPI-C"  context C_import_task=task sv_import_task ();
  15.         export "DPI-C"  multi_export_inc=function  sv_inc;
  16.  
  17.  
  18.  
  19.         function  int sv_inc(int i1);//Cから呼ばれるExportファンクション
  20.                 $display("........................私は sv_inc 関数です。%m");
  21.                 return i1+1;
  22.         endfunction
  23.  
  24.        
  25.         initial
  26.                 sv_import_task();//Cのインポートファンクションを呼ぶ
  27.  
  28.         export "DPI-C" delay_task_by_parameter_m= task delay_task_by_parameter;
  29.         export "DPI-C"  get_sv_time_m=function  get_sv_time;
  30.  
  31.         function longint get_sv_time();
  32.                 return $time;
  33.         endfunction
  34.         task delay_task_by_parameter( inputlongint delay);
  35.                 #(delay);
  36.         endtask
  37. endmodule


C++側ソースです。

  1. extern "C" __declspec(dllexport) int C_import_task()
  2. {       static int instance_counter=0;//STATIC変数
  3.         int i;
  4.  
  5.         vpi_printf("私は、C_import_task()です。ロケーション=%s\n",svGetNameFromScope(svGetScope()));
  6.  
  7.         i=multi_export_inc(instance_counter++);//SV Functionを呼ぶ
  8.         vpi_printf(" multi_export_incを呼び出した結果=%dが返ってきました。#10待ちます。\n\n",i);
  9.         delay_task_by_parameter_m(10);
  10.         __int64 t=get_sv_time_m();//SV Functionを呼ぶ
  11.  
  12.         vpi_printf(" ロケーション=%s\n",svGetNameFromScope(svGetScope()));
  13.         vpi_printf("  現在時刻は%ldです。\n",t);//printfの代わりにvpi_printfを使う
  14.  
  15.         vpi_printf("  ローカル変数i=%dです。\n\n",i);
  16.  
  17.         return 0;
  18. }

上のC++ソースで、ローカル変数iと static 変数 instance_counterに注目します。initialから3個のインポートtaskを起動していますが、instance_counterは、一つですので、インポートtask間で共通な変数になります。ところが、ローカル変数iは、そのインポートtask中(スレッド中)に独立した領域を持っていて、スレッドが終了するまで、値を保持しています。記述は、通常のCですが、これらの動作は、SystemVerilongのautomatic taskと全く同じに動くことが分かります。

***** Veritak SV Engine Version 0.25 Build Oct.23.2009 *****

私は、C_import_task()です。ロケーション=multi_export.genblk0[0].ex1
........................私は sv_inc 関数です。multi_export.genblk0[0].ex1.sv_inc
 multi_export_incを呼び出した結果=1が返ってきました。#10待ちます。

私は、C_import_task()です。ロケーション=multi_export.genblk0[1].ex1
........................私は sv_inc 関数です。multi_export.genblk0[1].ex1.sv_inc
 multi_export_incを呼び出した結果=2が返ってきました。#10待ちます。

私は、C_import_task()です。ロケーション=multi_export.genblk0[2].ex1
........................私は sv_inc 関数です。multi_export.genblk0[2].ex1.sv_inc
 multi_export_incを呼び出した結果=3が返ってきました。#10待ちます。

 ロケーション=multi_export.genblk0[0].ex1
  現在時刻は10です。
  ローカル変数i=1です。

 ロケーション=multi_export.genblk0[1].ex1
  現在時刻は10です。
  ローカル変数i=2です。

 ロケーション=multi_export.genblk0[2].ex1
  現在時刻は10です。
  ローカル変数i=3です。


**** Test Done. Total 10.00[msec] ****



一個のモジュールについて、内部を図にすると以下のようになります。Cの記述は、C functionのローカルスタックとコンテキストは、スレッドのスタックに含まれSVの他のスレッドと違うところはありません。


<インポートタスクのスタックサイズに注意>
 
上図で分かる通り、C側のスタックは、どれだけ使われるかは、ユーザプログラムとCコンパイラの設定により、SVコンパイラは知るすべがありません。(VC++では、defaultで1MBのスタックを確保していますが、設定で変えることができます。)  VeritakSVでは、インポートスタックのサイズは、default1MBにしていますが、オプションでも設定が可能です。



16.2.7 他の言語との接続
16.2.7.1 ruby
<rubyを使う>
インストール
1)http://www.ruby-lang.org/ja/downloads/からWindows バイナリ版をインストールします。
2)パスを設定します。(筆者の環境では、C:\Program Files\ruby-1.8\bin\を追加しました。



 rubyを起動するタスクを呼び出します。

  1. module ruby;
  2.  
  3.        
  4.         import "DPI-C"  context task  ruby_task();
  5.  
  6.  
  7.         initial
  8.                 ruby_task();//C インポートtaskを呼ぶ
  9.                
  10. endmodule


 C++側ソースです。Cの標準関数systemを用いて、スクリプトLibraryTime2.rbを動かしています。system関数は、ブロッキング動作をします。rubyが終わるまで返ってきません。

  1. extern "C"  __declspec(dllexport) int ruby_task()
  2. {
  3.        
  4.  
  5.         system("ruby LibraryTime2.rb");//ブロッキングプロセス
  6.        
  7.         return 0;
  8.  
  9. }

実行すると、次のようにVeritakSVコンソール上に出力されます。

C:\Users\tak.sugawara\Documents\Visual Studio 2005\Projects\dpi_sample\ruby1.v(1)::ruby
Verilogのシミュレーションの準備が完了しました。スタートは,Goボタンを押してください。
***** Veritak SV Engine Version 0.26 Build Nov.23.2009 *****


2009年11月23日
月曜日
12時46分42秒
TimeZone:東京 (標準時)
今年の元旦から数えて327日目

**** Test Done. Total 94.00[msec] ****

 

16.2.7.2 DOS コマンドライン
DOSのコマンドラインを使ってファイルのCOPYを行ってみます。SystemVerilogから引き数を与えられると、より汎用的に使えるので、そのように書いてみました。

  1. module ruby;
  2.  
  3.        
  4.  
  5.  
  6.         import "DPI-C"  context task  dos_command(string);//
  7.         initial
  8.                 dos_command("copy ruby_task.v  copy_task.v");//C インポートtaskを呼ぶ
  9.                
  10. endmodule

SystemVerilogのstringは、Cの入力では、const char* になります。そのままsystem関数に渡せばOKです。
最初の例は、次のようにも書けます。

  1. extern "C" __declspec(dllexport) int dos_command( const char*command)
  2. {
  3.        
  4.         system(command);
  5.         return 0;
  6. }
  1. module ruby;
  2.  
  3.        
  4.         import "DPI-C"  context task  dos_command(string);
  5.  
  6.        
  7.         initial
  8.         dos_command("ruby LibraryTime2.rb");//C インポートtaskを呼ぶ
  9.  
  10. endmodule
  11.  
  12.  
  13.  
  14.  

rubyのソースは、こちらから頂戴しました。

このように、簡単かつ自在にrubyを呼べるので、ファイル処理や、正規表現等、verification工程で役立つことと思います。また、上記の例は、rubyに限ったものではなく、SV側のコマンドラインをいじるだけで、perlや、pythonも同様に呼び出せる筈です。