FPGA HLS 卷积单元 数据类型&hls优化&约束设置

这篇具有很好参考价值的文章主要介绍了FPGA HLS 卷积单元 数据类型&hls优化&约束设置。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

数据类型

自定义精度整形:

ap_int<4> in1, in2;
ap_int<8> concat;
concat = (in1, in2);	// in1和in2拼起来(按照补码拼起来)
/*
例子:
in1 = 1, in2 = -1
补码:
in1 =  0001
in2 =  1001 ==> 1110+1 ==> 1111
concat = 00011111 = 31
*/
concat.xor_reduce();	// 按位异或

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

自定义定点数

为了替换float,double类型的数,加快运算,节约资源

ap_fixed<11, 6> Var1 = 22.96875; // 一共11个bit,其中6个bit表示整数,5个bit表示小数;剩一个bit表示正负数
ap_ufixed<12, 11> Var2 = 512.5;	// 一共12个bit,11位表示整数,最后一位表示小数

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

卷积的量化或定点化

根据输入的数据,找到卷积层的数据范围

A= aaaaaaaaaaaaaaaa, fix_point=12
B= bbbbbbbbbbbbbbbb, fix_point=13
C= ????????????????, fix_point=13

????????????????*(2^13)=A*B/2^(12+13)

求C的编码:????????????????
= A*B/2^(12+13-13)
= A*B/2^(fix_pointA + fix_pointB – fix_pointC)

例子:
A:0010.1100 = 44/16 = 2+0.5+0.25 = 2.75 fix_point = 4
B:00101.100 = 44/8 = 5+0.5 = 5.5 fix_point = 3
C:????.???? Fix_point = 4
A*B =44*44 = 011110010000 == 右移(3+4) = 01111.0010000 = 15.125
一共有(3+4)位小数,但是C的精度是4,所以需要把多余的小数移出去
移出去的位数就是(3+4-4) = 3
所以C = 1111.0010 fix_point = 4

自定义卷积

特征的内存排布方式

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

权重的内存排布方式

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

卷积的大小不固定,需要根据在内存中的排布方式算出地址

新建conv_core项目

conv_core.h

#ifndef __CONV_CORE_H__
#define __CONV_CORE_H__

#include <ap_int.h>
#include <iostream>
using namespace std;

#define K 8

typedef ap_int<16> data_type;	// 单个数据的大小
typedef ap_int<16*K> tile_type;	// 分块数据的大小

typedef ap_int<32> mul_type;	// 两个数据相乘的数据大小:16*16==>32
typedef ap_int<32*K> mul_tile_type; // 分块数据相乘的大小

typedef ap_int<40> acc_type;  // 一次卷积内的数据相加后的大小,按照经验推断

// 卷积的定义
void conv_core(
		ap_uint<16> in_channel, // 输入特征的通道数
		ap_uint<16> in_height, // 输入特征高度
		ap_uint<16> in_width, // 输入特征宽度
		ap_uint<16> out_channel, // 输出特征通道数

		// 卷积核参数
		ap_uint<8> kernel_width, // 卷积核宽度
		ap_uint<8> kernel_height, // 卷积核高度

		ap_uint<8> stride_x, // 宽度方向步长
		ap_uint<8> stride_y, // 高度方向步长

		ap_uint<1> padding_mode, // 卷积的模式; 0: valid(没有padding填充), 1:same(输入输出的图大小不变)

		ap_uint<1> relu_en, // 激活函数

		tile_type input_feature[], ap_uint<4> input_feature_precision,	// 输入特征图地址和精度(小数点位置)
		tile_type weight[], ap_uint<4> weight_precision,// 权重地址和精度(小数点位置)
		tile_type output_feature[], ap_uint<4> out_feature_precision// 输出特征图地址和精度(小数点位置)
);

#endif

conv_core.cpp

#include "conv_core.h"

