【HLS教程】HLS入门与精通

总纲

HLS和FPGA实现是怎么一回事
HLS已有库说明
HLS语法讲解与实例
HLS自定义模板
HLS实现OpenCV函数

前言

主要还是工作中用到,HLS毕竟还是小众模版库,就目前来看,连Verilog普及都没有,希望原厂能有的放矢,一蹴而就。学语言,是程序员的一把斧,需要熟练与精通。希望今天的付出值得。

HLS的出现提高了FPGA实现逻辑的复杂程度,但同时又带来一些不确定的结果,总的来说,提高了实现的效率。

内容

1-HLS和FPGA实现是怎么一回事

FPGA是一种集成电路IC,可以编写不同算法的介质。现代FPGA包含200万个逻辑单元,可以实现不同的算法。

使用HLS编译器,很重要的一点是知道FPGA使用资源、以及他们是如何在FPGA上执行的。

FPGA架构主要由几部分组成:LUT、FF、Wires、IO pads.


(待续)


2-HLS已有库说明
  • 介绍

HLS视频库是通过Vivado HLS调用C/C++库加速机器视觉、图像处理在FPGA上实现。它包括通用的数据结构、OpenCV接口、AXI4-Stream接口和视频处理函数。视频库使用OpenCV库作为参考模型,大多数视频处理功能与相应的opencv函数有类似的界面和同等的功能。

简单来说,有两个头文件可以帮助我们进行HLS 视频库调用:

#include <hls_video.h>
#include <hls_opencv.h>

当然,这两个文件中包含了视频处理的一些头文件,具体可以查看安装目录下Include,也可以直接在工程中点击对应头文件,查看源文件。



  • 视频库数据结构

下面是HLS视频库基本数据结构的模板类,是针对FPGA进行优化设计。

hls::Mat<ROWS, COLS, T>
hls::Scalar<N, T>
hls::Window<ROWS, COLS, T>
hls::LineBuffer<ROWS, COLS, T>

  • 视频库函数

1)OpenCV接口函数

OpenCV接口函数是保证重建的OpenCV可以在FPGA实现,满足一种以AXI4-Streaming流的数据处理方式。

IplImage2AXIvideo
AXIvideo2IplImage
cvMat2AXIvideo
AXIvideo2cvMat
CvMat2AXIvideo
AXIvideo2CvMat
IplImage2hlsMat
hlsMat2IplImage
cvMat2hlsMat
hlsMat2cvMat
CvMat2hlsMat
hlsMat2CvMat
CvMat2hlsWindow
hlsWindow2CvMat


2)AXI4-Stream接口函数

IO函数是将HLS::Mat格式转化为AXI4-Stream设计的。

hls::AXIvideo2Mat
hls::Mat2AXIvideo


3)视频处理函数

下面函数是根据OpenCV相同功能进行加速设计的库函数。

hls::AbsDiff
hls::AddS
hls::AddWeighted
hls::And
hls::Avg
hls::AvgSdv
hls::Cmp
hls::CmpS
hls::CornerHarris
hls::CvtColor
hls::Dilate
hls::Duplicate
hls::EqualizeHist
hls::Erode
hls::FASTX
hls::Filter2D
hls::GaussianBlur
hls::Harris
hls::HoughLines2
hls::Integral
hls::InitUndistortRectifyMap
hls::Max
hls::MaxS
hls::Mean
hls::Merge
hls::Min
hls::MinMaxLoc
hls::MinS
hls::Mul
hls::Not
hls::PaintMask
hls::Range
hls::Remap
hls::Reduce
hls::Resize
hls::Set
hls::Scale
hls::Sobel
hls::Split
hls::SubRS
hls::SubS
hls::Sum
hls::Threshold
hls::Zero



(待续)


3-HLS语法讲解与实例

关于数据类型,HLS有自己独特的定义方式,也包含C++的大部分数据类型,但是,有些类型还没有实现可综合。

HLS Scale

hls::Scale: 该标量是HLS像素点表示方法;

HLS Mat

hls::Mat<int ROWS, int COLS, int T>: 该模板类是HLS视频处理库代表一幅图像。



(待续)


