微信公众号:佛系入门ZYNQ图像处理

“狂浪是一种态度”

“八卦HLS”
本次Demo目的及功能
本次Demo原理分析
HLS开发->源文件设计
HLS开发->控制协议的“冲突”
HLS开发->仿真文件设计
HLS开发->C仿真
HLS开发->RTL综合
HLS开发->C/RTL协同仿真
HLS开发->IP核打包
VIVADO使用HLS IP->路径添加
VIVADO使用HLS IP->Zynq调用
XSDK-> HLS IP的C驱动
XSDK->中断控制器及HLS IP中断建立
XSDK->动态配置PL的Led
实验现象->视频演示

“八卦HLS”

道听途说,HLS这碗鸡汤特别的硬核。主要原因有俩:

其一,它在很大程度上降低了FPGA的开发门槛(Xilinx 7系列),支持使用C/C++取代传统的Verilog/VHDL进行FPGA的开发,这可以让那些对FPGA不是很熟知的软件开发人员,快速部署和实现算法的硬件Speedup;

其二,HLS又号称FPGA版本的OpenCV,封装了许多支持RTL综合、功能相同并且拥有相同函数原型的”OpenCV”函数,这也进一步的降低了使用ZYNQ(FPGA+ARM)加速图像处理的门槛。

所以,确认过,HLS这个东东实属硬核!
HLS简直神助攻FPGA的开发:

不过,
硬核的东西通常也有它的“任性”之处,HLS主要有俩:

其一,HLS的开发不能像传统的C/C++那样“肆无忌惮”,因为HLS的RTL综合并不支持内存的动态分配、系统层面的函数调用、复杂的数据类型(标准模板、union等)。

所以,HLS开发有一定的任性要求,需要讲究一些“技巧”。举个简单例子:一个很“大”的数组,直接去分配存储空间,这对堆栈的内存要求其实是巨大的,即使算法本身没有毛病,也很有可能导致C-Sim 或Co-Sim的仿真通不过,从而无法观测波形等后果。

其二,HLS的开发,离不开程序代码的优化,同时,没有规矩也成不了方圆,一个好的优化策略,往往还要求HLS的设计,必须刻意的符合一定的Coding Style。

比如,如何将C/C++的(多维)数组映射到FPGA的(有限)RAM/ROM;又比如,(多层嵌套)for循环或者子函数之间,如何将代码从Unperfect改进到Semi-perfect的状态,进而使用展开、流水、数据流等一系列的优化策略,这背后其实要求,具备一定的FPGA基础和算法并行的思想。

综上,HLS这个东东,硬核与任性并存!
像极了。。

Demo目的及功能

万物皆可盘。

本次Demo,不妨借助Led实验,也算是“抛砖引玉”,简单的盘一下HLS开发FPGA的流程,以及如何在ARM动态配置FPGA的HLS IP,换言之, AXI-Lite通信协议的佛系使用!!

那么,最终的实验现象如何呢?
HLS IP在ARM的配置下,可以实现FPGA四个Led的流水,同时, Led流水快慢可由ARM动态控制。

Demo原理分析

FPGA外接四个Led灯,电路如下图。显而易见,该电路是共阳的特性,只要FPGA的HLS IP在ARM的控制下,依次(延时)输出低电平,就可以实现Led流水的效果以及改变流水的快慢。

那么,问题来了:直接在FPGA例化一个现成的GPIO IP,再添加AXI接口,不也可以在ARM的控制下实现流水及其快慢程度吗?所以,有必要使用HLS这个东东吗?

答案是肯定的!假设在FPGA,例化一个GPIO IP(四个位宽),需要在ARM调用延时函数才能实现这四个Led灯的流水效果,那么,这个延时函数的计时,其实是ARM完成的!

所以,如果要FPGA实现这个延时过程,可以借助HLS,使用高效的C/C++取代传统的Verilog完成IP核的开发, 再由ARM通过AXI-Lite总线接口,传送一个函数参数至FPGA,FPGA的HLS IP根据这个参数完成延时/计时。

上面佛系讲解了,本次Demo使用HLS 开发自定义IP的必要性。

