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