一起学习用Verilog在FPGA上实现CNN----(三)激活层设计

该篇博客详细介绍了如何使用Verilog在FPGA上实现LeNet-5网络中的Tanh激活层。首先,它分解了Tanh操作为HyperBolicTangent单元,通过3个乘法和1个加法步骤实现。接着,构建了UsingTheTanh层,该层由多个HyperBolicTangent单元组成,处理卷积层的输出。博主提供了从设计输入、代码实现、分析综合到功能仿真的完整过程,并展示了如何将激活层与卷积层(integrationConv)集成。最后,通过实例验证了设计的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 激活层设计

LeNet-5网络的激活函数是双曲正切函数(TanH),项目中tanh函数模块由完整的层UsingTheTanh构成,该层由较小的处理单元HyperBolicTangent组成

1.1 HyperBolicTangent

处理单元HyperBolicTangent,对每个输入执行Tanh操作,原理图如图所示,输入为位宽16的数,输出位宽也是16。该单元将Tanh运算分为3个乘法操作和1个加法操作:

  • 首先,得到x项的增量项,即x^2
  • 然后,将当前x项与下一项相乘
  • 然后,将每个相应的最终x项与其系数相乘
  • 最后,将每个结果项与前一项相加

在这里插入图片描述

1.2 UsingTheTanh

UsingTheTanh是Tanh层,由HyperBolicTangent单元组成,原理图如图所示。Tanh层的输入数据位宽为75264,即为卷积层的输出数据

在这里插入图片描述

2 代码实现

一起学习用Verilog在FPGA上实现CNN----(二)卷积层设计已经完成卷积层的设计,下面我们继续激活层的代码实现

2.1 HyperBolicTangent16

2.1.1 设计输入

创建HyperBolicTangent16文件,操作如图:

在这里插入图片描述

输入文件名:

在这里插入图片描述

确认创建:

在这里插入图片描述
双击打开,输入如下代码:

module HyperBolicTangent16 (x,reset,clk,OutputFinal,Finished);
parameter DATA_WIDTH=16;
localparam taylor_iter=4;//I chose 4 Taylor Coefficients to undergo my tanh operation
input signed [DATA_WIDTH-1:0] x;

input clk;
input reset;
output reg Finished;
output reg[DATA_WIDTH-1:0]  OutputFinal;
reg [DATA_WIDTH*taylor_iter-1:0] Coefficients ; //-17/315 2/15 -1/3 1
wire [DATA_WIDTH-1:0] Xsquared; //To always generate a squared version of the input to increment the power by 2 always.
reg [DATA_WIDTH-1:0] ForXSqOrOne; //For Multiplying The power of X(1 or X^2)
reg [DATA_WIDTH-1:0] ForMultPrevious; //output of the first multiplication which is either with 1 or x(X or Output1)
wire [DATA_WIDTH-1:0] OutputOne; //the output of Mulitplying the X term with its corresponding power coeff.
wire [DATA_WIDTH-1:0] OutOfCoeffMult; //the output of Mulitplying the X term with its corresponding power coeff.
reg  [DATA_WIDTH-1:0] OutputAdditionInAlways;
wire [DATA_WIDTH-1:0] OutputAddition; //the output of the Addition each cycle 

floatMult16 MSquaring (x,x,Xsquared);//Generating x^2   得到x项的增量项,即x^2
floatMult16 MGeneratingXterm (ForXSqOrOne,ForMultPrevious,OutputOne); //Generating the X term [x,x^3,x^5,...]  将当前x项与下一项相乘
floatMult16 MTheCoefficientTerm (OutputOne,Coefficients[DATA_WIDTH-1:0],OutOfCoeffMult); //Multiplying the X term by its corresponding coeff. 将每个相应的最终x项与其系数相乘
floatAdd16 FADD1 (OutOfCoeffMult,OutputAdditionInAlways,OutputAddition); //Adding the new term to the previous one     ex: x-1/3*(x^3) 将每个结果项与前一项相加
reg [DATA_WIDTH-1:0] AbsFloat; //To generate an absolute value of the input[For Checking the convergence]