4-HLS自定义模板
例一:
// 单通道转双通道
void gray2YUYV(
		IMAGE_C1 &gray,
		IMAGE_C2 &yuyv
		)
{
    int rows = gray.rows;
    int cols = gray.cols;
    PIXEL_C1 s;
    PIXEL_C2 d;
    for (int row = 0; row < rows; row++) {
        for (int col = 0; col < cols; col++) {
#pragma HLS loop_flatten off
#pragma HLS pipeline II=1
            gray >> s;
            d.val[0] = s.val[0];
            d.val[1] = 128;
            yuyv << d;
        }
    }
}

该摸板函数实现单通道灰度图像转双通道YUV图像。上述函数只实现像素点操作,不需要进行缓存,所以,实现起来相对容易。




(待续)


5-HLS实现OpenCV函数

实例一:

hls::FASTX

该函数实现FAST算法的角点检测,并生成一副Mask图像,或者角点数组。

具体代码:

(PART ONE)

template<int SRC_T,int ROWS,int COLS>
void FASTX(
        Mat<ROWS,COLS,SRC_T>    &_src,
        Mat<ROWS,COLS,HLS_8UC1> &_mask,
        HLS_TNAME(SRC_T)    _threshold,
        bool   _nomax_supression
        )
{
#pragma HLS INLINE
    int flag[16][2]={{3,0},{4,0},{5,1},{6,2},{6,3},{6,4},{5,5},{4,6},
        {3,6},{2,6},{1,5},{0,4},{0,3},{0,2},{1,1},{2,0}};
    FAST_t_opr<16,7>(_src,_mask,_threshold,_nomax_supression,flag);
}
(PART TWO)

//generate array 
template<int PSize,int KERNEL_SIZE,typename T, int N, int SRC_T,int ROWS,int COLS>
void FAST_t_opr(
        Mat<ROWS,COLS,SRC_T>    &_src,
        Point_<T>                    (&_keypoints)[N],
        HLS_TNAME(SRC_T)                    _threshold,
        bool                    _nonmax_supression,
        int                     (&flag)[PSize][2]
        )
{
    typedef typename pixel_op_type<HLS_TNAME(SRC_T)>::T INPUT_T;
    LineBuffer<KERNEL_SIZE-1,COLS,INPUT_T>    k_buf;
    LineBuffer<2,COLS+KERNEL_SIZE,ap_int<16> >         core_buf;
    Window<3,3,ap_int<16> >                            core_win;
    Window<KERNEL_SIZE,KERNEL_SIZE,INPUT_T>       win;
    Scalar<HLS_MAT_CN(SRC_T), HLS_TNAME(SRC_T)>             s;
    int rows= _src.rows;
    int cols= _src.cols;
    assert(rows <= ROWS);
    assert(cols <= COLS);
    int kernel_half=KERNEL_SIZE/2;
    ap_uint<2> flag_val[PSize+PSize/2+1];
    int  flag_d[PSize+PSize/2+1];
#pragma HLS ARRAY_PARTITION variable=flag_val dim=0
#pragma HLS ARRAY_PARTITION variable=flag_d dim=0
    int index=0;
    int offset=KERNEL_SIZE/2;

    if(_nonmax_supression)
    {
        offset=offset+1;
    }
 loop_height: for(HLS_SIZE_T i=0;i<rows+offset;i++) {
    loop_width: for(HLS_SIZE_T j=0;j<cols+offset;j++) {
#pragma HLS LOOP_FLATTEN off
#pragma HLS PIPELINE II=1
            if(i<rows&&j<cols) {
                for(int r= 0;r<KERNEL_SIZE;r++) {
                    for(int c=0;c<KERNEL_SIZE-1;c++) {
                        win.val[r][c]=win.val[r][c+1];//column left shift
                    }
                }
                win.val[0][KERNEL_SIZE-1]=k_buf.val[0][j];
                for(int buf_row= 1;buf_row< KERNEL_SIZE-1;buf_row++) {
                    win.val[buf_row][KERNEL_SIZE-1]=k_buf.val[buf_row][j];
                    k_buf.val[buf_row-1][j]=k_buf.val[buf_row][j];
                }
                //-------
                _src>>s;
                win.val[KERNEL_SIZE-1][KERNEL_SIZE-1]=s.val[0];
                k_buf.val[KERNEL_SIZE-2][j]=s.val[0];
            }
            //------core
            for(int r= 0;r<3;r++)
            {
                for(int c=0;c<3-1;c++)
                {
                    core_win.val[r][c]=core_win.val[r][c+1];//column left shift
                }
            }
            core_win.val[0][3-1]=core_buf.val[0][j];
            for(int buf_row= 1;buf_row< 3-1;buf_row++)
            {
                core_win.val[buf_row][3-1]=core_buf.val[buf_row][j];
                core_buf.val[buf_row-1][j]=core_buf.val[buf_row][j];
            }
            int core=0;
            //output
            //if(i>=KERNEL_SIZE-1&&j>=KERNEL_SIZE-1)
            if(i>=KERNEL_SIZE-1 && i<rows && j>=KERNEL_SIZE-1 & j<cols)
            {
                //process
                bool iscorner=fast_judge<PSize>(win,(INPUT_T)_threshold,flag_val,flag_d,flag,core,_nonmax_supression);
                if(iscorner&&!_nonmax_supression)
                {
                    if(index<N)
                    {
                    _keypoints[index].x=j-offset;
                    _keypoints[index].y=i-offset;
                    index++;
                    }
                }
            }
            if(i>=rows||j>=cols)
            {
                core=0;
            }
            if(_nonmax_supression)
            {
                core_win.val[3-1][3-1]=core;
                core_buf.val[3-2][j]=core;
                if(i>=KERNEL_SIZE&&j>=KERNEL_SIZE&&core_win.val[1][1]!=0)
                {
                    bool iscorner=fast_nonmax(core_win);
                    if(iscorner)
                    {
                    if(index<N)
                    {
                        _keypoints[index].x=j-offset;
                        _keypoints[index].y=i-offset;
                        index++;
                    }
                    }
                }
            }

        }
    }
}



(待续)


注意该函数的使用:

hls::Duplicate(img_0, img_1, img_1_); 该函数可以避免 Hanging Up和read empty.



后续

学习一门语言,编成一系列博客,这个过程可以看出一个工程师的思维逻辑。所以,每一次这样去做的时候,总是不断修正自己的逻辑,达到一种与时俱进的状态。



  • 16
    点赞
  • 127
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
视频课程源:http://xilinx.eetop.cn/category-83 目录 1 从软件工程师的角度来看 FPGA 架构 3 2 Vivado HLS的工作机制 5 3-4 HLS设计流程基本概念 9 5 任意精度数据类型 15 5.1 C语言支持的数据类型 15 5.2 sizeof()函数使用 16 5.3 设置Visual Studio支持任意精度数据类型 17 6 数组类型转换 17 6.1 变量的定义和初始化 17 6.2 定点数据类型 18 6.3 浮点数据类型的定义和初始化 19 6.4 隐式数据类型转换 19 6.5 显示数据类型转换 19 7 Vivado HLS中的复合数据类型 20 7.1 结构体 20 7.2 枚举类型 22 8 Vivado HLS中的C++基本运算 23 9 测试平台的基本架构 25 9.1 Test Bench 25 9.2 C Test Bench 26 10 测试激励 28 11 测试输出检测与格式控制 28 11.1 Scoreboard 28 11.2 输出格式控制 30 12 接口综合基本介绍 33 12.1 接口综合概述 33 12.2 block-level interface protocol和port-level interface protocol 34 13 接口综合之数组 35 14 接口综合案例演示 37 14.1 添加寄存器 37 14.2 添加时钟使能信号 38 14.3 指令优化 38 15 for循环优化-基本性能指标 40 15.1 基本衡量指标 40 15.2 for循环pipeline 41 15.3 for循环UNROLL展开 41 15.4 for循环变量i 42 16 for循环优化-循环合并 42 17 for循环优化-数据流 46 18 for循环优化-嵌套的for循环优化 54 18.1 循环嵌套类型 54 18.2 Perfect loop nest示例 55 18.3 Imperfect loop nest示例 56 19 for循环优化-其他优化方法 59 19.1 for循环的并行性 59 19.2 for循环pipeline时的rewind选项 61 19.3 for循环的循环边界是变量时处理方法 64 20 数组优化-数组分割 67 20.1 数组接口 67 20.2 数组分割 67 21 数组优化-数组映射和重组 69 21.1 数组的映射 69 21.2 数组的重组 72 21.3 综合对比 72 22 数组优化-其他优化方法 72 22.1 定义ROM 72 22.2 数组的初始化 74 23 函数层面优化 75 23.1 代码风格 75 23.2 Inline 75 23.3 Allocation 75 23.3 Dataflow 75 24 总结分析 77 24.1 改善吞吐率(Throughput) 77 24.2 改善时延(Latency) 78 24.3 改善资源(Area) 79

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值