3、联调环境搭建:SV与C/C++的DPI接口
说实话,做芯片验证这么多年,我最怕的就是仿真跑得慢。一个算法模块,纯SV仿真可能要跑几个小时,甚至几天。你想想看,这谁受得了?后来我学乖了——把计算密集的部分用C/C++实现,通过DPI接口跟SV搭桥。这就是我们今天要聊的联调环境搭建。
3.1 SystemVerilog DPI 基础用法
DPI,全称是Direct Programming Interface。说白了,就是SV和C/C++之间的一个翻译官。它让两边能互相调用函数,传递数据。我刚开始接触时觉得挺玄乎,用起来才发现,其实就两个方向:
- SV调用C函数:把C代码编译成动态库,SV里用
import声明一下就能用 - C调用SV函数:SV里用
export导出函数,C代码可以直接回调
先看个最简单的例子。假设我们要在SV里调用一个C写的加法函数:
// C代码:dpi_add.c
#include "svdpi.h"
int add(int a, int b) {
return a + b;
}
// SV代码
import "DPI-C" function int add(input int a, input int b);
module test;
initial begin
int result;
result = add(3, 5);
$display("3 + 5 = %0d", result);
end
endmodule
嗯,这里要注意:import "DPI-C"后面的函数名必须跟C函数名一致。大小写敏感,别搞错了。我曾经在这上面栽过跟头,编译半天报错,最后发现是函数名拼写不一致。
3.2 数据交互的握手协议
光能调用函数还不够。实际项目中,SV和C之间要传递大量数据,比如图像帧、网络包。这时候就需要一套握手协议,确保数据不会丢,也不会乱。
我个人习惯用这种简单的valid-ready握手协议:
核心思想:发送方拉高valid信号,表示数据有效;接收方拉高ready信号,表示可以接收。当valid和ready同时为高时,数据完成一次传输。
看个具体的SV实现:
// SV侧:数据发送接口
import "DPI-C" function void send_data(
input int data,
input bit valid,
output bit ready
);
// 握手逻辑
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
valid <= 1'b0;
data_q <= '0;
end else begin
if (valid && ready) begin
// 数据被成功接收
valid <= 1'b0;
$display("Data %0d sent", data_q);
end else if (has_new_data) begin
// 准备发送新数据
data_q <= new_data;
valid <= 1'b1;
end
end
end
C侧对应的接收逻辑:
// C侧:数据接收接口
#include "svdpi.h"
int data_buffer[1024];
int buffer_idx = 0;
void send_data(int data, svBit valid, svBit* ready) {
if (valid && buffer_idx < 1024) {
data_buffer[buffer_idx++] = data;
*ready = 1;
printf("Received data: %d\n", data);
} else {
*ready = 0;
}
}
小技巧:我建议在握手协议中加入超时机制。曾经有个项目,C侧处理速度跟不上SV的发送速度,导致数据丢失。加了个超时计数器,问题就解决了。
3.3 实战中的避坑指南
做DPI接口,有几个坑是新人最容易踩的:
- 数据类型匹配:SV的
int是32位,C的int也是32位,但longint对应C的long long。别搞混了。 - 指针传递:SV里没有指针概念,传数组要用
open array。我刚开始用c数组传参,结果C侧拿到的全是乱码。 - 多线程安全:C代码里如果有全局变量,要考虑多线程访问的问题。SV的DPI调用默认是串行的,但C侧自己开线程就不好说了。
特别注意:DPI-C函数不能包含任何时序逻辑。它必须是纯组合逻辑函数。如果你需要在C里维护状态,用全局变量或者静态变量。
3.4 知识体系总览
下面这张图是我自己整理的DPI联调环境的核心逻辑。你看一眼就能明白整个流程:
这张图展示了整个联调环境的骨架。左边是SV侧,负责生成测试激励和控制握手时序;右边是C侧,负责跑算法模型。中间通过valid-ready握手协议交换数据。底部的编译命令是实际干活时用的,记下来能省不少时间。
我个人觉得,DPI联调环境搭建最关键的,不是语法本身,而是理解数据是怎么流动的。你只要把握手协议搞清楚了,剩下的就是填函数体的事。好了,这一节的内容就到这。记住,多动手试,光看是学不会的。
核心要点回顾:
- DPI-C是SV和C/C++之间的桥梁,支持双向调用
- 握手协议用valid-ready信号对,确保数据可靠传输
- 数据类型要严格匹配,指针传递用open array
- 编译时把C代码打成动态库,SV里import声明即可
可可精品资料库