一起学习用Verilog在FPGA上实现CNN----(六)SoftMax层设计

这篇具有很好参考价值的文章主要介绍了一起学习用Verilog在FPGA上实现CNN----(六)SoftMax层设计。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 SoftMax层设计

1.1 softmax

SoftMax函数的作用是输入归一化,计算各种类的概率,即计算0-9数字的概率,SoftMax层的原理图如图所示,输入和输出均为32位宽的10个分类,即32x10=320

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

本项目softmax实现逻辑为:

  • 指数计算(通过exponent实现)
  • 计算指数和(通过floatAdd实现)
  • 求指数和倒数(通过floatReciprocal实现)
  • 计算每个元素的softmax值(通过floatMult实现)

1.2 exponent

每个输入分别输入到各自的exponent模块,计算指数,该模块的输入和输出位宽均为32位,输入1个数,计算输出1个指数

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

exponent模块展开原理图,如图所示,包含2个乘法器和1个加法器

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

1.3 floatAdd

加法器计算所有指数的和

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

1.4 floatReciprocal

floatReciprocal模块计算指数和的倒数

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

floatReciprocal模块展开原理图,如图所示

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

1.5 floatMult

乘法器计算各输入的softmax值,然后输出

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2 代码实现

2.1 floatAdd

2.1.1 设计输入

创建floatAdd文件,操作如图:
verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

输入文件名:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

双击打开,输入代码:

module floatAdd (floatA,floatB,sum);
	
input [31:0] floatA, floatB;
output reg [31:0] sum;

reg sign;
reg [7:0] exponent;
reg [22:0] mantissa;
reg [7:0] exponentA, exponentB;
reg [23:0] fractionA, fractionB, fraction;	//fraction = {1,mantissa}
reg [7:0] shiftAmount;
reg cout;

