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
2
3
4
5
6
7
8
9
10
11
12
13
14
import "DPI-C" pure function real cos(input real in_data);

real fs = 1000000;
real fi = 1000;
reg [10:0] wave_data;

initial begin
for(int i = 0; i <= 1000; i++)begin
@(posedge clk)begin
wave_data = cos(fi/fs*2*3.1415926*i) * 512;
$display("the sim result is %f", wave_data);
end
end
end

详细分析一下:

  • 首先是function类型为pure,实现的仅仅是计算cos的值,函数的结果必须仅仅依赖于通过形参传递进来的数值,注意pure只能是用于function

  • 对于real类型,主要是在c语言中cos返回的类型为double,因此通过查表对应位real;cos的input也是double类型的,对应过来也是real