[數字芯片]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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM