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

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

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

本项目为了初学者放心食用选择比较简单的第一种方式,当然前提是硬件资源足够多的,如果实现一个较大型的网络这样的方式是不可能的,还是需要使用第二种方法,不过偶从入门来说,第一种方法是非常容易理解的。
在最开始先要设置一个时钟模块,调用片上PLL,也就是clocking Wizard IP核。我使用的片上输入时钟为50M,输出时钟设置为两路,一路为原本的50M时钟,用于和系统时钟相关的地方(当然这个不一定会的上),另一路先随意设置为200M,在后面实际使用的时候再进行调整。
基于FPGA的卷积神经网络实现(二)框架_第2张图片
基于FPGA的卷积神经网络实现(二)框架_第3张图片
设置好后在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也可以达到同样的目的。在所有的子模块介绍完后我将针对这一问题单独进行讲解。

你可能感兴趣的:(基于FPGA的卷积神经网络实现(二)框架)