上一篇文章(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}=... 5∗23+7∗25=(5∗2−2)∗23+2+7∗25=(5∗2−2+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. ∣XE−YE∣=Δ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=XS′∗2max{XE,YE}Y=YS′∗2max{XE,YE}相加:X+Y=(XS′+YS′)∗2max{XE,YE}
由于指数仅需作一致化处理即可,所以本模块需要在处理"底数"的溢出等情况下加大力度。
`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
变量解释:
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
{cout,fraction} = fractionA + fractionB;
if (cout == 1'b1) begin
{cout,fraction} = {cout,fraction} >> 1; //把最末一位舍弃,最高位cout纳入
exponent = exponent + 1;
end
sign = floatA[15];
end
B > A A − B = A + [ B ] 补码 = A + ( [ B ] 反码 + 1 ) B > A \\ A - B = A+ [B]_{补码} = A + ([B]_{反码} + 1) B>AA−B=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符号不同时,可能有多重情况了:
对于上述两种需要标准化处理的情况,处理如下:
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];
使用"拼接"语法,最后结果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;
(个人认为其实这里意义虽然不大但还是有的,如果运算量庞大,上亿次的这种特殊运算能节省不少的时空)
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 @ (floatA or floatB) begin
/* -------------------- */
end
开源项目github-URL:CNN-FPGA