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地址 在配…

【配置】Docker安装可道云网盘

环境 一台云服务器&#xff0c;centos8&#xff0c;必须安装docker Docker安装 1、卸载旧版 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine2、需要的安装包 yum ins…

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型系列水封式防暴器是安装在抽放瓦斯泵吸…

第十五届蓝桥杯Java A组参赛总结

一、比赛 4月13号那天上午9点到下午1点&#xff0c;线上比赛总共4小时。 因为很久没有参加过竞赛了&#xff0c;所以还是很紧张&#xff0c;睡觉都有点睡不好&#xff0c;生怕出什么差错 我参加的是java的A组&#xff0c;两道填空&#xff08;每道5分&#xff09;和六道大题…

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

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

Docker 常用命令教程

Docker 常用命令教程 1. Docker 镜像相关命令 查看本地所有镜像&#xff1a; docker images搜索镜像&#xff1a; docker search <镜像名>拉取镜像&#xff1a; docker pull <镜像名>导出镜像&#xff1a; docker save <镜像名> > <导出文件路径…

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

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…

运维工具火绒安全使用方法教程案例

火绒安全是一款专业的安全运维工具&#xff0c;旨在帮助企业和个人用户有效防范网络威胁&#xff0c;保护系统和数据安全。以下是关于火绒安全使用方法的基础教程和一些实际案例。 使用方法教程&#xff1a; 一、安装与设置 下载安装&#xff1a;访问火绒安全官网&#xff0…

[leetcode] 53. 最大子数组和

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

数据结构与算法——21.哈希表

这篇文章我们来学习哈希表。 1.什么是哈希表 下面来看一下哈希表 目录 1.什么是哈希表 1.1前言 1.2哈希表的介绍 2.哈希表的实现 1.1前言 在学习什么是哈希表之前&#xff0c;我们先来看下面在这一种情况。 我这里有一堆数据&#xff0c;我给每个数据上一个编号&#x…

如何应对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;最…

Macbook M3新机器安装cocoapods失败

这个问题昨天困扰了一个下午 中午拿到的全新的m3 2024 MacBook air&#xff0c;操作系统是Sonoma 14.3 安装Android studio与Flutter一切顺利 在安装cocoapods的时候&#xff0c; sudo gem install cocoapods 一直失败&#xff0c;开始是提示timeout&#xff0c;后来想办法…

一文读懂工作流原理及其在多元应用场景下的实践

一文读懂工作流原理及其在多元应用场景下的实践 引言工作流原理概述应用场景示例及详细描述1. 人力资源招聘流程2. 项目管理流程3. 财务报销审批流程4. 合同审核签署流程 总结 引言 工作流&#xff08;Workflow&#xff09;&#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;使得反编译出来的代码很难…