SystemVerilog Tutorial           

                                                                      Last Update Jul.8.2010

下記は、SystemVerilog Tutorial(リリース前の予稿)です。 LRM2005-P1800を元にしていますが、改訂VersionであるLRM2010では、変更されている箇所があります。(Draft8で確認。全てにサンプル記述とVeritakSVによる結果がありますが、VeritakSVは、未だリリースの予定をお話しできる段階ではありません。 )

16.SystemVerilogの新機能
 

16.1 fork join / fork join_any/ fork join_none
16.1.1 平行プロセスjoin_anyとjoin_noneの追加
 
verilogでは、fork joinで挟まれたステートメントは、平行プロセスになります。
たとえば、my_taskを起動するのに、

  1. initial my_task;
  2. initial my_task; 


と書く代わりに、二つの平行プロセスを同時に起動するforkを使い、

  1. fork  
  2.         my_task;
  3.         my_task;
  4. join 


と書くことができます。joinは、合流するという意味合いです。二つのプロセスの終了を待ってjoin以下が始まります。
SVでは、さらにjoin_anyとjoin_noneが追加されています。

ベンチです。

module fork_test2;

  task  my_task;
                $display("Hi  %d ",$time);
                
  endtask 

 


initial begin
    fork//:Fork_Join
       #5  my_task;//process1
       #10 my_task;//process2
    join
    $display("Join Any Time=%d",$time); 
end

initial begin
    
    fork//:Fork_Any
       #5  my_task;//process1
       #10 my_task;//process2
    join_any
    $display("Join Any Time=%d",$time); 
end

initial begin
    
    
    fork//:Fork_None
       #5  my_task;//process1
       #10 my_task;//process2
    join_none
    $display("Join None Time=%d",$time); 
end

endmodule 


結果です。join_noneの方は、すぐに fork- join_noneを抜けていることが分かります。しかし、タスクの起動は、時刻#5と#10でしっかり行われています。この記述は、なにかtaskを起動しておいて、main() を処理する形の定石かもしれません。

join_anyの方は、#5と#10のスレッドの内、早いほうのスレッドが終了した時点で抜けます。
fork-join は、fork-join内の全てのスレッドの完了を待って抜けます。

Loading vpi.dll
Load Done.
***** Veritak SV Engine Version 0.021 Build Dec.30.2007 *****

Join None Time=                   0
Hi                     5
Hi                     5
Join Any Time=                   5
Hi                     5
Hi                    10
Join Any Time=                  10
Hi                    10
Hi                    10

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


join_noneは、子プロセスの終了を待たずに、起動します。スルーしているかのように見えますが。親プロセス、子プロセス共、平行に走ります。(正確には、親プロセスのブロッキング文の実行後に子プロセスはスタートします。)いわば、”ノンブロキング”なプロセス形態です。

join_anyは、子プロセスのうち最初に終了したプロセスの後(どれでもよい)、親プロセスが始まります。従い、終了していない子プロセスと親プロセスとで平行に走ります。使い道としては、たとえばタイムアウト処理に使えそうです。

fork join_xx を使うと、複雑なプロセスが記述できます。


ベンチで少し詳しく動作を見ていきましょう。

module top;
   initial begin
      
      fork
         do begin
            #1;
            $display("1st thread running time=%d",$time);
         end while($time<10);
      join_none // join_non なので、do while を起動してすぐに下に行く

      fork
         
         begin
            #3;
            $display("2nd thread finished time=%d",$time);
         end

         
         begin
            #4;
            $display("3rd thread finished time=%d",$time);
         end
      join_any //join_any なので、一つ起動したら下に行く
     // disable fork;
   end
endmodule


***** Veritak SV Engine Version 0.031 Build Jun.23.2008 *****

1st thread running time=                    1
1st thread running time=                    2
2nd thread finished time=                    3
1st thread running time=                    3
3rd thread finished time=                    4
1st thread running time=                    4
1st thread running time=                    5
1st thread running time=                    6
1st thread running time=                    7
1st thread running time=                    8
1st thread running time=                    9
1st thread running time=                   10
sim_finished
**** Test Done. Total 23.00[msec] ****

このベンチでは、3つのスレッドが起動していることが分かりますね。

最後の コメントアウトしているdisable fork をイネーブルしてみましょう。

***** Veritak SV Engine Version 0.031 Build Jun.23.2008 *****

1st thread running time=                    1
1st thread running time=                    2
2nd thread finished time=                    3
sim_finished
**** Test Done. Total 10.00[msec] ****

disable forkは、現在のスレッドが起動した全ての子スレッドをdisable します。disable fork の行にやってくるのは、fork -any 中の早く終了するスレッド#3が終わった時点になります。その時点で、現在のinitial スレッドが起動した全てのスレッドを disable します。 disable されたスレッドは、消滅します。従って、予約イベントも取り消され、終了するという訳です。従って、1st 2nd 3rd スレッド全てが終了されます。ここでdisable という意味は、停止というよりは、消滅です。消えてなくなると思って構いません。

