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を使った場合に比べてどれ位になるか次のソースでやってみました。
-
`define USE_SV_FUNCTION//USE_INLINE
-
module
incremental_call_test;
-
-
`ifdef USE_DPI
-
import "DPI-C" pure function int inc (int n1);
-
`else
-
-
function int inc(int i1);
-
return
i1+1;
-
endfunction
-
-
`endif
-
-
int i;
-
-
initial begin
-
i=0;
-
repeat(100000000)
begin
-
`ifdef USE_INLINE
-
i++;
-
`elsif USE_DPI
-
i=inc(i);
-
`elsif USE_SV_FUNCTION
-
i=inc(i);
-
`elsif USE_VPI
-
i=$Increment(i);
-
`endif
-
-
end
-
$display("i=%d",i);
-
-
end
-
-
endmodule
DPI C ソースは、svdpi.h をインクルードし
- __declspec(dllexport) int inc (int i)
-
{
-
return i+1;
-
}
一方、VPIについては、 vpi_user.hをインクルードして次のようなCソースになります。
-
-
staticint
sys_Increment(char*
name)
-
{
-
-
vpiHandle
systfref, argsiter, argh;
-
-
s_vpi_value value;
-
unsignedint num;
-
unsigned i;
-
-
systfref = vpi_handle(vpiSysTfCall,
NULL);
/* get system function that invoked C routine
*/
-
argsiter = vpi_iterate(vpiArgument,
systfref);/* get iterator (list)
of passed arguments */
-
-
for (i=0;i<1;i++){
-
argh = vpi_scan(argsiter);/* get the one argument - add loop for more args
*/
-
if(!argh){
-
vpi_printf("$Increment: missing
parameter. \n");
-
return 0;
-
-
}
-
-
value.format = vpiIntVal;
-
vpi_get_value(argh ,&value);
-
num=value.value.integer;
-
}
-
-
-
-
if(argh)
vpi_free_object(argsiter);
-
-
value.value.integer =num+1;//インクリメント本体
-
value.format = vpiIntVal;/* return the result
*/
-
-
vpi_put_value(systfref ,&value,
NULL, vpiNoDelay);
-
-
return(0);
-
-
}
-
-
//32bit 専用
-
staticint
sys_systems_size_tf(PLI_BYTE8* p)//VPI をコンパイラに報告
-
{
-
return 32;
-
}
-
-
void sys_inc_vpi_register()//VPI call/callback register routine
-
{
-
s_vpi_systf_data tf_data;
-
-
tf_data.type =vpiSysFunc;
-
tf_data.sysfunctype =vpiIntFunc;
-
tf_data.tfname = "$Increment";
-
tf_data.user_data = "$Increment";
-
tf_data.calltf = sys_Increment;//関数本体
-
tf_data.compiletf = 0;
-
tf_data.sizetf = sys_systems_size_tf;//サイズ
-
vpi_register_systf(&tf_data);
-
-
}
-
-
__declspec(dllexport) void (*vlog_startup_routines[])() = {//VPI
スタートアップルーチンを登録
-
sys_inc_vpi_register,
-
0
-
};
このように、単にインクリメント値を返すだけのファンクションですが、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);
-
-
`define PAI 3.141529
-
initial begin
-
-
for(int i=0;i<=5;i++)
-
$display("sin=%f",Sin(i*`PAI/180));
-
-
end
-
C側
3)インポート(関数またはタスク)を定義する
-
#include <math.h>
-
#include <svdpi.h>
-
-
extern "C" __declspec(dllexport)
double Sin(double r)
-
{
-
return sin(r);
-
}
のようになります。
<Cランタイムは直接呼べる>
ところで、上の例の場合、C側の関数でCのランタイムを呼び出しています。ユーザのSin関数は、結局のところCのランタイムを呼び出しているだけで少し冗長ですね。このようなCのランタイム関数呼び出しについては、SystemVerilogから直接呼び出すことが可能です。以下のように インポート定義を書くだけで、DLL作成は必要です。
-
module
incremental_call_test;
-
-
-
import "DPI-C" pure function real cos (real n1);//DLL作成不要 インポート宣言だけでよい
-
import "DPI-C" pure function real sinh (real n2);//DLL作成不要 インポート宣言だけでよい
-
-
`define PAI 3.14159265358979323846
-
initial begin
-
-
for(int i=0;i<=90;i++)
-
$display("cos=%f
sinh=%f",cos(i*`PAI/180),sinh(i*`PAI/180));
-
-
end
-
-
endmodule
16.2.3 エクスポート
SystemVerilogの関数やタスクがCから呼ばれることをエクスポートすると言います。エクスポートするには、まず、インポートすることから始める必要があります。
- SV側がCの関数または、タスクをインポートする
- インポートされたC関数・タスクの中で、C側がSVの関数・タスクを呼び出す(エクスポート)
という順序が必然です。システムスケジューラ(シミュレーションカーネル)は、SystemVerilog側にあるので、必ずこのような形態になります。
SV側:
1)インポート宣言
import "DPI-C" context function void import_func ();//Exportするにはimportが必要
2)インポートファンクション・タスクを起動
-
initial
-
import_func();//Cのインポートファンクションを呼ぶ
3)エクスポート宣言
-
export "DPI-C" function sv_inc
;//Export宣言では、ファンクション名・タスク名しか書かない
4)エクスポートファンクション・タスクの中身
-
function int sv_inc(int i1);//Cから呼ばれるExportファンクション
-
$display("私は sv_inc
関数です。");
-
return
i1+1;
-
endfunction
C側:
5)Cインポートファンクション
-
extern "C" __declspec(dllexport)
void import_func()
-
{
-
int i;
-
vpi_printf("私は、C のimport_func()です。\n");
-
i=sv_inc(100);//SV
Functionを呼ぶ
-
vpi_printf("私はCです。 sv_incを呼び出した結果=%dが返ってきました。\n",i);//printfの代わりにvpi_printfを使う
-
-
}
printfの代わりにVPIファンクションvpi_printfを使っているのは、SV上のコンソールにSV/C両方の結果が出るからです。
SV側全体です
-
module export_test;
-
-
-
import "DPI-C" context function void import_func ();//Exportするにはimportが必要
-
export "DPI-C" function sv_inc;//Export宣言では、ファンクション名・タスク名しか書かない
-
-
function int sv_inc(int i1);//Cから呼ばれるExportファンクション
-
$display("私は sv_inc
関数です。");
-
return
i1+1;
-
endfunction
-
-
-
initial
-
import_func();//Cのインポートファンクションを呼ぶ
-
-
-
-
実行結果です。
***** 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側記述
-
module export_test;
-
-
-
import "DPI-C" context task import_task();//Exportするにはimportが必要
-
export "DPI-C" function get_sv_time;//Export宣言では、ファンクション名・タスク名しか書かない
-
-
-
-
function longint get_sv_time();
-
return
$time;
-
endfunction
-
-
-
initial begin
-
#10;
-
import_task();//Cのインポートタスクを呼ぶ
-
end
-
-
-
endmodule
C側記述:
-
extern "C" __declspec(dllexport)
void import_task()
-
{
-
__int64 t;
-
vpi_printf("私は、C のimport_task()です。\n");
-
t=get_sv_time();//SV
Functionを呼ぶ
-
vpi_printf("私はCです。 現在時刻は%ldです。\n",t);//printfの代わりにvpi_printfを使う
-
-
}
結果:
***** 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側:
-
`timescale 1ns/1ns
-
module export_test1;
-
-
-
import "DPI-C" context task import_task1();//Exportするにはimportが必要
-
-
export "DPI-C" function get_sv_time;//Export宣言では、ファンクション名・タスク名しか書かない
-
export "DPI-C" task delay_task;
-
-
-
function longint get_sv_time();
-
return
$time;
-
endfunction
-
-
task
delay_task();
-
#(10);
-
endtask
-
-
initial begin
-
#1;
-
fork
-
import_task1();//Cのインポートタスクを呼ぶ
-
repeat(6) #2 $display("..............SV実行中です。時刻= %2d",$time);
-
join
-
$display("join
しました。");
-
end
-
-
-
endmodule
C側:
-
-
extern "C" __declspec(dllexport)
void import_task1()
-
{
-
-
-
-
vpi_printf("私はCです。 現在時刻は%ldです。これから#10後に戻ってきます。\n",get_sv_time());
-
-
delay_task();//時間消費export task を呼ぶ
-
-
vpi_printf("私はCです。 戻ってきました。現在時刻は%ldです。\n",get_sv_time());
-
-
-
}
実行結果です。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つのスレッドと平行して動きます。
-
`timescale 1ns/1ns
-
module export_test1;
-
-
-
import "DPI-C" context task import_task2();//Exportするにはimportが必要
-
-
export "DPI-C" function get_sv_time;//Export宣言では、ファンクション名・タスク名しか書かない
-
export "DPI-C" task delay_task_by_parameter;
-
export "DPI-C" task wait_n_clks;
-
export "DPI-C" task wait_trigger;
-
export "DPI-C" task wait_level_high;
-
-
event ev;
-
reg level=0;
-
-
function longint get_sv_time();
-
return
$time;
-
endfunction
-
-
//クロックジェネレーター
-
reg clk=0;
-
always #1 clk=~clk;
-
-
//指定時間待つ
-
task
delay_task_by_parameter( inputlongint d);
-
#(d);
-
endtask;
-
-
//指定クロック数待つ
-
task
wait_n_clks( inputint n);
-
repeat(n) begin
-
@(negedge clk);
-
end
-
endtask
-
-
//イベント待ち
-
task
wait_trigger();
-
@(ev);//イベント待ち
-
endtask
-
-
//レベルH待ち
-
task
wait_level_high();
-
wait(level==1);
-
endtask
-
-
-
initial begin
-
#1;
-
fork
-
import_task2();//Cのインポートタスクを呼ぶ
-
#40 level=1;
-
repeat(21) #2 $display("..............SV実行中です。時刻= %2d",$time);
-
#30 ->ev;
-
join
-
$display("join しました。
%d",$time);
-
$finish();
-
end
-
-
-
endmodule
このCプログラムです。
-
extern "C" __declspec(dllexport)
void import_task2()
-
{
-
-
-
-
vpi_printf("私はCです。 現在時刻は%ldです。これから#10後に戻ってきます。\n",get_sv_time());
-
-
delay_task_by_parameter(10);//時間消費export task を呼ぶ
-
vpi_printf("私はCです。 戻ってきました。現在時刻は%ldです。5CLK待ちます。\n",get_sv_time());
-
-
wait_n_clks(5);
-
vpi_printf("私はCです。 戻ってきました。現在時刻は%ldです。トリガを待ちます。\n",get_sv_time());
-
-
wait_trigger();
-
vpi_printf("私はCです。 戻ってきました。現在時刻は%ldです。レベルがHighになるのを待ちます。\n",get_sv_time());
-
wait_level_high();
-
vpi_printf("私はCです。 戻ってきました。現在時刻は%ldです。C importを抜けます。\n",get_sv_time());
-
-
-
}
結果です。時刻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を使うときだけ存在意義があります。
-
module chandle_test;
-
-
-
-
import "DPI-C" function chandle malloc(longint size);
-
import "DPI-C" function void free(chandle);
-
import "DPI-C" function chandle calloc(longint n,longint size);
-
-
-
-
chandle
c_handle1,c_handle2;
-
-
-
-
-
initial begin// chandleで許されるほぼ全てのオペレーション
-
c_handle1=calloc(10,100);//C-RunTime使用
-
c_handle2=calloc(10,100);
-
if (c_handle1 ==c_handle2)
$display("assert(0).
なんかおかしい");
-
elseif (c_handle1 !=c_handle2)
;
-
-
-
free(c_handle1);//サブルーチンの使用はOK
-
c_handle1=c_handle2;//c_handle同士の代入はOK
-
if (c_handle1 !=c_handle2)
$display("assert(0).
なんかおかしい");//比較もOK
-
free(c_handle1);
-
c_handle1=null;//アサインは、nullだけ可能
-
c_handle2=null;
-
-
-
end
-
-
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++の型を全く理解していません。単なるホルダーにすぎないことに注意してください。
-
module cpp_test;
-
-
import "DPI-C" function chandle make_base_class();
-
import "DPI-C" function chandle make_child_class();
-
import "DPI-C" function chandle
make_grand_child_class();
-
import "DPI-C" function void disp_name( inputchandle );
-
import "DPI-C" function void delete_class( inputchandle);
-
chandle ptr,c1,c2,c3;
-
-
-
-
-
initial begin
-
c1=make_base_class();//インスタンス生成
-
c2=make_child_class();//インスタンス生成
-
c3=make_grand_child_class();//インスタンス生成
-
-
-
disp_name(c1);
-
disp_name(c2);
-
disp_name(c3);
-
-
//仮想関数
-
$display("");
-
-
ptr=c1;
-
disp_name(ptr);
-
delete_class(ptr);//インスタンス消去
-
-
ptr=c2;
-
disp_name(ptr);
-
delete_class(ptr);//インスタンス消去
-
-
ptr=c3;
-
disp_name(ptr);
-
delete_class(ptr);//インスタンス消去
-
-
-
-
end
-
-
endmodule
C++コードです。
-
-
class
Base_class
-
{
-
public:
-
virtual void disp_name (){ vpi_printf("私はベースクラスです。\n");}
-
virtual ~Base_class (){}
-
-
};
-
-
class
Child_Class: public Base_class
-
{
-
public:
-
virtual ~Child_Class (){}
-
virtual void disp_name (){ vpi_printf("私は子クラスです。\n");}
-
-
};
-
class
Grand_Child_Class: public Child_Class
-
{
-
public:
-
virtual ~Grand_Child_Class (){}
-
virtual void disp_name (){ vpi_printf("私は孫クラスです。\n");}
-
-
};
-
-
extern "C" __declspec(dllexport)
void disp_name(void* chandle1){
-
Base_class* base_ptr=reinterpret_cast<Base_class*>(chandle1);
-
assert(base_ptr);
-
base_ptr->disp_name();
-
}
-
extern "C" __declspec(dllexport)
void delete_class(void* chandle1){
-
Base_class* base_ptr=reinterpret_cast<Base_class*>(chandle1);
-
assert(base_ptr);
-
delete base_ptr;
-
}
-
extern "C" __declspec(dllexport)
void*
make_base_class()
-
{
-
return reinterpret_cast<void*>(new
Base_class());
-
-
}
-
extern "C" __declspec(dllexport)
void*
make_child_class()
-
{
-
return reinterpret_cast<void*>(new
Child_Class());
-
-
}
-
extern "C" __declspec(dllexport)
void*
make_grand_child_class()
-
{
-
return reinterpret_cast<void*>(new
Grand_Child_Class());
-
-
}
-
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 引数
引数の受け渡しは、原則的に"値渡し"か"ポインタ渡し"のいずれかです。([ ]アレーは例外でハンドル渡し) "値渡し"は、小さいサイズの引数でのみ次のように定義されており、これ以外は、すべて"ポインタ渡し"になります。
- byte, shortint, int, longint, real, shortreal
- Scalar bit / logic
- chandle, string
要するに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側:
-
module chandle_test;
-
-
-
-
-
import "DPI-C" function bit get_bit( input bit);
-
import "DPI-C" function logic get_logic( input logic);
-
import "DPI-C" function byte get_byte( input byte );
-
import "DPI-C" function shortint get_shortint( input shortint);
-
import "DPI-C" function int get_int( input int);
-
import "DPI-C" function longint get_longint( input longint);
-
import "DPI-C" function shortreal get_shortreal( input shortreal);
-
import "DPI-C" function real get_real(input real);
-
import "DPI-C" function chandle get_chandle( input chandle);
-
-
-
-
-
bit b0;
-
logic logic0;
-
byte byte0;
-
shortint
shortint0;
-
int int0;
-
longint
longint0;
-
shortreal
shortreal0;
-
real real0;
-
chandle
chandle0;
-
-
initialbegin
-
b0=get_bit(3'b111);
-
$display("b0=%b",b0);
-
-
logic0=get_logic(3'bxxx);
-
$display("logic0=%b",logic0);
-
-
byte0=get_byte(-1);
-
$display("byte0=%b",byte0);
-
-
shortint0=get_shortint(-1);
-
$display("shortint0=%b",shortint0);
-
-
int0=get_int(-1);
-
$display("int0=%d",int0);
-
-
longint0=get_longint(-1);
-
$display("longint0=%x",longint0);
-
-
shortreal0=get_shortreal(-1);
-
$display("shortreal0=%f",shortreal0);
-
-
real0=get_real(-1);
-
$display("real0=%f",real0);
-
-
chandle0=get_chandle(chandle0);
-
-
end
-
-
endmodule
C側:
svdpi.h
-
/* common type for 'bit' and 'logic' scalars.
*/
-
typedef uint8_t svScalar;
-
typedef svScalar svBit; /* scalar
*/
-
typedef svScalar svLogic; /* scalar
*/
-
-
-
extern "C" __declspec(dllexport)
svBit get_bit( svBit i)
-
{
-
return i;
-
}
-
-
extern "C" __declspec(dllexport)
svLogic get_logic( svLogic i)
-
{
-
return i;
-
}
-
-
extern "C" __declspec(dllexport)
char get_byte( char i){
-
-
return i;
-
}
-
extern "C" __declspec(dllexport)
short get_shortint( short i){
-
-
return i;
-
}
-
-
extern "C" __declspec(dllexport)
svBitVecVal get_int( int i){
-
-
return i;
-
}
-
-
extern "C" __declspec(dllexport)
__int64 get_longint( __int64 i){
-
-
return i;
-
}
-
-
extern "C" __declspec(dllexport)
float get_shortreal( float i){
-
-
return i;
-
}
-
-
extern "C" __declspec(dllexport)
double get_real( double i){
-
return i;
-
}
-
-
extern "C" __declspec(dllexport)
void*
get_chandle( const void* i){
-
return const_cast<void*>(i);
-
}
結果です
***** 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構造体/アレーのポインタ渡しで行えます。
-
module real2_test;
-
-
-
import "DPI-C" context task import_task4();
-
-
export "DPI-C" function get_logic1;
-
export "DPI-C" function get_logic2;
-
export "DPI-C" function get_logic3;
-
-
export "DPI-C" function get_bit1;
-
export "DPI-C" function get_bit2;
-
export "DPI-C" function get_bit3;
-
-
export "DPI-C" function get_byte1;
-
export "DPI-C" function get_byte2;
-
export "DPI-C" function get_byte3;
-
-
export "DPI-C" function get_shortint1;
-
export "DPI-C" function get_shortint2;
-
export "DPI-C" function get_shortint3;
-
-
export "DPI-C" function get_int1;
-
export "DPI-C" function get_int2;
-
export "DPI-C" function get_int3;
-
-
export "DPI-C" function get_integer1;
-
export "DPI-C" function get_integer2;
-
export "DPI-C" function get_integer3;
-
-
export "DPI-C" function get_bitvec1;
-
export "DPI-C" function get_bitvec2;
-
export "DPI-C" function get_bitvec3;
-
-
export "DPI-C" function get_logicvec1;
-
export "DPI-C" function get_logicvec2;
-
export "DPI-C" function get_logicvec3;
-
-
export "DPI-C" function get_longint1;
-
export "DPI-C" function get_longint2;
-
export "DPI-C" function get_longint3;
-
-
export "DPI-C" function get_real1;
-
export "DPI-C" function get_real2;
-
export "DPI-C" function get_real3;
-
-
export "DPI-C" function get_shortreal1;
-
export "DPI-C" function get_shortreal2;
-
export "DPI-C" function get_shortreal3;
-
-
//logic
-
function void get_logic1( input logic b0);
-
if (b0 ===1'bz) $display("................ SV
logic0=%b",b0);
-
else $display("Fail
%b",b0);
-
return
;
-
endfunction
-
-
function void get_logic2( inout logic b0);
-
if (b0 !==1'bx) $display("Fail
%b",b0);
-
else
$display("................ SV
logic0=%b",b0);
-
b0=1'b1;
-
return
;
-
endfunction
-
-
function void get_logic3( output logic b0);
-
b0=1'bx;
-
return
;
-
endfunction
-
-
//bit
-
function void get_bit1( input bit b0);
-
if (b0 ===1'b0) $display("................ SV
bit0=%b",b0);
-
else $display("Fail");
-
return
;
-
endfunction
-
-
function void get_bit2( inout bit b0);
-
if (b0 ===1'b1) $display("................ SV
bit0=%b",b0);
-
else $display("Fail");
-
b0=1'b0;
-
return
;
-
endfunction
-
-
function void get_bit3( outputbit b0);
-
b0=1'b1;
-
return
;
-
endfunction
-
-
//byte
-
function void get_byte1( input byte b0);
-
if (b0 ===8'h7f) $display("................ SV
byte0=%x",b0);
-
else $display("Fail");
-
return
;
-
endfunction
-
-
-
function void get_byte2( inout byte b0);
-
if (b0 !==8'h7f) $display("Fail");
-
b0=1;
-
return
;
-
endfunction
-
-
function void get_byte3( output byte b0);
-
b0=8'h02;
-
return
;
-
endfunction
-
-
//shortint
-
function void get_shortint1( input shortint b0);
-
if (b0 !==16'habcd) $display("Fail1
%x",b0);
-
return
;
-
endfunction
-
-
-
function void get_shortint2( inoutshortint b0);
-
if (b0 !==16'habcd) $display("Fail");
-
b0=32'hdead1234;
-
return
;
-
endfunction
-
-
function void get_shortint3( output shortint b0);
-
b0=16'h1122;
-
return
;
-
endfunction
-
-
//int
-
function void get_int1( input int b0);
-
if (b0 !==32'hdead_beaf) $display("FailA
%x",b0);
-
return
;
-
endfunction
-
-
-
function void get_int2( inout int b0);
-
if (b0 !==32'hdead_beaf) $display("Fail");
-
b0=32'h1234_5678;
-
return
;
-
endfunction
-
-
function void get_int3( output int b0);
-
b0=32'haabb1234;
-
return
;
-
endfunction
-
-
//integer
-
function void get_integer1( input integer b0);
-
if (b0 !==32'habcd_1234) $display("Fail
%x",b0);
-
return
;
-
endfunction
-
-
-
function void get_integer2( inout integer b0);
-
if (b0 !==32'habcd_1234) $display("Fail");
-
b0=32'hdeadbeaf;
-
return
;
-
endfunction
-
-
function void get_integer3( output integer b0);
-
b0=32'haabb1234;
-
return
;
-
endfunction
-
-
//longint
-
function void get_longint1( input longint b0);
-
if (b0 !==64'habcd_1234_dead_beaf) $display("FailB
%x",b0);
-
return
;
-
endfunction
-
-
-
function void get_longint2( inout longint b0);
-
if (b0 !==64'habcd_1234_dead_beaf) $display("Fail
%x",b0);
-
b0=64'hdeadbeaf_abcd_1234;
-
return
;
-
endfunction
-
-
function void get_longint3( output longint b0);
-
b0=64'h12345678;
-
return
;
-
endfunction
-
-
//real
-
function void get_real1( input real b0);
-
if (b0 !=1.0) $display("Fail
%f",b0);
-
return
;
-
endfunction
-
-
-
function void get_real2( inout real b0);
-
if (b0 !=1.0) $display("Fail
%f",b0);
-
b0=-1.0;
-
return
;
-
endfunction
-
-
function void get_real3( output real b0);
-
b0=-11.0;
-
return
;
-
endfunction
-
-
//shortreal
-
function void get_shortreal1( input shortreal b0);
-
if (b0 !=1.0) $display("Fail
%f",b0);
-
return
;
-
endfunction
-
-
-
function void get_shortreal2( inout shortreal b0);
-
if (b0 !=1.0) $display("Fail
%f",b0);
-
b0=-1.0;
-
return
;
-
endfunction
-
-
function void get_shortreal3( output shortreal b0);
-
b0=-21.0;
-
return
;
-
endfunction
-
-
-
-
//bit vec
-
function void get_bitvec1(input bit [7:0] b0);
-
if (b0 !==8'h11) $display("Fail4
%x",b0);
-
return
;
-
endfunction
-
-
-
function void get_bitvec2( inout bit [7:0] b0);
-
if (b0 !==8'h11) $display("Fail3");
-
b0=32'hdeadbeaf;
-
return
;
-
endfunction
-
-
function void get_bitvec3(output bit [7:0] b0);
-
b0=8'haa;
-
return
;
-
endfunction
-
-
//logic vec
-
function void get_logicvec1(input logic [7:0] b0);
-
if (b0 !==8'hzz) $display("Fail2
%b",b0);
-
return
;
-
endfunction
-
-
-
function void get_logicvec2( inout logic [7:0] b0);
-
if (b0 !==8'hzz) $display("Fail1
%b",b0);
-
b0=32'hdeadbeaf;
-
return
;
-
endfunction
-
-
function void get_logicvec3(output logic [7:0] b0);
-
b0=8'haa;
-
return
;
-
endfunction
-
-
-
-
initial begin
-
import_task4();
-
end
-
-
endmodule
C++側:
ヘッダファイル(シミュレータが自動生成)
-
/* Copyright 2010 www.sugawara-systems.com
-
* Note:
-
* This file is automatically generated.
-
* Please do not edit this file - you will lose your
edits.*/
-
-
#ifndef INCLUDED_DPIHEADER
-
#define INCLUDED_DPIHEADER
-
#ifdef __cplusplus
-
#define DPI_LINK_DECL extern "C"
-
#else
-
#define DPI_LINK_DECL
-
#endif
-
#include "svdpi.h"
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_bit1(svBit );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_bit2(svBit* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_bit3(svBit* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_bitvec1(const
svBitVecVal* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_bitvec2(svBitVecVal*
);
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_bitvec3(svBitVecVal*
);
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_byte1(char );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_byte2(char* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_byte3(char* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_int1(svBitVecVal );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_int2(svBitVecVal*
);
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_int3(int *
);
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_integer1(const
svLogicVecVal* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_integer2(svLogicVecVal* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_integer3(svLogicVecVal* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_logic1(svLogic );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_logic2(svLogic*
);
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_logic3(svLogic*
);
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_logicvec1(const
svLogicVecVal* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_logicvec2(svLogicVecVal* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_logicvec3(svLogicVecVal* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_longint1(int64_t );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_longint2(int64_t*
);
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_longint3(int64_t*
);
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_real1(double );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_real2(double* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_real3(double* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_shortint1(short );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_shortint2(short* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_shortint3(short* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_shortreal1(float );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_shortreal2(float* );
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void get_shortreal3(float* );
-
-
DPI_LINK_DECL
DPI_DLLESPEC
-
int import_task4();
-
-
#endif
C++ソースファイル
-
-
-
-
extern "C" __declspec(dllexport)
void import_task4()
-
{
-
svLogic svlogic0;
-
-
-
//スカラーlogic
-
svlogic0=sv_z;
-
get_logic1(svlogic0);
-
svlogic0=sv_x;
-
get_logic2(&svlogic0);
-
vpi_printf("svlogic0=%x \n",svlogic0);
-
get_logic3(&svlogic0);
-
vpi_printf("svlogic0=%x \n",svlogic0);
-
-
//スカラーbit
-
svBit svbit0=sv_0;
-
get_bit1(svbit0);
-
-
-
svbit0=sv_1;
-
get_bit2(&svbit0);
-
vpi_printf("svbit0=%x \n",svbit0);
-
get_bit3(&svbit0);
-
vpi_printf("svbit0=%x \n",svbit0);
-
-
//byte
-
char byte0=0x7f;
-
get_byte1(byte0);
-
get_byte2(&byte0);
-
vpi_printf("byte0=%x \n",byte0);
-
get_byte3(&byte0);
-
vpi_printf("byte0=%x \n",byte0);
-
-
//shortint
-
short short0=0xabcd;
-
get_shortint1(short0);
-
get_shortint2(&short0);
-
vpi_printf("short0=%x \n",short0);
-
get_shortint3(&short0);
-
vpi_printf("short0=%x \n",short0);
-
-
//int
-
int int0=0xdeadbeaf;
-
get_int1(int0);
-
get_int2(&int0);
-
vpi_printf("int0=%x \n",int0);
-
get_int3(&int0);
-
vpi_printf("int0=%x \n",int0);
-
-
//integer
-
svLogicVecVal logic0={0xabcd1234,0};
-
get_integer1(&logic0);
-
get_integer2(&logic0);
-
vpi_printf("integer={%x,%x}\n",logic0.aval,logic0.bval);
-
get_integer3(&logic0);
-
vpi_printf("integer={%x,%x}\n",logic0.aval,logic0.bval);
-
-
//longint
-
__int64 longint0=0xabcd1234deadbeafL;
-
get_longint1(longint0);
-
get_longint2(&longint0);
-
vpi_printf("longint0=%x \n",longint0);
-
get_longint3(&longint0);
-
vpi_printf("longint0=%x \n",longint0);
-
-
//real
-
double real0=1.0;
-
get_real1(real0);
-
get_real2(&real0);
-
vpi_printf("real0=%f \n",real0);
-
get_real3(&real0);
-
vpi_printf("real0=%f \n",real0);
-
-
//shortreal
-
float shortreal0=1.0;
-
get_shortreal1(shortreal0);
-
get_shortreal2(&shortreal0);
-
vpi_printf("shortreal0=%f \n",shortreal0);
-
get_shortreal3(&shortreal0);
-
vpi_printf("shortreal0=%f \n",shortreal0);
-
-
//bitvector
-
svBitVecVal bitvec0=0x11;
-
get_bitvec1(&bitvec0);
-
get_bitvec2(&bitvec0);
-
vpi_printf("bitvec0=%x \n",bitvec0);
-
get_bitvec3(&bitvec0);
-
vpi_printf("bitvec0=%x \n",bitvec0);
-
-
//logicvector
-
svLogicVecVal logicvec0={0x00,0xff};
-
get_logicvec1(&logicvec0);
-
get_logicvec2(&logicvec0);
-
vpi_printf("logicvec0={%x,%x}\n",logicvec0.aval,logicvec0.bval);
-
get_logicvec3(&logicvec0);
-
vpi_printf("logicvec0={%x,%x}\n",logicvec0.aval,logicvec0.bval);
-
-
-
-
-
-
-
}
結果です
***** 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
サンプルソースです。
-
module test;
-
-
import "DPI-C" function void packet_C( input logic [127:0]);
-
-
logic [127:0] packet;
-
initial begin
-
-
packet=128'h1234_5678_aaaa_bbbb_cccc_dddd_eeee_ffff;
-
$display("SVからデータを送ります。
%x",packet);
-
packet_C(packet);
-
-
-
-
end
-
-
endmodule
-
これに対して、自動生成されたCのヘッダファイルが次です。
-
/* Copyright 2010 www.sugawara-systems.com
-
* Note:
-
* This file is automatically generated.
-
* Please do not edit this file - you will lose your
edits.*/
-
-
#ifndef INCLUDED_DPIHEADER
-
#define INCLUDED_DPIHEADER
-
#ifdef __cplusplus
-
#define DPI_LINK_DECL extern "C"
-
#else
-
#define DPI_LINK_DECL
-
#endif
-
#include "svdpi.h"
-
-
-
-
DPI_LINK_DECL
DPI_DLLESPEC
-
void packet_C(const
svLogicVecVal* );
-
-
-
-
#endif
-
このように配列の大きさの情報が消えてしまいました。ポインタで渡される為です。
設計者は、配列の大きさは、128ビットであることが分かっているので、例えば、次のように書けます。
SV_PACKED_DATA_NELEMSは、sv_dpi.hで定義されているマクロで、ビット幅からチャンク数に変換します。この場合、128ビット幅のVectorを収納可能な、svLogicVecVal構造体の要素数を返します。
-
void packet_C(const
svLogicVecVal* packet)
-
{
-
const unsigned elements=SV_PACKED_DATA_NELEMS(128);//チャンク数を得るマクロ
-
svLogicVecVal mem[elements];
-
-
-
memcpy(mem,packet,sizeof(mem));//ローカル変数にCOPYコピー
-
printf("SVから来たデータを表示します。\n");
-
for (unsigned
i=0;i<
elements;i++){
-
printf("mem[%2d]={%4x,%4x}\n",i,mem[i].aval,mem[i].bval);
-
-
}
-
-
}
結果です。
***** 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 を使って書き換えます。
-
module test;
-
-
import "DPI-C" function void packet_C1( inout logic [127:0]);
-
-
-
logic [127:0] packet;
-
initial begin
-
-
packet=128'h1234_5678_aaaa_bbbb_cccc_dddd_eeee_ffff;
-
$display("SVからデータを送ります。
%x",packet);
-
packet_C1(packet);
-
$display("Cで変更されたデータです。
%x",packet);
-
-
-
end
-
-
endmodule
-
C++ソースです。inoutなので、constが付きません。svGetPartselLogicは、sv_dpi.hで定義されるAPIで、packed_arrayに対して、32ビット以下のパートセレクトを読み出しを返します。svPutPartselLogicは、同様にパートセレクト(32ビット以下)が書き込みになります。このAPIの制限事項は、
- ビット幅は32ビット以下であること
- packed array部に対して適用
です。
-
void packet_C1(svLogicVecVal* packet)
-
{
-
const unsigned elements=SV_PACKED_DATA_NELEMS(128);//チャンク数を得るマクロ
-
-
printf("SVから来たデータを表示します。\n");
-
for (unsigned
i=0;i<
elements;i++){
-
printf("packet[%2d]={%4x,%4x}\n",i,packet[i].aval,packet[i].bval);
-
-
}
-
-
svLogicVecVal data;
-
svGetPartselLogic(&data,packet,64,32);//sv_dpi.h
-
printf("パートセレクトAPIで
packet[64 +:32]={%4x,%4x}\n",data.aval,data.bval);
-
-
data.aval=0xdeadbeaf;
-
data.bval=0x0;
-
-
svPutPartselLogic(packet,data,64,32);//sv_dpi.h
-
//戻る
-
-
-
}
結果です。
***** 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]と正規化された解釈でアクセスされます。
-
module test;
-
-
import "DPI-C" function void packet_C2( inout bit [3:0][31:0]);
-
-
-
bit [3:0][31:0]
packet;
-
initial begin
-
-
packet=128'h1234_5678_aaaa_bbbb_cccc_dddd_eeee_ffff;
-
$display("SVからデータを送ります。
%x",packet);
-
packet_C2(packet);
-
$display("Cで変更されたデータです。
%x",packet);
-
-
-
end
-
-
endmodule
-
-
void packet_C2(svBitVecVal*
packet)
-
{
-
-
constunsigned elements=SV_PACKED_DATA_NELEMS(128);//チャンク数を得るマクロ
-
-
-
printf("SVから来たデータを表示します。\n");
-
for (unsigned
i=0;i<
elements;i++){
-
printf("packet[%2d]={%4x}\n",i,packet[i]);
-
-
}
-
-
svBitVecVal data;
-
svGetPartselBit(&data,packet,64,32);//sv_dpi.h
-
printf("パートセレクトAPIで
packet[64 +:32]={%4x}\n",data);
-
-
data=0xdeadbeaf;
-
-
-
svPutPartselBit(packet,data,64,32);//sv_dpi.h
-
//戻る
-
-
-
}
***** 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
アンパックド配列の例です。
-
module test;
-
parameter int MAX_MSB=2;
-
import "DPI-C" function void packet_C3( inout bit [127:0] mem[0:MAX_MSB]);
-
-
-
bit [127:0] packet[0:MAX_MSB];
-
-
initial begin
-
-
packet[MAX_MSB]=128'h1234_5678_aaaa_bbbb_cccc_dddd_eeee_ffff;
-
-
$display("SVからデータを送ります。
%x",packet[MAX_MSB]);
-
packet_C3(packet);
-
-
for (int i=0;i<=MAX_MSB;i++) $display("Cで変更されたデータです。
%x",packet[i]);
-
-
-
end
-
-
endmodule
-
パックドアレイーは、chunk_elements分ビットが連続しています。一方、アンパックド次元では、必ずしも連続にはならないことに注意してください。
-
void packet_C3(svBitVecVal*
packet)
-
{
-
-
const unsigned chunk_elements=SV_PACKED_DATA_NELEMS(128);//チャンク数を得るマクロ
-
const unsigned unpacked_elements=3;
-
-
printf("SVから来たデータを表示します。\n");
-
for (unsigned
j=0;j<unpacked_elements;j++){
-
for (unsigned
i=0;i<
chunk_elements;i++){
-
printf("packet[%2d][%2d]={%4x}\n",j,i,packet[j*chunk_elements+i]);
-
}
-
-
}
-
-
svBitVecVal data;
-
svGetPartselBit(&data,&packet[(unpacked_elements-1)*chunk_elements],64,32);//sv_dpi.h
-
printf("パートセレクトAPIで
packet[%2d][64 +:32]={%4x}\n",unpacked_elements-1,data);
-
-
-
-
for (unsigned
i=0;i< (unpacked_elements-1)*chunk_elements;i++){
-
packet[i]=i;//書き換え
-
}
-
data=0xdeadbeaf;
-
svPutPartselBit(&packet[(unpacked_elements-1)*chunk_elements],data,64,32);//sv_dpi.h
-
//戻る
-
-
-
}
***** 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)に対応します。パックド次元と違うので注意してください。
-
module test;
-
parameter int MAX_MSB=2;
-
-
import "DPI-C" function void packet_C3( inout bit [127:0] mem[MAX_MSB+1]);
-
-
-
bit [127:0] packet[MAX_MSB+1];
-
-
initial begin
-
-
packet[MAX_MSB]=128'h1234_5678_aaaa_bbbb_cccc_dddd_eeee_ffff;
-
-
$display("SVからデータを送ります。
%x",packet[MAX_MSB]);
-
packet_C3(packet);
-
-
for (int i=0;i<=MAX_MSB;i++) $display("Cで変更されたデータです。
%x",packet[i]);
-
-
-
end
-
-
endmodule
<多次元アンパックド配列>
-
module test;
-
parameter int MAX_MSB=2;
-
parameter int MAX_MSB2=3;
-
-
import "DPI-C" function void packet_C5( inout bit [127:0] mem[MAX_MSB+1][MAX_MSB2+1]);
-
-
-
bit [127:0] packet[MAX_MSB+1][MAX_MSB2+1];
-
-
initial begin
-
-
packet[MAX_MSB][MAX_MSB2]=128'h1234_5678_aaaa_bbbb_cccc_dddd_eeee_ffff;
-
-
$display("SVからデータを送ります。
%x",packet[MAX_MSB][MAX_MSB2]);
-
packet_C5(packet);
-
-
-
for (int i=0;i<=MAX_MSB;i++)
-
for
(int m=0;m<=MAX_MSB2;m++)
-
$display("Cで変更されたデータです。
%x",packet[i][m]);
-
-
-
end
-
-
endmodule
-
多次元でも基本的に同じ考え方です。
-
-
void packet_C5(svBitVecVal*
packet)
-
{
-
-
const unsigned chunk_elements=SV_PACKED_DATA_NELEMS(128);//チャンク数を得るマクロ
-
const unsigned unpacked_elements=3*4;
-
-
printf("SVから来たデータを表示します。\n");
-
for (unsigned
j=0;j<unpacked_elements;j++){
-
for (unsigned
i=0;i<
chunk_elements;i++){
-
printf("packet[%2d][%2d]={%4x}\n",j,i,packet[j*chunk_elements+i]);
-
}
-
-
}
-
-
svBitVecVal data;
-
svGetPartselBit(&data,&packet[(unpacked_elements-1)*chunk_elements],64,32);//sv_dpi.h
-
printf("パートセレクトAPIで
packet[%2d][64 +:32]={%4x}\n",unpacked_elements-1,data);
-
-
-
-
for (unsigned
i=0;i< (unpacked_elements-1)*chunk_elements;i++){
-
packet[i]=i;
-
}
-
data=0xdeadbeaf;
-
svPutPartselBit(&packet[(unpacked_elements-1)*chunk_elements],data,64,32);//sv_dpi.h
-
//戻る
-
-
-
}
***** 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のアンパックドアレーにクラス配列のアドレスを設定しています。
-
module test;
-
-
parameter int MEM_SIZE=10;
-
import "DPI-C" function void set_chandles( inout chandle mem[MEM_SIZE ]);
-
import "DPI-C" function void disp_data( input chandle);
-
import "DPI-C" function void delete_chandles( input chandle mem[MEM_SIZE]);
-
-
chandle
chandles[MEM_SIZE];
-
initial begin
-
set_chandles(chandles);//ハンドルのセット
-
for (int i=0;i<
MEM_SIZE;i++)
disp_data(chandles[i]);//クラス内データの表示
-
delete_chandles(chandles);//ハンドルのリセット
-
-
end
-
-
endmodule
-
C++ソースです。
-
-
const unsigned
class_handle_size=10;
-
class Sample_Class
-
{
-
public:
-
Sample_Class (){
-
-
}
-
void set_data(unsigned u ){
data=u;}
-
void disp_data (){
-
std::cout<<
"データは、"<< data<<"です。"<<std::endl;
-
}
-
private:
-
unsigned data;
-
};
-
void set_chandles(void** mem){
-
-
Sample_Class*ptr=new
Sample_Class[class_handle_size];//クラス配列生成
-
for (unsigned
i=0;i<
class_handle_size;i++){
-
std::cout <<
"クラスオブジェクト アドレスは"<< ptr << std::endl;
-
ptr->set_data(i);
-
mem[i]=ptr++;//ハンドルのセット
-
}
-
}
-
void disp_data( const void* ptr){
-
-
reinterpret_cast<Sample_Class*>(const_cast<void*>(ptr))->disp_data();
-
-
-
}
-
void delete_chandles( const void**mem){
-
delete [] mem[0];//クラス配列の消去
-
for (unsigned
i=0;i<
class_handle_size;i++){
-
mem[i]=0;//ハンドルのリセット
-
}
-
}
結果です。
***** 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のメモリ配置と同じにしています。恐らく、同じになる実装が大半だと思います。
-
module test;
-
-
parameter int MEM_SIZE=10;
-
-
-
import "DPI-C" function void set_bit_array( inout bit mem[MEM_SIZE ]);//パックド次元がないbit のアンパック
-
import "DPI-C" function void set_logic_array( inout logic mem[MEM_SIZE ]);//パックド次元がないlogicのアンパック
-
-
-
import "DPI-C" function void set_byte_array( inout byte mem[MEM_SIZE ]);
-
import "DPI-C" function void set_shortint_array( inout shortint mem[MEM_SIZE ]);
-
import "DPI-C" function void set_int_array( inout int mem[MEM_SIZE ]);
-
import "DPI-C" function void set_integer_array( inout integer mem[MEM_SIZE ]);
-
import "DPI-C" function void set_longint_array( inout longint mem[MEM_SIZE ]);
-
import "DPI-C" function void set_shortreal_array( inout shortreal mem[MEM_SIZE ]);
-
import "DPI-C" function void set_real_array( inout real mem[MEM_SIZE ]);
-
import "DPI-C" function void set_time_array( inout time mem[MEM_SIZE ]);
-
-
bit
bit_array[MEM_SIZE];
-
logic
logic_array[MEM_SIZE];
-
byte
byte_array[MEM_SIZE];
-
shortint
shortint_array[MEM_SIZE];
-
int
int_array[MEM_SIZE];
-
integer
integer_array[MEM_SIZE];
-
longint
longint_array[MEM_SIZE];
-
shortreal
shortreal_array[MEM_SIZE];
-
real
real_array[MEM_SIZE];
-
time
time_array[MEM_SIZE];
-
-
initialbegin
-
-
set_bit_array(bit_array);
-
for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。bit_array[%2d]=%b",i,bit_array[i]);
-
$display("");
-
-
set_logic_array(logic_array);
-
for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。logic_array[%2d]=%b",i,logic_array[i]);
-
$display("");
-
-
set_byte_array(byte_array);
-
for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。byte_array[%2d]=%2x",i,byte_array[i]);
-
$display("");
-
-
set_shortint_array(shortint_array);
-
for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。shortint_array[%2d]=%2x",i,shortint_array[i]);
-
$display("");
-
-
set_int_array(int_array);
-
for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。int_array[%2d]=%2x",i,int_array[i]);
-
$display("");
-
-
set_integer_array(integer_array);
-
for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。integer_array[%2d]=%2x",i,integer_array[i]);
-
$display("");
-
-
set_longint_array(longint_array);
-
for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。longint_array[%2d]=%2x",i,longint_array[i]);
-
$display("");
-
-
set_shortreal_array(shortreal_array);
-
for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。shortreal_array[%2d]=%f",i,shortreal_array[i]);
-
$display("");
-
-
set_real_array(real_array);
-
for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。real_array[%2d]=%f",i,real_array[i]);
-
$display("");
-
-
set_time_array(time_array);
-
for (int i=0;i<MEM_SIZE;i++) $display("Cで設定されました。time_array[%2d]=%4x",i,time_array[i]);
-
$display("");
-
end
-
-
endmodule
-
C++のソースです。
-
const unsigned
array_size=10;
-
void set_bit_array(svBit*
bit_array)
-
{
-
for (unsigned
i=0;i<array_size;i++) bit_array[i]=i%2;
-
-
}
-
void set_logic_array(svLogic*
logic_array)
-
{
-
for (unsigned
i=0;i<array_size;i++) logic_array[i]=i%4;
-
-
}
-
-
void set_byte_array(char* byte_array)
-
{
-
for (unsigned
i=0;i<array_size;i++) byte_array[i]=i;
-
-
}
-
void set_shortint_array(short* shortint_array)
-
{
-
for (unsigned
i=0;i<array_size;i++) shortint_array[i]=i;
-
-
}
-
-
void set_int_array(int*
int_array)
-
{
-
for (unsigned
i=0;i<array_size;i++) int_array[i]=i;
-
-
}
-
void set_integer_array(svLogicVecVal* integer_array)
-
{
-
for (unsigned
i=0;i<array_size;i++){
-
integer_array[i].aval=i;
-
integer_array[i].bval=0;
-
}
-
}
-
void set_longint_array(int64_t*
longint_array)
-
{
-
for (unsigned
i=0;i<array_size;i++) longint_array[i]=i;
-
-
}
-
void set_shortreal_array(float* shortreal_array)
-
{
-
for (unsigned
i=0;i<array_size;i++) shortreal_array[i]=static_cast<float>(i);
-
-
}
-
-
void set_real_array(double* real_array)
-
{
-
for (unsigned
i=0;i<array_size;i++) real_array[i]=static_cast<double>(i);
-
-
}
-
-
void set_time_array(svLogicVecVal* time_array)
-
{
-
const unsigned chunk_elements=2;
-
for (unsigned
i=0;i<array_size;i++){
-
time_array[i*chunk_elements].aval=i;
-
time_array[i*chunk_elements+1].aval=0;
-
time_array[i*chunk_elements].bval=0;
-
time_array[i*chunk_elements+1].bval=0;
-
}
-
}
結果です。
***** 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ソースです。
-
-
-
-
module root_scope;
-
-
export "DPI-C" function func;
-
import "DPI-C" context task struct_operation();
-
-
typedef struct {
-
byte
red;
-
byte
green;
-
byte
blue;
-
} RGB_TYPE;
-
-
-
RGB_TYPE A;
-
-
function void func(inout RGB_TYPE A);
-
$display("私は、SVファンクションです。Cで設定した値を読みます。 赤=%3d 緑=%3d
青blue=%3d",A.red,A.green,A.blue);
-
A.red=101;
-
A.green=102;
-
A.blue=103;
-
$display("SVで設定し直しました。");
-
-
endfunction
-
-
-
initial begin
-
struct_operation();
-
-
end
-
-
endmodule
-
-
これのソースを読み込ませて自動生成されたヘッダファイルが次です。
-
/* Copyright 2010 www.sugawara-systems.com
-
* Note:
-
* This file is automatically generated.
-
* Please do not edit this file - you will lose your
edits.*/
-
-
#ifndef INCLUDED_DPIHEADER
-
#define INCLUDED_DPIHEADER
-
#ifdef __cplusplus
-
#define DPI_LINK_DECL extern "C"
-
#else
-
#define DPI_LINK_DECL
-
#endif
-
#include "svdpi.h"
-
-
-
-
typedef struct
{
-
char red;
-
char green;
-
char blue;
-
} RGB_TYPE;
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void func(RGB_TYPE*);
-
-
DPI_LINK_DECL
DPI_DLLESPEC
-
int struct_operation();
-
-
#endif
このヘッダファイルでRGB_TYPEという構造体のメンバ配置が分かります。次は、C++ソースファイルです。
-
int struct_operation()
-
{
-
-
RGB_TYPE rgb;
-
rgb.red=1;
-
rgb.green=2;
-
rgb.blue=3;
-
-
func(&rgb);//SV functionを呼び出す
-
-
printf("SVで設定した値は、赤=%3d
緑=%3d 青=%3dです。",rgb.red,rgb.green,rgb.blue);
-
-
-
-
return 0;
-
}
結果です。予想通りに動きます。
***** 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ヘッダファイルのイメージでアクセスすればよいです。
次は、少し複雑な例です。
-
-
-
-
module root_scope;
-
-
export "DPI-C" function func_example;
-
import "DPI-C" context task struct_operation();
-
-
typedef struct {
-
byte
red;
-
byte
green;
-
byte
blue;
-
} RGB_TYPE;
-
-
-
RGB_TYPE A;
-
-
typedefstruct {
-
RGB_TYPE RGB;//メンバーが構造体
-
bit b0;//2値スカラー
-
logic
b1;//4値スカラー
-
bit [8:0] b2;//2値ベクター
-
logic [8:0] b3;//4値ベクター
-
byte
b4;
-
shortint
b5;
-
int
b6;
-
integer
b7;
-
longint
b8;
-
time
b9;
-
shortreal
b10;
-
real
b11;
-
bit [127:0] b12;//パックド配列
-
logic [127:0] b13;//パックド配列
-
logic [127:0] b14 [10][10];//アンパックド配列
-
bit [3:0][31:0] b15 [0:3][0:3];//アンパックド配列
-
} EXAMPLE_TYPE;
-
-
-
function void func_example(inout EXAMPLE_TYPE A);
-
-
$display("私は、SVファンクションです。Cで設定した値を読みます。");
-
$display("RGB.red=%d",A.RGB.red);
-
$display("RGB.green=%d",A.RGB.green);
-
$display("RGB.blue=%d",A.RGB.blue);
-
$display("b0=%b",A.b0);
-
$display("b1=%b",A.b1);
-
$display("b2=%d",A.b2);
-
$display("b3=%b",A.b3);
-
$display("b4=%d",A.b4);
-
$display("b5=%d",A.b5);
-
$display("b6=%d",A.b6);
-
$display("b7=%d",A.b7);
-
$display("b8=%d",A.b8);
-
$display("b9=%d",A.b9);
-
$display("b10=%f",A.b10);
-
$display("b11=%f",A.b11);
-
$display("b12=%x",A.b12);
-
$display("b13=%x",A.b13);
-
$display("b14=%x",A.b14[9][9]);
-
$display("b15=%x",A.b15[3][3]);
-
-
A.RGB.red=101;
-
A.RGB.green=102;
-
A.RGB.blue=103;
-
A.b0=1'b0;
-
A.b1=1'bx;
-
A.b2=256;
-
A.b3=99;
-
A.b4=4;
-
A.b5=5000;
-
A.b6=6000;
-
A.b7=7000;
-
A.b8=8000;
-
A.b9=9000;
-
A.b10=1000.0;
-
A.b11=1100.0;
-
A.b12=32'h12000;
-
A.b13=32'h13000;
-
A.b14[9][9]=32'h14000;
-
A.b15[3][3]=32'h15000;
-
$display("SVで設定し直しました。");
-
-
endfunction
-
-
-
initial begin
-
struct_operation();
-
-
end
-
-
endmodule
-
-
これに対するVeritakSVが生成したCヘッダ出力です。
-
/* Copyright 2010 www.sugawara-systems.com
-
* Note:
-
* This file is automatically generated.
-
* Please do not edit this file - you will lose your
edits.*/
-
-
#ifndef INCLUDED_DPIHEADER
-
#define INCLUDED_DPIHEADER
-
#ifdef __cplusplus
-
#define DPI_LINK_DECL extern "C"
-
#else
-
#define DPI_LINK_DECL
-
#endif
-
#include "svdpi.h"
-
-
typedefstruct
{
-
char red;
-
char green;
-
char blue;
-
} RGB_TYPE;
-
-
typedefstruct
{
-
RGB_TYPE RGB;
-
svBit b0;
-
svLogic b1;
-
svBitVecVal b2[SV_PACKED_DATA_NELEMS(9)];
-
svLogicVecVal b3[SV_PACKED_DATA_NELEMS(9)];
-
char b4;
-
short b5;
-
int b6;
-
svLogicVecVal b7;
-
int64_t b8;
-
svLogicVecVal b9[SV_PACKED_DATA_NELEMS(64)];
-
float b10;
-
double b11;
-
svBitVecVal b12[SV_PACKED_DATA_NELEMS(128)];
-
svLogicVecVal b13[SV_PACKED_DATA_NELEMS(128)];
-
svLogicVecVal b14[10][10][SV_PACKED_DATA_NELEMS(128)];
-
svBitVecVal b15[4][4][SV_PACKED_DATA_NELEMS(128)];
-
} EXAMPLE_TYPE;
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void func_example(EXAMPLE_TYPE*);
-
-
DPI_LINK_DECL
DPI_DLLESPEC
-
int struct_operation();
-
-
#endif
32ビット幅がパックド配列における1チャンク(塊)になります。
SVとCの表現の対応は次の通りです。X86(リトルエンディアン)のアンパック構造体では、記述順にオフセットが足されます。
SV表現 |
C表現 |
備考 |
メモリ占有バイト数(バイト) |
構造体先頭アドレスからのオフセットバイト(X86) |
bit b0 |
svBit b0; |
2値スカラー |
1 |
3 |
logic
b1; |
svLogic b1; |
4値スカラー |
1 |
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
- 32行目:timeは、64ビット4値です。32ビットの倍なので2チャンク必要になりメモリ所要は8バイトになります。
- SV_PACKED_DATA_NELEMS(64)は、チャンク数2を返します。
- SV_PACKED_DATA_NELEMS(128)は、チャンク数4を返します。
- 37行目:svLogicVecVal b14[10][10][SV_PACKED_DATA_NELEMS(128)]のsvLogicVecVal b14[10][10]までがアンパっクド項、[SV_PACKED_DATA_NELEMS(128)]がパックド項です。
- 38行目:多次元パックド項は、Cでは1次元にまとめられます
- メモリ占有バイト数の足し算がメンバー位置(オフセット値)に一致しないのは、構造体メンバーのアライメントによります。メンバーの占有バイト数とロケーションの関係で、アクセスしやすいようにCコンパイラがパディングバイトを暗黙に附加します。パディングバイトが、実行時不定になるのは、C/C++でもシミュレータ側どちらの側についても然りです。パディングがあると構造体memcmpが使用できなくなりコンパイラは、メンバー毎の比較を余儀なくされるので、必要がなければパディングのないような構造体にすることが望ましいです。メンバーのアライメントは、8/4/2で起きるので、メモリ占有バイト数からアライメントバイト数を割り出してアライメント値が大きい順にメンバーを記述すれば、パディングは生じません。
C++ソースです。
-
int struct_operation()
-
{
-
EXAMPLE_TYPE rgb;
-
-
rgb.RGB.red=1;
-
rgb.RGB.green=2;
-
rgb.RGB.blue=3;
-
-
rgb.b0=sv_1;
-
rgb.b1=sv_z;//sv_z;
-
rgb.b2[0]
=20;
-
-
rgb.b3[0].aval=0x1ff;
-
rgb.b3[0].bval=0x1ff;
-
-
rgb.b4=40;
-
rgb.b5=500;
-
rgb.b6=600;
-
-
rgb.b7.aval=700;
-
rgb.b7.bval=0;
-
-
rgb.b8=800;
-
-
rgb.b9[0].aval=900;//Timeは、2チャック
-
rgb.b9[0].bval=0;
-
rgb.b9[1].aval=0;
-
rgb.b9[1].bval=0;
-
-
rgb.b10=100.0;//float
-
rgb.b11=110.0;//double
-
-
memset(&rgb.b12,0, sizeof(svBitVecVal)*SV_PACKED_DATA_NELEMS(128));//zero クリア
-
rgb.b12[0]=0x1200;
-
-
memset(&rgb.b13,0, sizeof(svLogicVecVal)*SV_PACKED_DATA_NELEMS(128));//zero クリア
-
rgb.b13[0].aval=0x1300;
-
-
memset(&rgb.b14,0, sizeof(svLogicVecVal)*SV_PACKED_DATA_NELEMS(128)*100);//zero
クリア
-
rgb.b14[9][9][0].aval=0x1400;
-
-
memset(&rgb.b15,0, sizeof(svBitVecVal)*SV_PACKED_DATA_NELEMS(128)*4*4);//zero クリア
-
rgb.b15[3][3][0]=0x1500;
-
-
func_example(&rgb);//SV functionを呼び出す
-
-
printf("SVで設定した値は、赤=%3d
緑=%3d 青=%3dです。\n",rgb.RGB.red,rgb.RGB.green,rgb.RGB.blue);
-
printf("SVで設定した値は、b0=%1d\n",rgb.b0);
-
printf("SVで設定した値は、b1=%1d\n",rgb.b1);
-
printf("SVで設定した値は、b2=%1d\n",rgb.b2[0]&
SV_MASK(9));//範囲外はINVALIDな値なのでマスクする
-
printf("SVで設定した値は、b3=%3d\n",rgb.b3[0].aval & SV_MASK(9));//範囲外はINVALIDな値なのでマスクする
-
printf("SVで設定した値は、b4=%3d\n",rgb.b4);
-
printf("SVで設定した値は、b5=%3d\n",rgb.b5);
-
printf("SVで設定した値は、b6=%3d\n",rgb.b6);
-
printf("SVで設定した値は、b7=%3d\n",rgb.b7.aval );
-
printf("SVで設定した値は、b8=%3d\n",rgb.b8);
-
printf("SVで設定した値は、b7=%3d\n",rgb.b9[0].aval );
-
-
printf("SVで設定した値は、b10=%f\n",rgb.b10);
-
printf("SVで設定した値は、b11=%f\n",rgb.b11);
-
-
printf("SVで設定した値は、b12=%x\n",rgb.b12[0]);
-
printf("SVで設定した値は、b13=%x\n",rgb.b13[0].aval);
-
printf("SVで設定した値は、b14=%x\n",rgb.b14[9][9][0].aval);
-
printf("SVで設定した値は、b15=%x\n",rgb.b15[3][3][0]);
-
-
return 0;
-
}
結果です。
***** 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側のビットになります。
-
-
-
-
module root_scope;
-
-
export "DPI-C" function func;
-
import "DPI-C" context task struct_operation();
-
-
typedef struct packed {//パックド構造体のビット配置
-
byte
red;//MSB
-
byte
green;
-
byte
blue;//LSB
↑
-
} RGB_TYPE;
-
-
-
-
-
function void func(inout RGB_TYPE A);
-
$display("私は、SVファンクションです。Cで設定した値を読みます。 赤=%3d 緑=%3d
青=%3d",A.red,A.green,A.blue);
-
A.blue=101;
-
A.green=102;
-
A.red=103;
-
$display("SVで設定し直しました。");
-
-
endfunction
-
-
-
initial begin
-
-
-
struct_operation();
-
-
end
-
-
endmodule
-
-
生成されたCファイルでは、メンバー情報は消えて単なる2値ベクタになってしまいます。
-
/* Copyright 2010 www.sugawara-systems.com
-
* Note:
-
* This file is automatically generated.
-
* Please do not edit this file - you will lose your
edits.*/
-
-
#ifndef INCLUDED_DPIHEADER
-
#define INCLUDED_DPIHEADER
-
#ifdef __cplusplus
-
#define DPI_LINK_DECL extern "C"
-
#else
-
#define DPI_LINK_DECL
-
#endif
-
#include "svdpi.h"
-
-
DPI_LINK_DECL
DPI_DLLISPEC
-
void func(svBitVecVal*
);
-
-
DPI_LINK_DECL
DPI_DLLESPEC
-
int struct_operation();
-
-
#endif
-
C++ソースです。
メンバー位置をSV構造体から読み取りパートセレクトで取り出しています。パックド構造体は、基本的にベクターと同義だからです。
-
-
int struct_operation()
-
{
-
-
svBitVecVal rgb;
-
svPutPartselBit(&rgb,1,0,8);//青
-
svPutPartselBit(&rgb,2,8,8);//緑
-
svPutPartselBit(&rgb,3,16,8);//赤
-
-
func(&rgb);//SV functionを呼び出す
-
-
svBitVecVal blue=0;
-
svBitVecVal red=0;
-
svBitVecVal green=0;
-
-
-
svGetPartselBit(&blue,&rgb,0,8);//SVで設定した値を取り出す
-
svGetPartselBit(&green,&rgb,8,8);//SVで設定した値を取り出す
-
svGetPartselBit(&red,&rgb,16,8);//SVで設定した値を取り出す
-
-
-
printf("SVで設定した値は、赤=%3d
緑=%3d 青=%3dです。\n",red,green,blue);
-
-
return 0;
-
}
結果です。
***** 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でのリンケージ名になります。
-
module gloval_name_space;
-
-
import "DPI-C" context task sv_import_task ();
-
-
-
endmodule
全モジュールで、名前は、ユニークである必要があります。Cのリンケージは、グローバルだからです。
そこで、
Cリンケージ名を別名にする方法が下です。それぞれ、SV上の名前は同じですが、C上では、C_task1,C_task2と言う別名でリンクが行われます。
(Cからは、sv_import_taskは、見えません)
-
module gloval_name_space1;
-
-
import "DPI-C" context C_task1=task sv_import_task ();
-
-
-
endmodule
-
-
module gloval_name_space2;
-
-
import "DPI-C" context C_task2=task sv_import_task ();
-
-
-
endmodule
-
-
-
16.2.10 スコープ
複数のインスタンスを持つ場合は、どうなるのでしょうか?
-
module multi_export;
-
-
-
for (genvar g=0;g<3;g++)
export_test ex1();//3個のインスタンスを生成
-
-
-
-
endmodule
-
-
-
module export_test;
-
-
-
import "DPI-C" context function void multi_import_func ();
-
export "DPI-C" multi_export_inc=function sv_inc;
-
-
-
function int sv_inc(int i1);//Cから呼ばれるExportファンクション
-
$display("........................私は sv_inc 関数です。%m");
-
return
i1+1;
-
endfunction
-
-
-
-
-
initial
-
multi_import_func();//Cのインポートファンクションを呼ぶ
-
-
-
-
endmodule
C側のソースです。
-
extern "C" __declspec(dllexport)
void multi_import_func()
-
{
-
-
int i;//ローカル変数
-
-
vpi_printf("私は、C のimport_func()です。ロケーション=%s\n",svGetNameFromScope(svGetScope()));
-
-
-
i=multi_export_inc(100);//SV
Functionを呼ぶ
-
vpi_printf("私はCです。 multi_export_incを呼び出した結果=%dが返ってきました。\n\n",i);
-
-
-
}
一個のモジュールについてシミュレータ内部では次のような構成になります。
結果です。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個のインスタンスを生成してみます。
-
module multi_export;
-
-
-
for (genvar g=0;g<3;g++)
export_test ex1();
-
-
-
-
endmodule
-
-
-
module export_test;
-
-
-
import "DPI-C" context C_import_task=task sv_import_task ();
-
export "DPI-C" multi_export_inc=function sv_inc;
-
-
-
-
function int sv_inc(int i1);//Cから呼ばれるExportファンクション
-
$display("........................私は sv_inc 関数です。%m");
-
return
i1+1;
-
endfunction
-
-
-
initial
-
sv_import_task();//Cのインポートファンクションを呼ぶ
-
-
export "DPI-C" delay_task_by_parameter_m= task
delay_task_by_parameter;
-
export "DPI-C" get_sv_time_m=function get_sv_time;
-
-
function longint get_sv_time();
-
return
$time;
-
endfunction
-
task
delay_task_by_parameter( inputlongint delay);
-
#(delay);
-
endtask
-
endmodule
C++側ソースです。
-
-
extern "C" __declspec(dllexport)
int C_import_task()
-
{ static int instance_counter=0;//STATIC変数
-
int i;
-
-
vpi_printf("私は、C_import_task()です。ロケーション=%s\n",svGetNameFromScope(svGetScope()));
-
-
i=multi_export_inc(instance_counter++);//SV
Functionを呼ぶ
-
vpi_printf(" multi_export_incを呼び出した結果=%dが返ってきました。#10待ちます。\n\n",i);
-
delay_task_by_parameter_m(10);
-
__int64 t=get_sv_time_m();//SV
Functionを呼ぶ
-
-
vpi_printf(" ロケーション=%s\n",svGetNameFromScope(svGetScope()));
-
vpi_printf(" 現在時刻は%ldです。\n",t);//printfの代わりにvpi_printfを使う
-
-
vpi_printf(" ローカル変数i=%dです。\n\n",i);
-
-
return 0;
-
}
上の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を起動するタスクを呼び出します。
-
module ruby;
-
-
-
import "DPI-C" context task ruby_task();
-
-
-
initial
-
ruby_task();//C インポートtaskを呼ぶ
-
-
endmodule
C++側ソースです。Cの標準関数systemを用いて、スクリプトLibraryTime2.rbを動かしています。system関数は、ブロッキング動作をします。rubyが終わるまで返ってきません。
-
extern "C" __declspec(dllexport)
int ruby_task()
-
{
-
-
-
system("ruby LibraryTime2.rb");//ブロッキングプロセス
-
-
return 0;
-
-
}
実行すると、次のように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から引き数を与えられると、より汎用的に使えるので、そのように書いてみました。
-
module ruby;
-
-
-
-
-
import "DPI-C" context task dos_command(string);//
-
initial
-
dos_command("copy ruby_task.v copy_task.v");//C インポートtaskを呼ぶ
-
-
endmodule
SystemVerilogのstringは、Cの入力では、const char* になります。そのままsystem関数に渡せばOKです。
最初の例は、次のようにも書けます。
-
extern "C" __declspec(dllexport)
int dos_command( const char*command)
-
{
-
-
system(command);
-
return 0;
-
}
-
module ruby;
-
-
-
import "DPI-C" context task dos_command(string);
-
-
-
initial
-
dos_command("ruby LibraryTime2.rb");//C インポートtaskを呼ぶ
-
-
endmodule
-
-
-
-
rubyのソースは、こちらから頂戴しました。
このように、簡単かつ自在にrubyを呼べるので、ファイル処理や、正規表現等、verification工程で役立つことと思います。また、上記の例は、rubyに限ったものではなく、SV側のコマンドラインをいじるだけで、perlや、pythonも同様に呼び出せる筈です。