Vitis HLS 学习笔记--硬件卷积加速 Filter2DKernel

目录

加速器功能

Window2D()函数

实现代码

变量解释

ARRAY_PARTITION

DEPENDENCE

LOOP_TRIPCOUNT

ramp_up

更新Window

更新LineBuffer

Filter2D()函数

ARRAY_PARTITION

window_stream.read()

 计算过程

备注


加速器功能

  1. 硬件加速单元从全局内存(DDR)中读取图像数据和卷积核系数矩阵;
  2. 计算卷积;
  3. 计算结果返回全局内存(DDR)中;

加速器顶层代码

void Filter2DKernel(const char           coeffs[256],float                factor,short                bias,unsigned short       width,unsigned short       height,unsigned short       stride,const unsigned char  src[MAX_IMAGE_WIDTH*MAX_IMAGE_HEIGHT],unsigned char        dst[MAX_IMAGE_WIDTH*MAX_IMAGE_HEIGHT])
{#pragma HLS INTERFACE m_axi     port=src    offset=slave bundle=gmem0  #pragma HLS INTERFACE m_axi     port=coeffs offset=slave bundle=gmem1#pragma HLS INTERFACE m_axi     port=dst    offset=slave bundle=gmem1// Stream of pixels from kernel input to filter, and from filter to outputhls::stream<char,2>    coefs_stream;hls::stream<U8,2>      src_stream;hls::stream<window,3>  window_stream; // Set FIFO depth to 0 to minimize resourceshls::stream<U8,64>     dst_stream;#pragma HLS DATAFLOW// Read image data from global memory over AXI4 MM, and stream pixels outReadFromMem(width, height, stride, coeffs, coefs_stream, src, src_stream);// Read incoming pixels and form valid HxV windowsWindow2D(width, height, src_stream, window_stream);// Process incoming stream of pixels, and stream pixels outFilter2D(width, height, factor, bias, coefs_stream, window_stream, dst_stream);// Write incoming stream of pixels and write them to global memory over AXI4 MMWriteToMem(width, height, stride, dst_stream, dst);
}

加速单元占用了两个AXI Master端口,AXI M1用于读取图像数据;AXI M2用于读取卷积核系数,并回传计算结果。

Window2D()函数

该函数(硬件)实现如下功能:

实现代码

struct window {U8 pix[FILTER_V_SIZE][FILTER_H_SIZE];};
hls::stream<U8>      &src_stream
hls::stream<window>  &window_streamvoid Window2D(unsigned short        width,unsigned short        height,hls::stream<U8>      &src_stream,hls::stream<window>  &window_stream) {U8 LineBuffer[FILTER_V_SIZE-1][MAX_IMAGE_WIDTH];  
#pragma HLS ARRAY_PARTITION variable=LineBuffer dim=1 complete
#pragma HLS DEPENDENCE variable=LineBuffer inter false
#pragma HLS DEPENDENCE variable=LineBuffer intra falsewindow Window;unsigned col_ptr = 0;unsigned ramp_up = width*((FILTER_V_SIZE-1)/2)+(FILTER_H_SIZE-1)/2;unsigned num_pixels = width*height;unsigned num_iterations = num_pixels + ramp_up;const unsigned max_iterations = MAX_IMAGE_WIDTH*MAX_IMAGE_HEIGHT + MAX_IMAGE_WIDTH*((FILTER_V_SIZE-1)/2) +(FILTER_H_SIZE-1)/2;update_window: for (int n=0; n<num_iterations; n++){#pragma HLS LOOP_TRIPCOUNT max=max_iterations#pragma HLS PIPELINE II=1U8 new_pixel = (n<num_pixels) ? src_stream.read() : 0;for(int i = 0; i < FILTER_V_SIZE; i++) {for(int j = 0; j < FILTER_H_SIZE-1; j++) {Window.pix[i][j] = Window.pix[i][j+1];}Window.pix[i][FILTER_H_SIZE-1] = (i<FILTER_V_SIZE-1) ? LineBuffer[i][col_ptr] : new_pixel;}for(int i = 0; i < FILTER_V_SIZE-2; i++) {LineBuffer[i][col_ptr] = LineBuffer[i+1][col_ptr];}LineBuffer[FILTER_V_SIZE-2][col_ptr] = new_pixel;if (col_ptr==(width-1)) {col_ptr = 0;} else {col_ptr++;}if (n>=ramp_up) { window_stream.write(Window);  }}
}

变量解释

  • window_stream变量:窗口缓冲区
  • LineBuffer只缓存了两行图像数据!
  • ramp_up初始化滑动窗口需要的像素数

