vivado_hls demo 实现LZ77压缩算法

来自 杰克部落

Zynq 高层次综合HLS 项目开发经历(1) 概要

今年寒假我有幸参加了Xilinx的Winter Camp,在Camp上,讲者对HLS整个开发流程做了简单的介绍。为了巩固同学们的学习,主办方特意要求我们在冬令营结束之后自己用HLS做一个项目。本着拿一个证书和提高一下自己知识水平的想法,我也自己给自己定一个选题参加了这个项目。

原本是想要开发一个MD5的硬件实现的,后来在Github上面搜了一下,发现已经有人开发过HLS的MD5算法了。不得已只能放弃。后来经Foobar院猴哥推荐LZ77压缩算法,我发现也比较合适。于是就决定在硬件上实现一个LZ77压缩算法。值得注意的是,LZ77压缩算法只是一个大概的实现模板,具体怎么实现可以根据开发者的想法任意指定规范。所以这个压缩算法中的所有规范都是我自己指定的。因此和其他的压缩软件是不通用的。

现在硬软件设计基本上都已经做得差不多了,这一路走来坑真的不少。所以我决定写一个系列来记录一下自己遇到的坑,也许会对后来的同学有帮助。如果我的文章或者代码中有什么错误,也欢迎在博客里评论、Github上开issue留言给我。

首先,大概说一下高层次综合HLS是个什么东西。通俗易懂的讲,HLS就是可以把C/C++语言转换为Verilog/VHDL/System C的一个处理过程/软件。这样对于软件开发者来说,能够比较轻松简单的开发硬件设计。当然,其中也有很多与全软件开发不完全相同的部分。这些都需要我们另行学习。
然后,简介Zynq的两大部分:PS (Processing System) 指处理系统,它是在FPGA上提前实现好的硬核ARM处理器。PL (Programming Logic) 指FPGA可编程逻辑部分,它就是真正可操纵的硬件部分。PS和PL两部分可以通过预先定义好的总线接口相互连接。

我的开发流程是这样的:先在x86体系结构的软件上实现LZ77压缩算法,经过测试通过之后,转而开始开发硬件部分。首先使用Vivado HLS软件,移植软件C代码到硬件C代码,使用Directive规定接口和优化,生成IP核;打开Vivado,使用其中的IP Integrator,将Zynq处理系统 (Zynq Processing System) 的IP核以及其他各配套IP核与HLS开发的IP核连接在一起。综合实现生成比特流,导出硬件描述文件和比特流(到此为止的代码运行在PL端);切换到Vivado SDK中进行开发,使用硬件描述文件创建Standalone软件项目,编写在PS端运行的程序以及PL与PS的交互。到这里整体的开发流程就算完毕了。

在后面的文章里,我会依次的认真介绍一下各个部分的开发流程。

LZ77硬件实现项目:https://github.com/bjrjk/HLS-LZ77
LZ77软件实现项目:https://github.com/bjrjk/LZ77-Demo
Xilinx HLS设计流程课程:https://github.com/xupgit/High-Level-Synthesis-Flow-on-Zynq-using-Vivado-HLS

本条目发布于2月 12, 2021。属于硬件设计分类,被贴了 Zynq 标签。

Zynq 高层次综合HLS 项目开发经历(2) LZ77 x86软件实现

LZ77的x86软件实现repo地址可参见[1]。

要想实现软件,肯定需要把LZ77的算法读懂。慢慢啃就是了。我查询的参考资料可参见[2]。我主要参考了其中的“短语字典的维护”、“压缩和解压缩数据”、“LZ77的接口定义”三部分。而实现与定义则完全由自己制定。

我的LZ77算法要达到的目标是压缩二进制数据,对于一个Byte,有256种可能。对于“短语标记”,必须加上相应的定界符作为标志。因此对于定界符在普通数据中出现的情况,必须进行转义(Escape)。我选择的转义符和定界符(Escape Char)是反斜杠(\)[3]。即:[4]

对于符号标记:如果非转义符,原样输出;如果为转义符,输出两个转义符。
对于短语标记:边界输出一个转义符以标记其为短语。

此时,短语标记的结构伪代码如下:

struct phrase{
   
int8 escape_start; //反斜杠
int32 slide_window_offset;
int32 origin_string_length;
int8 buffer_first_char;
int8 escape_end; //反斜杠
};

但是,在后面调试时发现了短语标记的结构定义存在问题。对于x86体系结构,数字是小端法存储的。当slide_window_offset的最低字节是0x5c,也即反斜杠时。就出现了两个转义符相连的情况。解压程序就会将短语标记的开始识别为符号标记,这是严重错误的。为了避免这个问题,加入一个split_char(\0即字符串结束符,为0x00[5]),放在escape_start后面和escape_end前面。这样escape_start后面绝对不会再跟一个escape,也就避免了上述问题的发生。此时,短语标记的结构伪代码如下:

struct phrase{
   
int8 escape_start; //反斜杠
int8 split_start; //0x00
int32 slide_window_offset;
int32 origin_string_length;
int8 buffer_first_char;
int8 split_end; //0x00
int8 escape_end; //反斜杠
};

再一个问题,当待压缩文件末尾恰好可以以一个短语标记代替时,前向缓冲区中的第一个符号(即buffer_first_char)为空,需要进行特殊标记。我选择两个右方括号0x5d进行代替[6]。并在解压缩时进行特判[7]。

实际上,对于getLongestMatchingPhrase函数来说,可以使用KMP算法进行优化。当前的暴力做法复杂度O(N^2),而KMP做法可以做到O(N+M)。[8]

参考:
[1]: LZ77-Demo,LZ77算法的x86软件实现: https://github.com/bjrjk/LZ77-Demo
[2]: 数据压缩算法—LZ77算法 的分析与实现: https://www.cnblogs.com/idreamo/p/9249367.html
[3]: https://github.com/bjrjk/LZ77-Demo/blob/5b6e718cd45cbe337a0eac04eb685cd7e7ad4549/LZ77-Demo/lz77.cpp#L9
[4]: https://github.com/bjrjk/LZ77-Demo/blob/5b6e718cd45cbe337a0eac04eb685cd7e7ad4549/LZ77-Demo/lz77.cpp#L11
[5]: https://github.com/bjrjk/LZ77-Demo/blob/5b6e718cd45cbe337a0eac04eb685cd7e7ad4549/LZ77-Demo/lz77.cpp#L10
[6]: https://github.com/bjrjk/LZ77-Demo/blob/5b6e718cd45cbe337a0eac04eb685cd7e7ad4549/LZ77-Demo/lz77.cpp#L97
[7]: https://github.com/bjrjk/LZ77-Demo/blob/5b6e718cd45cbe337a0eac04eb685cd7e7ad4549/LZ77-Demo/lz77.cpp#L114
[8]: https://github.com/bjrjk/LZ77-Demo/blob/5b6e718cd45cbe337a0eac04eb685cd7e7ad4549/LZ77-Demo/lz77.cpp#L48

原文–Zynq 高层次综合HLS 项目开发经历(3) 代码移植及Vivado HLS的使用

首先简介一下HLS的整套开发流程。详细流程可参考[1][2]。
1、设计C/C++代码以及C/C++的TestBench
2、C Simulation,验证软件代码是否正确
3、C Synthesis,将C/C++化为Verilog/VHDL
4、C/RTL Co-Simulation,将C的TestBench与生成的RTL一起进行Simulation
5、Export RTL,导出IP核
HLS的使用本身不是一个难点,我猜Vivado HLS本身就是一个Eclipse搭起来的IDE,所以只要对Eclipse上手就行。难点在于代码移植。