那么,问题又双叒叕来了:HLS IP确实可以帮助FPGA实现计时,但ARM怎么知道FPGA的HLS IP当前流水到了哪个状态?因为不知道当前流水到哪个状态,ARM就无法传递HLS IP下一个流水状态。

如果直接在FPGA例化一个GPIO IP,ARM其实可以很轻松的完成这个流水的赋值过程(移位操作)。那使用HLS IP的话,能不能实现同样的操作呢?

答案也是肯定的!可以借助AXI-Lite总线协议的中断功能,HLS IP每流水一次,使能ARM中断响应,ARM读取HLS IP当前的输出,更新之后(移位)再写到HLS IP的输入。(动态配置)

综上,FPGA的HLS IP完成延时,流水灯延时多久,由ARM传参决定;FPGA流水到哪个状态,由FPGA使能中断告知ARM。周而复始,流水点亮FPGA的每个Led!

HLS开发->源文件设计

如下图,头文件定义了HLS IP输入的数据类型data_in,输出类型data_out,任意精度ap_uint<4>对应FPGA的4个Led。

data_in声明的in参数,可以理解为ARM响应HLS IP的中断之后,从FPGA读取到的前一个输出(e.g:1110)。

*pl_out声明为data_out类型,可以理解为in在HLS IP内部流水之后的Led输出(1110->1101)。

那么,头文件还声明led这个函数的返回是data_out类型不直接是C/C++的Void类型,有何用处呢?(稍后会讲解)

下图的源文件,定义了FPGA现流水操作的HLS IP,将in和cnt两个参数定义为AXI-Lite接口,可以由ARM初始化和动态配置;

pl_out输出的接口指定为ap_none模式,因为指针类型的参数,默认的接口模式是ap_vld,本次Demo的Led输出可以不做数据有效性的判断,故而显式指定为ap_none模式。
(可以选择默认ap_vld,只不过RTL综合结果会多消耗寄存器)

上图的这个源文件,还将函数接口的控制协议定义为s-axilite模式(#pragma HLS INTERFACE s_axilite port=return),主要目的是让HLS IP能够产生中断告知ARM,从而更新HLS IP的输入in。

所以,对于之前的问题:函数的返回是data_out类型不直接是C/C++的Void类型,其作用就在这里!如果定义为Void类型,即使ARM响应HLS IP的中断之后,也读不到数据。

综上,整个HLS设计,相当于有两个输出(同步),一个是pl_out输出,专门驱动FPGA的Led;一个是函数返回值,HLS IP通过中断告知ARM读取这个data_out类型的ps_out返回值,ps_out代表FPGA上一次流水的结果,也是即将要写入HLS IP的输入(in)。

HLS开发->控制协议的“冲突”

实际上,在这个Demo,顶层函数接口的控制协议定义为s-axilite模式,除了上面所说的主要目的(使能HLS IP中断),还有另外的一个用处:

HLS IP的顶层接口,通常有四种“块级”的控制协议(Block-Level-protocol),包括ap-ctrl-hs,ap-ctrl-chain,ap-ctrl-none和s-axilite。对于C/RTL协同仿真,HLS只支持前面的两种(默认是ap-ctrl-hs)。

然而,本次Demo的输入参数(in和cnt)已经指定为axi-lite/s-axilite接口,如果顶层函数的控制协议,不显式的指定为s-axilite,采用默认的ap-ctrl-hs或者显示的指定为ap-ctrl-chain,HLS工程的协同仿真都会报错。

对于这个报错问题,菜鸟本人的浅见是,不能在ARM通过多个接口协议同时控制同一个HLS IP的数据端口。

那么,为什么要刻意的强调是数据端口呢?

因为,在HLS IP的控制协议显式指定为s-axilite模式的情况下,时钟信号和复位信号可以采用默认的ap-ctrl-hs或者指定为ap-ctrl-chain,进而支持C/RTL协同仿真,以便观测波形结果。

HLS开发->仿真文件设计

HLS的仿真文件有三个要求和一个注意。
Testbench三个要求:测试激励、参考模型、自检。

测试激励,顾名思义,输入到HLS IP进行仿真的数据,可以通过Testbench“在线”生成,或者数组定义,或者文件流输入。

参考模型,测试激励输入到HLS IP之后的期望输出/参考,也可以理解成C/C++层面的软件算法结果,可以事先的通过数组或者文件的形式保存好,具体可视数据量而定。

自检,HLS IP实际输出与参考模型的一一比对,允许有一定的误差,因为在软件层面,C/C++支持浮点型数据的处理,而FPGA的是定点型处理。

当然,硬核的HLS也自带一些支持浮点型运算的IP核,但是这种IP的数量极少,支持的数学运算也是特定的,或者说,这些浮点型IP并不通用。

Testbench一个注意:自检完之后,如果仿真通过,必须返回0(非1);如果仿真失败,可以返回1-255任意数值(非-1)。

所以,即使HLS IP的输出与参考模型的完全一致或在误差允许的范围之内,如果自检完毕不返回0,也会导致HLS的协同仿真通不过进而报错。

如下图,是本次Demo的Testbench仿真测试文件:

对于仿真文件的设计,安利一个小小的技巧,也算是自己踩的一个坑:

假设ARM提供给FPGA的时钟是100MHz,理论上,在FPGA应该计数100_000_000个时钟才算一秒。

但这其实是在板子上的实现,对于上图的延时参数cnt,如果在仿真过程指定成100_000_000的大小,那基本上,可以先去吃个饭,或者睡个觉,再回来查看仿真结果。

因为C-sim、Co-Sim仿真的时间单位,永远跟FPGA的实际时钟无关,只跟HLS新建工程指定的那个时钟有关(默认10纳秒)。

所以,这也是本次Demo为什么设置cnt参数比较小,100_000_000实在是伤不起!

HLS开发->C仿真

HLS的C仿真,可以说是在C/C++这个层面,对HLS的整个设计进行算法上的仿真验证(Top-Function),至于进行怎么样的仿真,完全由仿真文件Testbench决定。可以“简单”的把HLS的C仿真想象成Visual Studio或者Eclipse这样的工具。

本次Demo的HLS IP进行C仿真,结果如下:

HLS开发->RTL综合

C仿真通过,下一步进行HLS设计的RTL综合,HLS会根据用户在源文件指定的一系列优化策略(directive指令),完成C/C++到Verilog/VHDL的转换过程。

怎么优化HLS的转换过程,不在本次Demo详讲范围,留至以后Demo。

其实,这种转换过程(高层次综合),或者说FPGA的C/C++/Python开发,算得上FPGA行业发展的一种趋势。

本次Demo的综合结果如下:

从上图RTL综合可以看出,对于HLS的顶层函数data_out led(data_cnt cnt,data_in in,data_out *pl_out),函数的输入参数in和cnt并没有独立的端口。

因为这两个参数挂载到AXI-Lite总线上,也就是s-axi接口,它们由ARM直接进行配置 ;同时顶层函数还多了一个interrupt输出,也就是HLS IP的中断输出,告知ARM可以读取FPGA的输出结果。

HLS开发->C/RTL协同仿真

RTL综合之后,下一步要执行Co-Sim仿真,以便观测波形。

Co-Sim仿真的设置,如下图,可以默认使用Vivado自带的仿真功能,也可以“下拉”指定第三方的仿真软件如Modelsim等;

至于,是否要观测除顶层端口以外的其他波形,可以由Dump Trace“下拉”选项自定义,自定义务必要选择all或port!!!因为默认的是none选项,RTL综合之后,它不会输出波形文件。

本次Demo协同仿真之后,输出波形如下,可以看到FPGA的输出(pl_out端口),依次流水1110->1101->1011->0111->1110->,符合预期结果。

HLS开发->打包IP核

HLS开发自定义IP的最后一步,完成IP核的打包输出。如下图,选择Verilog语言,并在Format Selection选择IP Catalog选项,这代表HLS将以压缩包的形式输出IP核,以便在VIVADO完成IP的管理及调用。

VIVADO使用HLS IP->路径添加

完成上述的IP打包过程,会在HLS工程文件下找到压缩包的“路径”,如下图所示:

接下来,可以在VIVADO的IP Catalog(集成管理器),根据该路径添加本次Demo的HLS IP,具体步骤如下图的“操作”

VIVADO使用HLS IP->Zynq调用

VIVADO添加HLS IP之后,ZYNQ进行调用,最终的块设计,如下图:

本次Demo用到了FPGA的资源,需要根据板子的电路,给Led分配引脚(约束文件),如下图:

最后,还需要经过:比特流文件产生->导出硬件->运行XSDK等几个步骤,才能在ARM开发应用程序,完成ZYNQ系统和HLS IP的配置。

上述几个步骤,包括前面在ZYNQ调动HLS IP的块设计,篇幅有限,实在不能一一细说,一一种草。

相信使用过VIVADO工具的老铁,都掌握了这些基本操作!!!

XSDK->HLS IP的C驱动

通常,如果HLS设计带有AXI接口(S-axilite),在打包IP核输出的时候,会自动生成了HLS IP的C语言驱动程序,用户可以直接调用这些封装好的函数。

如下图,本次Demo的HLS IP驱动程序,有四部分:

第一部分,XLed_LookupConfig、XLed_CfgInitialize、XLed_Initialize等函数,完成HLS IP的查找、配置、初始化;其中XLed_Initialize是对前面两个函数的再次封装。

言外之意,初始化HLS IP,要么调用前面两个函数,要么只调用最后一个。

第二部分,XLed_Start\IsDone\IsIdle\IsReady等函数,实现RTL接口的ap_ctrl_hs控制协议,完成整个HLS IP的“顶层”控制(主要是启动)。

显而易见,这部分还有一个XLed_Get_return函数。HLS IP产生中断告知ARM之后,通过这个函数读取FPGA输出。

第三部分,HLS顶层函数对应的RTL数据端口,其中,XLed_Set_cnt_V和XLed_Set_in_V对应之前源文件添加了S-axilite接口协议的cnt和in参数。

ARM可以通过这些函数传递参数到FPGA的HLS IP,动态的配置函数参数从而改变本次Demo的流水快慢。

第四部分,管理外设中断的内部使能、全局使能和清除工作。

XSDK->中断控制器及HLS IP中断建立

借助中断控制器,可以在ZYNQ内部完成中断的建立。本次Demo是FPGA到ARM的中断,也有单独的ARM内部定时等中断。

它们的建立过程都离不开中断控制器,同时,外部中断(按键、HLS IP)和内部的定时中断,对控制器的配置都是一样的,只跟应用程序使用ARM内部的哪个CPU有关(默认是CPU0),可以把中断控制器看成ARM的外设进行初始化的配置,如下图:

初始化之后,下一步就是,外设中断的真正建立。建立过程可以指定中断的优先级;指定中断的触发类型(上升沿、高电平等);指定中断服务的函数句柄,当中断响应之后,完成服务函数的调用。如下图的“0”代表优先级最高,“3”代表是上升沿触发:

本次Demo的中断服务函数,编写如下,通过中断服务函数,完成ARM读取FPGA输出的工作:

最后,建个顶层函数,对上面的中断控制器和中断建立的子函数进行封装,方便于ARM对HLS IP的动态配置,同时根据头文件“xparameters.h”找到外设的宏定义,完成子函数的初始化如下:

XSDK->动态配置PL的Led

附上本次Demo的主函数部分,注释直接明了:

顶层的头文件声明如下:

实验现象->视频演示

ARM配置HLS IP实现FPGA的Led流水,同时,流水的快慢,可由ARM动态控制。

关注公众号:佛系入门ZYNQ图像处理,查看本次Demo录制的视频!

解锁HLS开发|Demo(1):ARM动态配置FPGA的自定义IP相关推荐

  1. 【Android FFMPEG 开发】Android Studio 工程配置 FFMPEG ( 动态库打包 | 头文件与函数库拷贝 | CMake 脚本配置 )

    文章目录 I . FFMPEG 交叉编译后的函数库及头文件 II . FFMPEG 静态库打包动态库 ( 仅做参考 ) III . 创建 Android Studio 工程 IV . FFMPEG 头 ...

  2. Xilinx HLS FFT IP核运行时动态配置FFT长度

    如上图所示,xilinx hls的fft ip核不仅可以计算固定长度的FFT变换,还可以在运行时动态配置fft变换长度,但其可配置的长度仅限于小于等于最大长度的所有可能的2的幂,即若该fft ip可支 ...

  3. input自适应_【正点原子FPGA连载】第十一章基于OV5640的自适应二值化实验-领航者ZYNQ之HLS 开发指南...

    1)摘自[正点原子]领航者ZYNQ之HLS 开发指南 2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761 3)全套实验源码+手 ...

  4. 通过Dapr实现一个简单的基于.net的微服务电商系统(十七)——服务保护之动态配置与热重载...

    在上一篇文章里,我们通过注入sentinel component到apigateway实现了对下游服务的保护,不过受限于目前变更component需要人工的重新注入配置以及重启应用更新componen ...

  5. 【专栏精选】实战:动态配置图片

    本文节选自洪流学堂公众号技术专栏<大话Unity2019>,未经允许不可转载. 洪流学堂公众号回复专栏,查看更多专栏文章. 洪流学堂,让你快人几步.你好,我是郑洪智. 小新:"大 ...

  6. maven springboot 除去指定的jar包_Spring Boot打包瘦身 Docker 使用全过程 动态配置、日志记录配置...

    springBoot打包的时候代码和jar包打包在同一个jar包里面,会导致jar包非常庞大,在不能连接内网的时候调试代码,每次只改动了java代码就需要把所有的jar包一起上传,导致传输文件浪费了很 ...

  7. uni-form动态配置以及表单验证坑

    开发环境:uniapp小程序开发 问题:动态配置uni-form表单时,uni-easyinput写入默认值不显示,且表单验证,在输入数据后不自动刷新验证的问题 解决方案: 源代码 修改后 增加了属性 ...

  8. java常量配置_java项目动态配置常量和peiconfig.properties配置使用详解

    java项目动态配置常量和peiconfig.properties配置使用详解 点击:2 Java工程中配置信息一般都写在 properties文件中,下面这篇文章是对java基础配置的一点学习心得整 ...

  9. springboot整合Quartz实现动态配置定时任务

    版权声明:本文为博主原创文章,转载请注明出处. https://blog.csdn.net/liuchuanhong1/article/details/60873295 前言 在我们日常的开发中,很多 ...