ARRAY_PARTITION

#pragma HLS ARRAY_PARTITION variable=LineBuffer dim=1 complete

这条指令的效果是将LineBuffer数组完全分解为单个元素。这种分解可以将一个大的存储器解析为多个寄存器,从而增加LineBuffer的端口数,从而提高设计的吞吐量,但也会增加存储器实例或寄存器的数量。

DEPENDENCE

#pragma HLS DEPENDENCE variable=LineBuffer inter false //循环间无依赖

指示LineBuffer在不同的循环迭代之间没有数据相关性,即每一次循环对LineBuffer的读写操作都不会影响其他循环的读写操作。如果这个指令指定为true,就表示有数据相关性,那么编译器就会保证每一次循环的读写操作都按照顺序执行,不能并行化。如果指定为false,就表示没有数据相关性,那么编译器就可以在流水线化或展开循环时并行执行多个循环的读写操作,从而提高吞吐量。

#pragma HLS DEPENDENCE variable=LineBuffer intra false //循环内无依赖

指示LineBuffer在同一个循环迭代内没有数据相关性,即同一循环内对LineBuffer的读写操作互不相关。如果这个指令指定为true,就表示有数据相关性,那么编译器就会保证每一次循环内的读写操作都按照顺序执行,不能移动位置。如果指定为false,就表示没有数据相关性,那么编译器就可以在同一个循环内自由地移动读写操作的位置,从而提高操作的可调度性和潜在的性能或面积优化。

LOOP_TRIPCOUNT

#pragma HLS LOOP_TRIPCOUNT max=max_iterations

手动指定一个循环的最大迭代次数。只是用于分析,不会影响综合的结果。它可以帮助HLS工具计算循环的延迟,从而在报告中显示循环对总设计延迟的贡献,并帮助确定适合的优化方法。

ramp_up

window Window;
unsigned col_ptr = 0;
unsigned ramp_up = width*((FILTER_V_SIZE-1)/2)+(FILTER_H_SIZE-1)/2;
unsigned num_pixels = width*height;
unsigned num_iterations = num_pixels + ramp_up;

代入计算:

unsigned ramp_up = width*((FILTER_V_SIZE-1)/2)+(FILTER_H_SIZE-1)/2;

               ramp_up = 5*((3-1)/2)+(3-1)/2 = 5+1 = 6

这个值表示了在输出滑动窗口stream之前,需要从输入流中读取多少个像素。也就是说,第一个滑动窗输出时,至少要读取0~67)个像素。如下图所示:

更新Window

U8 new_pixel = (n<num_pixels) ? pixel_stream.read() : 0;

num_pixels个像素,每次从stream中读取,之后的pixel赋值为0

更新LineBuffer

                        FILTER_V_SIZE-2 = 3-2 = 1

for(int i = 0; i < FILTER_V_SIZE-2; i++) {

    LineBuffer[i][col_ptr] = LineBuffer[i+1][col_ptr];

}

LineBuffer[FILTER_V_SIZE-2][col_ptr] = new_pixel;

 FILTER_V_SIZE-2 = 3-2 = 1

表示:对于一个高度为vkernel,其最后一列由new_pixel更新,前v-1列由原kernel上移更新,因此v=3时,只需更新一个数。

每次update_window迭代,LineBuffer仅更新一个pixel

 

Filter2D()函数

