SV通過DPI調用C


Verilog與C之間進行程序交互,PLI(Programming Language Interface)經過了TF,ACC,VPI等模式。

使用PLI可以生成延時計算器,來連接和同步多個仿真器,並可以通過波形顯示等調試工具。

通過PLI方式連接一個簡單的C程序,需要編寫很多代碼,並理解多仿真階段的同步,調用段,實例指針等概念。

PLI方式給仿真帶來了額外的負擔,為了保護Verilog的數據結構,仿真器需要不斷的在Verilog和C之間復制數據。

 

SystemVerilog引入了DPI(Direct Programming Interface),能夠更簡潔的連接C,C++或者其他非Verilog的編程語言。
只要使用import聲明和使用,導入一個C子程序,就可以像調用SystemVerilog中的子程序一樣來調用它。

 

SystemVerilog和C語言之間傳遞的最基本的數據類型是int,雙狀態的32位的數據類型,

通過import聲明定義C任務和函數的原型,帶有返回值的C函數被映射成一個systemverilog的函數(function),

void類型的C函數被映射為一個systemverilog的任務(task)或者void 函數(function)

通過“DPI-C”引入的C函數,可以直接在function中調用,但是只在該DPI被聲明的空間內有效

所以可以在package中將所有的DPI函數在做封裝,打包為function。然后在需要的地方,import package。

使用關鍵字DPI-C表示,使用壓縮值(packed)的方式來保存數據類型。

 

import "DPI-C" function int factorial(input int i);

               program automatic test;     

                               initial  begin                

                                            for(int i=1;i<=10;i++)                

                                            $dispaly("%0d != %0d",i,factorial(i));  //像調用正常的function int一樣。           

                                         end                                                       //凡是可以聲明function的地方,module,program,interface,package都可以import DPI

              endprogram

 

如果C函數名和SystemVerilog中的命名沖突,可以在import導入時,賦予新的函數名。

import "DPI-C" function void test();  //將C中的test函數,import進來,變為SV中的void function

import "DPI-C" test=function void my_test();  //將C中的test函數,修改名字為my_test。 

 

通過DPI傳遞的每個變量都有兩個相匹配的定義,一個在SystemVerilog中,一個在C語言中。 在使用中必須,確認使用的是兼容的數據類型。

C輸出數據給SV,只能通過指針的方式,輸出。所以輸出數據也是在SV中建立空間,然后在C中得到指針,將值寫進去,這樣C的內存空間的

控制不會影響到SV端。

SystemVerilog                         C(輸入)                         C(輸出)

     byte                                    char                            char*    

   short int                             short int                        short int*             

      int                                      int                                int*

    longint                             long long int                    long int*

     real                                  double                            double*

    string                              const char*                       char**

    string[N]                         const char**                     char**

      bit                            svBit/unsigned char       svBit*/unsigned char   //注意在輸出時,將不需要的高位屏蔽掉

   logic/reg                     svLogic/unsigned char   svLogic*/unsigned char*  //注意在輸出時,將不需要的高位屏蔽掉

    bit[N:0]                      const svBitVecVal*              svBitVecVal*   //注意在輸出時,將不需要的高位屏蔽掉

   reg[N:0]                      const svLogicVecVal*          svLogicVecVal*   //注意在輸出時,將不需要的高位屏蔽掉

   open array[]             const svOpenArrayHandle    svOpenArrayHandle

    chandle                              const void*                    void*

以上這些定義,都可以在svdpi.h中找到相應的操作函數。該頭文件必須被包含到C函數實現端

由於C中都是使用packed方式來表示數據的,所以import到SV的數據,也是使用packed的方式,而且SV仿真器不會對變量中未使用的高位,自動屏蔽

所以在C語言中,需要保證這些變量的未使用的空間部分的值,也是初始化正確的。好讓SV端,正確接收。

雙狀態變量使用svBit(實際存儲空間是unsigned char)表示,雙狀態變量帶下標使用svBitVecVal*表示

四狀態變量使用svLogic(實際存儲空間是unsigned char)表示,四狀態變量帶下標使用svLogicVecVal*表示

  0---在C中對應0x0,1---在C中對應0x1,Z---在C中對應0x2,X---在C中對應0x3

logic[0:0] word 使用一對SVLogicVecVal來表示,一個域叫做aval--表示數值0/1,一個域叫做bval--表示x/z

 

關於DPI調入的C的函數返回值,SV LRM推薦使用small values----void,byte,shortint,int,longint,real,shortreal,chandle,string,bit,logic

不推薦使用bit[6:0]或者logic[6:0]這樣的值,因為這樣需要返回一個svBitVecVal或者svLogicVecVal的指針。

 

直接通過DPI調用C中的標准函數

import "DPI-C" function real sin(input real r);

initial $display("sin(0) = %f", sin(0.0));

 

被導入的C子程序,可以有多個參數或者沒有參數,缺省情況下,參數的方向是input(數據從SystemVerilog流向C函數)

參數的方向也可以定義為output和inout,ref類型目前不支持。

(只表示在import語句和C環境中,經過封裝之后的function,完全符合SV語法) 

import "DPI-C" function int addmul(input int a, b, output int sum);

 

對輸入的參數常常被定義為const。這樣一旦對輸入的變量進行寫操作,C編譯器就會報錯。

              int factorial (const int i) {}

 

連接C語言的例子。

#include <svdpi.h>

void counter7(svBitVecVal * o,

                     const svBitVecVal * i,

                     const svBit reset,

                     const svBit load)

{   static unsigned char count = 0;

     if(reset)   count = 0;

     else if(load)  count = * i;

     else count++;

     count &= 0x7F;

     *o = count;

}

reset和load是一個雙狀態的比特信號,以svBit類型進行傳遞。

輸入i是雙狀態7bit 變量,用svBitVecVal類型傳遞。

測試平台:

import  “DPI-C” function void counter7(output bit [6:0] out,

                                                         input bit [6:0] in,

                                                         input bit reset, load);

 

program  automatic  counter;

    bit[6:0]  out, in;

    bit  reset, load;

    initial  begin

                   $monitor("SV: out=% 3d, in =%3d, reset = %0d, load = %0d\n", out,in,reset,load);

                    reset = 0;

                    load = 0;

                    in = 126;

                    out = 42;

                    counter7(out, in, reset, load);

              end

endprogram

如果reset/load使用svLogic的類型,C程序中需要檢查X、Z的狀態

if(reset & 0x02) //檢查變量bval中的X/Z

printf("reset val include X/Z value");

如果counter使用svLogicVal類型,C程序中檢查X、Z的狀態

counter.aval = inst->cnt;

counter.bval = 0;   //aval與bval實際存在一個logicval的結構體中

 

chandle類型允許在System Verilog中存儲一個C/C++的指針,指向一段地址,來保存一些常量。

typedef struct{unsigned char cnt;} c7;

void *counter7_new() { c7* c=(c7*) malloc (sizeof(c7));

                                    c-> cnt = 0;

                                    return c;}

void counter7(c7* inst, ...)

測試平台:

import “DPI-C” function  chandle  counter7_new();

import "DPI-C" function void counter7(input chandle inst, ...);

program automatic test;

    initial begin

         chandle inst1;

         inst1 = counter7_new();

         counter7(inst1,...);

    end

endprogram

 

C與SV之間傳遞數組,可以是openarray,也可以是定寬數組。

定寬數組:

void fib(svBitVecVal data[20]) {

}

import "DPI-C" function void fib(output bit[31:0] data[20])

program automatic test;

  bit[31:0] data[20];

  initial begin

     fib(data);

  end

endprogram

 

openarray型指針,需要在C端通過svGetArrayPtr來得到來自SV的動態數據的地址

其他類型的指針,可以直接在C中通過*ptr來賦值或調用。

void fib_oa(const svOpenArrayHandle data_oa) {

}

import "DPI-C" function void fib_ca(output bit[31:0]data[])

program automatic test;

    bit[31:0] data[20],r;

    fib_ca(data);

endprogram

openarray定義的查詢方法:

int svSizeOfArray(h): 以字節計量的數組大小

int svSize(h,d): 維數d的元素總個數

int svLeft(h,d): 維數d的左邊界, svLeft(h,1)一維數組的左邊界,svleft(h,2)二維數組的左邊界

int svRight(h,d): 維數d的右邊界,=

openarray定義的定位函數:

void * svGetArrayPtr(h): 整個數組的存儲位置

void *svGetArrElemPtr(h,i1,...): 數組中的一個元素

void *svGetArrElemPtr1(h,i1): 一維數組中的一個元素

void *svGetArrElemPtr2(h,i1,i2): 二維數組中的一個元素

 

使用DPI也可以將SV的function/task export到C環境中

module  block;

  export "DPI-C" function sv_display;

  ....

endmodule

extern void sv_display();

void c_display() {

   sv_display();

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM