• HLS接口的简单介绍


    一、HLS简单介绍

    vivado HLS是xilinx推行的高级综合工具,可以使用C/C++以及system C来实现xilinx的可编程器件的编程,不用手写verilog语言,官方提供了两个重要的HLS开发文档:ug871-vivado-high-level-synthesis-tutorial.pdf和ug902-vivado-high-level-synthesis.pdf,详细讲解了HLS工程的建立,编写testbench,和怎么优化。简单总结:在vivado HLS软件右侧中有个Directive栏(如果没有可以在功能栏中的window将其显示出来),里边列出了所有的变量、函数、循环结构,右键点击就可以对其进行配置;简单讲解一下如何进行配置,对于循环结构体,一般选择unroll(展开循环),可以自己设定展开的因子factor;对于函数,为了提高程序的并行处理能力,可以右键选择PIPELINE;对于数组,可以设置为ARRAY_PARTITION,数组维数根据需求设置。每一个优化的方案都可以保存在一个solution中,可以创建多个solution。

    二、OpenCV和HLS视频库介绍

    vivado HLS中包含了大量的视频库函数,方便进行视频处理。HLS中的视频库函数代替了处理算法的openCV功能函数,而OpenCv函数用来访问输入输出图像。HLS视频库中许多视频的概念和OpenCV非常的相似,很多图像处理函数和OpenCV库函数一致。比如,OpenCV中用于代表图片很重要的一个类便是cv::Mat,cv::Mat对象定义如下:
    cv::Mat image(1080,1920,CV_8UC3)该行的代码声明了一个1080*1920的像素,每一个像素由3个8位无符号数表示的变量image,对应HLS视频库模板类hls::Mat<>声明如下:

    hls::Mat<2047,2047,HLS_8UC3> image(1080,1920)
    
    • 1

    这两行代码的参数形式,图像尺寸最大值、语法规则不同,生成的对象是类似的。如果图片规定的最大尺寸和图像的实际尺寸相同的花也可以用下面的代码来代替:

    hls::Mat<1080,1920,HLDD_8UC3> image();
    
    • 1

    这位博主关于HLS的解释更为详细,推荐阅读【传送门

    三、AXI4流和视频接口

    vivado HLS包含两个可综合的视频接口转化函数
    在这里插入图片描述
    视频库还提供了其他不可综合的视频接口函数,这些函数用于基于OpenCV测试平台与综合后的函数结合。在这里插入图片描述VivadoHLS 视频处理函数库使用 hls::Mat<>数据类型,这种类型用于模型化视频像素流处理,实质等同于
    hls::steam<>流的类型,而不是 OpenCV 中在外部 memory 中存储的 matrix 矩阵类型。因此,在用 Vivado HLS 实现
    OpenCV 的设计中,需要将输入和输出 HLS 可综合的视频设计接口,修改为 Video stream 接口,也就是采用 HLS 提
    供的 video 接口可综合函数,实现 AXI4 video stream 到 VivadoHLS 中 hls::Mat<>类型的转换。

    四、代码优化

    class ap_fixed:publicc ap_fixed_base<_AP_W,_AP_I,ture,_AP_Q,_AP_0,_AP_N>
    
    • 1

    这个函数怎么使用呢?它的定义可以这么理解:ap_fixed,第一个M代表数据总位宽,N代表数据部分的位宽,那么小数部分的位宽为:M-N;
    代码:

    typedef int led_t;
    typedef int cnt32_t;
    
    • 1
    • 2

    可以优化为:

    typedef ap_fixed<4,4> led_t;
    typedef ap_fixed<32,32> cnt32_t;
    
    • 1
    • 2

    这样就实现了端口数据位宽的约束。
    另外还需要在directive目录下对端口进行优化。比如某一个接口src_axi(举例子),所以directive选择INTERFACE,Destination选择source file,那么这两个有什么区别呢?source file 就是针对所有的solution采用同一个优化手段,而Directive File是对当前的solution有效,mode(optional)选择ap_ovld,即输出使能。
    在这里插入图片描述
    在这里插入图片描述

    说明一下有两个类似的接口,他们的区别(ap_ovld和ap_vld):ap_ovld这种接口和ap_vld基本是一样的,没啥区别,只是将输入的信号设为ap_ovld后,是没有效果的,只针对输出有效。但ap_vld对输入和输出都是有效的。详细接口的介绍可以看这个博主的文章【传送门
    将重点部分搬过来,方便理解,不过建议还是去阅读原文,原文讲得比较细,我这里只是提炼重点而已:

    1. ap_none:仅仅有数据,没有数据有效信号的这种接口,就是ap_none,这种ap_none在一个地方有用到,就是组合逻辑的实现时
      
      • 1

      在这里插入图片描述

    2.   ap_vld:产生一个数据有效的标志。因为,在第一点中提到的,在数据传输中,如果只单独有数据信号,
        在传输中无法判断哪些信号是有效的,必须要一个数据有效信号,而ap_vld接口就产生了这种数据的有效信号。
        再举个输入的例子,如果把输入接口in设成ap_vld,那么当数据输入时,也会有一个数据的有效信号输入,
        如果模块需要检测到4个数据输入后,再进行工作,就会不断等待4个vld的标志。
        如果在输出模块与输入模块之间,都使用ap_vld接口,可以实现点对点的通信,也即相连基本没有阻碍。
        但也会存在一个问题,就是后面一个模块是否可以接收数据,前一个模块是不知道的,如果一直发出去,
        后一个模块无法接收,那还是会出错。需要一个ready反馈信号,变成了
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    3.   ap_hs:handshake,即握手信号,后续用的非常多的一个接口类型。握手是为了解决模块间是否能通信而诞生的
      
      • 1

    在这里插入图片描述

    1.       ap_ack:这种接口相当于ap_hs的特例,即原来的vld信号给去除掉了。
            数据传输时只有data和ack两根线,当后一级模块需要接收X个数据时,
            就会发出X个脉冲的ack信号,而前一级模块收到ack为1的情况,
            就会把数据传输给后一级模块。
            这种接口适合于前一级模块的数据都是有效的情况。
            这种接口其实不是很保险,因为前一级模块的数据很难做到都有效
            ,或者说很难ack一为高,数据就准备好。
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    2. ap_fifo:和ap_hs非常的像,会形成一个fifo的通路,ap_hs中的vld和ready相当于ap_fifo中的wr_en和full(read和empty)。
      
      • 1

    在这里插入图片描述

    1. ap_ovld:这种接口和ap_vld基本是一样的,没啥区别,只是将输入的信号设为ap_ovld后,是没有效果的,只针对输出有效。但ap_vld对输入和输出都是有效的。
      
      • 1

    在这里插入图片描述

    1. ap_stable:这个接口和ap_none很像,用于表示某个接口的信号在一次工作期间都是稳定的,用在配置寄存器中非常多,比如fir模块中的length就可以做成ap_stable的形式,每次工作期间都是个常数,但这次工作和下次工作期间可能会发生变化。再举个例子:卷积运算时需要KX和KY来配置kernel的大小,3355,先配置好,单次运算的过程中,并不会发生改变。
      
      • 1
    2. c ap_memory:数组就是这种,假设数据来源于存储器的接口。

    3. ap_bus:这种接口是Xilinx自己定义的一个接口,基本上用不着。通常都是用ap_hs来代替其。

    这些是自学过程中总结的一些重点,好记忆不如烂笔头,虽然看的时候已经大致了解,但是过后肯定会遗忘,所以用博客的形式记录,方便日后回顾。

    五、代码优化指令补充

    1、情况一:

    在这里插入图片描述
    在这里插入图片描述
    指令#pragma HLS UNROLL是优化语句,它的作用:
    是针对与for循环的,将256个数展开并行计算。对于没有加UNROLL语句时,for循环中的乘法tmp=w[i][j]*I[j]是串行进行的,需要256个周期才能完成遍历计算,因为当前只用了一个乘法器来完成计算,当加入了unroll语句,将256个数展开来,一个周期就可以完成256个数据计算,但此时需要256个乘法器,从而实现了并行计算。

    上面的指令是对for循环进行展开优化,其实这样子还不能完全的优化,因为参与运算的数据W和I是存储在ram中,而ram一个周期只能读取一个数,如果是双端口的ram可以一个周期读取2个数,所以为了匹配上unroll优化语句一个周期计算完,那么读取的熟读也要跟上,不然优化结果不是很明显,所以需要用指令展开存储器。首先介绍一下array_partiton指令:中文的意思就是队列的划分,将数据划分到不同的ram里面去
    在这里插入图片描述
    在这里插入图片描述

    2、情况二:采用pipleine优化指令来展开for,即流水线的方式

    在这里插入图片描述
    II=1是按照最优的标准来优化,如果不行系统会自动往上加。

    3、情况三:array_reshape指令来优化代码

    这个指令可以节省ram的使用,用一个block ram存储数据,里面的数据可以同时取出分给不同的模块进行运算。举个例子,从block ram中取出32bit的数据,然后计算模块再从32bit的总线上读取数据,其中高16bit给a模块,低16bit数据给b模块进行处理,这样既可以提高并行度,又可以节约block ram的使用。这就是array_reshape指令的用法

    在这里插入图片描述

    1).按照complete完全展开

    这样做的缺点就是系统将总线的位宽设置得很大,大的程度主要看存储数据得大小,这将会导致查找表资源用掉很多。当存储得数据位宽不是很大的时候可以全部展开。

    在这里插入图片描述

    2)、cyclic方式展开数据

    其中参数factor的含义是将数据分为几个块。
    在这里插入图片描述
    cyclic方法分组的解释,假设原来的数据再block ram的存储方式是这样

    在这里插入图片描述
    按照cyclic方式成两块(factor=2)
    在这里插入图片描述

    3)、如果按照block方式来分块,那么得到的数据排列如下图:

    在这里插入图片描述

    4、函数接口的约束

    1)对函数整体进行约束

    在这里插入图片描述

    mode的选择
    • 1.ap_ctrl_none:没有什么协议,单纯做信号线
    • 2.s_axilite:使用axilite协议来控制IP的启动、检查IP是否在工作、中断等功能
    depth参数可以不填
    latency参数可以不填
    bundle是总线的名称,这个可以命名方便区分

    2)对函数数据接口进行约束

    在这里插入图片描述

    • 1.m_axi:相当于将IP看做主机,从机就是从ps端的ddr
    • 2.offset:一般设置为slave
    • 3.bundle:命名,用于区分

    最后的约束指令

    在这里插入图片描述

    HLS代码机构上的优化

    原来代码:

    for(){
    	for(){
    		for(){
    		
    			}
    	a=1+2;
    	}
    	b1=3+1;
    	b2=3+2;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    优化后的代码:但是有时候这样子作的效果不明显。

    for(){
    	for(){
    		for(){
    			if(条件1){
    				a=1+2;
    				}
    			if(条件2){
    				b1=3+1;
    				b2=3+2;
    				}
    			}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    	--晓凡  202293日于南宁书
    
    • 1
  • 相关阅读:
    【论文阅读】WATSON:通过聚合上下文语义从审计日志中抽象出行为(NDSS-2021)
    TPA3045-ASEMI光伏二极管TPA3045
    一对一直播系统源码——如何只需三步搭建
    java计算机毕业设计ssm企业日常事务管理系统sl5xl(附源码、数据库)
    Django-文件上传
    蓝桥杯算法 一.
    C++11标准模板(STL)- 算法(std::partial_sort)
    柠檬水找零【贪心1】
    ghidra
    节点属性、offset*获取元素尺寸、offsetLeft获取元素定位
  • 原文地址:https://blog.csdn.net/xzs520xzs/article/details/126673128