always @ (floatA or floatB) begin
	exponentA = floatA[30:23];
	exponentB = floatB[30:23];
	fractionA = {1'b1,floatA[22:0]};
	fractionB = {1'b1,floatB[22:0]}; 
	
	exponent = exponentA;

	if (floatA == 0) begin						//special case (floatA = 0)
		sum = floatB;
	end else if (floatB == 0) begin					//special case (floatB = 0)
		sum = floatA;
	end else if (floatA[30:0] == floatB[30:0] && floatA[31]^floatB[31]==1'b1) begin
		sum=0;
	end else begin
		if (exponentB > exponentA) begin
			shiftAmount = exponentB - exponentA;
			fractionA = fractionA >> (shiftAmount);
			exponent = exponentB;
		end else if (exponentA > exponentB) begin 
			shiftAmount = exponentA - exponentB;
			fractionB = fractionB >> (shiftAmount);
			exponent = exponentA;
		end
		if (floatA[31] == floatB[31]) begin			//same sign
			{cout,fraction} = fractionA + fractionB;
			if (cout == 1'b1) begin
				{cout,fraction} = {cout,fraction} >> 1;
				exponent = exponent + 1;
			end
			sign = floatA[31];
		end else begin						//different signs
			if (floatA[31] == 1'b1) begin
				{cout,fraction} = fractionB - fractionA;
			end else begin
				{cout,fraction} = fractionA - fractionB;
			end
			sign = cout;
			if (cout == 1'b1) begin
				fraction = -fraction;
			end else begin
			end
			if (fraction [23] == 0) begin
				if (fraction[22] == 1'b1) begin
					fraction = fraction << 1;
					exponent = exponent - 1;
				end else if (fraction[21] == 1'b1) begin
					fraction = fraction << 2;
					exponent = exponent - 2;
				end else if (fraction[20] == 1'b1) begin
					fraction = fraction << 3;
					exponent = exponent - 3;
				end else if (fraction[19] == 1'b1) begin
					fraction = fraction << 4;
					exponent = exponent - 4;
				end else if (fraction[18] == 1'b1) begin
					fraction = fraction << 5;
					exponent = exponent - 5;
				end else if (fraction[17] == 1'b1) begin
					fraction = fraction << 6;
					exponent = exponent - 6;
				end else if (fraction[16] == 1'b1) begin
					fraction = fraction << 7;
					exponent = exponent - 7;
				end else if (fraction[15] == 1'b1) begin
					fraction = fraction << 8;
					exponent = exponent - 8;
				end else if (fraction[14] == 1'b1) begin
					fraction = fraction << 9;
					exponent = exponent - 9;
				end else if (fraction[13] == 1'b1) begin
					fraction = fraction << 10;
					exponent = exponent - 10;
				end else if (fraction[12] == 1'b1) begin
					fraction = fraction << 11;
					exponent = exponent - 11;
				end else if (fraction[11] == 1'b1) begin
					fraction = fraction << 12;
					exponent = exponent - 12;
				end else if (fraction[10] == 1'b1) begin
					fraction = fraction << 13;
					exponent = exponent - 13;
				end else if (fraction[9] == 1'b1) begin
					fraction = fraction << 14;
					exponent = exponent - 14;
				end else if (fraction[8] == 1'b1) begin
					fraction = fraction << 15;
					exponent = exponent - 15;
				end else if (fraction[7] == 1'b1) begin
					fraction = fraction << 16;
					exponent = exponent - 16;
				end else if (fraction[6] == 1'b1) begin
					fraction = fraction << 17;
					exponent = exponent - 17;
				end else if (fraction[5] == 1'b1) begin
					fraction = fraction << 18;
					exponent = exponent - 18;
				end else if (fraction[4] == 1'b1) begin
					fraction = fraction << 19;
					exponent = exponent - 19;
				end else if (fraction[3] == 1'b1) begin
					fraction = fraction << 20;
					exponent = exponent - 20;
				end else if (fraction[2] == 1'b1) begin
					fraction = fraction << 21;
					exponent = exponent - 21;
				end else if (fraction[1] == 1'b1) begin
					fraction = fraction << 22;
					exponent = exponent - 22;
				end else if (fraction[0] == 1'b1) begin
					fraction = fraction << 23;
					exponent = exponent - 23;
				end
			end
		end
		mantissa = fraction[22:0];
		sum = {sign,exponent,mantissa};			
	end		
end

endmodule

如图所示:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.1.2 分析与综合

将floatAdd设置为顶层:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

综合完成,关闭即可:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.1.3 功能仿真

创建仿真激励文件,操作如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

输入激励文件名:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

确认创建:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

`timescale 1ns / 1ps

module tb_floatAdd();
reg [31:0] floatA;
reg [31:0] floatB;
wire [31:0] sum;

initial begin
	
	// 0.0004125 + 0.000000525
	#0
	floatA = 32'hbab1cf4b;
	floatB = 32'h3aaaad74;

	// 0.0004125 + 0
	#10
	floatA = 32'b00111001110110000100010011010000;
	floatB = 32'b00000000000000000000000000000000;

	#10
	$stop;
end

floatAdd FADD
(
	.floatA(floatA),
	.floatB(floatB),
	.sum(sum)
);

endmodule

如图所示:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

将tb_floatAdd设置为顶层:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

开始进行仿真,操作如下:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

仿真波形,如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado
仿真结束,关闭仿真:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.2 floatMult

2.2.1 设计输入

创建floatMult文件,操作如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado
双击打开,输入代码:

module floatMult (floatA,floatB,product);

input [31:0] floatA, floatB;
output reg [31:0] product;

reg sign;
reg [7:0] exponent;
reg [22:0] mantissa;
reg [23:0] fractionA, fractionB;	//fraction = {1,mantissa}
reg [47:0] fraction;

always @ (floatA or floatB) begin
	if (floatA == 0 || floatB == 0) begin
		product = 0;
	end else begin
		sign = floatA[31] ^ floatB[31];
		exponent = floatA[30:23] + floatB[30:23] - 8'd127 + 8'd2;
	
		fractionA = {1'b1,floatA[22:0]};
		fractionB = {1'b1,floatB[22:0]};
		fraction = fractionA * fractionB;
		
		if (fraction[47] == 1'b1) begin
			fraction = fraction << 1;
			exponent = exponent - 1; 
		end else if (fraction[46] == 1'b1) begin
			fraction = fraction << 2;
			exponent = exponent - 2;
		end else if (fraction[45] == 1'b1) begin
			fraction = fraction << 3;
			exponent = exponent - 3;
		end else if (fraction[44] == 1'b1) begin
			fraction = fraction << 4;
			exponent = exponent - 4;
		end else if (fraction[43] == 1'b1) begin
			fraction = fraction << 5;
			exponent = exponent - 5;
		end else if (fraction[42] == 1'b1) begin
			fraction = fraction << 6;
			exponent = exponent - 6;
		end else if (fraction[41] == 1'b1) begin
			fraction = fraction << 7;
			exponent = exponent - 7;
		end else if (fraction[40] == 1'b1) begin
			fraction = fraction << 8;
			exponent = exponent - 8;
		end else if (fraction[39] == 1'b1) begin
			fraction = fraction << 9;
			exponent = exponent - 9;
		end else if (fraction[38] == 1'b0) begin
			fraction = fraction << 10;
			exponent = exponent - 10;
		end else if (fraction[37] == 1'b1) begin
			fraction = fraction << 11;
			exponent = exponent - 11;
		end else if (fraction[36] == 1'b1) begin
			fraction = fraction << 12;
			exponent = exponent - 12;
		end else if (fraction[35] == 1'b1) begin
			fraction = fraction << 13;
			exponent = exponent - 13;
		end else if (fraction[34] == 1'b1) begin
			fraction = fraction << 14;
			exponent = exponent - 14;
		end else if (fraction[33] == 1'b1) begin
			fraction = fraction << 15;
			exponent = exponent - 15;
		end else if (fraction[32] == 1'b1) begin
			fraction = fraction << 16;
			exponent = exponent - 16;
		end else if (fraction[31] == 1'b1) begin
			fraction = fraction << 17;
			exponent = exponent - 17;
		end else if (fraction[30] == 1'b1) begin
			fraction = fraction << 18;
			exponent = exponent - 18;
		end else if (fraction[29] == 1'b0) begin
			fraction = fraction << 19;
			exponent = exponent - 19;
		end else if (fraction[28] == 1'b1) begin
			fraction = fraction << 20;
			exponent = exponent - 20;
		end else if (fraction[27] == 1'b1) begin
			fraction = fraction << 21;
			exponent = exponent - 21;
		end else if (fraction[26] == 1'b1) begin
			fraction = fraction << 22;
			exponent = exponent - 22;
		end else if (fraction[27] == 1'b1) begin
			fraction = fraction << 23;
			exponent = exponent - 23;
		end
	
		mantissa = fraction[47:25];
		product = {sign,exponent,mantissa};
	end
end

endmodule

如图所示:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.2.2 分析与综合

将floatMult设置为顶层:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

关闭上次的分析文件:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

对设计进行综合,综合完成,关闭即可:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.2.3 功能仿真

创建激励文件tb_floatMult:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

双击打开,输入激励:

`timescale 1ns / 1ps

module tb_floatMult();
reg [31:0] floatA;
reg [31:0] floatB;
wire [31:0] product;

initial begin
	
	// 0.0004125 * 0.000000525
	#0
	floatA = 32'b00111001110110000100010011010000;
	floatB = 32'b00110101000011001110110110111010;

	// 0.0004125 * 0
	#10
	floatA = 32'b00111001110110000100010011010000;
	floatB = 32'b00000000000000000000000000000000;

	#10
	$stop;
end

floatMult FM
(
	.floatA(floatA),
	.floatB(floatB),
	.product(product)
);

endmodule

如图所示:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

将tb_floatMult设置为顶层:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

开始进行仿真:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

仿真波形如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

仿真结束,关闭仿真:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.3 exponent

2.3.1 设计输入

创建exponent文件,如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

双击打开,输入代码:

module exponent (x,clk,enable,output_exp,ack);
parameter DATA_WIDTH=32;
localparam taylor_iter=7;
input [DATA_WIDTH-1:0] x;
input clk;
input enable;
output reg ack;
output reg [DATA_WIDTH-1:0] output_exp;

reg [DATA_WIDTH*taylor_iter-1:0] divisors; // 1/6 1/5 1/4 1/3 1/2 1 1
reg [DATA_WIDTH-1:0] mult1; //is 1 in the first cycle and then the output of the second multiplication in the rest
reg [DATA_WIDTH-1:0] one_or_x; //one in the first cycle and then x for the rest
wire [DATA_WIDTH-1:0] out_m1; //output of the first multiplication which is either with 1 or x
wire [DATA_WIDTH-1:0] out_m2; //the output of the second muliplication and the input of the first
wire [DATA_WIDTH-1:0] output_add1;
reg [DATA_WIDTH-1:0] out_reg; //the output of the Addition each cycle 

floatMult FM1 (mult1,one_or_x,out_m1); 
floatMult FM2 (out_m1,divisors[31:0],out_m2); 
floatAdd FADD1 (out_m2,out_reg,output_add1); 

always @ (posedge clk) begin
    if(enable==1'b0) begin
        one_or_x=32'b00111111100000000000000000000000; //initially 1
        mult1=32'b00111111100000000000000000000000; //initially 1
        out_reg=32'b00000000000000000000000000000000; //initially 0
        output_exp=32'b00000000000000000000000000000000; //output zero until ack is 1
        divisors=224'b00111110001010101010101010101011_00111110010011001100110011001101_00111110100000000000000000000000_00111110101010101010101010101011_00111111000000000000000000000000_00111111100000000000000000000000_00111111100000000000000000000000;
        ack=1'b0; // acknowledge is 0 at the beginning
    end 
    else begin
	   one_or_x=x;
	   mult1=out_m2; //get the output of the second multiplication to multiply with x
	   divisors=divisors>>32; //shift 32 bit to divide the out_m1 with the new number to compute the factorial
	   out_reg=output_add1;
	   if(divisors==224'b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) 
	   begin
		  output_exp=output_add1;
		  ack=1'b1;
	   end
	  end
end

endmodule 

如图所示:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.3.2 分析与综合

将exponent文件设置为顶层:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

关闭上次的分析文件:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado
对设计进行综合,操作如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

综合完成,关闭即可:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.3.3 功能仿真

创建TestBench,操作如图所示:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado
双击打开,输入激励代码:

`timescale 1ns / 1ps

module tb_exponent();
localparam DATA_WIDTH=32;
reg [DATA_WIDTH-1:0] x;
reg clk;
reg enable;
wire [DATA_WIDTH-1:0] output_exp;
wire ack; 
exponent #(.DATA_WIDTH(DATA_WIDTH)) exp (.x(x),.clk(clk),.output_exp(output_exp),.ack(ack),.enable(enable));

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

initial begin
	clk=1'b1;
	x=32'b00111111010101100110110011110100; //0.8376
	enable=1'b0;
	#(PERIOD);
	enable=1'b1;
	while (ack!=1'b1) begin //7 clock cycles to finish
		#(PERIOD);
	end
	//output is 2.31074953079 and real should be 2.310814361840001 
	x=32'b10111111011101011100001010001111; //-0.96
	enable=1'b0;
   	#(PERIOD);
   	enable=1'b1;
    	while (ack!=1'b1) begin //7 clock cycles to finish
        	#(PERIOD);
    	end
	//output is 0.383025914431 and real should be 0.38289288597511206
end

endmodule

如图所示:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

将tb_exponent设置为顶层:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

开始进行仿真,操作如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

仿真波形,如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

仿真完成,关闭:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.4 floatReciprocal

2.4.1 设计输入

创建floatReciprocal文件,如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

双击打开,输入代码:

module floatReciprocal(number,enable,clk,output_rec,ack);

parameter DATA_WIDTH=32;
input [DATA_WIDTH-1:0] number; //the number that we need to get the 1/number of
input clk,enable; 
output reg[DATA_WIDTH-1:0] output_rec; // = 1/number
output reg ack;

wire [DATA_WIDTH-1:0] Ddash; // D' = Mantissa of D and exponent of -1
wire [DATA_WIDTH-1:0] P2Ddash; // (-32/17) * D'
wire [DATA_WIDTH-1:0] Xi ; // X[i]= 43/17 - (32/17)D'
wire [DATA_WIDTH-1:0] Xip1; //X[i+1]
wire [DATA_WIDTH-1:0] out0; // Xi*D
wire [DATA_WIDTH-1:0] out1; // 1-Xi*D
wire [DATA_WIDTH-1:0] out2; // X*(1-Xi*D)
reg  [DATA_WIDTH-1:0] mux;

localparam P1=32'b01000000001101001011010010110101; // 43/17
localparam P2=32'b10111111111100001111000011110001; // -32/17

assign Ddash={{1'b0,8'b01111110},number[22:0]};

floatMult FM1 (P2,Ddash,P2Ddash); // -(32/17)* D'
floatAdd FADD1 (P2Ddash,P1,Xi); // 43/17 * (-32/17)D'
floatMult FM2 (mux,Ddash,out0); // Xi*D'
floatAdd FSUB1 (32'b00111111100000000000000000000000,{1'b1,out0[DATA_WIDTH-2:0]},out1); // 1-Xi*D
floatMult FM3 (mux,out1,out2); // X*(1-Xi*D)
floatAdd FADD2 (mux,out2,Xip1); //Xi+Xi*(1-D*Xi)

/*always @(number) begin
	//when a new input is entered the ack signal is reset and the mux is Xi
	ack=1'b0; //reset finish bit
	reset=1'b1;
end*/


always @ (negedge clk) begin
	if (enable==1'b0) begin
		mux=Xi;
		ack=1'b0;
	end
	else begin 
		if(mux==Xip1) begin
			ack=1'b1; //set ack bit to show that the division is done
			output_rec={{number[31],8'b11111101-number[30:23]},Xip1[22:0]}; //sign of number, new exponent, mantissa of Xip1
		end 
		else begin
			mux=Xip1; //continue until ack is 1
		end
	end
	
end

endmodule

如图所示:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.4.2 分析与综合

将floatReciprocal文件设置为顶层:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

关闭上次的分析文件:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

综合完成,关闭:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.4.3 功能仿真

创建tb_floatReciprocal激励文件:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

`timescale 1ns / 1ps

module tb_floatReciprocal();
localparam DATA_WIDTH=32;
reg [DATA_WIDTH-1:0] num;
reg clk,enable;
wire [DATA_WIDTH-1:0] output_rec;
wire ack;

floatReciprocal #(.DATA_WIDTH(DATA_WIDTH))  FA (.number(num),.clk(clk),.output_rec(output_rec),.ack(ack),.enable(enable));

localparam PERIOD = 100;

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


initial begin
	clk=1'b1; //positive edge first
	num=32'b00111110101100001010001111010111; //0.345
	enable=1'b0;
	#(PERIOD);
	enable=1'b1;
	while( ack!=1'b1) begin
		#(PERIOD);
	end
	//output is 2.89855074883
	num=32'b10111110111111101111100111011011;
	enable=1'b0;
	#(PERIOD);
	enable=1'b1; 
	while (ack!=1'b1) begin
		#(PERIOD);
	end
	//output is -2.00803232193
	$stop;
end

endmodule

如图所示:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado
将tb_floatReciprocal设置为顶层:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

开始进行仿真,操作如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

仿真波形,如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

仿真结束,关闭仿真:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.5 softmax

2.5.1 设计输入

创建softmax文件,如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado
双击打开,输入代码:

module softmax(inputs,clk,enable,outputs,ackSoft);
parameter DATA_WIDTH=32;
localparam inputNum=10;
input [DATA_WIDTH*inputNum-1:0] inputs;
input clk;
input enable;
output reg [DATA_WIDTH*inputNum-1:0] outputs;
output reg ackSoft;

wire [DATA_WIDTH-1:0] expSum;
wire [DATA_WIDTH-1:0] expReciprocal;
wire [DATA_WIDTH-1:0] outMul;
wire [DATA_WIDTH*inputNum-1:0] exponents ;
wire [inputNum-1:0] acksExp; //acknowledge signals of exponents 
wire ackDiv; //ack signal of the division unit

reg enableDiv; //signal to enable division unit initially zero
reg [DATA_WIDTH-1:0] outExpReg;
reg [3:0] mulCounter;
reg [3:0] addCounter;

genvar i;
generate
	for (i = 0; i < inputNum; i = i + 1) begin
		exponent #(.DATA_WIDTH(DATA_WIDTH)) exp (
		.x(inputs[DATA_WIDTH*i+:DATA_WIDTH]),
		.enable(enable),
		.clk(clk),
		.output_exp(exponents[DATA_WIDTH*i+:DATA_WIDTH]),
		.ack(acksExp[i]));
	end
endgenerate

floatAdd FADD1 (exponents[DATA_WIDTH*addCounter+:DATA_WIDTH],outExpReg,expSum);
floatReciprocal #(.DATA_WIDTH(DATA_WIDTH)) FR (.number(expSum),.clk(clk),.output_rec(expReciprocal),.ack(ackDiv),.enable(enableDiv));
floatMult FM1 (exponents[DATA_WIDTH*mulCounter+:DATA_WIDTH],expReciprocal,outMul); //multiplication with reciprocal

always @ (negedge clk) begin
	if(enable==1'b1) begin
		if(ackSoft==1'b0) begin 
			if(acksExp[0]==1'b1) begin //if the exponents finished
				if(enableDiv==1'b0) begin //division still did not start
					if(addCounter<4'b1001) begin
						addCounter=addCounter+1;
						outExpReg=expSum;
					end
					else begin
						enableDiv=1'b1;
					end
				end
				else if(ackDiv==1'b1) begin //check if the reciprocal is ready
					if(mulCounter<4'b1010) begin
						outputs[DATA_WIDTH*mulCounter+:DATA_WIDTH]=outMul;
						mulCounter=mulCounter+1;
					end
					else begin
						ackSoft=1'b1;
					end
				end
			end
		end
	end
	else begin
		//if enable is off reset all counters and acks
		mulCounter=4'b0000;
		addCounter=4'b0000;
		outExpReg=32'b00000000000000000000000000000000;
		ackSoft=1'b0;
		enableDiv=1'b0;
	end
	
end

endmodule

如图所示:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.5.2 分析与综合

将softmax文件设置为顶层:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

关闭上次的分析,操作如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

对设计进行分析,如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.5.3 功能仿真

创建tb_softmax文件,如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado
双击打开,输入激励代码:

`timescale 1ns / 1ps

module tb_softmax();
localparam DATA_WIDTH=32;
localparam inputNum=10;
reg [DATA_WIDTH*inputNum-1:0] inputs;
reg clk;
reg enable;
wire [DATA_WIDTH*inputNum-1:0] outputs;
wire ackSoft;
softmax #(.DATA_WIDTH(DATA_WIDTH)) soft(inputs,clk,enable,outputs,ackSoft);

localparam PERIOD = 100;
integer count;
always 
	#(PERIOD/2) clk = ~clk;

initial begin
	clk=1'b1;
	inputs=320'b00111110010011001100110011001101_10111110010011001100110011001101_00111111100110011001100110011010_00111111101001100110011001100110_10111111011001100110011001100110_00111110100110011001100110011010_01000000010001100110011001100110_10111100101000111101011100001010_00111111100011100001010001111011_00111110101001010110000001000010;
	//inputs are 0.2 -0.2 1.2 1.3 -0.9 0.3 3.1 -0.02 1.11 0.323
	count=1;
	enable=1'b0;
	#(PERIOD);
	enable=1'b1;
	
	while(ackSoft!=1'b1) begin
		count=count+1;
		#(PERIOD);		
	end
	//outputs are 0.03255, 0.02182, 0.08847, 0.09776, 0.0108, 0.0359, 0.5687,  0.02612, 0.0808, 0.03681

	inputs=320'b00111111001100001010001111010111_10111110010011001100110011001101_00111111100110011001100110011010_00111111101001100110011001100110_10111111011001100110011001100110_00111110100110011001100110011010_01000000010001100110011001100110_10111100101000111101011100001010_00111111100011100001010001111011_00111110101001010110000001000010;
	//inputs are 0.69 -0.2 1.2 1.3 -0.9 0.3 3.1 -0.02 1.11 0.323
	count=1;
	enable=1'b0;
	#(PERIOD);
	enable=1'b1;
	while(ackSoft!=1'b1) begin
		count=count+1;
		#(PERIOD);

	end
	//outputs are 0.05207118 0.0213835  0.0866926  0.09579553 0.01062096 0.03525543 0.5572659  0.0256007  0.07923851 0.0360757
 										
end
endmodule

如图所示:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

将tb_softmax文件设置为顶层:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

开始进行仿真,如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

仿真波形:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

仿真结束,关闭:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.6 integrationFC

2.6.1 设计输入

创建integrationFC文件,操作如图:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado
双击打开,输入代码:

module integrationFC(clk,reset,iFCinput,CNNoutput);

parameter DATA_WIDTH = 32;
parameter IntIn = 120;
parameter FC_1_out = 84;
parameter FC_2_out = 10;

input clk, reset;
input [IntIn*DATA_WIDTH-1:0] iFCinput;
output [FC_2_out*DATA_WIDTH-1:0] CNNoutput;

wire [FC_1_out*DATA_WIDTH-1:0] fc1Out;
wire [FC_1_out*DATA_WIDTH-1:0] fc1OutTanh;

wire [FC_2_out*DATA_WIDTH-1:0] fc2Out;
wire [FC_2_out*DATA_WIDTH-1:0] fc2OutSMax;

reg SMaxEnable;
wire DoneFlag;

    
softmax SMax(
      .inputs(fc2Out),
      .clk(clk),
      .enable(SMaxEnable),
      .outputs(CNNoutput),
      .ackSoft(DoneFlag)
      );
      
endmodule

如图所示:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

2.6.2 分析与综合

将integrationFC设置为顶层:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

关闭上次的分析文件:

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

verilog 归一化,一起学ZYNQ,笔记,fpga开发,cnn,ZYNQ,神经网络,Vivado

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

分享决定高度,学习拉开差距文章来源地址https://www.toymoban.com/news/detail-816898.html

到了这里,关于一起学习用Verilog在FPGA上实现CNN----(六)SoftMax层设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • CNN的硚口实现: 由Verilog编写并在FPGA上合成

    目录 前言 一、环境设置 二、CNN的硬件设计思路 三、使用Verilog实现CNN

    2024年02月13日
    浏览(27)
  • 【FPGA】Verilog设计入门——时序模块及其Verilog表述

    目录 1.边沿触发型触发器及其Verilog表述 2.电平触发型锁存器及其Verilog表述  3.含异步复位/时钟使能型触发器及其Verilog表述 4.同步复位型触发器及其Verilog表述  5.异步复位型锁存器及其Verilog表述 6.Verilog的时钟过程表述的特点和规律   7.异步时序模块的Verilog表述  8.4位二进制

    2024年02月07日
    浏览(27)
  • FPGA设计Verilog基础之Verilog的运算符

    注意:后续技术分享,第一时间更新,以及更多更及时的技术资讯和学习技术资料 ,将在公众号 CTO Plus 发布,请关注公众号: CTO Plus FPGA设计Verilog基础之Verilog的运算符 Verilog是一种硬件描述语言,支持多种运算符,包括算术运算符、比较(关系)运算符、逻辑运算符、条件

    2024年02月03日
    浏览(32)
  • FPGA设计Verilog基础之Verilog全局变量和局部变量定义

    注意:后续技术分享,第一时间更新,以及更多更及时的技术资讯和学习技术资料 ,将在公众号 CTO Plus 发布,请关注公众号: CTO Plus   在Verilog中,变量可以分为全局变量和局部变量两种类型。全局变量在整个模块中都可以使用,而局部变量只能在某个特定的代码块中使用。

    2024年02月15日
    浏览(26)
  • FPGA Artix7-100T实现手写字硬件加速,纯Verilog编写的CNN神经网络加速器,有效减轻误识别问题

    fpga实现cnn神经网络加速 手写字硬件加速 FPGA artix7-100t 纯verilog编写 神经网络硬件加速 使用ov5640摄像头dvp接口 verilog实现手写字识别 包括卷积层、全连接层、池化层、softmax,有效减轻误识别问题。 注意: 该项目并未使用到arm核,是使用传统fpga的逻辑资源实现的。 ID:92299 7141

    2024年04月23日
    浏览(17)
  • FPGA(Verilog)时钟无缝切换设计与验证

    时钟切换基本模型,本文围绕“ 基本组合电路切换、解决前毛刺切换、解决后毛刺切换 ”三方面完成时钟无缝切换。 组合逻辑切换,本质就是二选一多路器 如下图,CLK_SEL 0与1分别控制时钟CLK_A CLK_B输出。 组合逻辑输出只跟当前输入状态有关,CLK_SEL异步不可控导致输出毛刺

    2023年04月10日
    浏览(28)
  • FPGA的Verilog设计(二)——异步FIFO

    阅读本文前,建议先阅读下面几篇文章: 同步FIFO 二进制转格雷码的实现   在上篇文章同步FIFO中简要介绍了FIFO的基本概念以及同步FIFO的实现。本篇文章将重点介绍异步FIFO的工作原理以及硬件实现。   异步FIFO的读写时钟不同,FIFO的读写需要进行异步处理, 异步FIFO常用

    2024年02月04日
    浏览(35)
  • FPGA verilog设计的MODBUS CRC算法

    已经测试通过。 `timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 20:14:12 05/18/2023 // Design Name: // Module Name: Modbus_CRC // Project Name: // Target Devices: // Tool versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // // module Modbus_CRC( input clk, input rst

    2024年02月06日
    浏览(33)
  • 基于FPGA的CAN通讯verilog代码设计

    FPAGA本篇文章参考github网站的开源项目can-FPGA-master编写改进 在调试过程中,发现该项目无法在quartus pro13.0的环境下运行通过,代码存在错误,并且对于EP4系列的芯片来说有太多的IO口,无法在烧录,所以笔者对此进行了改进。  can_top模块 can_tx传输数据模块 can_rx接收数据模块

    2024年02月08日
    浏览(30)
  • FPGA设计——verilog实现乒乓操作并modelsim仿真

    乒乓操作是FPGA设计中常用的一种技巧,它通过数据流控制实现按节拍相互配合的切换,来提高数据处理效率,达到无缝缓冲和处理的效果。本文针对乒乓操作进行学习总结。 完整工程 一、原理图如下 : 1、二选一控制器来对缓冲模块1和2进行选择。 2、数据缓冲模块一般就是

    2023年04月08日
    浏览(27)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包