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();
}
相关文章
- 【HTML】通过不同的分辨率调用不同的css,两种方法:css本身判断、js调用css
- C# 通过HttpWebRequest在后台对WebService进行调用
- Java通过JNI调用C++程序
- Java通过继承外部类来建立该外部类的protected内部类的实例(转)
- jq通过对象获取其ID值,再简单ajax传到后台改值
- 数据运营经验分享:通过精细化运营驱动产品增长
- python通过socket实现多个连接并实现ssh功能详解
- 如何通过ps -ef|grep tomcat只获得你需要的查询进程,排除掉grep本身的进程信息
- 【转】通过 IHttpClientFactory 和 Polly 策略实现使用指数退避算法的 HTTP 调用重试
- go执行二进制文件的方法:通过shell脚本来调用二进制文件,直接执行go的二进制文件会存在参数传递问题
- docker容器的重启策略:通过--restart来指定
- SAP Cloud for Customer的产品主数据通过PI同步到CRM
- Atitit 图像处理 调用opencv 通过java api attilax总结
- Windows7 PSR通过程序调用并自动开始记录
- NLP之Chatgpt:基于openai框架通过调用API接口实现Chatgpt的吊炸天功能的图文教程(基于python代码实现)、案例应用之详细攻略
- 【项目实战】使用Maven插件(maven-antrun-plugin),实现通过调用ant-contrib中的if任务来进行条件判断
- m在simulink进行DS-CDMA建模,然后通过MATLAB调用simulink模型进行误码率仿真
- 【Android 逆向】Android 进程注入工具开发 ( 注入代码分析 | 获取注入的 libbridge.so 动态库中的 load 函数地址 并 通过 远程调用 执行该函数 )
- C#通过反射获取对象属性,打印所有字段属性的值
- js-template-art【四】通过helper方法注册,调用外部方法
- mysql创建 存储过程 并通过java程序调用该存储过程
- LabVIEW使用调用节点与通过引用调用节点调用VI时的差别
- zynq linux系统上通过寄存器读写直接调用HLS IP
- 【SQL开发实战技巧】系列(二十四):数仓报表场景☞通过执行计划详解”行转列”,”列转行”是如何实现的
- VC++通过SetWindowHookEx去实时拦截窗口消息,实现视频会议中桌面共享图像的实时拖动(附源码)
- PySpark 的背后原理--在Driver端,通过Py4j实现在Python中调用Java的方法.pyspark.executor 端一个Executor上同时运行多少个Task,就会有多少个对应的pyspark.worker进程。
- Mac上通过iterm 上传文件到服务器
- 明明可以通过调用API来操作数据库,那么SQL还有存在的意义吗?
- 人工智能写的十段代码,九个通过测试了
- 1003 我要通过!
- prometheus通过文本获取metrics值