我们在进行图像处理IP设计验证时,如何将图像转化为激励输入DUT呢。SystemVerilog提供了DPI-C接口,意味着可以进行调用C语言进行交互,那么这里就可以调用三方库丰富的C/C++语言进行原本SV不能进行的操作或者算法。网站上许多DPI-C的示例[1],但是基于OpenCV的示例少之又少,github中有一个基于UVM的工程[2],但是对于初学者看起来比较繁琐,这里就直接说明一下SV调用OpenCV的函数读取图片,返回RGB的简单流程。
一、接口数据类型
DPI-C传递的每个变量都有两个相应匹配的定义,一边面向SystemVerilog,一边面向C/C++。这里需要确保每个数据类型必须匹配兼容。
二、接口函数编写与调用
由于没有指针类型,这里使用longint代替指针类型,记录图片的数据空间地址。随后通过OpenCV的Mat构建函数将地址还原为Mat对象,进而使用Mat的一些方法进行读取,流程如下:
1)imread读取图像,申请内存空间保存图片,返回该空间指针pp:readframe()返回指针pp
2)读取图像时,利用指针pp重新构建Mat对象并使用其方法读取相应的RGB数据:getChannel(pp,i,j,c)返回图像i列j行c通道的灰度级
调用函数代码如下:
`include "cvFunction.svh"
module img_tb();
reg [8:0] r,g,b;
reg clk;
longint unsigned img_ptr;
string file_name = "/home/dzqiu/code/verilog_ws/sv_learn/dpi-opencv/test.png";
int width,heigth,pixel_r,pixel_g,pixel_b;
initial begin
r = 8'h0;
g = 8'h0;
b = 8'h0;
clk = 0;
end
always #5 clk =~clk;
initial begin
$display("test....
");
img_ptr=readframe(file_name);
width = getWidth();heigth= getHeight();
for(int i=0;i<heigth;i++)
for(int j=0;j<width;j++) begin
@(posedge clk);
pixel_b = getChannel(img_ptr,i,j,0);
pixel_g = getChannel(img_ptr,i,j,1);
pixel_r = getChannel(img_ptr,i,j,2);
end
$display("finish a picture.
");
end
endmodule
接口函数与C/C++的函数编写相似,不过需要注意接口数据类型的匹配,和使用extern "C"告诉C++编译器使用C风格 ,以及使用import语句进行声明,才能被SystemVerilog调用,注意参数在定义时使用C/C++的数据类型,在声明时使用SystemVerilog的数据类型。
以下为函数的定义cvFunction.c,采用C风格编写
#include "svdpi.h"
#include "stdio.h"
#include "stdlib.h"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
using namespace cv;
extern "C" unsigned long long allocFrame(int width,int height)
{
Mat frame(height,width,CV_8UC3);
void *frame_data = malloc(frame.total()*frame.elemSize());
return (unsigned long long)frame_data;
}
extern "C" unsigned long long readframe(char * filename)
{
Mat img = imread(filename,1);
if(!img.data)
printf("can not read image
");
printf("read image %d height:%d
",img.cols,img.rows);
HEIGHT = img.rows; WIDTH = img.cols;
void *frame_data =(void *)allocFrame(img.cols,img.rows);
Mat frame(img.cols,img.rows,CV_8UC3,(void *)frame_data);
memcpy(frame.data,img.data,img.total()*img.elemSize());
return (unsigned long long)frame.data;
}
extern "C" int getChannel(unsigned long long pp,int i,int j,int c)
{
Mat img(WIDTH,HEIGHT,CV_8UC3,(void*)pp);
Vec3b intensity = img.at<Vec3b>(i,j);
return intensity.val[c];
}
extern "C" int getWidth()
{
return WIDTH;
}
extern "C" int getHeight()
{
return HEIGHT;
} `
以下为函数声明cvFunction.svh,按照SystemVerilog编写
`ifndef CVFUNCTION
`define CVFUNCTION
import "DPI-C" context function longint unsigned readframe(string filename);
import "DPI-C" context function void c_fun_printf(string p_in);
import "DPI-C" context function longint allocFrame();
import "DPI-C" context function int getChannel(longint unsigned pp,int i,int j,int c);
import "DPI-C" context function int getWidth();
import "DPI-C" context function int getHeight();
`endif
三、g++编译以及ModelSim仿真
一般modelsim仿真分为三步走:1、vlib 建立目录2、vlog 编译 3、vsim 开启仿真,这里需要添加g++编译以及链接过的OBJ文件即可。
1)vlib work
2)vlog -sv -dpiheader dpiheader.h $(SV_FILE) +acc //vlog编译SV文件
3)g++ -c -fpic -m64 -I$(MODELSIM_HOME)/include $(CV_COPTS) $< //g++编译C文件,生成对应.o
4)g++ -shared -m64 $(OBJ) -o $(DPI_OBJ).so $(CV_COPTS) $(CV_LOPTS) //g++将上诉.o文件连接为静态库
5)vsim -64 -sv_lib $(DPI_OBJ) $(TOP_NAME) -do "run -all" //vsim开启仿真,并连接静态库,添加波形
Modelsim添加波形如下:
具体Makefile如下:
#source
SV_FILE = img_tb.sv
TOP_NAME = img_tb
SRC = cvFunction.c
DPI_OBJ = svdpi
#Too;l
MODELSIM_HOME = /home/dzqiu/Modelsim10.2c/modeltech
VLIB = vlib
VLOG = vlog
VSIM = vsim
CC = g++
OBJ = $(SRC:%.c=%.o)
# definitions needed for OpenCV:
CV_COPTS=`pkg-config --cflags opencv`
CV_LOPTS=`pkg-config --libs opencv`
run : vlib vlog vsim
vlib:
$(VLIB) work
vlog:
$(VLOG) -sv -dpiheader dpiheader.h $(SV_FILE) +acc
vsim: $(DPI_OBJ).so
$(VSIM) -64 -sv_lib $(DPI_OBJ) $(TOP_NAME) -do "add wave -position insertpoint sim:/img_tb/clk sim:/img_tb/pixel_r sim:/img_tb/pixel_g sim:/img_tb/pixel_b;run -all"
.c.o:
$(CC) -c -fpic -m64 -I$(MODELSIM_HOME)/include $(CV_COPTS) $<
$(DPI_OBJ).so :$(OBJ)
$(CC) -shared -m64 $(OBJ) -o $(DPI_OBJ).so $(CV_COPTS) $(CV_LOPTS)
qv:
LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:$(LIBRARY_PATH) qverilog $(SV_FILE) $(SRC)
clean:
rm -rf work dpi.so $(DPI_OBJ).so $(OBJ) transcript *.wlf dpiheader.h
可能遇到的问题
1)g++版本问题skipping incompatible,参考[3]
sudo apt-get install gcc-multilib
2)Modelsim libstdc++.so.6: version `GLIBCXX_3.4.21' not found #386,参考[4]
参考:
本文源码
[1]github上简单调用dpi-c的demo
[2]github上基于UVM的OpenCV调用
[3]回归CSDN & 更换gcc版本 & 编译报错/usr/bin/ld: skipping incompatible解决
[4]Modelsim libstdc++.so.6: version `GLIBCXX_3.4.21' not found #386