LZ77的软件算法在实现的时候,是用std::vector实现的。std::vector用了动态内存分配,是HLS综合不了的。所以数组必须要用固定长度的数组。而对于FPGA来说,片上SRAM又是有限的,不可能把文件的所有数据都存进来。又恰好因为LZ77本身就是一个流式算法。因此我决定在FPGA上开一个缓冲区,PS与PL的数据交换采用流(hls::stream[3],接口是axi_stream),实现了一个FIFO_Array类在硬件实现上来替代std::vector[6]。

但后来在Vivado里IP Integrator里Validate Design的时候发现了问题。Vivado一直提醒Stream没有TLast信号[4][10]。这个是一个非常大的坑。对于AXI_Stream,TLast信号为高标志着一批数据发送的结束。应当携带在本批数据的最后一个数据上向外发送。当使用纯C数据类型初始化hls::stream template时,HLS的Compiler是不会综合出TLast信号的。没有TLast信号,就会导致PL端IP与DMA、以及PS的数据传输出现严重的问题。PS端一直空等DMA,因为没有TLast信号,DMA无从得知数据是否传输完毕。

所以,我们必须要使用ap_axis<8,0,0,0>数据类型来传输字节[5]。这样综合的时候就会综合出TLast信号,因此我把FIFO_Array做了改动,使其适应ap_axis数据类型,变成了FIFO_Array_APInt类[7]。除此之外,在对应的Hardware C Code里面需要有对应的发送tlast信号的语句:发送普通数据tlast信号为0[8];发送EOF时tlast信号为1[9]。

在这里插入图片描述

AXI_Stream是个不好搞的东西,流式传输数据必须要用到DMA,它和axi-lite总线不大相同。axi-lite总线只要简单的Run Block/Connection Automation就可以在Vivado IP Integrator里面配置好了连线。而AXI_Stream需要手动添加、设置DMA IP核与AXI Stream Data FIFO IP核才可以。下一篇文章详细讲解一下。

参考资料:
[1]: https://github.com/xupgit/High-Level-Synthesis-Flow-on-Zynq-using-Vivado-HLS/blob/master/slides/11_HLS_Intro.pdf
[2]: https://github.com/xupgit/High-Level-Synthesis-Flow-on-Zynq-using-Vivado-HLS/blob/master/slides/12_Using_VivadoHLS.pdf
[3]: Using HLS Streams, UG902, Page 214: https://www.xilinx.com/support/documentation/sw_manuals/xilinx2018_3/ug902-vivado-high-level-synthesis.pdf
[4]: AXI4-Stream Signal Summary, UG1037, Page 167: https://www.xilinx.com/support/documentation/ip_documentation/axi_ref_guide/latest/ug1037-vivado-axi-reference-guide.pdf
[5]: AXI4-Stream Interfaces with Side-Channels, UG902, Page 98: https://www.xilinx.com/support/documentation/sw_manuals/xilinx2018_3/ug902-vivado-high-level-synthesis.pdf
[6]: https://github.com/bjrjk/HLS-LZ77/blob/e3d81421c180502c08b35db0fbc83a652eb09947/LZ77-HLS/fifo_array.cpp#L13
[7]: https://github.com/bjrjk/HLS-LZ77/blob/e3d81421c180502c08b35db0fbc83a652eb09947/LZ77-HLS/fifo_array.cpp#L103
[8]: https://github.com/bjrjk/HLS-LZ77/blob/e3d81421c180502c08b35db0fbc83a652eb09947/LZ77-HLS/fifo_array.cpp#L171
[9]: https://github.com/bjrjk/HLS-LZ77/blob/e3d81421c180502c08b35db0fbc83a652eb09947/LZ77-HLS/fifo_array.cpp#L203
[10]: http://mux.eee.u-ryukyu.ac.jp/lecture/reconf-lecture-11.pdf

本条目发布于2月 13, 2021。属于HLS分类,被贴了 Zynq 标签。

Zynq 高层次综合HLS 项目开发经历(4) Vivado IP Integrator