void conv_core(
		// 特征图参数
		ap_uint<16> in_channel, // 输入特征的通道数
		ap_uint<16> in_height, // 输入特征高度
		ap_uint<16> in_width, // 输入特征宽度
		ap_uint<16> out_channel, // 输出特征通道数

		// 卷积核参数
		ap_uint<8> kernel_width, // 卷积核宽度
		ap_uint<8> kernel_height, // 卷积核高度

		ap_uint<8> stride_x, // 宽度方向步长
		ap_uint<8> stride_y, // 高度方向步长

		ap_uint<1> padding_mode, // 卷积的模式; 0: valid(没有padding填充), 1:same(输入输出的图大小不变)

		ap_uint<1> relu_en, // 激活函数

		tile_type input_feature[], ap_uint<4> input_feature_precision,	// 输入特征图地址和精度(小数点位置)
		tile_type weight[], ap_uint<4> weight_precision,// 权重地址和精度(小数点位置)
		tile_type output_feature[], ap_uint<4> out_feature_precision// 输出特征图地址和精度(小数点位置)
)
{
	// Feature: [CHin/K][H][W][K]
	// Weight: [CHout][CHin/K][KH][KW][K]

	// 根据卷积模式,计算padding
	ap_uint<8> padding_x, padding_y;
	if(padding_mode == 0){
		padding_x = padding_y = 0;
	}else{
		padding_x = (kernel_width-1)/2;
		padding_y = (kernel_height-1)/2;
	}
	// 计算分块个数
	ap_uint<16> div_tile_num = (in_channel + K-1) / K;
	// 计算输出截断精度
	ap_uint<5> out_truncate = input_feature_precision + weight_precision - out_feature_precision;
	/*
	 * [x x x] x x x
	 * x x [x x x] x
	 */
	// 计算输出宽度和高度
	ap_uint<16> out_width = (in_width + padding_x*2) / stride_x + 1;
	ap_uint<16> out_height = (in_height + padding_y*2) / stride_y + 1;

	// 选择输出特征的第y行,第x列,第c_out个输出通道的数据
	// 选择第c_out个权重的第y行,第x列,第tile_index个分块

	LOOP_out_channel:
	for(int out_index = 0; out_index < out_channel; ++ out_index){
		LOOP_out_height:
		for(int i = 0; i < out_height; ++ i){
			LOOP_out_width:
			for(int j = 0; j < out_width; ++ j){
				// 相乘结果累加
				acc_type sum=0;
				// 计算输出特征中一个tile的数据
				ap_int<16> out_tile = 0;
				LOOP_kernel_height:
				for(int kh = 0; kh < kernel_height; ++ kh){
					LOOP_kernel_width:
					for(int kw = 0; kw < kernel_width; ++ kw){
						LOOP_div_tile_num:
						for(int tile_index = 0; tile_index < div_tile_num; ++ tile_index){

							// 获取计算点
							ap_uint<16> in_h = i*stride_y-padding_y + kh;
							ap_uint<16> in_w = j*stride_x-padding_x + kw;

							// 获取输入特征和权重的一个块数据
							tile_type data_tile, weight_tile;
							// 有padding会越界
							if(in_h >= 0 && in_h < in_height && in_w >= 0 && in_w < in_width){
								data_tile = input_feature[in_width*in_height*tile_index + in_width*in_h + in_w];
								weight_tile = weight[kernel_width*kernel_height*div_tile_num*out_index
													 + kernel_width*kernel_height*tile_index
													 + kernel_width*kh+kw];
							}else{
								data_tile = 0; weight_tile = 0;
							}
							// 块数据相乘
							mul_tile_type mul_tile_data;
							for(int k = 0; k < K; ++ k)
								mul_tile_data.range(k*32+31, k*32) =
										(data_type)data_tile.range(k*16+15, k*16)*
										(data_type)weight_tile.range(k*16+15, k*16);
							// 相乘结果累加
							for(int k = 0; k < K; ++ k)
								sum += (mul_tile_type)mul_tile_data.range(k*32+31, k*32);
						}
					}
				}

				// 激活函数
				if(relu_en & sum < 0) sum = 0;
				// 截断多余精度
				acc_type res = sum >> out_truncate;

				if (res > 32767)
					res = 32767;
				else if (res < -32768)
					res = -32768;
				out_tile.range((out_index % K) * 16 + 15, (out_index % K) * 16) = res;
				// 存tile里的一个数据
				// 一个tile都存完了或者存到最后一个通道凑不够一个tile
				if((out_index%K) == K - 1 || out_index == (out_channel - 1))
					output_feature[(out_index/K)*out_width*out_height + out_width*i+j] = out_tile;
			}
		}
	}
}