void Filter2D(unsigned short       width,unsigned short       height,float                factor,short                bias,hls::stream<char>   &coeff_stream,hls::stream<window> &window_stream,hls::stream<U8>     &pixel_stream ) {char coeffs[FILTER_V_SIZE][FILTER_H_SIZE];
#pragma HLS ARRAY_PARTITION variable=coeffs complete dim=0load_coefs: for (int i=0; i<FILTER_V_SIZE; i++) {for (int j=0; j<FILTER_H_SIZE; j++) {
#pragma HLS PIPELINE II=1coeffs[i][j] = coeff_stream.read(); } }apply_filter: for (int y = 0; y < height; y++)  {for (int x = 0; x < width; x++)  {
#pragma HLS PIPELINE II=1window w = window_stream.read();int sum = 0;for(int row=0; row<FILTER_V_SIZE; row++) {for(int col=0; col<FILTER_H_SIZE; col++) {unsigned char pixel;int xoffset = (x+col-(FILTER_H_SIZE/2));int yoffset = (y+row-(FILTER_V_SIZE/2));// Deal with boundary conditions : clamp pixels to 0 when outside of image if ( (xoffset<0) || (xoffset>=width) || (yoffset<0) || (yoffset>=height) ) {pixel = 0;} else {pixel = w.pix[row][col];}sum += pixel*(char)coeffs[row][col];} }unsigned char outpix = MIN(MAX((int(factor * sum)+bias), 0), 255);pixel_stream.write(outpix);}} }

ARRAY_PARTITION

#pragma HLS ARRAY_PARTITION variable=coeffs complete dim=?

B[0][0]

B[0][1]

B[0][2]

B[0][3]

B[1][0]

B[1][1]

B[1][2]

B[1][3]

dim=0:则相当于将B[2][4]拆分成两个四元素数组B[0][0:3]B[1][0:3],每一行都可以同时访问四个元素,但是每一列只能访问一个元素。

dim=1:则相当于将B[2][4]拆分成四个双元素数组B[0:1][0]B[0:1][1]B[0:1][2]B[0:1][3],每一列都可以同时访问两个元素,但是每一行只能访问一个元素。

window_stream.read()

window w = window_stream.read();

此语句所在循环中,每个像素点都会读取stream,但不是所有window都会参与coeffs计算。

 计算过程

for(int row=0; row<FILTER_V_SIZE; row++) {for(int col=0; col<FILTER_H_SIZE; col++) {unsigned char pixel;int xoffset = (x+col-(FILTER_H_SIZE/2));int yoffset = (y+row-(FILTER_V_SIZE/2));// Deal with boundary conditions : clamp pixels to 0 when outside of image if ( (xoffset<0) || (xoffset>=width) || (yoffset<0) || (yoffset>=height) ) {pixel = 0;} else {pixel = w.pix[row][col];}sum += pixel*(char)coeffs[row][col];}
}

备注

本文所称的函数,并非函数,最终映射为硬件电路。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/817909.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Centos7配置IP地址

1、找到网卡名字 使用root用户登陆&#xff0c;输入命令 ifconfig 2、打开配置文件 输入命令&#xff0c;打开配置文件 vi /etc/sysconfig/network-scripts/ifcfg-ens33 3、添加IP地址 3.1修改BOOTPROTO 将“BOOTPROTOdhcp” 改为 “BOOTPROTOstatic” 3.2添加IP地址 在配…

D365开发-在视图按钮的js里,引用别的js里的公共方法

公共方法写法&#xff1a; "use strict"; var JJMC window.JJMC || {}; JJMC.SamMCommon JJMC.SamMCommon || {}; (function () { this.cloneRecord function (excludeAttrbuteNames){ / } }).call(JJMC.SamMCommon); 然后在需要调方法的command里面&#xff0c;之…

解读《算者生存:商业分析的方法与实践》:构建企业经营分析框架的必备指南

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

Java:定时任务无法正常执行(scheduling + ShedLock)

目录 一、场景二、代码片段三、排查四、原因五、解决 一、场景 1、使用定时任务(scheduling) 分布式锁(ShedLock)定期执行一段代码 2、configureTasks()对于任务执行周期的更新是正常的 3、但任务方法无法被执行 二、代码片段 三、排查 1、确认Trigger没有问题 2、查看red…

水封式防暴器 我的真诚一直不变

天气在变&#xff0c;服务不变&#xff0c;季节在变&#xff0c;态度不变&#xff0c;时代在变&#xff0c;品质不变&#xff0c;不管世界怎么变&#xff0c;我的真诚一直不变&#xff01; 一、水封式防暴器的用途介绍&#xff1a; FBQ型系列水封式防暴器是安装在抽放瓦斯泵吸…

如何在没有备份的情况下恢复iPhone数据

想要找到没有备份的最佳iPhone数据恢复软件吗&#xff1f;本文介绍了一款专业的iPhone数据恢复软件&#xff0c;无需备份即可恢复iPhone数据。 许多iPhone用户可能对上述情况并不陌生。丢失重要的iPhone数据确实是一件令人沮丧的事情。通常&#xff0c;检索iPhone数据的最佳方…

第九届少儿模特明星盛典 全球赛首席体验官『魏堃明』精彩回顾

2024年1月30日-2月1日&#xff0c;魔都上海迎来了龙年第一场“少儿形体行业美育春晚”&#xff01;由IPA模特委员会主办的第九届少儿模特明星盛典全球总决赛圆满收官&#xff01;近2000名少儿模特选手从五湖四海而来&#xff0c;决战寒假这场高水准&#xff0c;高人气&#xff…

Golang 开发实战day10 - Maps

&#x1f3c6;个人专栏 &#x1f93a; leetcode &#x1f9d7; Leetcode Prime &#x1f3c7; Golang20天教程 &#x1f6b4;‍♂️ Java问题收集园地 &#x1f40d; Python工具 &#x1f334; 成长感悟 欢迎大家观看&#xff0c;不执着于追求顶峰&#xff0c;只享受探索过程 G…

[leetcode] 53. 最大子数组和

文章目录 题目描述解题方法分治法java代码复杂度分析 题目描述 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组是数组中的一个连续部分。 示例 1&#xff1a; 输…

如何应对MySQL单表数据量过大:垂直分表与水平分表策略解析

话接上回&#xff0c;单表最大数据建议两千万&#xff0c;那如果开发一个项目&#xff0c;预计注册量达到一个亿怎么办。 单表内放这么多数据&#xff0c;MYSQL底层B树的层级结构就可能会变得很高&#xff0c;磁盘io次数变多&#xff0c;性能会大幅度降低。所以考虑数据库分表…

解析CopyOnWrite机制 以java的CopyOnWriteArrayList为例

什么是CopyOnWrite 写时复制&#xff08;Copy-on-write&#xff0c;简称COW&#xff09;是读写分离的一种实现方式&#xff0c;因为读和写在不同的容器中。 核心思想&#xff1a;线程在修改数据的时&#xff0c;会将原数据复制一份&#xff0c;然后在副本上修改&#xff0c;最…

imx.7交叉编译libX11

背景&#xff1a; 还是之前提到的触摸屏无响应问题&#xff0c;通过GDB调试&#xff0c;发现APP并非人为代码卡死&#xff0c;而是卡在官方的libc.so.6中&#xff0c;这个库出现了一些错误。排除自己代码问题&#xff0c;就剩官方版本问题&#xff0c;移植X11库&#xff0c;或…

提高APP安全性的必备加固手段——深度解析代码混淆技术

​ APP 加固方式 iOSAPP 加固是优化 APK 安全性的一种方法&#xff0c;常见的加固方式有混淆代码、加壳、数据加密、动态加载等。下面介绍一下 iOSAPP 加固的具体实现方式。 混淆代码&#xff1a; 使用 ProGuard 工具可以对代码进行混淆&#xff0c;使得反编译出来的代码很难…

前端本地搭建gninx环境

nginx下载地址&#xff1a; https://nginx.org/en/download.html nginx下载后&#xff0c;解压即用&#xff0c;注意解压目录不要含中文 nginx常用命令 查看版本 nginx -v 开启nginx服务 start nginx 重启服务 nginx -s reload 关闭服务 nginx -s stopnginx目录简析

three.js能够实现的3D动画效果大阅兵,有图有真相。

three.js能够实现许多不同类型的3D交互动画&#xff0c;包括但不限于以下几种&#xff1a; 旋转和缩放&#xff1a;可以通过鼠标或触摸手势来旋转和缩放3D模型或场景。 序列动画&#xff1a;可以创建复杂的动画序列&#xff0c;包括移动、旋转、缩放、颜色变化等。 粒子效果&…

使用Flask部署ppocr模型_3

PaddleOCR环境搭建、模型训练、推理、部署全流程&#xff08;Ubuntu系统&#xff09;_1_paddle 多进程推理-CSDN博客 PP-Structure 文档分析-CSDN博客 接前两篇继续完成Flask部署 一、使用Flask部署ppocr模型 GET方法用于从服务器获取资源&#xff0c;即客户端向服务器请求数据…

单链表讲解

一.链表的概念以及结构 链表是一种物理结构上不连续&#xff0c;逻辑结构上连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 链表的结构与火车是类似的&#xff0c;一节一节的&#xff0c;数据就像乘客一样在车厢中一样。 与顺序表不同的…

如何使用pytorch进行图像分类

如何使用pytorch进行图像分类https://featurize.cn/notebooks/5a36fa40-490e-4664-bf98-aa5ad7b2fc2f

【软考】UML中的图之用例图

目录 1. 说明2. 建模2.1 说明2.2 语境建模2.3 需求建模 3. 图示4. 组成部分 1. 说明 1.用例图&#xff08;Use Case Diagram&#xff09;。2.展现了一组用例、参与者&#xff08;Actor&#xff09;以及它们之间的关系。3.用例图通常包括以下的内容&#xff1a;用例、参与者、用…

配置IP地址并验证连通性

1.实验环境 主机 A和主机 B通过一根网线相连&#xff0c;如图6.13所示。 图6.13 实验案例一示意图 2.需求描述 为两台主机配置!P地址&#xff0c;验证P地址是否生效&#xff1b;验证同一网段的两台主机可以互通&#xff0c;不同网段的主机不能直接互通。 3.推荐步骤 为两台…