• [数字芯片]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....
    ");
        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添加波形如下:
    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

  • 相关阅读:
    svn: E120106: ra_serf: The server sent a truncated HTTP response body.
    HTTP method POST is not supported by this URL解决
    TinyOS文件结构清单解析
    Crush Course 神话学笔记
    Django 入门
    Crush Course 心理学笔记
    网络相关知识汇总链接
    9.21 小程序开发培训讲座
    论文简读之LAIA
    Android studio 的那些坑
  • 原文地址:https://www.cnblogs.com/dzqiu/p/12656674.html
Copyright © 2020-2023  润新知