ここで問題です。特定の一つのスレッドをdisable するには、どうしたらよいでしょうか?

それには、ラベルをつけて disable すればよいですね。下では、THREAD1というラベルをつけて それを disable しています。

module top;
   initial begin

    
      fork :THREAD1
         do begin 
            #1;
            $display("1st thread running time=%d",$time);
         end while($time<10);
      join_none : THREAD1

      fork
      
         begin
            #3;
            $display("2nd thread finished time=%d",$time);
         end

         begin 
            #4;
            $display("3rd thread finished time=%d",$time);
         end 
      join_any 
      disable THREAD1;
        $display("THEAD1 Disabled");
   end
endmodule


16.1.3 disable と disable fork の違い

結果です。disable THREAD1を行うことで、THREAD1は、意図通りdisableされました。

***** Veritak SV Engine Version 0.031 Build Jun.23.2008 *****

1st thread running time=                    1
1st thread running time=                    2
2nd thread finished time=                    3
THEAD1 Disabled
3rd thread finished time=                    4
sim_finished
**** Test Done. Total 12.00[msec] ****


ところで、このやり方は注意することがあります。disable は、Staticなスコープ中の全スレッドに対して作用してしまいます。
次のベンチでその辺を見てみましょう。

同じタスクを平行プロセスで起動しています。
(このとき引数が意味を持つためには、task は、automatic である必要がありました。さもないと引数は、Static変数で上書きされてしまうのでしたね。)

module top1;
   initial begin
      fork
         fork_processes(4);
         fork_processes(7);
      join_none

   end 
   task automatic fork_processes(int delay);
      fork : a
         begin
            #delay;
            $display("Thread finished");
         end
      join_any : a
      //disable a;
   endtask
endmodule 

結果です。

***** Veritak SV Engine Version 0.031 Build Jun.23.2008 *****

Thread finished
Thread finished
sim_finished
**** Test Done. Total 11.00[msec] ****


コメントアウトしている disable a; をイネーブルしてみましょう。
すると、

***** Veritak SV Engine Version 0.031 Build Jun.23.2008 *****

Thread finished
sim_finished
**** Test Done. Total 7.00[msec] ****

disable a; で、aスレッドを disable しています。このとき、同じスコープ名のスレッドが2個起動していることに注意してください。 disable は、この2個のスレッドに対して作用します。つまり、どちらのスレッドもdisable されてしまいます。したがって、Thread finished は、2個でません。2個目が出る前に消されてしまいます。
そこで、disable の代わりにdisable fork を使ってみます。

module top1;
   initial begin
      fork
         fork_processes(4);
         fork_processes(7);
      join_none

   end 
   task automatic fork_processes(int delay);
      fork : a
         begin
            #delay;
            $display("Thread finished");
         end
      join_any : a
      disable fork;
   endtask
endmodule 

disable fork は、現在のスレッドから生まれた子供、孫..に対してのみ作用します。同じスコープであっても自分が起動していないスレッドのことは関知しません。

***** Veritak SV Engine Version 0.031 Build Jun.23.2008 *****

Thread finished
Thread finished
sim_finished
**** Test Done. Total 11.00[msec] ****

16.1.4 wait fork

wait fork は、現在のスレッドが生成した全てのスレッドの完了を待ちます。

下の fork_blocks タスクでは、4つのtask を起動しています。wait fork は、現在のスレッドfork_block が起動した4つのスレッドの完了を待ちます(ブロックします)。
全てが完了すると下に行きfork_blocks を抜けます。
i

module top1;
        initial begin
                fork 
                        fork_blocks(1);
                        fork_blocks(4);
                join
                $display("All Done. time=%d",$time);
        end



  task automatic fork_blocks(int a);

      fork
         fork_processes(4);
         fork_processes(7);
      join_none
      
      fork
         fork_processes(14);
         fork_processes(17);
      join_none
        
        $display("Join None a=%d time=%d",a,$time);

        // if (a>3)     begin
                        wait fork;
                        $display("wait fork time=%d",$time);
        // end
        $display("Fork_blocks end Time=%d",$time);
  endtask

 
   task automatic fork_processes(int delay);
      fork : a
         begin
            #delay;
            $display("Thread finished Time=%d",$time);
         end
      join_any : a
   endtask
endmodule 

***** Veritak SV Engine Version 0.031 Build Jun.23.2008 *****

Join None a= 1 time= 0
Join None a= 4 time= 0
Thread finished Time= 4
Thread finished Time= 4
Thread finished Time= 7
Thread finished Time= 7
Thread finished Time= 14
Thread finished Time= 14
Thread finished Time= 17
wait folk time= 17
Fork_blocks end Time= 17
Thread finished Time= 17
wait folk time= 17
Fork_blocks end Time= 17
All Done. time= 17
sim_finished
**** Test Done. Total 59.00[msec] ****

なお、引数を指定して、特定のスレッドだけ、wait fork をイネーブルさせることもできます。 作用は、同じスコープでも現在のスレッドのみに働きます。


16.1.2 fork 中の automatic 変数

次の例は、難しいです。ここでの問題はmが不定(undetermined,シミュレータ依存)になることです。

initial 
        for( int j = 1; j <= 3; ++j ) 
                fork
                        automatic int k = j; // local copy, k, for each value of j
                        begin
                                #(k);
                                $display( "time=%d k=%d ",$time,k );
                        end
                        begin
                                automatic int m =j;// the value of m is undetermined
                                $display("time=%d m=%d",$time,m);
                        end
                join_none

1)for loop内のint jは、automatic変数で、下位スコープから参照可能です。スコープとは、可視範囲のことで、SVのautomatic変数はC++のそれに準じた扱いになります。ここで、intは、2値integerです。

2)for loop下のfork -join_noneは、ノンブロッキングプロセスですから、イタレーション3回分の3つのプロセスが時刻0で起動します。つまり時刻0時点では3つのダイナミックインスタンスプロセスが存在します。このイタレーションループ中、fork 下のautomatic 内部変数kは、jで初期化されます。kは、起動したインスタンスプロセスNoと言ってもよいでしょう。

3)fork下のステートメントは、独立した各々プロセスとして起動されますが、その実行は、スケジュールされます。スケジュールは(時刻0には違いないのですが、)実装依存です。なので、参照するjがいつの時点のjなのかは、不定になります。しかし、kは、プロセス起動時にイニシャライズされるので、子プロセスからは、自分のプロセスのkを参照することができます。

結果です。

Loading vpi.dll 
Load Done.
***** Veritak SV Engine Version 0.021 Build Dec.30.2007 *****

time= 0 m= 4 
time= 0 m= 4
time= 0 m= 4
time= 1 k= 1
time= 2 k= 2
time= 3 k= 3

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

16.1.3 Function中のfork/join_none

次のソースのように、fork-join_none中では、
verilog HDLでのfunction制限事項
 1)イベントは書けない
 2)ディレイは書けない
 3)taskは、起動できない
 4)ノンブロッキング文は使用できない
という制限が、fork-join_none内ではなくなります。function自体が、時間0で戻る事(時間を消費しない)は、従来と同じです。fork-join_noneは、いわば、ノンブロッキングなプロセスになりますが、そこで参照される変数については、気をつける必要があります。例えば、下のfunction 引き数a1は、automaticであり、
その生存期間は、function returnで返るまでです。functionが、return で抜けた後も、fork-join_noneプロセスは、独立プロセスとして生き続けることに注意してください。fork-join_noneのプロセス生成と、block_itemsの初期化は、functionが生きている間に行われますが。起動は、function return の後、イベント文(#delay,@(x)等)のブロッキング文に出会ってからです。つまり、fork -join_none内に、a1を参照するコードがあると、dangling参照になってしまいます。
下のように、block_itemの宣言で、a1をa2に初期化コピーすることで、dangling参照を防止するようにしてください。

  1. module test;
  2.         logic a;
  3.         function automatic  void f( input integer a1);
  4.                 $display("function a1=%x",a1);
  5.                 fork
  6.                         automatic integer a2=a1;
  7.                         $display("a2=%x",a2);
  8.                         begin
  9.                                 repeat(2) begin
  10.                                         @(a);
  11.                                         $display("a changed %d",$time);
  12.                                 end
  13.                         end
  14.                         a<=1;
  15.                         a <=#10 0;
  16.                         $display("Hi2 time=%d",$time);
  17.                         #1 $display("Hi4 time=%d",$time);
  18.                         #20 t1;
  19.                 join_none
  20.         endfunction
  21.  
  22.         task t1();
  23.                 $display("Hi Task %d",$time);
  24.                 #10;
  25.                 $display("Hi task2 %d",$time);
  26.                 fork
  27.                         $display("Hi3 A %d",$time);
  28.                         #10 $display("Hi3 B %d",$time);
  29.                 join_none
  30.         endtask
  31.  
  32.        
  33.        
  34.         integer li;
  35.         initial begin
  36.                 $monitor ("a=%b %d",a,$time);
  37.                 li=32'h1234_5678;
  38.                 f(li);
  39.                 $display("HiHi2");
  40.        
  41.         end
  42.        
  43.  
  44. endmodule

結果です。

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

function a1=12345678
HiHi2
Hi2 time=                   0
a2=12345678
a changed                    0
a=1                    0
Hi4 time=                   1
a changed                   10
a=0                   10
Hi Task                   20
Hi task2                   30
Hi3 A                   30
Hi3 B                   40

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


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.