VeritakSV DPI-C/DPI-SC Tutorial
                                                                                Apr.20.2010 Update

VeritakSVでのDPI-C/DPI-SCについて説明します。
DPI-Cは、C/C++ とSystemVerilog間での汎用的な通信手順で,import/export function/taskにより通信を行います。systemverilog p1800 2009でLRM化されています。

一方、DPI-SCは、systemcとSystemVerilog協調シミュレーションでの通信手段です。systemcは、内部にスレッドを持つので、DPI-Cでは、通信することができません。そのためにsystemc 専用の通信路を使う必要があり、それがDPI-SCです。これは、LRM化されたものではなくベンダユニークな実装になります。

1.DPI-Cコンパイル、リンク手順
1.1 フロー
  概ね下図の手順です。

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


 

1.2 サンプル
1.2.1 完成後のVeritakプロジェクト

  開発環境を整えるまでが面倒です。
 最初に、リンク作業完成後のVeritakプロジェクトを走らせて,完成後のイメージを掴んでください。

samples\dpi\dpi_samples\import_func\Release\import_func.vtakprj

をロードしてGoすると、

C:\Users\tak.sugawara\Documents\Visual Studio 2010\Projects\veritak_sv52\release\samples\dpi\dpi_samples\import_func\import_func.sv(1)::export_test
Verilogのシミュレーションの準備が完了しました。スタートは,Goボタンを押してください。
***** Veritak SV Engine Version 0.34 Build Jun.4.2010 *****

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

---------- シミュレーションを終了します。time=0ns

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

となります。
太字部がDPI-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のインポートファンクションを呼ぶ
 
 
 
endmodule

です。


1.2.2 DLLの生成

DPI-Cで実行する部分は、基本的にDLL(Dynamic Link Library)という部品を作っておき、それを実行時(=シミュレーション時)にロードすることによって実現します。
実際、上のプロジェクト構成を見てみると、下のように、SVソースの他、二つのDLLから成ることが分かります。この二つのDLLは、C/C++コンパイラで生成します。
以降、DLLの作り方を見ていきます。




1.2.2.1 C Headerファイルの生成

プロジェクトファイル
samples\dpi\dpi_samples\import_func\import_func_gen_header.vtakprj
をロードしてください。すると、下の画面のように

DPI-C Headerを生成しました、

と出ます。これが正常終了の状態です。

このプロジェクトは、上のSVソースだけから成るプロジェクトですが、CのHeaderファイルを生成するように、プロジェクト設定 で、DPI-IF -> Generate C Headerをチェックしてください。(チェックすると、ソースファイル解析して、Cヘッダファイル、エクスポートC ファイルがプロジェクトフォルダに生成される仕様になっています。)






この例で、生成されるのは、次の二つのファイルです。

dpi_veritak_export.h

