【CNN-FPGA开源项目解析】02--floatAdd16模块

文章目录

    • 前言
    • 浮点数加法的思路
    • floatAdd16完整代码
    • floatMult16代码逐步解析
      • 指数化为一致
      • 底数相加,处理进位溢出
      • 结果标准化和舍位
      • 整合为最后的16位浮点数结果[sign,exponent,fraction]
    • 其他
      • 变量宽度表
      • 特殊情况处理
      • always敏感列表

前言

​ 上一篇文章(floatMult16模块解析)内,已经详细阐述了"半精度浮点数"的含义和乘法运算方法。同时,我们结合了开源的代码,逐步分析了"乘法模块"的具体实现细节。

​ 这一篇文章将继续上一篇的思路,分析半精度浮点数加法(floatAdd16)模块的构建思路和代码实现。

浮点数加法的思路

​ 对于两个二进制指数的加法,我们完全可以参考平时我们运算指数的习惯去计算。举个例子:
5 ∗ 2 3 + 7 ∗ 2 5 = ( 5 ∗ 2 − 2 ) ∗ 2 3 + 2 + 7 ∗ 2 5 = ( 5 ∗ 2 − 2 + 7 ) ∗ 2 5 = . . . 5*2^{3}+7*2^{5} = (5*2^{-2})*2^{3+2}+7*2^{5} = (5*2^{-2}+7)*2^{5}=... 523+725=(522)23+2+725=(522+7)25=...
​ 我们依次做了这些事情:

  • 两个指数不同时,指数化为一致;
  • 将幂指数前面的常数相加;
  • 运算得到最终结果。

​ 对于16bit的半精度浮点数而言,我们也完全可以这样做。只不过具体的运算有些小的出入,同时需要考虑进位和舍位等细节问题。

  • 指数化为一致:

    (小的往大的化,指数底数同步变化)