main.cpp

#include "stdio.h"
#include "conv_core.h"

#define IN_WIDTH 10
#define IN_HEIGHT 10
#define IN_CHANNEL 1
#define IN_DIV_TILE_NUM	((IN_CHANNEL+K-1)/K)

#define KERNEL_WIDTH 5
#define KERNEL_HEIGHT 5
#define STRIDE_X 1
#define STRIDE_Y 1

#define RELU_EN 0
#define PADDING_MODE 0 // 0:valid 1:same
#define PADDING_X (PADDING_MODE?(KERNEL_WIDTH-1)/2:0)
#define PADDING_Y (PADDING_MODE?(KERNEL_HEIGHT-1)/2:0)

#define OUT_CHANNEL 1
#define OUT_WIDTH ((IN_WIDTH+PADDING_X*2-KERNEL_WIDTH)/STRIDE_X+1)
#define OUT_HEIGHT ((IN_HEIGHT+PADDING_Y*2-KERNEL_HEIGHT)/STRIDE_Y+1)
#define OUT_DIV_TILE_NUM ((OUT_CHANNEL+K-1)/K)

int main(void)
{
	tile_type input_feature[IN_DIV_TILE_NUM][IN_HEIGHT][IN_WIDTH];
	tile_type weight[OUT_CHANNEL][IN_DIV_TILE_NUM][KERNEL_HEIGHT][KERNEL_WIDTH];
	tile_type output_feature[OUT_DIV_TILE_NUM][OUT_HEIGHT][OUT_WIDTH];

	for(int tile_index = 0; tile_index < IN_DIV_TILE_NUM; ++ tile_index){
		for(int i = 0; i < IN_HEIGHT; ++ i){
			for(int j = 0; j < IN_WIDTH; ++ j){
				for(int k = 0; k < K; ++ k){
					// 可能除不尽
					if(tile_index*K+k < IN_CHANNEL)
						input_feature[tile_index][i][j].range(k*16+15, k*16) = (1<<14);
					else
						input_feature[tile_index][i][j].range(k*16+15, k*16) = 0;
				}
			}
		}
	}

	for(int out_index = 0; out_index < OUT_CHANNEL; ++ out_index){
		for(int tile_index = 0; tile_index < OUT_DIV_TILE_NUM; ++ tile_index){
			for(int i = 0; i < OUT_HEIGHT; ++ i){
				for(int j = 0; j < OUT_WIDTH; ++j){
					for(int k = 0; k < K; ++ k){
						// 输入特征赋值为0,特征值就不用考虑除不尽的问题
						weight[out_index][tile_index][i][j].range(16*k+15, 16*k) = (1<<14);
					}
				}
			}
		}
	}

	for(int tile_index = 0; tile_index < OUT_DIV_TILE_NUM; ++ tile_index){
		for(int i = 0; i < OUT_HEIGHT; ++ i){
			for(int j = 0; j < OUT_WIDTH; ++ j){
				output_feature[tile_index][i][j] = 0;
			}
		}
	}

	printf("initial down\n");

	conv_core(IN_CHANNEL, IN_HEIGHT, IN_WIDTH, OUT_CHANNEL,
			KERNEL_WIDTH, KERNEL_HEIGHT,
			STRIDE_X, STRIDE_Y,
			PADDING_MODE, RELU_EN,
			&input_feature[0][0][0], 14,
			&weight[0][0][0][0], 14,
			&output_feature[0][0][0], 10
	);

	for(int tile_index = 0; tile_index < OUT_DIV_TILE_NUM; ++ tile_index){
		for(int i = 0; i < OUT_HEIGHT; ++ i){
			for(int j = 0; j < OUT_WIDTH; ++ j){
				cout << "out[" << tile_index <<"]["<<i<<"]["<<j<<"]="<<
				(data_type)output_feature[tile_index][i][j].range(15, 0) << "\t";
			}
			cout << endl;
		}
	}


	return 0;
}

