97.【SV】SystemVerilog DPI
📞 SystemVerilog DPI —— 验证工程师的“跨语言翻译官”
在芯片验证中,我们有时需要借助 C/C++ 的强大能力:比如运行复杂的算法模型、调用现成的数学库、或者与硬件模拟器外的系统交互。
但是,SystemVerilog 本身是硬件描述和验证语言,不能直接执行 C 代码。怎么办?SystemVerilog 的 DPI(Direct Programming Interface,直接编程接口) 就是专门解决这个问题的——它像一个“跨语言翻译官”,让 SystemVerilog 和 C/C++ 能够互相调用,实现“1+1>2”的效果。
今天,我们就来彻底搞懂 DPI 的核心概念和用法,让你轻松地在验证环境中引入 C 代码。
为什么需要 DPI?
想象一下,你要验证一个图像处理芯片,需要用到一个复杂的图像压缩算法。用 SystemVerilog 从头实现这个算法既费时又容易出错,而这个算法可能已经有成熟的 C 库了。如果能直接在 SystemVerilog 测试平台中调用 C 函数,那该多方便!DPI 正是为此而生。
DPI 的优势:
- 复用现有代码:直接使用已经调试好的 C/C++ 模型,避免重复劳动。
- 高性能:C 代码执行效率高,适合复杂的计算任务。
- 灵活性:可以调用操作系统 API,实现文件操作、网络通信等 SystemVerilog 本身难以完成的功能。
- 简单易用:相比传统的 PLI/VPI,DPI 不需要了解复杂的内部机制,只需简单的声明即可。
DPI 的两个方向
DPI 支持双向通信:
- 从 SystemVerilog 调用 C 函数(使用
import声明) - 从 C 调用 SystemVerilog 函数(使用
export声明)
这两种方向都需要在 SystemVerilog 代码中做相应的声明。
1. 从 SystemVerilog 调用 C 函数(import)
如果你想让 SystemVerilog 调用一个 C 函数,需要用 import 声明这个函数的原型。语法如下:
import "DPI-C" [function/task] 函数名 (参数列表);
"DPI-C"表示使用 C 语言的调用约定(也可以使用DPI,但通常都用DPI-C)。- 可以声明为
function(返回一个值,无时间消耗)或task(可以消耗时间,比如调用$time)。 - 参数列表中的类型可以是 SystemVerilog 的基本类型(
int、byte、string等),也可以是指针(通过chandle类型)。
简单例子:调用 C 函数打印消息
假设我们有一个 C 函数 system_init,它打印一条初始化信息。我们希望从 SystemVerilog 的测试模块中调用它。
SystemVerilog 代码(tb_top.sv):
module tb_top;
// 声明要从 C 导入的函数
import "DPI-C" function void system_init();
initial begin
$display("[%0t] 开始调用 C 函数...", $time);
system_init(); // 调用 C 函数
$display("[%0t] 调用完成", $time);
end
endmodule
C 代码(dpi_example.c):
#include <stdio.h>
// 这里的函数名必须与 SystemVerilog 中声明的名称一致
void system_init() {
printf("系统初始化中...\n");
}
如何运行(以常见仿真器为例):
- 将 C 代码编译成共享库(.so 或 .dll),例如:
gcc -shared -o libdpi.so dpi_example.c - 启动仿真器时指定链接该库,例如:
vsim -sv_lib libdpi tb_top(不同仿真器命令略有不同)
输出:
[0] 开始调用 C 函数...
系统初始化中...
[0] 调用完成
很简单吧!就像调用 SystemVerilog 自己的函数一样。
参数传递:输入、输出、返回值
C 函数可以有输入、输出参数,也可以有返回值。SystemVerilog 的参数类型和 C 的类型需要对应好。常用的对应关系:
| SystemVerilog 类型 | C 类型 |
|---|---|
byte |
char |
int |
int |
longint |
long long |
shortint |
short |
real |
double |
string |
const char*(注意内存管理) |
bit |
unsigned char 或 svBit |
logic |
svLogic |
chandle |
void*(用于传递指针) |
例子:带参数和返回值的函数
C 函数:计算两个整数的和,并返回结果。
int add(int a, int b) {
return a + b;
}
SystemVerilog 调用:
module tb;
import "DPI-C" function int add(int a, int b);
initial begin
int result;
result = add(3, 5);
$display("3 + 5 = %0d", result);
end
endmodule
输出:
3 + 5 = 8
注意:
- 参数传递默认是值传递(C 函数得到的是值的拷贝),但你可以通过指针(如
int*)实现引用传递,这时需要在 SystemVerilog 端使用inout或output关键字,并配合chandle或直接传递数组等。 - 对于字符串,C 函数接收的是
const char*,但要注意字符串的生命周期,通常由 SystemVerilog 管理。
2. 从 C 调用 SystemVerilog 函数(export)
有时候,C 代码需要反过来调用 SystemVerilog 中定义的函数。比如,C 模型在某个事件发生时,想通知验证环境。这时就需要在 SystemVerilog 中用 export 声明该函数,然后在 C 中通过特定的接口调用它。
语法
export "DPI-C" function 函数名;
或者对于 task(可能耗时):
export "DPI-C" task 任务名;
例子:C 调用 SV 函数生成随机数
假设我们有一个 C 函数,它需要调用 SystemVerilog 的随机函数来获取一个随机数。
SystemVerilog 代码(tb.sv):
module tb;
// 定义一个函数,准备被 C 调用
function int get_random(int max);
return $urandom_range(max, 0);
endfunction
// 导出这个函数,让 C 能看到
export "DPI-C" function get_random;
// 其他代码...
endmodule
C 代码(call_sv.c):
#include <stdio.h>
#include "svdpi.h" // 必须包含此头文件,它定义了 DPI 相关的类型和函数
// 声明要调用的 SV 函数(函数名需与 SV 中一致)
extern int get_random(int max);
void use_random() {
int val = get_random(100);
printf("C 调用 SV 获得随机数:%d\n", val);
}
注意:在 C 中调用 SV 函数时,需要包含 svdpi.h,该头文件由仿真器提供,定义了数据类型和调用约定。编译时需链接仿真器的 DPI 库。
运行:和之前类似,将 C 编译成共享库,仿真时加载即可。当 C 代码调用 get_random 时,它会进入 SV 的函数执行并返回结果。
为什么需要 export?
因为 C 代码需要知道 SV 函数的地址和调用方式,export 声明告诉仿真器将这个函数暴露给外部,并生成相应的封装代码。
更高级的话题
1. 开放数组(Open Arrays)
当需要传递大小可变的数组时,可以使用 SystemVerilog 的开放数组类型(如 int array[]),在 C 端对应 svOpenArrayHandle。这需要更复杂的处理,包括获取数组维度、数据指针等。
2. 内存管理
- 从 SV 传递字符串给 C:C 收到的是
const char*,只读;如果 C 需要修改或存储字符串,应该拷贝一份。 - 从 C 返回动态分配的字符串给 SV:SV 不负责释放,需要 C 自己管理内存,或者使用
svSetScope等机制。 - 使用
chandle传递 C 指针:chandle是一个不透明的指针,SV 只能存储和传递,不能解引用。这样可以安全地在 SV 中保存 C 对象句柄。
3. 多线程安全性
DPI 调用可能是并发的(例如多个线程同时调用同一个函数),需要在 C 代码中考虑线程安全。通常建议 C 函数是可重入的。
总结
- DPI 是 SystemVerilog 与 C/C++ 互操作的桥梁。
- import:让 SV 调用 C 函数。
- export:让 C 调用 SV 函数。
- 使用时需要遵循数据类型映射规则,并正确编译链接。
- DPI 简单易用,大大增强了验证环境的灵活性和复用性。
希望这篇讲解能让你对 DPI 有个清晰的认识。在实际项目中,你可以用它来集成参考模型、加速仿真、与外部系统交互,让验证工作如虎添翼!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)