/* 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 import_func();
 
DPI_LINK_DECL DPI_DLLISPEC
int sv_inc(int );
 
#endif

dpi_veritak_c_export.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
int sv_inc(int i1)
{
}


1.2.2.2 エクスポートファイルのコンパイル - export.dllの生成

上で生成したファイルのコンパイルを行いexport.dllのを生成を行う作業です。サポートC/C++ コンパイラは、VisualStudio10のみです。無償のExpress版でも機能、性能に制約/違いはありません。 
Visual Studio 10をインストールしたら、
samples\dpi\dpi_samples\import_func\import_func..ソリューション

というファイルをクリックしてください。
すると次のような画面になります。
ソリューションの中には、プロジェクトが二つ入っています。exportプロジェクトは、export.dll を生成するためのプロジェクト、import_funcプロジェクトは、import_func.dllを
を生成させるためのプロジェクトです。

exportプロジェクトでは、上で生成したソースファイル dpi_veritak_exports.c をプロジェクトに指定しています。その他のファイルは、WizardのWin32->DLLを生成のWizardの指定に従って勝手にプロジェクトに入ってくるファイルですので、とりあえず無視して構いません。


上のexport プロジェクトをビルドするとexport.dll が

samples\dpi\dpi_samples\import_func\Debug

に生成されている筈です。

詳しい設定は、プロジェクトを見て頂きたいと思いますが、Wizardから生成されるdefault 設定から以下を追加変更しています。

テクニカルノート
 cの関数の中身が空でwarning が出ていますが無視してください。この関数は、VeritakSVでは、単なるWrapperとして使っています。次のプロジェクトでは、
便宜上、この関数を呼び出すような構成にしていますが、DLLロード時に、IAT(ImportAddressTable)を書き換え、シミュレータ内で生成した関数のアドレスに置き換えています。
ですから、「空」のままでOKです。

1.2.2.3 インポートファイルのコンパイル - import_func.dllの生成

 import_funcプロジェクト中、

inc1.cpp

がユーザDPI-Cのソースです。上の画面の右側に関数本体が記述されています。
vpi_printfは、VPIファンクションですが、これについては、後述します。

その他のソースは、やはりWizardが勝手に生成したもので、差し当たり無視します。ビルドを行うと import_func.dllが

samples\dpi\dpi_samples\import_func\Debug

に生成されている筈です。

詳しい設定は、プロジェクトを見て頂きたいと思いますが、Wizardから生成されるdefault 設定から以下を追加変更しています。

特に、リンカーで、上で生成したexport.lib とライブラリフォルダにあるveritak_sv.lib を追加することにご注意ください。

これで、必要なDLLが揃いました。

1.2.3  Veritakプロジェクト

 プロジェクトをDLL が生成されたフォルダ上で作ります。上で作った二つのDLLをプロジェクトに加えます。
  


 バッファリングの問題について
 printf や、cout といったストリームは、defaultでバッファリングされています。これに起因する問題で、DLL-EXE間で実行順通りに出力されない現象が起こります。
 具体的には、DPI-C側でのprintf 出力が出なかったり、$display-printf間の出力順が実行順と異なる、といった現象になります。この問題は、コンソールをVeritak GUI上で、共有した場合に起こります。

 この問題に対処するには、次の二つを行ってください。

 1)EXE側への指示

  プロジェクト設定DPI-IF -> No Buffer にチェックしてください。(これは、同時にコンソールをGUI上で共有する、という指示になります。)
 
 2)DLL側で指示。バッファリングしないことの明示
  
  setvbuf(stdout,NULL,_IONBF,0);

  を呼び出せばよいです。一度、実行すれば十分なので、例えば、次の位置に挿入します。

  


テクニカルノート

基本的には、上のようにすればよいのですが、大量にテキストを吐く場合には、バッファリングなしだと遅くなってしまいます。その場合には、上の対策は実行せずにprintfの代わりに、VPI系の構文 vpi_printf を呼び出します。vpi_printfと $displayは、結局のところ、同じC−RUNTIMEを呼び出すので、バッファリング付きでも問題なく実行できます


2 DPI-Cのデバッグ

2.1プロジェクトのロード

samples\dpi\dpi_samples\import_func\Debug\import_func.vtakprj

をロードします。この段階では、Goボタンは未だ押しません





2.2ブレークポイントの設定

inc1.cpp上のソース524行目と526行目にブレークポイントを置いてみます。




2.3デバッガのアタッチ
 デバッグ対象にデバッガを接続することをアタッチといいます。(反対にデバッガの接続を解除することをデタッチといいます)。DPI-Cのデバッグには、VisualStudioを使います。

アタッチするには、下の画面でデバッグー>プロセスにアタッチ を押します。




 次の画面になります。
 
 DPI−CのDLLは、VeritakSVのシミュレーションエンジンveritak_sv.exeと同じプロセスです。veritak_sv.exeを選択しアタッチを押します。

 


2.4 Goボタン

を押します。

すると、DPI-Cのソース上で設定したブレークポイントで実行が止まります。この状態で、各変数の状態は、ツールチップもしくは、ローカルWindow内で見ることができます。
F5キーを押すと実行が再開し次のブレークポイントで止まります。さらに押すとシミュレータに制御が戻り最後まで実行します。



テクニカルノート
デバッガは一つしかアタッチできない

 HDLソースをデバッグするVeritakSVのデバッガは、HDLソースに対し、Veripad/Notepad++と連携し

 を行うことができますが、これもVeritakSV搭載のプロセスデバッガによるものです。VisualStudioと同じような原理で動作するデバッガです。この手のデバッガは、アタッチした後でも、速度の低下は殆どないという利点がある反面、アタッチできるデバッガは、一つという制限があります。つまり、HDLソース上でブレークポイントを置きつつ、DPI-Cのコードデバッグを行うことが(今のところVeritakSVα0.35現在)できません。

 HDL上にブレークポイントがあった場合は、自動的にVeritakSV搭載のデバッガがアタッチします。この場合上のように、veritak_svにアタッチしようとしても、グレーになっていてVisualStudioの側からアタッチさせることが出来ません。その場合は、

シミュレーションー>デタッチ

で、VeritakSVのデバッガをデタッチさせるか、ブレークポイントをAll Clearして、クリーンなプロジェクトにしてやり直してください。




3.SystemCのシミュレーション

 VeritakSVでは、SystemCシミュレーションカーネルを統合しており、SystemCのシミュレーションをすることができます。また、OSCIシミュレータのセマンティクスを維持しており、SVとの通信がなければ、全く同じ挙動になります。 次のようなシミュレーションが可能です。

  1. SystemC単独
  2. VerilogHDL単独
  3. VerilogHDLからSystemCのインスタンス化
  4. SystemCからVerilogHDLのインスタンス化
  5. 1-4の組み合わせ
  6. DPI-SC

3.0.1 統合カーネルのスケジューラ
  SystemCのカーネルスケジューラは、デルタサイクルと呼ばれるループで現在時刻でイベントが発生しなくなるまで、続けられます。
  

 一方、SystemVerilogの1タイムスロットは、下のように領域に分けられます。SystemVerilogがなぜこのような複雑な状態遷移を持つのかは、歴史的な経緯もあり触れません。各領域(region)の意味については、以下のサンーバースト社の論文(英文)が詳しいです。

http://www.sunburst-design.com/papers/CummingsSNUG2006Boston_SystemVerilog_Events.pdf

一方、SystemCは、上のEvaluate/Updateの2領域だけです。
これを、VerilogHDLと統一カーネルにするには、どのようにしたらよいでしょうか?実装の要件としては、SystemCとVerilogHDLが干渉しないで独立に走っているときには、各々の言語は、単独の言語カーネルで走った結果と全く同じになることです。

 VeritakSVは、SystemCデルタサイクルを、下図Activeスロット中にマイナーループとして実行します。この結果、SystemCの値更新により、VerilogHDLのメジャーループに影響を与えます。逆に、VerilogHDLのメジャーループが、SystemCにイベントともたらす場合もあります。その場合は、再度Activeスロットに戻り、再びSystemCマイナーループを駆動します。こうして値の変化がなくなるまで(=イベントが発生しなくなるまで)ループは続きます。





3.0.2 VerilogHDLとSystemCのシミュレーションの仕組み
  SysemCのルール
 

  は、VerilogHDLのルール

.. と、なんだか似たところがあります。実際、どちらもイベントドリブンのシミュレーション言語で、

 の二つのソフトウェア要素から成ります。言語間で呼び名は、違ってもソフトウェアの視点からは同じものです。
 

SystemC VerilogHDL 記憶
C関数 SC_METHOD function 記憶を持たない
ローカル変数は持てるが関数を抜けると忘れる
スレッド SC_THREAD
SC_CTHREAD
task
always
initial
fork -join
イベント構文で、スレッドを抜ける。イベントトリガによりイベント構文の次にある文の実行に戻る。
このとき、抜ける前のローカル変数はすべてその状態で保持される。
イベント構文がなければ、そのスレッドを脱出することができない

3.0.2.1 C関数は組み合わせ回路
  例として、AND回路をモデリングすることを考えます。
 
   c=a & b;

  この回路のシミュレーションは、aまたは、bの値が変化したときに,次のC関数を呼べばよいですね。
  
  int and_sim(int a,b) { return a&b;}

  SystemCのSC_METHOD、VerilogHDLのfunctionは、これに同じです。記憶のない、組み合わせ回路は、C関数で実現できます

3.0.2.2 順序回路は、スレッド
  内部に状態を持つ回路(順序回路)では、その状態を記憶しておくことが必要です。必要な状態は、ローカル変数としてスタックに保存しておけばよいのですが、C関数の場合は、関数を抜けた時点でスタックは、破棄されてしまうののでC関数では実現不可能です。そのためにスレッドという仕組みを使います。
  
  
 T.B.D.
 

3.1 SystemC単独のシミュレーション

3.1.1Visual C++の設定



  下のように にStaticライブラリ SystemC.libを加えます。
 (VeritakSVインストールフォルダ\samples\systemc\systemc_only\)
  

以下の設定に注意します。

3.1.1.1 Cランタイムライブラリは"マルチスレッドDLL"ではなく、STATICライブラリであるマルチスレッド(デバッグ)を使う

添付しているSystemC.lib は、スタティックライブラリです。そのために同じスタティック系のライブラリを使用してください。

VeritakSVインストールフォルダ\systemc-2.2.0\msvc71\SystemC下にDebug/Release フォルダがあります。それぞれデバッグモードとリリースモードでコンパイルしたスタティックライブラリSystemc.lib が入っています。ユーザプロジェクトでのデバッグ/リリースに合わせて使用してください。(ライブラリが合わないとリンク時にエラーが出ます)



3.1.1.2 vmg を下のように追加する

 この設定がないと正常にコンパイルできません。(Warningがでますがエラーにはならないので注意してください。)
Systemc.lib とリンクするプロジェクトは、以下のように指定してください。


 その他の設定

 を行います。

3.1.2 systemc ソース形式

  systemc ソースは、sc_main()を消去して、全てSC_MODULE形式にしてください。 sc_start()も不要です。
 トップモジュールは、マクロ SC_MODULE_EXPORTで、エクスポートします。以下は、hello モジュールをトップレベルに置く宣言の例です。
 VerilogHDLが、systemc インスタンスを呼び出すときは、トップ/ノントップ に関係なく、マクロ SC_MODULE_EXPORTで、エクスポートしてください。

#include <systemc.h>
 
SC_MODULE(hello){
	void disp_hello(){
		cout << "ようこそ。SystemCの世界へ!"<<endl;
	}
	SC_CTOR(hello){
		SC_THREAD(disp_hello);
	}
 
};
SC_MODULE_EXPORT(hello);


3.1.3 ビルド
  VisualC++でコンパイルし hello.dllを生成します。

3.1.4 Veritak プロジェクト
  SystemC単独シミュレーションでは、上で生成したDLLだけが必要なファイルです。
(Veritakプロジェクトは、VeritakSVインストールフォルダ\samples\systemc\systemc_only\Debug)


3.1.5 RUN

結果です。

***** Veritak SV Engine Version 0.36 Build Jul.10.2010 *****

Veritak-SystemC Simulation Kernel Initialized. Jul.10.2010. 
ようこそ。SystemCの世界へ!

---------- シミュレーションを終了します。time=0ns

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

3.2 SystemCとVerilog HDLの並列シミュレーション

次のソースをhello.sv としてプロジェクトに追加します。

module hello1;
 
	initial $display("ようこそ。SystemVerilogの世界へ!");
 
endmodule


プロジェクトと実行結果です。


3.3 SystemCのインスタンス化
3.3.1 ポートを持たないSystemCモジュールのインスタンス化

Verilog HDL からSystemCのモジュールをインスタンス化します。この例では、DLLは、上で生成したものをそのまま使います。

module hello1;
 
	initial $display("ようこそ。SystemVerilogの世界へ!");
 
		hello sc_dut();//SystemC モジュールhelloのインスタンス化
 
endmodule;


結果です。systemc インスタンスsc_dut がインスタンス化されている様子がScope Tree View で分かります。




generateによるsystemc モジュールのインスタンス化
 generate構文によるインスタンス化もサポートしています。

module hello1;
 
	initial $display("ようこそ。SystemVerilogの世界へ!");
 
	for (genvar g1=0;g1<3;g1++) 
		hello sc_dut();//systemc モジュールのインスタンス化
 
endmodule;


結果です。3個のsystemcモジュール sc_dutがインスタンス化されている様子がScopeTreeViewから分かります。







3.3.2 SystemCインスタンスとVerilogHDLとの一般的な接続


systemcのhello モジュールをインスタンス化しています。
SystemC モジュールのインスタンス化は、verilogHDLと全く同様で簡単です。

ただし、接続は、下のように、名前結合としてください。(順番結合はサポートしていません。)

module hello1;
 
	initial $display("ようこそ。SystemVerilogの世界へ!");
 
 
	reg [13:0] a;
	wire [7:0] out;
 
 
        hello dut(.in1(a),.out1(out));//SystemC Moduleのインスタンス化 名前結合で接続
 
	always @(out) begin//SystemCからの信号をセンスする
		$display(" Veriloag Reading : out=%d time=%t",out,$time);
	end
 
         initial begin
		$timeformat(-9,3,"ns",8);
		a=0;
		repeat(4) begin
			a=a+1;
			$display("Verilog  Writing: a=%3d sv time=%t",a,$time)
			;#1;
        end
     end
endmodule



SystemC 側の記述です。sc_in/sc_out を用いて上のVerilogHDLモジュールと接続します。 コンストラクタで、接続するVerilogHDLのポートの名前と同一にしてください。

SC_MODULE(hello
){
 
 
	sc_in<sc_int<8 >> in1;//Verilog HDLに接続するポート
	sc_out<sc_int<8 >> out1;//Verilog HDLに接続するポート
 
	SC_CTOR(hello)://コンストラクタ
	in1("in1"),out1("out1") //接続するVerilogポート名と同じにする
 
	{
 
		SC_THREAD(disp_hello);
 
		SC_METHOD(disp_in1);
		sensitive<< in1;
	}
	void disp_in1(){//in1をセンス
			cout << "--------------------------------- SystemC reading: :in1="<< in1.read()<<"  sc time="<<sc_time_stamp()<<endl;
	}
 
	void disp_hello(){
		wait(1000,SC_NS);
 		out1.write(0);
		wait(1,SC_PS);
		cout << "--------------------------------- SystemC Wriing : out1="<< out1.read()<< " sc_time=" <<sc_time_stamp()<<endl;
		for (int i=0;i<5;i++){
			 wait(100,SC_NS);
   			out1.write(out1.read()+1);
			 wait(1,SC_PS);
			 cout << "-------------------------------- SystemC Wriing : out1="<< out1.read()<< " sc_time=" <<sc_time_stamp()<<endl;
		}
 
		cout << "---------------------------------- ようこそ。SystemCの世界へ!"<<endl;
	}
 
};
SC_MODULE_EXPORT(hello);

結果です。左側が Verilogモジュール側の出力、右側が SystemCモジュールからの出力です。
VerilogからのWriteがSystemC側でセンス、SystemC側からのWriteでVerilog always プロセスが駆動されている様子が分かります。

C:\Users\tak.sugawara\Documents\Visual Studio 2010\Projects\veritak_sv52\systemc-2.2.0\examples\sysc\hello\systemc_instance.sv(1)::hello1
Verilogのシミュレーションの準備が完了しました。スタートは,Goボタンを押してください。
***** Veritak SV Engine Version 0.36 Build Jul.10.2010 *****

--------------------------------- SystemC reading: :in1=0  sc time=0 sec
ようこそ。SystemVerilogの世界へ!
Verilog  Writing: a=  1 sv time=   0.000ns
--------------------------------- SystemC reading: :in1=1  sc time=0 sec
Verilog  Writing: a=  2 sv time=   1.000ns
--------------------------------- SystemC reading: :in1=2  sc time=1 ns
Verilog  Writing: a=  3 sv time=   2.000ns
--------------------------------- SystemC reading: :in1=3  sc time=2 ns
Verilog  Writing: a=  4 sv time=   3.000ns
--------------------------------- SystemC reading: :in1=4  sc time=3 ns
--------------------------------- SystemC Wriing : out1=0 sc_time=1000001 ps
 Veriloag Reading : out=  1 time=1100.001ns
-------------------------------- SystemC Wriing : out1=1 sc_time=1100002 ps
 Veriloag Reading : out=  2 time=1200.002ns
-------------------------------- SystemC Wriing : out1=2 sc_time=1200003 ps
 Veriloag Reading : out=  3 time=1300.003ns
-------------------------------- SystemC Wriing : out1=3 sc_time=1300004 ps
 Veriloag Reading : out=  4 time=1400.004ns
-------------------------------- SystemC Wriing : out1=4 sc_time=1400005 ps
 Veriloag Reading : out=  5 time=1500.005ns
-------------------------------- SystemC Wriing : out1=5 sc_time=1500006 ps
---------------------------------- ようこそ。SystemCの世界へ!

---------- シミュレーションを終了します。time=1500006ps

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



systemcのインスタンス化は、verilog HDLモジュールを等価なサイクルアキュレートモデル(Cycle Accurate Model)で置き換えて全体のシミュレーション高速化や、検証を行う際に有効な手段だと思います。大規模なブロック、例えばCPUをRTLで(Verilog HDL)走らせるとシミュレーションのボトルネックになる可能性がありますが、これをISS(Instruction Set Simulator)に置き換えることが出来れば、シミュレーションの高速化が期待できます。あるいは、SystemCモデルをリファレンスモデルとして走らせ、検証に利用することも可能でしょう。


3.4 Verilog モジュールのインスタンス化と接続

 Verilogモジュールのインスタンス化の場合は、SystemCラッパモジュールが必要になります。

 下は、インスタンス化するモジュールの例です。

module counter16(input clk,input enable,  output bit [3:0] counter);
  
	always @(negedge clk) begin
		if (!enable) counter<=0;
		else counter <=counter+1;
	end
  
endmodule

上のモジュールをインスタンス化するには、下のようなSystemCの階層でラッパモジュールが必要になります。VerilogHDL側のモジュール名、ポート名をそのまま
引き継いだだけのモジュールです。ScopeTreeViewで見ると上のVerilogモジュールと下のSystemCモジュールは、同じScopeになります。

#include <systemc.h>

class counter16 :public sc_core::sc_module
{
public:
	 sc_in<bool > clk,enable;//verilog ポート方向に合わせる
         sc_out<sc_uint<4 >> counter;//verilog ポート方向に合わせる  
 
       counter16 (sc_module_name nm) :
 	sc_core::sc_module (nm,"counter16"), //Verilog HDL側のモジュール名として参照される
 	clk("clk"),enable("enable"),counter("counter"	)//Verilog HDL側のポート名に合わせる
 {
	
 }
};

このラッパーモジュールを制御するSystemCテストベンチが下の記述です。 このテストベンチは、上のcounter16モジュールをインスタンス化しています。enable 端子を制御しています。

SC_MODULE(test_bench
){
 
	counter16 dut;
 
	sc_signal<sc_uint<4 >> counter;//モニタ用
	sc_clock clock;//テストベンチクロック源
	sc_signal<bool> enable;//制御信号
 
	SC_CTOR(test_bench):
 		dut("counter16"),
		clock("clock",10,SC_NS,0.5,1,SC_NS,true	)//レースを防ぐために1ns後に発振開始 Duty 50% 10ns サイクル Pos  
{
 		dut.clk(clock);
 		dut.counter(counter);
 		dut.enable(enable);
 
		SC_THREAD(enable_control);
		SC_METHOD(disp_counter);
		sensitive << counter;
 
	}
 
	void enable_control(){
		wait(1000,SC_NS);
		cout<< "イネーブル端子をアサートしました。time="<<sc_time_stamp()<<endl;
 		enable.write(true);
 }
	void disp_counter(){//モニタ
		cout << "counter="<< counter.read() << " time="<<sc_time_stamp()<<endl;
 }
};

SC_MODULE_EXPORT(test_bench);

全体の記述を通して、トップ階層は、test_benchになります。SC_MODULE_EXPORTでsystemc側のトップ階層を指示しています。

結果です。

***** Veritak SV Engine Version 0.36 Build Jul.10.2010 *****

counter=0 time=0 sec
イネーブル端子をアサートしました。time=1 us
counter=1 time=1006 ns
counter=2 time=1016 ns
counter=3 time=1026 ns
counter=4 time=1036 ns
counter=5 time=1046 ns
counter=6 time=1056 ns
counter=7 time=1066 ns
counter=8 time=1076 ns
counter=9 time=1086 ns
counter=10 time=1096 ns
counter=11 time=1106 ns
counter=12 time=1116 ns
counter=13 time=1126 ns
counter=14 time=1136 ns
counter=15 time=1146 ns
counter=0 time=1156 ns
...

このように、Verilogのテストベンチをsystemcで記述することができます。

3.5 DPI-SC systemcとverilog HDL間の通信

function /task を使ってsystemc と通信を行います。systemverilog側で定義したexport function/task をsystemc から呼ぶ手続きになります。

3.5.1 ビルドの手順
  DPI-Cの手順に同じです。
 

  1. SVソースの作成
  2. 1に対して、veritakプロジェクトを作成し、Cヘッダ/ソースファイルの生成する
  3. 2のソースのDLL化 (exportプロジェクトのビルド)
  4. systemc ユーザプロジェクトのDLL化(下の例ではdpi_sc_test)
  5. SVソースとDLLをVeritakプロジェクトに入れてRun


  下は、ビルド後時のVisualStudioです。
  


SVソースです。systemcのスレッドから呼び出されるtaskのみ"DPI-SC" で宣言します。書法はDPI-Cに同じです。

module dpi_sc_dut;
 
 
 
	export "DPI-SC"  context task wait_task;
	import "DPI-C" function void get_data_fm_systemc(  inoutint );
	reg clock=0;
 
	always #10 clock=~clock;
 
 
 
 
	task wait_task( input int nclks);
		int data;
		$display("\t\t\t\t\t\t SV task に入りました。 nclks=%3d  時刻:%t",nclks, $realtime);
			repeat(nclks) begin
				@(posedge clock);
				get_data(data);					
			end
		#1;
		$display("\t\t\t\t\t\t SV task を終了します。 時刻:%t", $realtime);
	endtask
 
       
 
        function void get_data( inout int data);
		$display("\t\t\t\t\t\t DPI-Cを使ってSystemCクラス内データを取得します。時刻:%t",$realtime);
		get_data_fm_systemc(data);
		$display("\t\t\t\t\t\t取得したSystemCクラスメンバーデータは%3dです。時刻:%t",data,$realtime);	
	endfunction
 
         initialbegin
		$timeformat(-9,3,"ns",8);
		$display("\t\t\t\t\t\tSystemVerilogの世界へようこそ!"); 
 
               
                #1000 $finish;
	end
 
 
endmodule


SystemC ソースです。

#include <systemc.h>
#include "dpi_veritak_header.h"
 
struct hello;
static hello* hello_ptr=0;
void register_instance(hello* h ){hello_ptr=h;}
hello* get_instance (){ return hello_ptr;}
 
SC_MODULE(hello
){
	int my_data;
 
	void get_data_fm_systemc(int * data_ptr ){
  		cout<< "SystemCメンバー関数が呼び出されました。"<<endl;
		 cout << "my_data "<< my_data<<"をセットします。"<<endl;
		 *data_ptr=my_data++;
	}

	void disp_hello(){
 
		  cout << "ようこそ。SystemCの世界へ!"<<endl;
		 wait(100,SC_NS);
 
		 cout << "Systemc time="<<sc_time_stamp() <<" DPI-SCを使って"<<endl;
		 cout<< "SystemVerilogの時間消費タスクを呼び出します。"<< endl;
 		 wait_task(3);//DPI-SC export task
		 cout << "Systemc time="<<sc_time_stamp() <<" DPI-SCを使って"<<endl;
		 cout<< "SystemVerilogタスクから戻りました。"<< endl;
 
	}
 
	 SC_CTOR(hello){//コンストラクタ
  		SC_THREAD(disp_hello);//スレッド
 		 register_instance(this);//クラスオブジェクトをDPI-Cで接続する
 		 my_data=100;
 }
};
 
SC_MODULE_EXPORT(hello);
 
DPI_LINK_DECL DPI_DLLESPEC void get_data_fm_systemc(int* data ){
  	get_instance()->get_data_fm_systemc(data);
}


結果です。左側がSystemC出力 右側がVerilogHDL出力になっています。

Verilogのシミュレーションの準備が完了しました。スタートは,Goボタンを押してください。
***** Veritak SV Engine Version 0.36 Build Jul.10.2010 *****

Veritak-SystemC Simulation Kernel Initialized. Jul.10.2010. 
ようこそ。SystemCの世界へ!
                                                SystemVerilogの世界へようこそ!
Systemc time=100 ns DPI-SCを使って
SystemVerilogの時間消費タスクを呼び出します。
                                                 SV task に入りました。 nclks=  3  時刻: 100.000ns
                                                 DPI-Cを使ってSystemCクラス内データを取得します。時刻: 110.000ns
SystemCメンバー関数が呼び出されました。
my_data 100をセットします。
                                                取得したSystemCクラスメンバーデータは100です。時刻: 110.000ns
                                                 DPI-Cを使ってSystemCクラス内データを取得します。時刻: 130.000ns
SystemCメンバー関数が呼び出されました。
my_data 101をセットします。
                                                取得したSystemCクラスメンバーデータは101です。時刻: 130.000ns
                                                 DPI-Cを使ってSystemCクラス内データを取得します。時刻: 150.000ns
SystemCメンバー関数が呼び出されました。
my_data 102をセットします。
                                                取得したSystemCクラスメンバーデータは102です。時刻: 150.000ns
                                                 SV task を終了します。 時刻: 151.000ns
Systemc time=151 ns DPI-SCを使って
SystemVerilogタスクから戻りました。
Info: $finishコマンドを実行します。time=1000000ps

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