always @ (posedge clk) begin
AbsFloat=x;//Here i hold the input then i make it positive whatever its sign to be able to compare to implement the rule |x|>pi/2   which is the convergence rule
AbsFloat[15]=0;
if(AbsFloat>16'sb0011111001001000)begin 
  //The Finished bit is for letting the bigger module know that the tanh is finished
  if (x[15]==0)begin 
    OutputFinal= 16'b0011110000000000;Finished =1'b 1;//here i assign it an immediate value of Positive Floating one
  end 
    if (x[15]==1)begin 
    OutputFinal= 16'b1011110000000000;Finished =1'b 1;//here i assign it an immediate value of Negative Floating one
    end
end
//here i handle the case of it equals +- pi/2    so i got the exact value and handle it also immediately
else if (AbsFloat==16'sb0011111001001000)
  begin 
    if (x[15]==0)begin 
  OutputFinal=16'b0011110000000000;Finished=1'b 1;
  end
  else begin 
  OutputFinal=16'b1011110000000000;Finished=1'b 1;
  end
  end
else begin 
  //First instance of the tanh
if(reset==1'b1)begin  
	Coefficients=64'b1010101011101000_0011000001000100_1011010101010101_0011110000000000;//the 4 coefficients of taylor expansion
	ForXSqOrOne=16'b0011110000000000; //initially 1
	OutputAdditionInAlways=16'b0000000000000000; //initially 0
	ForMultPrevious=x;
Finished=0;
end
else begin
 	ForXSqOrOne=Xsquared;
	ForMultPrevious=OutputOne; //get the output of the second multiplication to multiply with x
	Coefficients=Coefficients>>DATA_WIDTH; //shift 32 bit to divide the out_m1 with the new number to compute the factorial
  OutputAdditionInAlways=OutputAddition;
  Finished=0;
end
// the end of the tanh
if(Coefficients==64'b0000000000000000_0000000000000000_0000000000000000_0000000000000000)begin 
	OutputFinal=OutputAddition;
	Finished =1'b 1;
end
end 
end
endmodule 

如图所示:

在这里插入图片描述

2.1.2 分析与综合

将HyperBolicTangent16设置为顶层:

在这里插入图片描述

对设计进行分析,操作如图:

在这里插入图片描述

分析后的设计,Vivado自动生成原理图,如图:

在这里插入图片描述

对设计进行综合,操作如图:

在这里插入图片描述

综合完成,关闭即可:

在这里插入图片描述

2.1.3 功能仿真

创建TestBench,操作如图所示:

在这里插入图片描述

输入激励文件名:

在这里插入图片描述

双击打开,输入激励代码:

`timescale 1ns / 1ps
module tb_HyperBolicTangent16();
reg clk, reset;
reg [15:0]x;
wire [15:0]OutputFinal;
wire Finished;

localparam PERIOD = 100;

always
	#(PERIOD/2) clk = ~clk;

initial begin
	#0 //starting the tanh 
	clk = 1'b1;
	reset = 1'b1;
	// trying a random input(0.600000023842)     where tanh(0.600000023842)=0.53704958
	x=16'b0011100011001101;

	#(PERIOD/2)
	reset = 0;	
	
	#400
	// waiting for 4 clock cycles then checking the output with approx.(0.53685)
	if(OutputFinal==32'b00111111000010010110111101111011 && Finished==1'b1)begin 
	 $display("Result is Right [Not in convergence region]");	  
	  end
	  else begin 
	 $display("Result is Wrong");	 
	    end	 
	    //trying another input which will be in the convergence region(3)  the output will be converged to 1    and reseting the function again
	    x=16'b0100001000000000;
	    
		reset = 1'b1;
		#(PERIOD/2)
		reset=1'b0;
		#200
		// checking if the output is 1
		if(OutputFinal==32'b00111111100000000000000000000000)begin 
	 $display("Result is Right [ convergence region]");	  
	  end
	  else begin 
	 $display("Result is Wrong ");	 
	    end	
	$stop;
end

HyperBolicTangent16 UUT
(
  .x(x),
	.clk(clk),
	.reset(reset),
	.OutputFinal(OutputFinal),
	.Finished(Finished)	
);
endmodule

如图所示:

在这里插入图片描述

将tb_HyperBolicTangent16设置为顶层:

在这里插入图片描述
开始进行仿真,操作如下:

在这里插入图片描述

开始仿真,如图:

在这里插入图片描述

仿真波形,如图:

在这里插入图片描述

关闭仿真:

在这里插入图片描述

2.2 UsingTheTanh16

2.2.1 设计输入

创建UsingTheTanh16文件,如图:

在这里插入图片描述

双击打开,输入代码:

module UsingTheTanh16(x,clk,Output,resetExternal,FinishedTanh);
parameter DATA_WIDTH=16;
parameter nofinputs=7;// deterimining the no of inputs entering the function
input resetExternal;// controlling this layer
input  signed [nofinputs*DATA_WIDTH-1:0] x;
input clk;
output reg FinishedTanh;
reg reset;// for the inner tanh
output reg [nofinputs*DATA_WIDTH-1:0]Output;
wire [DATA_WIDTH-1:0]OutputTemp;
reg [7:0]counter=0;
wire Finished;
reg [7:0]i;
// the inner tanh taking inputs in 32 bits and then increment using the i operator
HyperBolicTangent16 TanhArray (x[DATA_WIDTH*i+:DATA_WIDTH],reset,clk,OutputTemp,Finished);
 
 
always@(posedge clk)
begin 
  counter=counter+1;
// if the external reset =1 then make everything to 0
if(resetExternal==1) begin reset=1;i=0;FinishedTanh=0; end
//checking if the tanh is not finished so continue your operation and low down the reset to continue
  else if(FinishedTanh==0) begin 
    if(reset==1)begin reset=0; end 
    // if it is finished then store the output of the tanh and increment the input forward
      else if (Finished==1)begin Output[DATA_WIDTH*i+:DATA_WIDTH]=OutputTemp;reset=1;i=i+1;end
// check if all the inputs are finished then the layer is OK
if(i==nofinputs)
  begin FinishedTanh=1;end
end 

end
endmodule 

如图所示:

在这里插入图片描述

2.2.2 分析与综合

将UsingTheTanh16设置为顶层:

在这里插入图片描述

关闭上次的分析文件:

在这里插入图片描述

对设计进行分析,操作如图:

在这里插入图片描述
分析后的设计,Vivado自动生成原理图,如图:

在这里插入图片描述
对设计进行综合,操作如图:

在这里插入图片描述

2.2.3 功能仿真

创建TestBench,操作如图所示:

在这里插入图片描述

双击打开,输入激励代码:

module tb_UsingTheTanh16();
reg clk, resetExternal;
reg [63:0]x;
wire [63:0]Output;
wire FinishedTanh;

localparam PERIOD = 100;

always
	#(PERIOD/2) clk = ~clk;

initial begin
	#0 //starting the tanh 
	clk = 1'b1;
	resetExternal = 1'b1;
	// trying a random input(0.6,3)     where tanh(0.600000023842)=0.53704958, tanh(3)~=1
	x=64'b00111111000110011001100110011010_01000000010000000000000000000000;

	#(PERIOD/2)
	resetExternal = 1'b0;	
	
	#800
	// waiting for The final output which will be (0.57,1)
	if(Output==64'b00111111000010010110111101111011_00111111100000000000000000000000 && FinishedTanh==1'b1)begin 
	 $display("Result is right (for the full array of inputs)");	  
	  end
	  else begin 
	 $display("Result is Wrong");	 
	    end	 
	$stop;
end

UsingTheTanh16 UUT
(
  .x(x),
	.clk(clk),
	.Output(Output),
	.resetExternal(resetExternal),	
	.FinishedTanh(FinishedTanh)	
);

endmodule

如图所示:

在这里插入图片描述

将tb_UsingTheTanh16文件设置为顶层:

在这里插入图片描述
开始进行仿真,操作如下:

在这里插入图片描述

开始仿真,如图:

在这里插入图片描述

仿真波形,如图:

在这里插入图片描述

2.3 IntegrationConv

2.3.1 设计输入

双击打开integrationConv文件,修改代码为:

module integrationConv (clk,reset,CNNinput,Conv,ConvOutput);

parameter DATA_WIDTH = 16;
parameter ImgInW = 32;
parameter ImgInH = 32;
parameter ConvOut = 28;
parameter Kernel = 5;
parameter DepthC = 6;


input clk, reset;
input [ImgInW*ImgInH*DATA_WIDTH-1:0] CNNinput;
input [Kernel*Kernel*DepthC*DATA_WIDTH-1:0] Conv;
output [ConvOut*ConvOut*DepthC*DATA_WIDTH-1:0] ConvOutput;

reg TanhReset;
wire TanhFlag;

wire [ConvOut*ConvOut*DepthC*DATA_WIDTH-1:0] Cout;
wire [ConvOut*ConvOut*DepthC*DATA_WIDTH-1:0] CoutTanH;

convLayerMulti C1
(
	.clk(clk),
	.reset(reset),
	.image(CNNinput),
	.filters(Conv),
	.outputConv(ConvOutput)
);
UsingTheTanh16
#(.nofinputs(ConvOut*ConvOut*DepthC))
Tanh1(
      .x(Cout),
      .clk(clk),
      .Output(CoutTanH),
      .resetExternal(TanhReset),
      .FinishedTanh(TanhFlag)
      );
endmodule

如图所示:

在这里插入图片描述

2.3.2 分析与综合

将integrationConv设置为顶层:

在这里插入图片描述

关闭上次的分析文件:

在这里插入图片描述

对设计进行分析,操作如图:

在这里插入图片描述

分析后的设计,Vivado自动生成原理图,如图:

在这里插入图片描述

对设计进行综合,操作如图:

在这里插入图片描述

纪念一下,2022卡塔尔世界杯,阿根廷捧起大力神杯,梅西圆梦

在这里插入图片描述

希望本文对大家有帮助,上文若有不妥之处,欢迎指正

分享决定高度,学习拉开差距

<think>好的,我需要帮助用户了解如何使用VerilogFPGA实现CNN。首先,用户的问题是关于教程的,所以我需要整理出一个结构化的步骤指南。根据用户提供的个引用内容,他们涉及了总体概述、池化层实现和全连接层设计。我应该把这些部分整合成一个完整的实现流程。 首先,我应该从总体架构开始,引用[1]提到了LeNet-5在ZYNQ FPGA上的实现,这可以作为例子。需要分模块说明,比如卷积层、池化层、全连接层,每个部分对应不同的引用内容。 接下来是模块设计。卷积层需要并行处理,使用乘法器和加法器,可能涉及到数据缓冲和权重存储,这里引用[3]提到的processingElement模块,包含乘加单元,可以用来解释卷积操作。池化层部分引用[2]提到用最大值池化,需要设计比较器和选择器,同时考虑数据分割和并行处理。全连接层引用[3]中的累乘累加结构,需要处理矩阵向量乘法,这里可能需要展开成多个PE。 然后是数据流控制,需要协调各层的数据传输,可能用状态机控制。资源优化部分要考虑内存带宽和定点数量化,以及流水线设计提升速度。验证部分需要结合软件仿真和FPGA测试,比如MNIST数据集测试准确率。 最后,生成相关问题时要覆盖设计挑战、优化方法、优缺点比较、资源分配和新兴技术的影响。确保每个步骤都有对应的引用支持,并且回答结构清晰,分点说明,符合用户要求的系统级指令和中文回答。</think>### 使用VerilogFPGA实现CNN的步骤指南 #### 1. **总体架构设计** - **网络选择与简化**:从LeNet-5等轻量级CNN结构入手,如图像输入层→卷积层→池化层→全连接层[^1]。 - **模块划分**:按功能分离为卷积计算单元、池化单元、激活函数模块、数据缓存控制器等。 - **软硬协同**:通过ZYNQ FPGA的PS端(ARM处理器)控制PL端(Verilog逻辑),实现数据预处理与结果输出。 #### 2. **卷积层实现** - **并行计算核心**:设计processingElement模块,包含乘法器和加法器,支持多通道并行乘加运算[^3]。 ```verilog module processingElement( input [7:0] pixel, weight, output reg [15:0] result ); always @(*) begin result = pixel * weight; // 8位定点数乘法 end endmodule ``` - **数据复用优化**:采用行缓冲(line buffer)存储特征图,滑动窗口生成模块提供3×3/5×5卷积核输入。 - **权重存储**:使用Block RAM预存训练好的卷积核参数,通过地址映射实现多核切换。 #### 3. **池化层设计** - **硬件加速策略**:对2×2区域同时比较,用多路选择器输出最大值(最大池化)[^2]。 - **带宽优化**:池化后数据量减少75%,需调整FIFO深度匹配下游模块吞吐量。 - **参数化实现**: ```verilog module max_pooling( input [7:0] window[0:3], // 2x2输入窗口 output reg [7:0] pooled ); always @(*) begin pooled = (window[0] > window[1]) ? window[0] : window[1]; pooled = (pooled > window[2]) ? pooled : window[2]; pooled = (pooled > window[3]) ? pooled : window[3]; end endmodule ``` #### 4. **全连接层实现** - **矩阵向量乘法**:展开为多个PE并行计算,每个PE处理一个神经元权重累加。 - **激活函数**:ReLU用简单的符号位判断,Sigmoid可通过查找表(LUT)实现- **资源平衡**:层间插入FIFO缓冲解决计算与数据传输速度不匹配问题。 #### 5. **数据流控制** - **状态机设计**:定义初始化→数据加载→卷积计算→池化→全连接→结果输出等状态。 - **时序对齐**:为每层添加流水线寄存器,保证多级操作时序一致性。 - **带宽管理**:采用双端口RAM实现特征图乒乓操作,隐藏数据传输延迟。 #### 6. **优化关键点** - **定点量化**:将浮点权重转换为8位定点数,减少资源消耗20-40%[^1]。 - **并行度提升**:通过展开循环实现输入通道并行,例如同时处理4通道数据。 - **存储器复用**:动态配置Block RAM作为卷积权重缓存或特征图存储。 #### 7. **验证方法** - **协同仿真**:用MATLAB生成测试向量,通过Verilog Testbench验证功能正确性。 - **实际部署**:在PYNQ-Z2开发板上运行MNIST识别,实测识别速度可达200帧/秒[^1]。 - **精度分析**:对比FPGA输出与Python浮点模型结果,误差控制在±3%以内。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鲁棒最小二乘支持向量机

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值