Veritak & GPL Cver 用に VPI module を書いてみた


Veritak だったり GPL Cver だったり、 いろんな環境で Verilog HDL のシミュレーションするので、 VPI のソースも共通にできたらいいなと。

Veritak2 では VPI のサポートコンパイラは Visual C++ ということに なってるみたいですが、GCC ではだめなのかなと。

そういうことを調べた結果です。


環境


ソース

いきなりソースです (test.c)


// ※ VERITAK の場合、アライメントを 4 にしておかないと
//    引数の受け渡しなどがうまくできないみたい
#ifdef VERITAK
#pragma pack(push,4)
#endif

// ※ このへんは自分が必要なインクルードファイルを指定
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// ※ VERITAK / CVER の配布ファイルに含まれている
//    インクルードファイルが必要
#include <vpi_user.h>

// ※ さらに CVER の場合は下記ファイルも
#ifdef CVER
#include <cv_vpi_user.h>
#endif

// ※ Prototype 宣言
PLI_INT32 vpi_test(void);
extern void register_my_systfs(void);

// ※ CVER の場合は下記も
#ifdef CVER
void vpi_compat_bootstrap(void);
#endif

#ifdef VERITAK
#pragma pack(pop)
#endif

// ※ ここから VPI で呼び出す関数を自分の好きなように
PLI_INT32 vpi_test() {
	vpiHandle	href, iter, inh, outh;
	s_vpi_value	inval, outval;
	int		din, dout;
	
	// ※ 引数の受け渡し
	href = vpi_handle(vpiSysTfCall, NULL);
	iter = vpi_iterate(vpiArgument, href);
	//	引数の個数分だけ vpi_scan する
	inh = vpi_scan(iter);
	outh = vpi_scan(iter);
	
	// ※ 値の読み出し方
	inval.format = vpiIntVal;
	vpi_get_value(inh, &inval);
	din = inval.value.integer;
	
	// ※ 実際の処理、サンプルとして SQRT を求めてみた
	dout = sqrt(din);
	
	// ※ 値の書き出し方
	outval.format = vpiIntVal;
	outval.value.integer = dout;
	vpi_put_value(outh, &outval, NULL, vpiNoDelay);
	
	return 0;
}

// ※ 関数の登録方法
void register_my_systfs() {
	p_vpi_systf_data	systf_data_p;
	static s_vpi_systf_data	systf_data_list[] = {
		{vpiSysTask, 0, "$vpi_test", vpi_test, NULL, NULL, NULL},
		// ここにいくつでも並べられる
		// 0 で終わり
		{0, 0, NULL, NULL, NULL, NULL, NULL}
	};
	systf_data_p = &(systf_data_list[0]);
	while(systf_data_p->type != 0) vpi_register_systf(systf_data_p++);
}

#ifdef VERITAK

// ※ DLL の登録
__declspec(dllexport) void (*vlog_startup_routines[])() = {
      register_my_systfs,
      0
};

#endif

#ifdef CVER

// ※ CVER の場合
void (*vlog_startup_routines[])() = {
	register_my_systfs,
	0
};

void vpi_compat_bootstrap() {
	int	i;
	for(i=0; ; i++) {
		if(vlog_startup_routines[i] == NULL) break;
		vlog_startup_routines[i]();
	}
}

#endif


コンパイルしてみる : GPL Cver の場合

共有ライブラリを作ります

Windows の場合は Cygwin 上で libtest.dll を
Linux の場合は libtest.so を
MacOS X の場合は libtest.dylib を作ることになります

ちなみに Cygwin 上で GPL Cver を build する場合、普通に src dir で make -f makefile.cygwin しただけではダメで、objs dir に入って make -f makefile.dll dll exe する必要があります

で、こんな感じの Makefile になりました


# ここの名前をターゲットに合わせて変更
TARGET=test
# GPL Cver に付属のインクルードファイルの置き場所
CVERINC=/usr/local/include/cver

# Cygwin の場合
CC=gcc
CFLAGS=-I$(CVERINC) -DCVER
LDFLAGS=-shared -Wl,--out-implib=lib$(TARGET).dll.a -Wl,--export-all -Wl,--enable-auto-image-base -Wl,--export-dynamic -Wl,--output-def=lib$(TARGET).def -lcver
LIBSFX=dll

# Linux の場合
#CC=gcc
#CFLAGS=-fPIC -Wall -g -I$(CVERINC) -DCVER
#LDFLAGS=-shared --export-dynamic
#LIBSFX=so

# MacOS X の場合
#CC=gcc
#CFLAGS=-I$(CVERINC) -fPIC -Wall -dynamic -fno-common -DCVER
#LDFLAGS=-flat_namespace -bundle -undefined suppress
#LIBSFX=dylib

all: lib$(TARGET).$(LIBSFX)

$(TARGET).o: $(TARGET).c
	$(CC) $(CFLAGS) -DCVER -c $(TARGET).c

lib$(TARGET).$(LIBSFX): $(TARGET).o
	$(CC) $(TARGET).o $(LDFLAGS) -o lib$(TARGET).$(LIBSFX)


コンパイルしてみる : Veritak の場合

まずは veritak2.lib を gcc から使える形式に変換するために、mingw-utils に含まれるツールを使います

reimp veritak2.lib

これで libveritak2.a というファイルができます
これ以降の処理は MinGW でも Cygwin でも OK

Makefile はこんな感じ (Cygwin)
MinGW の場合は -mno-cygwin がいらないだけで同じ(と思う)
test.dll というファイルができます


# ここの名前をターゲットに合わせて変更
TARGET=test
# Veritak に付属のインクルードファイルの置き場所
VTKINC=.

CC=gcc
CFLAGS=-mrtd -I$(VTKINC) -mno-cygwin -mwindows -DVERITAK
DLLFLAGS=--export-all-symbols --dllname $(TARGET).dll --output-def $(TARGET).def --add-stdcall-alias --target i386-mingw32 -mno-cygwin -mwindow
LIBS=-lveritak2 -L.

all: $(TARGET).dll

$(TARGET).o: $(TARGET).c
	$(CC) $(CFLAGS) -c $(TARGET).c

$(TARGET).dll: $(TARGET).o
	dllwrap $(DLLFLAGS) $(TARGET).o $(LIBS)


シミュレーション

こんなベンチを書いてみました
入力データの sqrt() が表示されれば成功


module test();

reg signed [31:0] DIN;
wire signed [31:0] DOUT;
integer i;

initial begin
	#10 DIN = 32'sd0;
	for(i=0; i<20; i=i+1) begin
		#10 DIN = DIN + 32'sd1000;
		#10 $vpi_test(DIN, DOUT);
		#10 $display("SQRT(%d) = %d", DIN, DOUT);
	end
end

endmodule

GPL Cver の場合は

cver +loadvpi=libtest.dll:vpi_compat_bootstrap test.v

Veritak の場合はプロジェクトファイルに test.v と test.dll を登録してシミュレーション開始


戻る