VPIのページ
本ページは、VPIのコーディング技法について紹介していきます。
算術ライブラリ
VPI(Verilog Procedural Inteface)の例です。
Verilogでは、$xxxで、ユーザのシステムタスクまたは、ファンクションを他の言語で作成することができます。この例では、$sin,$cos...等、CのMathライブラリをそのまま呼び出しています。
本例は、システムファンクション(戻り値がある)です。
下記3ステップで作成します。
ステップ1 スタートアップテーブルに登録ルーチンをエントリする。
作成したVPIを登録するには、VPIを登録する関数を関数テーブルに登録します。下のCソースのように、vlog_startup_routinesにVPI登録ルーチンをエントリします。今回は、
VPI登録ルーチン名 | ファンクション/タスク | 内容 |
sys_math_vpi_register | ファンクション | 算術ライブラリの登録ルーチン |
sys_ruge_kutta_register | タスク | アナログシミュレータの登録ルーチン |
の二つを一つのDLL math_vpi.dllとして作成することにします。
シミュレータがDLLロードすると、スタートアップルーチンの関数テーブルを順に実行することで定義ファンクションが登録されることになります。
# include "..\..\vpi\vpi\vpi_user.h" # include "runge_kutta.h" extern void sys_math_vpi_register(void); extern void sys_ruge_kutta_register(void); __declspec(dllexport) void (*vlog_startup_routines[])() = { sys_math_vpi_register, sys_ruge_kutta_register, 0 };
ステップ2 ファンクションの登録ルーチンを定義する
下記ソースが今回作成した、算術演算ファンクションの登録ルーチンです。
シミュレータベンダーが提供するvpi_user.hには、VPI作成に必要な構造体、定義があるはずです。s_vpi_sys_tf_data構造体に登録するファンクション情報を乗せてvpi_register_systfで登録します。ここでは、引数なしが13ファンクション、引数一個が16ファンクション、引数2個が3ファンクション登録しています。注意する点は、ファンクションの場合、sysfunctypeを設定することです。シミュレータは、コンパイル時、この情報で型(double、Int,char*、Vector,,)を決定するためです。
また、ファンクション本体だけでなく、ビット幅情報も返してやらなければなりません。関数で返す約束になっていますが、通常は、CONST値を返すことになると思います。今回は、全てdouble(REAL)型で返しますので64ビット幅になります。
extern void sys_math_vpi_register()//VPI DLLロード時CALLされ、$Functionが登録される。 { s_vpi_systf_data tf_data; /* typedef struct t_vpi_systf_data { int type;// FUNC /TASKの区別 int sysfunctype; //FUNC の場合:返す型情報 char *tfname;//名前 int (*calltf)(char*);//Funcの飛び先 int (*compiletf)(char*); int (*sizetf)();//返すビット幅を返す関数への飛び先 char *user_data;//FUNCへの引数 } s_vpi_systf_data, *p_vpi_systf_data; */ tf_data.type = vpiSysFunc;//関数、値を戻す tf_data.sysfunctype =vpiRealFunc;//REAL で返す。 tf_data.tfname = "$log10"; tf_data.user_data = "$log10"; tf_data.calltf = sys_log10_calltf; tf_data.compiletf = 0; tf_data.sizetf = sys_log10_size_tf;//func なので返す // vpi_register_systf(&tf_data); for (unsigned i=0; i< 13;i++){ tf_data.tfname =(char*) Mtable[i].name; tf_data.user_data = tf_data.tfname; vpi_register_systf(&tf_data);//登録 } for (unsigned i=0; i< 16;i++){ tf_data.tfname =(char*) Mftable[i].name; tf_data.user_data = tf_data.tfname; vpi_register_systf(&tf_data);//登録 } for (unsigned i=0; i< 3;i++){ tf_data.tfname = (char*)Mftable2[i].name; tf_data.user_data = tf_data.tfname; vpi_register_systf(&tf_data);//登録 } }
ステップ3 ファンクション本体を定義する。
下記3行は定石です。
systfref = vpi_handle(vpiSysTfCall, NULL); /* get system function that
invoked C routine */
argsiter = vpi_iterate(vpiArgument, systfref);/* get iterator (list) of
passed arguments */
argh = vpi_scan(argsiter);/* get the one argument - add loop for more args */
この後、引数が続く場合は、再びvpi_scanで取り出します。
値を返すには、呼び出しハンドルに、t_vpi_value構造体に情報を乗せてvpi_put_valueで返してやります。
これで、$xxxが実行されると、今回定義したファンクションが検索されて呼ばれることとなります。
//Mar.17.2004 //www.sugawara-systems.com //Revision 1.0 # include "..\..\vpi\vpi\vpi_user.h" #include <math.h> #include <stdlib.h> # include <string.h> #include <assert.h> struct mstruct {const char* name; double real;}; struct mfstruct {const char* name; double (*func)(double);}; struct mfstruct2 {const char* name; double (*func)(double,double);}; struct mstruct Mtable[13]={ {"$M_E", 2.71828182845904523536}, {"$M_LOG2E", 1.44269504088896340736}, {"$M_LOG10E", 0.434294481903251827651}, {"$M_LN2", 0.693147180559945309417}, {"$M_LN10", 2.30258509299404568402}, {"$M_PI", 3.14159265358979323846}, {"$M_PI_2", 1.57079632679489661923}, {"$M_PI_4", 0.785398163397448309616}, {"$M_1_PI", 0.318309886183790671538}, {"$M_2_PI", 0.636619772367581343076}, {"$M_2_SQRTPI", 1.12837916709551257390}, {"$M_SQRT2", 1.41421356237309504880}, {"$M_SQRT1_2" , 0.707106781186547524401} }; struct mfstruct Mftable[16]={ {"$acos",acos}, {"$asin",asin}, {"$atan",atan}, {"$cos", cos}, {"$cosh",cosh}, {"$exp", exp}, {"$fabs",fabs}, {"$log", log}, {"$log10",log10}, {"$sin", sin}, {"$sinh",sinh}, {"$tan", tan}, {"$tanh",tanh}, {"$ceil",ceil}, {"$floor",floor}, {"$sqrt",sqrt}, }; struct mfstruct2 Mftable2[3]={ {"$fmod",fmod}, {"$atan2",atan2}, {"$pow",pow} }; static int sys_log10_size_tf()//Aug.42003 { return 64; } //Zero 除算チェックはしていない。 static int sys_log10_calltf(char* name) { double d1, d2,d3; vpiHandle systfref, argsiter, argh; struct t_vpi_value wrkval; //No Argument for (unsigned i=0; i< 13;i++){ if (strcmp(name,Mtable[i].name)==0){ systfref = vpi_handle(vpiSysTfCall, NULL); /* get system function that invoked C routine */ wrkval.format = vpiRealVal; wrkval.value.real=Mtable[i].real; vpi_put_value(systfref, &wrkval, NULL, vpiNoDelay); return(0); } } //One Argument for (unsigned i=0; i< 16;i++){ if (strcmp(name,Mftable[i].name)==0){ systfref = vpi_handle(vpiSysTfCall, NULL); /* get system function that invoked C routine */ argsiter = vpi_iterate(vpiArgument, systfref);/* get iterator (list) of passed arguments */ argh = vpi_scan(argsiter);/* get the one argument - add loop for more args */ if(!argh){ vpi_printf("$VPI パラメータが足りません。\n"); //Mar.25.2004vpi_free_object(argsiter); return 0; } wrkval.format = vpiRealVal;/* get the argument value */ vpi_get_value(argh, &wrkval); d1 = wrkval.value.real; d2 = Mftable[i].func(d1);/* any C library function can go here */ wrkval.value.real = d2; vpi_put_value(systfref, &wrkval, NULL, vpiNoDelay);/* return value by assigning to calling systf handle */ vpi_free_object(argsiter); return(0); } } //Two arguments for (unsigned i=0; i< 3;i++){ if (strcmp(name,Mftable2[i].name)==0){ systfref = vpi_handle(vpiSysTfCall, NULL); /* get system function that invoked C routine */ argsiter = vpi_iterate(vpiArgument, systfref);/* get iterator (list) of passed arguments */ argh = vpi_scan(argsiter);/* get the first argument */ if(!argh){ vpi_printf("$VPI パラメータが足りません。\n"); //Mar.25.2004vpi_free_object(argsiter); return 0; } wrkval.format = vpiRealVal;/* get the argument value */ vpi_get_value(argh, &wrkval); d1 = wrkval.value.real; argh = vpi_scan(argsiter);/* get second argument - add loop for more args */ if(!argh){ vpi_printf("$VPI パラメータが足りません。\n"); //vpi_free_object(argsiter);Mar.25.2005 return 0; } wrkval.format = vpiRealVal;/* get the argument value */ vpi_get_value(argh, &wrkval); d2 = wrkval.value.real; d3 = Mftable2[i].func(d1,d2);/* any C library function can go here */ wrkval.value.real = d3; vpi_put_value(systfref, &wrkval, NULL, vpiNoDelay);/* return value by assigning to calling systf handle */ vpi_free_object(argsiter); return(0); }//end if }//end for assert(0); return 0; } static int sys_lint_calltf(char* name) { double d1, d0,d3,d2,d4; vpiHandle systfref, argsiter, argh; struct t_vpi_value wrkval; systfref = vpi_handle(vpiSysTfCall, NULL); /* get system function that invoked C routine */ argsiter = vpi_iterate(vpiArgument, systfref);/* get iterator (list) of passed arguments */ argh = vpi_scan(argsiter);/* get the one argument - add loop for more args */ if(!argh){ vpi_printf("$VPI パラメータが足りません。\n"); //Mar.25.2004vpi_free_object(argsiter); return 0; } wrkval.format = vpiRealVal;/* get the argument value */ vpi_get_value(argh, &wrkval); d1 = wrkval.value.real; d2 =fabs(ceil(d1)-d1); d3 =fabs(floor(d1)-d1); d4 =d2 >d3 ? floor(d1) :ceil(d1); wrkval.value.real =d4; vpi_put_value(systfref, &wrkval, NULL, vpiNoDelay);/* return value by assigning to calling systf handle */ vpi_free_object(argsiter); return(0); } extern void sys_math_vpi_register()//VPI DLLロード時CALLされ、$Functionが登録される。 { s_vpi_systf_data tf_data; /* typedef struct t_vpi_systf_data { int type;// FUNC /TASKの区別 int sysfunctype; //FUNC の場合:返す型情報 char *tfname;//名前 int (*calltf)(char*);//Funcの飛び先 int (*compiletf)(char*); int (*sizetf)();//返すビット幅を返す関数への飛び先 char *user_data;//FUNCへの引数 } s_vpi_systf_data, *p_vpi_systf_data; */ tf_data.type = vpiSysFunc;//関数、値を戻す tf_data.sysfunctype =vpiRealFunc;//REAL で返す。 tf_data.tfname = "$log10"; tf_data.user_data = "$log10"; tf_data.calltf = sys_log10_calltf; tf_data.compiletf = 0; tf_data.sizetf = sys_log10_size_tf;//func なので返す // vpi_register_systf(&tf_data); for (unsigned i=0; i< 13;i++){ tf_data.tfname =(char*) Mtable[i].name; tf_data.user_data = tf_data.tfname; vpi_register_systf(&tf_data);//登録 } for (unsigned i=0; i< 16;i++){ tf_data.tfname =(char*) Mftable[i].name; tf_data.user_data = tf_data.tfname; vpi_register_systf(&tf_data);//登録 } for (unsigned i=0; i< 3;i++){ tf_data.tfname = (char*)Mftable2[i].name; tf_data.user_data = tf_data.tfname; vpi_register_systf(&tf_data);//登録 } //rint Mar.25.2004 tf_data.type = vpiSysFunc;//関数、値を戻す tf_data.sysfunctype =vpiRealFunc;//REAL で返す。 tf_data.tfname = "$rint"; tf_data.user_data = "$rint"; tf_data.calltf = sys_lint_calltf; tf_data.compiletf = 0; tf_data.sizetf = sys_log10_size_tf;//func なので返す vpi_register_systf(&tf_data); } |