我們在進行圖像處理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....\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添加波形如下:
具體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