运行

C仿真结果:

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

C综合:

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

出错了,需要加约束。报错信息:输入特征feature_in是没有固定长度的

但是我们只是把input_feature当作基地址,而不是数组,所以需要告诉工具,数据来自外部存储器

数据接口约束

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

#pragma HLS INTERFACE m_axi depth=999999999 port=weight offset=slave
#pragma HLS INTERFACE m_axi depth=999999999 port=output_feature offset=slave
#pragma HLS INTERFACE m_axi depth=999999999 port=input_feature offset=slave

再次C综合:

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

???是因为,循环次数是一个变量,工具无法估计性能

循环次数约束

那就根据test bench里的例子进行测试约束

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

#include "conv_core.h"

void conv_core(
		// 特征图参数
		ap_uint<16> in_channel, // 输入特征的通道数
		ap_uint<16> in_height, // 输入特征高度
		ap_uint<16> in_width, // 输入特征宽度
		ap_uint<16> out_channel, // 输出特征通道数

		// 卷积核参数
		ap_uint<8> kernel_width, // 卷积核宽度
		ap_uint<8> kernel_height, // 卷积核高度

		ap_uint<8> stride_x, // 宽度方向步长
		ap_uint<8> stride_y, // 高度方向步长

		ap_uint<1> padding_mode, // 卷积的模式; 0: valid(没有padding填充), 1:same(输入输出的图大小不变)

		ap_uint<1> relu_en, // 激活函数

		tile_type input_feature[], ap_uint<4> input_feature_precision,	// 输入特征图地址和精度(小数点位置)
		tile_type weight[], ap_uint<4> weight_precision,// 权重地址和精度(小数点位置)
		tile_type output_feature[], ap_uint<4> out_feature_precision// 输出特征图地址和精度(小数点位置)
)
{
	#pragma HLS INTERFACE m_axi depth=999999999 port=weight offset=slave
	#pragma HLS INTERFACE m_axi depth=999999999 port=output_feature offset=slave
	#pragma HLS INTERFACE m_axi depth=999999999 port=input_feature offset=slave
	// Feature: [CHin/K][H][W][K]
	// Weight: [CHout][CHin/K][KH][KW][K]

	// 根据卷积模式,计算padding
	ap_uint<8> padding_x, padding_y;
	if(padding_mode == 0){
		padding_x = padding_y = 0;
	}else{
		padding_x = (kernel_width-1)/2;
		padding_y = (kernel_height-1)/2;
	}
	// 计算分块个数
	ap_uint<16> div_tile_num = (in_channel + K-1) / K;
	// 计算输出截断精度
	ap_uint<5> out_truncate = input_feature_precision + weight_precision - out_feature_precision;
	/*
	 * [x x x] x x x
	 * x x [x x x] x
	 */
	// 计算输出宽度和高度
	ap_uint<16> out_width = (in_width + padding_x*2) / stride_x + 1;
	ap_uint<16> out_height = (in_height + padding_y*2) / stride_y + 1;

	// 选择输出特征的第y行,第x列,第c_out个输出通道的数据
	// 选择第c_out个权重的第y行,第x列,第tile_index个分块

	LOOP_out_channel:
	for(int out_index = 0; out_index < out_channel; ++ out_index){
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
		LOOP_out_height:
		for(int i = 0; i < out_height; ++ i){
#pragma HLS LOOP_TRIPCOUNT min=10 max=10 avg=10
			LOOP_out_width:
			for(int j = 0; j < out_width; ++ j){
#pragma HLS LOOP_TRIPCOUNT min=10 max=10 avg=10
				// 相乘结果累加
				acc_type sum=0;
				// 计算输出特征中一个tile的数据
				ap_int<16> out_tile = 0;
				LOOP_kernel_height:
				for(int kh = 0; kh < kernel_height; ++ kh){
#pragma HLS LOOP_TRIPCOUNT min=5 max=5 avg=5
					LOOP_kernel_width:
					for(int kw = 0; kw < kernel_width; ++ kw){
#pragma HLS LOOP_TRIPCOUNT min=5 max=5 avg=5
						LOOP_div_tile_num:
						for(int tile_index = 0; tile_index < div_tile_num; ++ tile_index){
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
							// 获取计算点
							ap_uint<16> in_h = i*stride_y-padding_y + kh;
							ap_uint<16> in_w = j*stride_x-padding_x + kw;

							// 获取输入特征和权重的一个块数据
							tile_type data_tile, weight_tile;
							// 有padding会越界
							if(in_h >= 0 && in_h < in_height && in_w >= 0 && in_w < in_width){
								data_tile = input_feature[in_width*in_height*tile_index + in_width*in_h + in_w];
								weight_tile = weight[kernel_width*kernel_height*div_tile_num*out_index
													 + kernel_width*kernel_height*tile_index
													 + kernel_width*kh+kw];
							}else{
								data_tile = 0; weight_tile = 0;
							}
							// 块数据相乘
							mul_tile_type mul_tile_data;
							for(int k = 0; k < K; ++ k)
								mul_tile_data.range(k*32+31, k*32) =
										(data_type)data_tile.range(k*16+15, k*16)*
										(data_type)weight_tile.range(k*16+15, k*16);
							// 相乘结果累加
							for(int k = 0; k < K; ++ k)
								sum += (mul_tile_type)mul_tile_data.range(k*32+31, k*32);
						}
					}
				}

				// 激活函数
				if(relu_en & sum < 0) sum = 0;
				// 截断多余精度
				acc_type res = sum >> out_truncate;

				if (res > 32767)
					res = 32767;
				else if (res < -32768)
					res = -32768;
				out_tile.range((out_index % K) * 16 + 15, (out_index % K) * 16) = res;
				// 存tile里的一个数据
				// 一个tile都存完了或者存到最后一个通道凑不够一个tile
				if((out_index%K) == K - 1 || out_index == (out_channel - 1))
					output_feature[(out_index/K)*out_width*out_height + out_width*i+j] = out_tile;
			}
		}
	}
}

