基于FPGA的卷积神经网络实现(二)框架

目录:

  1. 简介
  2. 框架
  3. 资源分配(1)
  4. 资源分配(2)
  5. 数据量化(1)
  6. 数据量化(2)
  7. 数据读写
  8. 卷积模块
  9. 池化、全连接与输出

我发现点下面的链接会跳到一个不知道是谁的CSDN下面需要付费下载,这个很迷惑,麻烦自行复制下面的链接。
Github:https://github.com/MasLiang/CNN-On-FPGA
那个不知道是谁的链接:https://download.csdn.net/download/weixin_42138780/18551586
没有下载不让举报,有办法的朋友麻烦举报一下

首先设计整体框架,再根据整体结构去设计每一个子模块,通过例化子模块来形成完整的工程程序。也就是基于FPGA的卷积神经网络实现里面的top.v文件。
本项目要实现的神经网络结构如下图所示:
本项目实现的整体网络结构
对于在FPGA当中实现神经网络而言,每一种功能的层都单独设计一个子模块。而使用这些子模块搭建一个完整的网络框架主要有两种方法:

  1. 每一层都单独进行例化,占用单独的硬件资源。
  2. 同样功能的层复用同一部分硬件资源,通过parameter来进行区分

本项目为了初学者放心食用选择比较简单的第一种方式,当然前提是硬件资源足够多的,如果实现一个较大型的网络这样的方式是不可能的,还是需要使用第二种方法,不过偶从入门来说,第一种方法是非常容易理解的。
在最开始先要设置一个时钟模块,调用片上PLL,也就是clocking Wizard IP核。我使用的片上输入时钟为50M,输出时钟设置为两路,一路为原本的50M时钟,用于和系统时钟相关的地方(当然这个不一定会的上),另一路先随意设置为200M,在后面实际使用的时候再进行调整。
时钟模块设置(1)
时钟模块设置(2)
设置好后在top文件中进行例化

clock_pll clock_control
   (
    .CLK_IN1			(clk_in)		,
    .CLK_OUT1			(clk_50)		,
    .CLK_OUT2			(clk_200)		,
    .RESET				(~rst_n)		,
    .LOCKED				()
	 );

那么这样一来我们就拥有一个快速的时钟,能够让我们的卷积神经网络飞速运行起来了。
下面我们需要一些子模块了,根据整个网络的结构,我们需要设计输入数据存储层、参数存储层、卷积层、池化层、全连接层,当然,还需要层间的缓存层。对于一个卷积层参数与输入数据需要同时进入,因此可以将参数存储层和层间输入缓存层合并为同一个模块。
以卷积层为例,先写出大概的框架,后面再慢慢填补。

module Layer1_Conv_Top#(
	parameter	filter_size=5		
	)
	(
	input				clk_in,
	input 				rst_n,
	input 				data_in,
	input				weight_in,
	output		reg 	data_out
    );
    data_out <= data_in*weight_in;
endmodule

各个子模块创建完毕后,再去top文件里面例化一下并串联在一起,一个完整的框架就搞定了。

pic_in layer0(	
	.clk_in			(clk_200)	,
	.rst_n			(rst_n)		,					
	.map			(l1d1)		,				
	.ready			(l1r1)		,
	.weight			(l1w1)		
    );
Layer1_Conv_Top layer1_conv(
	.clk_in			(clk_200)	,
	.rst_n			(rst_n)		,
	.data_in		(l1d1)		,
	.weights		(l1w1)		,
	.data_out		(l1d2)		
    );
Layer1_Pool_Top layer1_pool
	(
	.clk_in			(clk_200)	,	
	.rst_n			(rst_n)		,
	.data_in		(l1d2)		,
	.data_out		(l1d3)	
    );
pool1_out_buffer layer1_pool_buffer(
	.clk_in			(clk_200)	,
	.rst_n			(rst_n)		,
	.data_in		(l1d3)		,
	.data_out		(l2d1)		,
	.weight			(l2w1)
    );
	 
//layer2

Layer2_Conv_Top layer2_conv(
	.clk_in			(clk_200)	,
	.rst_n			(rst_n)		,
	.data_in		(l2d1)		,
	.weights		(l2w1)		,
	.data_out		(l2d2)	
    );
Layer2_Pool_Top layer2_pool
	(
	.clk_in			(clk_200)	,	
	.rst_n			(rst_n)		,
	.data_in		(l2d2)		,
	.data_out		(l2d3)		
    );
pool2_out_buffer layer2_pool_buffer(
	.clk_in			(clk_200)	,
	.rst_n			(rst_n)		,
	.data_in		(l2d3)		,
	.data_out		(l3d1)		,,
	.weight			(l3w1)
    );

//layer3

Layer3_Conv_Top layer3_conv(
	.clk_in			(clk_200)	,
	.rst_n			(rst_n)		,
	.data_in		(l3d1)		,
	.weights		(l3w1)		,
	.data_out		(l3d2)	
    );
conv3_out_buffer layer3_conv_buffer
	(
	.clk_in			(clk_200)	,
	.rst_n			(rst_n)		,
	.data_in		(l3d2)		,
	.data_out		(l3d3)	
    );
Layer3_Pool_Top layer3_pool
	(
	.clk_in			(clk_200)	,	
	.rst_n			(rst_n)		,
	.data_in		(l3d3)		,
	.data_out		(l4d1)		
    );

//full_connection
ful_conn  layer4_full_connect(
	.clk_in		(clk_200),
	.rst_n		(rst_n),
	.data_in	(l4d1),
	.data_out	(Dout)
    );

程序中用来连接各个子模块的wire类型数据的命名规则是

l(layer)+第几层+d(data)+第几个模块

到现在一个整体的网络框架应该是搭建完毕了。这里有个很值得注意的地方在于,为了编写简单,我将同样功能的模块都单独写了子模块,这样对于他们的不同就可以一个一个去处理。但是事实上,他们的整体思路是一样的,只需要改一些参数,也就是说通过设置许多的parameter也可以达到同样的目的。在所有的子模块介绍完后我将针对这一问题单独进行讲解。

  • 35
    点赞
  • 133
    收藏
    觉得还不错? 一键收藏
  • 20
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值