IP Integrator这一块比较难配置,网上的教程比较乱,而且也少有针对AXIS Stream IP核专门配置的教程。所以我尝试着手把手的把配置过程在博客里展现出来。
我使用的开发板是ALinx AX7020,FPGA芯片zynq-xc7z020clg400-2。有些配置与我的板子是高度相关的,如果使用的是其他开发板请参照开发板提供商给的教程或参考资料配置。

1、创建Vivado项目。

2、添加IP核扫描位置:Vivado左侧Flow Navigator——Project Manager——Settings——IP——Repository。点击+号选择你的HLS IP核所在的文件夹位置。一般来说,相对路径是solution/impl/ip。
在这里插入图片描述

选择完毕后,弹出如下对话框,提示1个IP核已经成功添加。点击OK返回。
在这里插入图片描述

3、添加Block Design:Vivado左侧Flow Navigator——IP Integrator——Create Block Design,属性保持默认点OK。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: vivado_hls是一种高级综合工具,用于将C/C++代码转换为硬件描述语言,如VHDL或Verilog。高斯图像处理是一种经典的图像滤波算法,用于平滑和降噪图像。 在vivado_hls实现高斯图像处理可以通过以下步骤完成: 1. 读取输入图像:将输入图像加载到HLS工程中。可以使用vivado_hls提供的图像处理库或自定义的图像读取函数来实现。 2. 图像预处理:首先对输入图像进行预处理,通常包括图像的剪裁、缩放或调整亮度等操作。这些步骤可以使用C/C++代码在vivado_hls实现。 3. 高斯滤波算法:使用高斯滤波算法对图像进行平滑处理。高斯滤波算法是基于像素周围的邻域进行计算的,可以通过卷积操作来实现。在vivado_hls中,可以使用C/C++代码来实现高斯滤波算法,并将其转换为硬件描述语言。 4. 图像后处理:在滤波完成后,可以对输出图像进行后处理,如亮度调整、对比度增加等操作。这些步骤也可以使用C/C++代码在vivado_hls实现。 5. 输出图像:将处理后的图像保存到指定的文件中。可以使用vivado_hls提供的图像处理库或自定义的图像保存函数来实现。 通过以上步骤,就可以在vivado_hls实现高斯图像处理。在进行设计时,需要考虑硬件资源的限制和性能要求,以确保设计能够在给定的约束下运行。 ### 回答2: vivado_hls是一种高级综合工具,可以将C/C++代码转换为FPGA(现场可编程门阵列)的硬件描述语言。高斯图像处理是一种常见的图像处理技术,用于平滑和模糊图像。以下是用vivado_hls实现高斯图像处理的基本步骤: 首先,需要编写一个C/C++函数来实现高斯图像处理算法。该函数应接受一个输入图像作为参数,并返回一个平滑后的输出图像。可以使用卷积运算来实现高斯模糊,其中对每个像素的值进行加权平均,以降低图像中噪声的影响。 接下来,使用vivado_hls工具加载编写好的C/C++函数,并进行综合和优化。vivado_hls会将输入函数转换为硬件描述语言(如VHDL或Verilog),然后进行优化以提高性能和效率。根据FPGA的资源限制和性能要求,可以调整优化参数,如时钟频率和资源利用率。 一旦综合和优化完成,就可以将生成的硬件描述文件加载到FPGA进行实际的高斯图像处理。将输入图像加载到FPGA的存储器中,然后通过配置FPGA来执行高斯模糊算法算法处理完毕后,从FPGA的输出端口获取平滑后的图像。 最后,可以将平滑后的图像保存到磁盘或进行进一步的图像处理。在保存图像之前,可以将输出图像转换为所需的格式(如JPEG或PNG),以便后续使用。 综上所述,使用vivado_hls实现高斯图像处理需要编写高斯模糊算法的C/C++函数,加载到vivado_hls进行优化和综合,然后在FPGA上执行实际的图像处理,并获取结果。这种方法可以确保在FPGA上高效地处理图像,并获得平滑后的图像。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值