综合结果:

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

优化

最内层的块相乘只用两个周期完成

一个周期用来去input_tile,另一个取weight_tile,输出计算结果的同时,也在取数据

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

添加 pipeline 优化

迭代间隔 II = 2

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

如果效果理想的话应该是 1*10*10*5*5*2=5000

但是综合结果并不是:

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

卷积一次:2*25 = 50

这里循环的latency是59,多了9个周期是迭代latency

但是顶层的迭代latency达到了70,其中多的11周期的计算在这里:

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

完美循环优化与计算顺序的调换

  • 完美循环

使用if语句使循环之间没有代码,使得循环可以合并

从存储器取块数据相乘累加的运算,其实可以和计算结果的存储并行

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

  • 计算顺序调换

将out_channel挪到kernel_width上面,这样每计算完一次卷积tile,就判断一次对输出特征写计算结果

#include "conv_core.h"

void conv_core(
		// 特征图参数
		ap_uint<16> in_channel, // 输入特征的通道数
		ap_uint<16> in_height, // 输入特征高度
		ap_uint<16> in_width, // 输入特征宽度
		ap_uint<16> out_channel, // 输出特征通道数

		// 卷积核参数
		ap_uint<8> kernel_width, // 卷积核宽度
		ap_uint<8> kernel_height, // 卷积核高度

		ap_uint<8> stride_x, // 宽度方向步长
		ap_uint<8> stride_y, // 高度方向步长

		ap_uint<1> padding_mode, // 卷积的模式; 0: valid(没有padding填充), 1:same(输入输出的图大小不变)

		ap_uint<1> relu_en, // 激活函数

		tile_type input_feature[], ap_uint<4> input_feature_precision,	// 输入特征图地址和精度(小数点位置)
		tile_type weight[], ap_uint<4> weight_precision,// 权重地址和精度(小数点位置)
		tile_type output_feature[], ap_uint<4> out_feature_precision// 输出特征图地址和精度(小数点位置)
)
{
	#pragma HLS INTERFACE m_axi depth=999999999 port=weight offset=slave
	#pragma HLS INTERFACE m_axi depth=999999999 port=output_feature offset=slave
	#pragma HLS INTERFACE m_axi depth=999999999 port=input_feature offset=slave
	// Feature: [CHin/K][H][W][K]
	// Weight: [CHout][CHin/K][KH][KW][K]

	// 根据卷积模式,计算padding
	ap_uint<8> padding_x, padding_y;
	if(padding_mode == 0){
		padding_x = padding_y = 0;
	}else{
		padding_x = (kernel_width-1)/2;
		padding_y = (kernel_height-1)/2;
	}
	// 计算分块个数
	ap_uint<16> div_tile_num = (in_channel + K-1) / K;
	// 计算输出截断精度
	ap_uint<5> out_truncate = input_feature_precision + weight_precision - out_feature_precision;
	/*
	 * [x x x] x x x
	 * x x [x x x] x
	 */
	// 计算输出宽度和高度
	ap_uint<16> out_width = (in_width + padding_x*2) / stride_x + 1;
	ap_uint<16> out_height = (in_height + padding_y*2) / stride_y + 1;

	// 计算输出特征中一个tile的数据
	ap_int<16> out_tile = 0;
	// 相乘结果累加
	acc_type sum=0;

	// 选择输出特征的第y行,第x列,第c_out个输出通道的数据
	// 选择第c_out个权重的第y行,第x列,第tile_index个分块


	LOOP_out_height:
	for(int i = 0; i < out_height; ++ i){
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
		LOOP_out_width:
		for(int j = 0; j < out_width; ++ j){
#pragma HLS LOOP_TRIPCOUNT min=10 max=10 avg=10
			LOOP_out_channel:
			for(int out_index = 0; out_index < out_channel; ++ out_index){
#pragma HLS LOOP_TRIPCOUNT min=10 max=10 avg=10

				LOOP_kernel_height:
				for(int kh = 0; kh < kernel_height; ++ kh){
#pragma HLS LOOP_TRIPCOUNT min=5 max=5 avg=5
					LOOP_kernel_width:
					for(int kw = 0; kw < kernel_width; ++ kw){
#pragma HLS LOOP_TRIPCOUNT min=5 max=5 avg=5
						LOOP_div_tile_num:
						for(int tile_index = 0; tile_index < div_tile_num; ++ tile_index){
#pragma HLS PIPELINE II=2
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
							// 获取计算点
							ap_uint<16> in_h = i*stride_y-padding_y + kh;
							ap_uint<16> in_w = j*stride_x-padding_x + kw;

							// 获取输入特征和权重的一个块数据
							tile_type data_tile, weight_tile;
							// 有padding会越界
							if(in_h >= 0 && in_h < in_height && in_w >= 0 && in_w < in_width){
								data_tile = input_feature[in_width*in_height*tile_index + in_width*in_h + in_w];
								weight_tile = weight[kernel_width*kernel_height*div_tile_num*out_index
													 + kernel_width*kernel_height*tile_index
													 + kernel_width*kh+kw];
							}else{
								data_tile = 0; weight_tile = 0;
							}
							// 块数据相乘
							mul_tile_type mul_tile_data;
							for(int k = 0; k < K; ++ k)
								mul_tile_data.range(k*32+31, k*32) =
										(data_type)data_tile.range(k*16+15, k*16)*
										(data_type)weight_tile.range(k*16+15, k*16);
							// 相乘结果累加
							for(int k = 0; k < K; ++ k)
								sum += (mul_tile_type)mul_tile_data.range(k*32+31, k*32);

							if(tile_index == div_tile_num-1 && kh == kernel_height-1 && kw == kernel_width-1){
								// 激活函数
								if(relu_en & sum < 0) sum = 0;
								// 截断多余精度
								acc_type res = sum >> out_truncate;

								if (res > 32767)
									res = 32767;
								else if (res < -32768)
									res = -32768;

								// 先缓存下来,下面一次写入
								out_tile.range((out_index % K) * 16 + 15, (out_index % K) * 16) = res;
								sum = 0;
								// 存tile里的一个数据
								// 一个tile都存完了或者存到最后一个通道凑不够一个tile
								if((out_index%K) == K - 1 || out_index == (out_channel - 1)){
									output_feature[(out_index/K)*out_width*out_height + out_width*i+j] = out_tile;
									out_tile = 0;
								}
							}
						}
					}
				}


			}
		}
	}
}

