uvm中的dpi接口
DPI接口介绍
info
为了方便与C、C++等语言的交互,uvm中引入了DPI接口,自定义的函数需要写函数主体,然后再import后声明和使用,验证环境中便可以像调用system verilog中的函数一样使用这些函数。而且,对于C语言中内置的函数,不需要写函数主体,直接声明使用。同理c语言也可以调用sv里面的函数或者task,需要export后申明使用
DPI定义
- 直接编程接口(Direct Programming Interface, DPI)是用于SystemVerilog与其他外部语言的接口
- 在SV与外部语言之间除了DPI作为连接双方的接口,再没有其他任何交互的部分
- SV编译器既不需要编译外部语言,外部语言也不需要编译SV
- 例如SV与C之间发生交互,那么仿真器需要编译SV,gcc编译器需要编译C,而在仿真时,SV和C由不同容易执行各自的代码
- 之所以需要DPI接口,是因为在实际的项目中,在验证环境或者设计中,有的时候一些组件是由于其他语言实现的,例如C/C++等,那么为了让仿真器与这些其他语言构成的模型实现交互,引入了DPI接口
- 目前DPI接口只支持C/C++,暂时还不支持与其他语言的接口
任务和函数
- 之所以被成为DPI,是因为在SV与C之间如果想达成交互,必须依赖于一些从SV导出而导入到C的task/function,或者一些从C导出而导入sv的function
- 对于SV而言,可以将task或者function通过export的方式导出,成为C一侧的头文件,从而供C一侧使用
- 对于C而言,可以将function通过export的方式导出,再从SV一侧通过import的方式导入,以供SV一侧使用
- 无论是上面哪一种方式,从SV到C导出,或者从C到SV导出,均需要再导出源处声明这些task或者function
attention
正确的声明导入的函数为pure还是context是用户的责任。缺省情况下,DPI函数假定是generic函数。调用一个不正确声明成pure的C函数可能返回不正确或者不一致的结果,导致不可预测的运行时错误,甚至于让仿真崩溃。同样,如果一个C函数访问Verilog PLI库或者其他API库,却没有声明为context函数,会导致不可预见的仿真结果甚至仿真崩溃。三者的比较说明如下:
| 申明类型 | 说明 |
|---|---|
| pure | 作为pure函数,函数的结果必须仅仅依赖于通过形参传递进来的数值。Pure函数的优点在于仿真器可以执行优化以改进仿真性能。Pure函数不能使用全局或者静态变量,不能执行文件I/O操作,不能访问操作系统环境变量,不能调用来自Verilog PLI 库的函数。只有没有输出或者inout的非void函数(必须有return值)可以被指定成pure。Pure函数不能作为Verilog任务导入。example:import "DPI" pure function real sine(real in) |
| context | context C函数明确函数声明所在工作域的Verilog的层次。可以是void函数,可以有输出和inout参数,可以从C库调用函数(用于文件I/O等),可以调用PLI库中的许多函数,这使得被导入的C函数能够调用来自PLI或者VPI库的函数,从而DPI函数可以充分利用PLI的优势特性,比如写仿真器的log文件以及Verilog源代码打开的文件。import "DPI" context task delay_ms(input int n) |
| generic (缺省pure/contect) | 那些既没有明确声明为pure,也没有声明为context的函数称为generic函数(SystemVerilog标准没有给除了pure或context之外的函数特定的称呼)。generic C函数可以作为Verilog函数或者Verilog任务导入。任务或者函数可以由输入、输出以及inout的参数。函数可以有一个返回值,或者声明为void。generic C函数不允许调用Verilog PLI函数,不能访问除了参数以外的任何数据,只能修改这些参数。 |
数据类型
attention
在sv和c之间需要进行数据类型的转换
- 在SV与C之间互相导出或导入的时候,只能使用SV和C的语言自身定义数据类型,无法使用另外一侧语言自定义的类型
- 在从SV导出到C时,对于一些task/function的参数或者返回类型,如果是四值逻辑变量、结构体或者数组,需要额外注意导出到C以后的函数声明
数据类型互转关系如下面这张表格所示:
| System Verilog | C (input) | C (output) |
|---|---|---|
| byte | char | char* |
| shortint | short int | short int* |
| int | int | int* |
| longint | long long int | long int* |
| shortreal | float | float* |
| real | double | double* |
| string | const char* | char** |
| string [N] | const char** | char** |
| bit | svBit or unsigned char | svBit* or unsigned char* |
| logic,reg | svLogic or unsigned char | svLogic* or unsigned char* |
| bit[N:0] | const svBitVecVal* | svBitVecVal* |
| reg[N:0], logic[N:0] | const svLogicVecVal* | svLogicVecVal* |
| unsized array[] | const svOpenArrayHandle | svOpenArrayHandle |
| chandle | const void* | void* |
简单例子
下面这个例子是在sv中通过dpi接口调用了cos函数
1 | import "DPI-C" pure function real cos(input real in_data); |
详细分析一下:
首先是function类型为pure,实现的仅仅是计算cos的值,函数的结果必须仅仅依赖于通过形参传递进来的数值,注意pure只能是用于function
对于real类型,主要是在c语言中cos返回的类型为double,因此通过查表对应位real;cos的input也是double类型的,对应过来也是real
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 zhzhuang的博客!