最新文章

  1. 你能找到心仪的妹子吗?- 时间复杂度进阶
  2. 【图灵】iOS技能书单——入门+进阶+精通
  3. SAP WM 批量修改Storage Bin的Storage Bin Type栏位值
  4. 交换机的基本概念和配置
  5. ubuntu 16.04 连接 阿里云服务器
  6. 官宣!极客邦科技获2000万元 A 轮融资,全面发力知识服务平台
  7. ropgadgets与ret2syscall技术原理
  8. 最全的Pycharm debug技巧
  9. 微型计算机中外储存器比内储存器,计算机笔试复习题集共23页.doc
  10. Windows 环境下配置 Oracle 11gR2 Data Guard 手记
  11. 让网页图片变灰色的三种方法
  12. mysql 查询表总行数字段_MySQL的count(*)的优化,获取千万级数据表的总行数
  13. AudioSource中的PlayClipAtPoint()和PlayOneShot()
  14. 对封装继承多态的理解
  15. 全国每年的考证时间大全
  16. 贴吧怎么引流_教您如何快速搭建自己的引流池-万能的小胡
  17. 新疆高一计算机学业水平测试,2017年新疆高中学业水平考试科目
  18. C语言中task的用法,c – 在std :: packaged_task中使用成员函数
  19. 台电tbook10s删除安卓系统_Andriod系统体验 简洁流畅_台电 TBook 10_平板电脑评测-中关村在线...
  20. webstorm 2018 激活破解方法亲测可用

热门文章

  1. The requested URL /lastid was not found on this server.
  2. 海思3559实现KCF算法
  3. 深度学习——神经网络(NN)、深度神经网络(DNN)、卷积神经网络(CNN)等概念介绍
  4. 树莓派用HDMI连接电视提示无信号
  5. Java Calendar before()方法与示例
  6. 阳光养猪场的套路有哪些?
  7. python numpy花哨索引
  8. 一网打尽Redis Lua脚本并发原子组合操作
  9. 爬取80s电影列表和豆瓣评分
  10. python读取xml文件信息_python读取xml文件方法解析