[数字芯片]System Verilog通过DPI-C调用OpenCV读取图片

我们在进行图像处理IP设计验证时,如何将图像转化为激励输入DUT呢。SystemVerilog提供了DPI-C接口,意味着可以进行调用C语言进行交互,那么这里就可以调用三方库丰富的C/C++语言进行原本SV不能进行的操作或者算法。网站上许多DPI-C的示例[1],但是基于OpenCV的示例少之又少,github中有一个基于UVM的工程[2],但是对于初学者看起来比较繁琐,这里就直接说明一下SV调用OpenCV的函数读取图片,返回RGB的简单流程。
demo图片

一、接口数据类型

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....\n");
    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.\n");
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\n");
    printf("read image width:%d height:%d\n",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添加波形如下:
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

posted @ 2020-04-09 12:37  dzqiu  阅读(2057)  评论(0编辑  收藏  举报