∣ X E − Y E ∣ = Δ E { X E 较大,化 Y = ( Y S > > Δ E ) ∗ 2 X E Y E 较大,化 X = ( X S > > Δ E ) ∗ 2 Y E |X_{E}-Y_{E}|= \Delta E \left\{\begin{matrix}X_{E} 较大,化Y=(Y_{S}>>\Delta E)*2^{X_{E}}\\Y_{E} 较大,化X=(X_{S}>>\Delta E)*2^{Y_{E}}\end{matrix}\right. XEYE=ΔE{XE较大,化Y=(YS>>ΔE)2XEYE较大,化X=(XS>>ΔE)2YE

  • 相加:

令处理后的结果是 : { X = X S ′ ∗ 2 m a x { X E , Y E } Y = Y S ′ ∗ 2 m a x { X E , Y E } 相加 : X + Y = ( X S ′ + Y S ′ ) ∗ 2 m a x { X E , Y E } 令处理后的结果是:\left\{\begin{matrix}X=X_S^{'}*2^{max\{X_E,Y_E\}}\\Y=Y_S^{'}*2^{max\{X_E,Y_E\}} \end{matrix}\right. \\ 相加: X+Y=(X_S^{'}+Y_S^{'})*2^{max\{X_E,Y_E\}} 令处理后的结果是:{X=XS2max{XE,YE}Y=YS2max{XE,YE}相加:X+Y=(XS+YS)2max{XE,YE}

  • 处理底数相加时的进位。最后结果舍位到正确的位数。

​ 由于指数仅需作一致化处理即可,所以本模块需要在处理"底数"的溢出等情况下加大力度。


floatAdd16完整代码

`timescale 100 ns / 10 ps

module floatAdd16 (floatA,floatB,sum);
	
input [15:0] floatA, floatB;
output reg [15:0] sum;

reg sign;
reg signed [5:0] exponent; //fifth bit is sign
reg [9:0] mantissa;
reg [4:0] exponentA, exponentB;
reg [10:0] fractionA, fractionB, fraction;	//fraction = {1,mantissa}
reg [7:0] shiftAmount;
reg cout;

always @ (floatA or floatB) begin
	exponentA = floatA[14:10];
	exponentB = floatB[14:10];
	fractionA = {1'b1,floatA[9:0]};
	fractionB = {1'b1,floatB[9: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[14:0] == floatB[14:0] && floatA[15]^floatB[15]==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[15] == floatB[15]) begin			//same sign
			{cout,fraction} = fractionA + fractionB;
			if (cout == 1'b1) begin
				{cout,fraction} = {cout,fraction} >> 1;
				exponent = exponent + 1;
			end
			sign = floatA[15];
		end else begin						//different signs
			if (floatA[15] == 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 [10] == 0) begin
				if (fraction[9] == 1'b1) begin
					fraction = fraction << 1;
					exponent = exponent - 1;
				end else if (fraction[8] == 1'b1) begin
					fraction = fraction << 2;
					exponent = exponent - 2;
				end else if (fraction[7] == 1'b1) begin
					fraction = fraction << 3;
					exponent = exponent - 3;
				end else if (fraction[6] == 1'b1) begin
					fraction = fraction << 4;
					exponent = exponent - 4;
				end else if (fraction[5] == 1'b1) begin
					fraction = fraction << 5;
					exponent = exponent - 5;
				end else if (fraction[4] == 1'b1) begin
					fraction = fraction << 6;
					exponent = exponent - 6;
				end else if (fraction[3] == 1'b1) begin
					fraction = fraction << 7;
					exponent = exponent - 7;
				end else if (fraction[2] == 1'b1) begin
					fraction = fraction << 8;
					exponent = exponent - 8;
				end else if (fraction[1] == 1'b1) begin
					fraction = fraction << 9;
					exponent = exponent - 9;
				end else if (fraction[0] == 1'b1) begin
					fraction = fraction << 10;
					exponent = exponent - 10;
				end 
			end
		end
		mantissa = fraction[9:0];
		if(exponent[5]==1'b1) begin //exponent is negative
			sum = 16'b0000000000000000;
		end
		else begin
			sum = {sign,exponent[4:0],mantissa};
		end		
	end		
end

endmodule

floatMult16代码逐步解析

指数化为一致

变量解释:

  • shiftAmount : 两数的指数之差。
  • exponent:化为一致后的指数。即max{A,B}。
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

底数相加,处理进位溢出

  1. 浮点数A和B符号相同时:可能出现"进位"。加法进位处理思路:
  • 用cout存储溢出的一位,初始为0。
  • 如果cout=1说明有进位,此时为了继续保持原先的位数,需要把最末一位舍弃,最高位cout纳入
if (floatA[15] == floatB[15]) begin	
	{cout,fraction} = fractionA + fractionB;
	if (cout == 1'b1) begin
        {cout,fraction} = {cout,fraction} >> 1;  //把最末一位舍弃,最高位cout纳入
		exponent = exponent + 1;
	end
	sign = floatA[15];
end
  1. 浮点数A和B符号不同时:运算结果可正可负。
  • 二进制小数减大数的算法:

B > A A − B = A + [ B ] 补码 = A + ( [ B ] 反码 + 1 ) B > A \\ A - B = A+ [B]_{补码} = A + ([B]_{反码} + 1) B>AAB=A+[B]补码=A+([B]反码+1)

​ 在这个过程中,若被减数较小减数较大,运算时产生进位导致cout=1,指示了这是一个负数的结果。

else begin         
    if(floatA[15] == 1'b1) begin   //[注] 1:- ; 0:+                   
        {cout,fraction} = fractionB - fractionA;        
    end	else begin                                   
        {cout,fraction} = fractionA - fractionB;        
    end                                    
sign = cout;                                     

结果标准化和舍位

​ 当A与B符号相同时,通过"移1位"的操作即可标准化运算结果。

​ 但是当A与B符号不同时,可能有多重情况了:

  • 若大数减小数:10+(-2)=8,此时结果为正,cout=0,但可能不是1.xx的标准浮点形式。
  • 若小数减大数:2+(-10)=-8,此时结果为负,cout=1,也可能不是1.xx的标准浮点形式。

​ 对于上述两种需要标准化处理的情况,处理如下:

  • 大数减小数,cout=0:寻找到最高的非零位,移位。
  • 小数减大数,cout=1:结果取负。(因为最后cout要被舍去,底数的正负必须由自己来表征)
			if (cout == 1'b1) begin
				fraction = -fraction; 
			end else begin
			end
			if (fraction [10] == 0) begin
				if (fraction[9] == 1'b1) begin
					fraction = fraction << 1;
					exponent = exponent - 1;
				end else if (fraction[8] == 1'b1) begin
					fraction = fraction << 2;
					exponent = exponent - 2;
				end else if (fraction[7] == 1'b1) begin
					fraction = fraction << 3;
					exponent = exponent - 3;
				end else if (fraction[6] == 1'b1) begin
					fraction = fraction << 4;
					exponent = exponent - 4;
				end else if (fraction[5] == 1'b1) begin
					fraction = fraction << 5;
					exponent = exponent - 5;
				end else if (fraction[4] == 1'b1) begin
					fraction = fraction << 6;
					exponent = exponent - 6;
				end else if (fraction[3] == 1'b1) begin
					fraction = fraction << 7;
					exponent = exponent - 7;
				end else if (fraction[2] == 1'b1) begin
					fraction = fraction << 8;
					exponent = exponent - 8;
				end else if (fraction[1] == 1'b1) begin
					fraction = fraction << 9;
					exponent = exponent - 9;
				end else if (fraction[0] == 1'b1) begin
					fraction = fraction << 10;
					exponent = exponent - 10;
				end 
			end

舍位:舍弃运算过程中的辅助符号位cout。

mantissa = fraction[9:0];

整合为最后的16位浮点数结果[sign,exponent,fraction]

使用"拼接"语法,最后结果product为16位。

product = {sign,exponent[4:0],mantissa};

其他

变量宽度表

input [15:0] floatA, floatB;
output reg [15:0] sum;

reg sign;
reg signed [5:0] exponent;   //fifth bit is sign
reg [9:0] mantissa;
reg [4:0] exponentA, exponentB;
reg [10:0] fractionA, fractionB, fraction;	//fraction = {1,mantissa}
reg [7:0] shiftAmount;
reg cout;

特殊情况处理

  • 输入中有一个为0。
  • 输入与输出大小相同,符号相反。

(个人认为其实这里意义虽然不大但还是有的,如果运算量庞大,上亿次的这种特殊运算能节省不少的时空)

	if (floatA == 0) begin						
		sum = floatB;
	end else if (floatB == 0) begin					
		sum = floatA;
	end else if (floatA[14:0] == floatB[14:0] && floatA[15]^floatB[15]==1'b1) begin
		sum=0;
	end else begin

always敏感列表

本模块非时序。一旦有输入发生改变,立刻执行模块功能。

always @ (floatA or floatB) begin
   /* -------------------- */ 
end

开源项目github-URL:CNN-FPGA

你可能感兴趣的:(fpga,cnn,深度学习)