C综合已经达到我们的预期了

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

访问方式的优化-通道方向的并行

给特征图和权重分别分配一条总线,那么就可以同时取出两个数据,只需要一个周期就可以完成一次tile的计算

bundle:一捆,不选的话默认是同一个总线

所以这里我们给input_feature和weight取两个不同的名字,就给它们分配了不同的总线:

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

AXI是全双工的,可以同时写和读,但是同时读两个就不太行,读一个的同时写一个可以。所以不需要给output_feature分配一个总线

然后我们就可以在一个周期内运行一次tile计算,设置pipeline的II = 1:

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

也就是K个数据的乘法和累加,需要K个乘法器同时进行

FPGA HLS 卷积单元 数据类型&hls优化&约束设置

可以看出来是2500个周期:1*10*10*5*5*(1 clock)=2500文章来源地址https://www.toymoban.com/news/detail-437063.html

到了这里,关于FPGA HLS 卷积单元 数据类型&hls优化&约束设置的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【嵌入式系统应用开发】FPGA——HLS入门实践之led灯闪烁

    HLS(High Level Synthesis) :一款高层次综合工具。 能够将 C/C++ 或者 system C 等高级语言转化为 RTL (底层硬件描述语言)电路,降低开发时间。 提供了常见的库(例如图像处理相关的 OpenCv 库和其 它的数学库)。 可以创建IP并通过例化或者使用 BlockDesign 的方式应用到项目中。 转化原

    2024年02月05日
    浏览(52)
  • FPGA多路视频叠加融合 HLS算法实现 提供2套工程源码和技术支持

    视频叠加和融合在FPGA图像处理领域有着广泛应用,但其复杂的内存访问机制和视频叠加透明度的融合,使得实现难度很大,让很多FPGA工程师望而却步,在目前的技术条件下,使用HLS实现视频叠加融合是最简单方便的实现方式,本设计也是基于此实现。 本设计提供2套vivado工程

    2024年02月12日
    浏览(34)
  • FPGA高端项目:FPGA基于GS2971的SDI视频接收+HLS多路视频融合叠加,提供1套工程源码和技术支持

    FPGA高端项目:FPGA基于GS2971的SDI视频接收+HLS多路视频融合叠加,提供1套工程源码和技术支持 目前FPGA实现SDI视频编解码有两种方案:一是使用专用编解码芯片,比如典型的接收器GS2971,发送器GS2972,优点是简单,比如GS2971接收器直接将SDI解码为并行的YCrCb422,GS2972发送器直接

    2024年03月15日
    浏览(64)
  • FPGA高端项目:FPGA基于GS2971+GS2972架构的SDI视频收发+HLS图像缩放+多路视频拼接,提供4套工程源码和技术支持

    FPGA高端项目:FPGA基于GS2971+GS2972架构的SDI视频收发+HLS图像缩放+多路视频拼接,提供4套工程源码和技术支持 目前FPGA实现SDI视频编解码有两种方案:一是使用专用编解码芯片,比如典型的接收器GS2971,发送器GS2972,优点是简单,比如GS2971接收器直接将SDI解码为并行的YCrCb422,

    2024年03月26日
    浏览(42)
  • vue+hls.js播放hls视频,踩坑记录

    需要在管理后台查看直播历史视频,历史视频为hls流视频,格式为 http://xxxxxxxx.m3u8 2.界面展示原型 1.查询调取改变接口列表数据,导致播放的视频未变化? 检查发现不同页面还是重复的hls组件实例,使每个页面的hls组件实例唯一性,不复用,给组件绑定key解决

    2024年02月04日
    浏览(31)
  • Vitis HLS 构建项目并生成IP核(Vivado HLS)

    Vitis HLS,可以通过它,用C和C++建立和封装一个IP核,从Vivado 2021的版本开始内置,用于替代Vivado HLS,由于它太新了,网上有关教程很少(2020的版本还是Vivado HLS),所以这个系列的文章,用于记录如何使用Vitis HLS。 使用Vitis HLS开发时,最好不要再使用C语言,而是C++,同时,使

    2024年01月21日
    浏览(33)
  • 分类预测 | MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双向门控循环单元数据分类预测

    分类效果 基本描述 1.MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双向门控循环单元数据分类预测,运行环境Matlab2021b及以上; 2.基于麻雀优化算法(SSA)、卷积神经网络(CNN)和双向门控循环单元(BiGRU)的数据分类预测程序; 3.多特征输入单输出的二分类及多分类模型。程序内注

    2024年02月08日
    浏览(30)
  • 分类预测 | MATLAB实现WOA-CNN-BiGRU鲸鱼算法优化卷积双向门控循环单元数据分类预测

    分类效果 基本描述 1.Matlab实现WOA-CNN-BiGRU多特征分类预测,多特征输入模型,运行环境Matlab2020b及以上; 2.基于鲸鱼算法(WOA)优化卷积神经网络-双向门控循环单元(CNN-BiGRU)分类预测,优化参数为,学习率,隐含层节点,正则化参数; 3.多特征输入单输出的二分类及多分类模型。

    2024年02月07日
    浏览(32)
  • HLS入门实践

    1.1 HLS简介 HLS(High-Level Synthesis)高层综合,就是将 C/C++的功能用 RTL 来实现,将 FPGA 的组件在一个软件环境中来开发,这个模块的功能验证在软件环境中来实现,无缝的将硬件仿真环境集合在一起,使用软件为中心的工具、报告以及优化设计,很容易的在 FPGA 传统的设计工具中

    2024年02月03日
    浏览(22)
  • HLS新手入门教程

    HLS是一种高级综合技术,它允许开发人员使用高级语言(如C、C++和SystemC)来描述数字电路的行为和功能,然后将其转换为硬件电路实现。这种转换过程是自动完成的,因此开发人员无需手动编写硬件描述语言(HDL)。 HLS的主要目的是简化FPGA设计流程,提高设计效率和设计质

    2024年02月02日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包