1. 前言
DPI是Direct Programming Interface的缩写,它提供了SystemVerilog与其它编程语言(特别是C语言)交互的接口。它允许编程人员轻松地从SystemVerilog调用C函数,且在C函数也可以调用Systemverilog的函数。
DPI极大地方便了使用现有的C代码,可以使用import “DPI-C”声明从SystemVerilog调用C实现的函数,这样的函数称为导入任务或函数,所有导入的任务或函数都必须声明。在SystemVerilog中实现并通过export "DPI-C"导出的函数或任务可以被C调用,这些任务和函数称为导出任务或函数。其它具体的DPI语法这里不多说,本文讲述在Questasim中如何编译和使用Systemverilog的DPI流程。正确的使用流程如下:
2. 代码举例
举个简单的Questasim DPI例子来演示如何在SystemVerilog和C语言中互相对象对方的函数。
首先,我们需要创建1个C函数,将其放在一个名为"dpi_example.c"的文件中:
#include "dpi_example.h"
void c_func(int a, int b, int* c) { *c = a + b; sv_func();
}
第一行include文件将使用Questasim的vlog自动从根据export/import “DPI-C”内容生成出来的,我们只需要在vlog参数-dpiheader里指定生成文件名为dpi_example.h就好了,当然也可以是其它名字,只要能和c文件里include后的文件名匹配上就行了。第四行的sv_func()在后文中介绍,它是一个SystemVerilog函数,通过export “DPI-C”提供给C函数调用。c_func函数计算了a加b的值,并调用了sv_func函数。
接下来,在SystemVerilog中使用DPI调用C函数。将以下代码放在名为"dpi_example.sv"的文件中:
module dpi_example; import "DPI-C" context function void c_func(int a, int b, output int c);export "DPI-C" function sv_func;initial begin int a = 600; int b = 66; int c; c_func(a, b, c); $display("c_func result: The sum of a(%0d) and b(%0d) is c(%0d)", a, b, c); end function void sv_func();$display("sv_func is called by c code");endfunction
endmodule
第2和3行是DPI的关键,import "DPI-C"是将C函数导入到SystemVerilog作用范围内,export "DPI-C" 是将SystemVerilog的函数或任务导出到C的作用范围内。在第8行调用了C函数c_func去计算变量a和变量b的相加,并将结果传递给变量c,第9行将c_func的结果打印出来。这里的sv_func通过export "DPI-C"导出给之前C代码使用。
3. 运行命令
确保工作站下有Questasim和C编译器的license,使用Questasim进行仿真,在terminal中运行以下命令:
步骤一:运行vlog命令生成dpi_example.h头文件
vlog -dpiheader dpi_example.h dpi_example.sv
这个文件定义了C和Questasim之间的接口,用于导出和导入任务和函数。dpi_example.h其实不是必须的文件,但在C代码中包含dpi_example.h可以更快解决由不正确定义的接口引起的问题。创建dpi_example.h的示例命令是:
vlog会自动提取dpi_example.sv中的export/import “DPI-C”内容去生成dpi_example.h头文件,大家生成后可以打开这个文件看看下。
Questasim建议任何export/import “DPI-C”的用户DPI C代码都应该包含在dpi_example.h中,方便C编译器验证C和Questasim之间的接口。
步骤二:运行vlog命令编译C代码
vlog dpi_example.c
可以在vlog命令行中指定c/c++文件,该命令根据传入的文件类型调用正确的c/c++编译器。vlog命令将所有Systemverilog文件和c/c++文件编译到work库中。vsim命令在elaboration阶段自动加载编译好的c代码。可以使用-ccflags选项将指定的C编译器选项传递给vlog,vlog不会检查您用-ccflags指定的选项的有效性。这些选项直接传递给编译器,如果它们无效,则由C编译器生成错误消息。还可以在-f文件中指定c/c++文件和选项,它们将以与-f文件中的Systemverilog文件和选项相同的方式处理。也可以使用-ldflags选项将指定的c/ c++链接器选项传递给vsim,vsim会将指定的选项传递给链接器去解析。
步骤三:开始仿真,运行vsim命令
vsim dpi_example -c -do "run -all; quit"
步骤四:总结以上步骤和运行结果
# 执行命令:
vlib work
vlog -dpiheader dpi_example.h dpi_example.sv
vlog dpi_example.c
vsim dpi_example -c -do "run -all; quit"
# 运行结果:
run -all
sv_func is called by c code
c_func: The sum of a(600) and b(66) is c(666)
quit
4. 其它
额外话题,Questasim支持将外部已经编译好的C代码直接传递给vsim去仿真,不需要vlog再重复编译了。使用以下vsim参数可以将DPI库指定给vsim命令。
参数 | 描述 |
-sv_lib <name> | 指定要搜索和使用的库名称。不需要指定文件名扩展名。(Questasim预期的扩展名是:.dll用于Win32/Win64, .so用于所有其他平台。) |
-sv_root <name> | 为shared object指定一个新的前缀,由-sv_lib指定 |
-sv_liblist <bootstrap_file> | 指定要使用的"bootstrap file",<bootstrap_file>的格式如下: #!SV_LIBRARIES <path>/<to>/<shared>/<library> <path>/<to>/<another> … shared library上不需要任何扩展。 |
当仿真器发现imported的task或function时,它会在使用这些参数指定的shared objects集合中查找。例如你可以指定以下的DPI库:
vsim -sv_lib dpiapp1 -sv_lib dpiapp2 -sv_lib dpiappn top
另外在PLI/VPI shared object中指定DPI导入的函数和任务是错误。但是DPI导入函数和任务可以调用PLI/VPI代码,前提是使用vsim -gblso以全局可见方式标记PLI/VPI shared object。