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);

      
}