自SystemVerilog3.1a之后,SystemVerilog推出了一个与第三方语言进行交互的强大功能,称之为DPI,DPI的全称就是Direct Programming Interface,是SystemVerilog与其他编程语言的一种接口,目前经常被用到的是SystemVerilog与C(C++)之间的交互,本文示例也将以C语言和SystemVerilog示例。
0 大鱼吃小鱼的DPI
在上世纪的时候,开发SuperLog的Co-Design Automation公司实现了SuperLog与C语言的交互,称之为CBlend技术。同时,很多其他EDA公司也开始开发类似的技术。2001年的时候Co-Design Automation公司向Accellera发布了SuperLog扩展综合子集ESS。2002年Synopsys收购了Co-Design Automation,Synopsys结合Co-Design开发的SuperLog与C语言交互的CBlend技术也开发了适合自己仿真器的交互的 DirectC接口(VCS DirectC),然后将DirectC和CBlend捐献给了Accellera,Accellera的SystemVerilog标准委员会把这两个捐献技术合并在一起,并定义了DPI接口,使得DPI能够与任何仿真器一起工作。又经过若干年的发展,DPI又进阶为了DPI-C(IEEE 1800-2012之后),渐渐地DPI逐步的被DPI-C替换成为了主流。后续我们说的DPI如果没有特殊说明均指DPI-C。
通过DPI可以在SystemVerilog中调用C语言中的function(C语言中没有task),当然,也可以通过DPI调用SystemVerilog中的task和function。如下例:
DPI实现了SystemVerilog与其他语言的接口,所以其一般由两部分组成,即SystemVerilog层和其他语言层,并且这两层彼此之间是相互独立的,两者的编译也是相互独立的,进行这样两层结构的出发点是设计人员对于其他语言开发的模块在SystemVerilog中使用的迫切需求(特别是C和C++)。在DPI的这种分层结构中,遵循的是一种“黑盒”规则:组件的规范和实现明确分开,实际实现对该语言编写代码的其余部分是透明的,而对于通过DPI使用其方法的语言为不透明的。例如,C语言编写的方法,对于通过DPI使用其的SystemVerilog来说是一个黑盒子但是SystemVerilog无需更改即可对其进行调用。那么在具体的使用过程中应该如何快速的使用DPI-C呢,我们下面分别从SV调用C函数和C调用SV方法进行示例说明。
1 SV中import方法
语法:
import "DPI" [from_c_name =] [pure][context] function type to_sv_name(args);
import "DPI" [from_c_name =] [context] task to_sv_ame (args);
从上述语法结构我们可以看出,import使用方式主要有三种,下面我们将示例import的三种用法。这里需要注意,pure和context仅能在import时使用,并且pure不能用于task。
1.1 context方式
导入方法时,有时需要知道被调用的环境的上下文信息,以决定调用PLI TF、ACC还是VPI方法或者其中又调用SystemVerilogexport的方法,简单说就是import的方法访问SystemVerilog中除了形参传入的数据以外的数据或者方法时,就必须要使用context。使用context导入方法的方式,因为需要记录import方法调用时上写文环境,会带来一些额外的开销导致仿真的效率变低,所以一般情况下不要使用context方式。使用import方式调用C函数的示意图如下图所示。
【示例】
【仿真结果】
1.2 pure方式
与context相比较,使用pure的函数能够提高仿真效率。使用pure的函数的结果仅依赖于其输入参数。一个pure函数将严格依据其输入计算输出,跟外部环境没有任何交互,也就是说pure函数不会访问任何全局或者静态变量,不会进行文件操作,不会跟函数体外的事物交互。如果没有使用pure函数的输出,SystemVerilog编译器会优化掉对该函数的调用,对于输入参数相同的两次调用,编译器会将第二次调用直借用第一次的输出进行替换。一般non_void类型的函数指定为pure,并且该函数没有output和inout类型。
【示例】
【仿真结果】
虽然pure可以提高仿真性能,但是并不是所有的地方都可以使用pure,就像上文所述之一,如果对文件进行操作,即此时存在函数体与函数体外进行交互时,不能在函数导入时指定pure。
【示例】
【仿真结果】
当遇到如下情况时,不要使用在import的函数前使用pure:
l 文件操作
l 对任何事物的读写,包括I/O, 环境变量,来自操作系统,程序,进程的对象, shared memory, socket等
l 访问任何永久变量,比如全局的或静态的变量,此时导入的方法就不再是pure方法了;
以上是如何从C语言程序中如何import函数或者方法到SystemVerilog中,那么SystemVerilog中的函数和任务也可以export到C语言中,被C语言程序使用。
1.3 generic方式
那些既没有明确声明为pure,也没有声明为context的缺省状态下的函数称为generic函数(Sutherland中的一篇文章称之为generie C函数,这种方式并没有在IEEE1800之中规定,只是将既不是pure也不是context那类方法称之为generic函数)。generic C函数可以作为Verilog函数或者Verilog任务导入。任务或者函数可以由输入、输出以及inout的参数。generic函数调用一个导出任务或者访问SystemVerilog数据对象的PLI函数,会导致仿真器崩溃。因此,正确的声明导入的函数为pure、context还是generic是用户的责任。generic函数导入的流程如下图所示,其实与context和pure基本雷同。
1.4 import步骤
通过上述示例,总结如何import C程序中的函数的步骤如下:
Step1 :在SystemVerilog中声明要导入的函数
import“DPI-C” function to_sv_func;
Step2 :在SystemVerilog中调用从C程序中导入的函数
Step3 :在C程序中定义要导出到SystemVerilog中的函数
这里需要注意,导入的函数跟正常的SystemVerilog函数是一样的,函数调用时会被阻塞立即执行,不会消耗仿真时间,这也是跟task的一大区别,这里也需要注意,如果企图在C程序中调用一个SystemVerilog的任务,那么这个从C程序导入到SystemVerilog中的方法只能是任务,不能是task,下文将说明其中缘由。
2 SV中export方法
语法:
export "DPI" [to_c_name =] function from_sv_name;
export "DPI" [to_c_name =] task from_sv_name;
从上述语法结构我们可以看出,不管函数还是任务在导出是,都不需要指明返回类型和参数列表。export流程如下图所示:
2.1 调用SystemVerilog中的函数
【示例】
【仿真结果】
示例中实现了在C程序中调用SystemVerilog已经定义好的function,除此之外,C程序中也可以调用SystemVerilog中已经定义好的task,如下例。
2.2 调用SystemVerilog中的任务
【示例】
【仿真结果】
示例中,因为C程序中的disp调用了导入的export_sv_func是一个task,而在SystemVerilog中我们知道任务可以调用函数和任务,但是函数不能调用任务,所以C程序中的disp在import到SystemVerilog中时,必须指定为task,并且需要指明为context(因为这task的执行还与其他task的执行有关)。
2.3 export步骤
通过上述示例,总结如何export SystemVerilog中方法(这里以function为例)的步骤如下:
Step1 :在SystemVerilog中声明要导出的函数
export “DPI-C” function export_sv_func;
Step2 :在SystemVerilog中定义要导出的函数
function void export_sv_func();
...// 函数体
endfunction
Step3 : 在C程序中通过extern声明要导入一个外部函数
extern void export_sv_func(void);
Step4 :在C程序中调用SystemVerilog中被导入到C程序中的函数
这里可能会有人有疑问,import或者export一般声明在什么地方呢?在SystemVerilog中,只要是正常SV方法可以被声明的地方就都可以通过DPI直接import和export方法,比如module、program、interface、construct、package等。
在上述的C程序的示例中,我们在头文件中都使用了一个特殊的头文件svdpi.h,那么这个svdpi.h是干什么的呢?下面简要说明下。
3 svdpi.h和svdpi_src.h
在C程序与SystemVerilog相互之间导入导出方法时,除了使用svdpi.h之外,其实有时还需要使用另一个头文件svdpi_src.h,这两个头文件的作用如下:
svdpi.h:C本身并不具有与SV进行类型交互的,所以必须要使用svdpi.h,其中定义了所有的基本数据类型、接口函数记忆一些宏定义和参数。该文件不依赖于任何一种仿真器,所有的仿真器都支持。
svdpi_src.h仅仅只定义一种数据结构实现了SystemVerilog的2值和4值压缩数组
本文通过一些简短的示例说明了如何通过DPI实现C程序和SystemVerilog之间的交互,希望对于准备学习DPI能起到抛砖引玉的作用,互相学习。
更多内容请关注下面公众号!
本文纯属学习之用,欢迎指正文中不足,封面图片若有侵权,请及时沟通!