在开发 Verilog IP 的过程中,比如图像处理或者密码学的 IP 。验证功能总是很重要的一步,也很是伤脑筋的。
这些 IP 最靠谱的验证方法往往是和软件结果进行对比。比如图像处理 IP 和 OpenCV 或者 Matlab 结果对比。密码学 IP 和事实上的标准软件 Openssl 对比。问题出在怎么对比上,下面欢迎我朋友的现身说法。
I
记得那是一次面试 ...
面试官:那你是怎么在 Modelsim 上验证你的 SM3 算法 IP 的呢?
朋友:和软件结果对比。具体怎么对比?还能怎么对比,一个一个眼睛看呗。
面试官:。。。你视力真好(我看你是吃饱了撑的 ^\_^)
II
后来我的朋友机智了一些,他使用 $display 将仿真中的结果打印出来,比如这样
integer file;
initial begin:inital_file
file = $fopen("result_frm_v.txt","w");
always@(*) begin
if(valid)
begin
$fdisplay(file,"%32h",result);
end
end
然后把软件结果也打印出来,打开 BeyondCompare 一比,成了。
III
再后来,他觉得这样太麻烦了,我们为什么不能在 Modelsim 里直接调用 C 语言呢?
好,搜索引擎走起,他发现了一个叫做 DPI 的东西,好像比 VPI ,PLI 都看起来比较方便的样子。他找到了一些文章:
- https://www.cnblogs.com/studyforever/p/5132452.html
- https://blog.csdn.net/immeatea\_aun/article/details/80569938?depth\_1-utm\_source=distribute.pc\_relevant.none-task-blog-BlogCommendFromBaidu-4&utm\_source=distribute.pc\_relevant.none-task-blog-BlogCommendFromBaidu-4
- https://blog.csdn.net/seabeam/article/details/28868345
但它们好像都语焉不详,后来他发现 Modelsim 安装时自带的手册其实写的很清楚:
1.在开始菜单找到手册↓
2.打开手册,选择 User Manul,我们就可以找到 Verilog Interface to C 的章节
我 Verilog 共有 3 种访问 C 函数的接口,我们来看第三种 System Verilog DPI,因为从作者前期调研来看,DPI 似乎使用最为简单。
而且 SV 也兼容 Verilog,Modelsim 也支持 SV,所以直接用 SV 好了(但是 ISE 不支持 SV)
我们打开左栏中的 DPI Example,手册提供了一个简单的例子
hello\_c.c:
#include "svdpi.h"
#include "dpiheader.h"
int c_task(int i, int *o)
{
printf("Hello from c_task()\n");
verilog_task(i, o); /* Call back into Verilog */
*o = i;
return(0); /* Return success (required by tasks) */
}
hello.v:
module hello_top;
int ret;
export "DPI-C" task verilog_task;
task verilog_task(input int i, output int o);
#10;
$display("Hello from verilog_task()");
endtask
import "DPI-C" context task c_task(input int i, output int o); initial
begin
c_task(1, ret); // Call the c task named 'c_task()'
end
endmodule
分别是一个 C 语言文件和 v 文件(所以啊,Verilog 也可以用咯)以及操作过程。在安装目录下还有其他例子。
但我发现例子其实是在 *(D:\modeltech64\_10.2 你的安装目录)\examples\systemverilog\dpi* 路径下。
分析一下
这里例子里演示了,Verilog 和 C 语言 export 和 import 的交互接口,我们从 Verilog 的角度来看:
import C 语言函数
使用 import ,以 context task 的形式导入了 C 语言的函数 c\_task ,并定义了这个 task (实际上的 C 语言函数),定义了 input 以及 output 端口
import "DPI-C" context task c_task(input int i, output int o);
在调用时,自然使用 verilog task 的形式:
initial
begin
c_task(1, ret); // Call the c task named 'c_task()'
end
在 C 语言中 c\_task 函数的定义,打印字符串到控制台
int c_task(int i, int *o)
{
printf("Hello from c_task()\n");
//....
return(0); /* Return success (required by tasks) */
}
除了导入为 task 外,C 语言函数还可以 import 为 Verilog 函数
import "DPI-C" function void c_print(int a);
Tiny Lab
由于时间关系,我没有跑官方的例子,我准备了一个只有 import ,并将参数从 Verilog 传至 C 的小实验。
首先,准备 C 语言和 SV 源文件,简单地实现传参打印功能
- C语言
//hellow.c
#include "stdio.h"
void c_print(int a)
{
printf("hw %d!",a);
};
int main()
{
return 0;
}
- SV
//dvi_test_demo.sv
module dvi_demo;
int a;
//void c_print(); 为声明在 hellow.c 中的函数
import "DPI-C" function void c_print(int a);
initial begin
a = 1;
c_print(a);
end
endmodule
新建一个目录,把两个文件置于该目录下,比如:D:\\pro\_sv\_dvi\_test
- 直接打开 Modelsim,而无需从 FPGA 的工具中打开(建议直接打开)
切换至工作目录,注意使用 /
cd D:/pro_sv_dvi_test
编译 c 和 sv 代码
vlib work
vlog ./dvi_test_demo.sv
vlog ./hellow.c
启动仿真,这里的 dvi\_demo 是 SV 中的顶层模块名
vsim dvi_demo
restart -f;run 1us
注意这里要 run 一下,就可以看到打印了,成功的调用了 c 语言函数,我们传入的参数正是 1
hw 1!
好了,收工之前说几句
- 如果修改了 SV,那么重新 vlog ./dvi\_test\_demo.sv 文件,然后 restart -f;run 1us 即可
- 但如果修改了 C 文件,那么建议除了 vlog ./hellow.c之外,再 vsim dvi\_demo;restart -f;run 1us 会比较好,如果出现无打印的情况,建议多试几次,我也有遇到这个问题,大家可以留言交流。
- 关于环境:win10
- 关于版本:10.2 和 10.5 都试没什么问题
另外,说下我知道可能的问题,trouble shooting 一下
- 未安装 gcc,如果在 Modelsim 控制台上 gcc -v 没有正确的输出,则需要安装 gcc。
- vsim 后出现一些错误,指向一个曾经的路径。建议删除原有的 work 库后重试