【CNN-FPGA开源项目解析】01--floatMult16模块

文章目录

    • (基础)半精度浮点数的表示和乘运算
      • 16位半精度浮点数
      • 浮点数的乘运算
    • floatMult16完整代码
    • floatMult16代码逐步解析
      • 符号位sign判断
      • 指数exponent计算
      • 尾数fraction计算
      • 尾数fraction的标准化和舍位
      • 整合为最后的16位浮点数结果[sign,exponent,fraction]
    • 其他
      • 变量宽度表
      • always敏感列表
      • 特殊情况处理

(基础)半精度浮点数的表示和乘运算

16位半精度浮点数

【CNN-FPGA开源项目解析】01--floatMult16模块_第1张图片

浮点数的乘运算

根本原理式:
X = X S ⋅ 2 X E Y = Y S ⋅ 2 Y E X ⋅ Y = ( X S ∗ Y S ) ⋅ 2 X E + Y E X = X_{S} · 2^{X_{E}} \\ Y = Y_{S} · 2^{Y_{E}} \\ X·Y = (X_{S}*Y_{S}) · 2^{X_{E} + Y_{E}} X=XS2XEY=YS2YEXY=(XSYS)2XE+YE
基本流程:

  • 判符号。

  • 算尾数:结果的尾数是输入两数的尾数之积。(未标准化)

  • 算指数:结果的指数是输入两数的指数之和。(未标准化)

  • 标准化和舍位:

    ① 结果化为二级制(1.xx)的形式,取出尾数。

    ② 舍去低位,保留高位。


floatMult16完整代码

完整代码:

`timescale 100 ns / 10 ps

module floatMult16 (floatA,floatB,product);

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

reg sign;
reg signed [5:0] exponent; //6th bit is the sign
reg [9:0] mantissa;
reg [10:0] fractionA, fractionB;	//fraction = {1,mantissa}
reg [21:0] fraction;


always @ (floatA or floatB) begin
	if (floatA == 0 || floatB == 0) begin
		product = 0;
	end else begin
		sign = floatA[15] ^ floatB[15];
		exponent = floatA[14:10] + floatB[14:10] - 5'd15 + 5'd2;
	
		fractionA = {1'b1,floatA[9:0]};
		fractionB = {1'b1,floatB[9:0]};
		fraction = fractionA * fractionB;
		
		if (fraction[21] == 1'b1) begin
			fraction = fraction << 1;
			exponent = exponent - 1; 
		end else if (fraction[20] == 1'b1) begin
			fraction = fraction << 2;
			exponent = exponent - 2;
		end else if (fraction[19] == 1'b1) begin
			fraction = fraction << 3;
			exponent = exponent - 3;
		end else if (fraction[18] == 1'b1) begin
			fraction = fraction << 4;
			exponent = exponent - 4;
		end else if (fraction[17] == 1'b1) begin
			fraction = fraction << 5;
			exponent = exponent - 5;
		end else if (fraction[16] == 1'b1) begin
			fraction = fraction << 6;
			exponent = exponent - 6;
		end else if (fraction[15] == 1'b1) begin
			fraction = fraction << 7;
			exponent = exponent - 7;
		end else if (fraction[14] == 1'b1) begin
			fraction = fraction << 8;
			exponent = exponent - 8;
		end else if (fraction[13] == 1'b1) begin
			fraction = fraction << 9;
			exponent = exponent - 9;
		end else if (fraction[12] == 1'b0) begin
			fraction = fraction << 10;
			exponent = exponent - 10;
		end 
	
		mantissa = fraction[21:12];
		if(exponent[5]==1'b1) begin //exponent is negative
			product=16'b0000000000000000;
		end
		else begin
			product = {sign,exponent[4:0],mantissa};
		end
	end
end

endmodule

floatMult16代码逐步解析

符号位sign判断

​ 正正得正,负负得正,正负得负。用异或运算即可。

sign = floatA[15] ^ floatB[15];

指数exponent计算

​ floatA与floatB的指数相加,初步得到了乘法结果的指数(尚未标准化)。

  • 后面进行标准化时,指数要随着"尾数小数点"的移动而变化。
exponent = floatA[14:10] + floatB[14:10] - 5'd15 + 5'd2;

尾数fraction计算

​ floatA与floatB的尾数相乘,初步得到了乘法结果的尾数(尚未标准化)。

  • 输入的两个浮点数是上一级操作完成的。fraction为省略整数1的小数部分,因此运算时需要先把这个整数1还回去。
  • 此步得到的运算结果尚未经历标准化。
  • 两个10bit的二进制相乘结果先暂存为20bit,避免在乘法过程中造成精度损失。后续再进行移位和舍位。
//输入的两个浮点数A,B都是标准的。把整数1先借回去,以便参与运算。
fractionA = {1'b1,floatA[9:0]};
fractionB = {1'b1,floatB[9:0]};
//尾数相乘
fraction = fractionA * fractionB;

尾数fraction的标准化和舍位

​ 通过小数点的移动,将运算结果变为二进制(1.xx)的形式。尾数即取小数点后(xx)的部分。同时,在这个过程中"指数"要同步发生变化。

  • 尾数小数点"左移n位",二进制右移:指数+n。
  • 尾数小数点"右移n位",二进制左移:指数-n。

代码的思路是:

  • 从乘法结果尾数的高位开始,寻找到最高位的1。通过左移,将"这一位"变为最高位。

  • 半精度浮点数尾数位只取5位。舍去低位。

标准化:

		if (fraction[21] == 1'b1) begin
			fraction = fraction << 1;
			exponent = exponent - 1; 
		end else if (fraction[20] == 1'b1) begin
			fraction = fraction << 2;
			exponent = exponent - 2;
		end else if (fraction[19] == 1'b1) begin
			fraction = fraction << 3;
			exponent = exponent - 3;
		end else if (fraction[18] == 1'b1) begin
			fraction = fraction << 4;
			exponent = exponent - 4;
		end else if (fraction[17] == 1'b1) begin
			fraction = fraction << 5;
			exponent = exponent - 5;
		end else if (fraction[16] == 1'b1) begin
			fraction = fraction << 6;
			exponent = exponent - 6;
		end else if (fraction[15] == 1'b1) begin
			fraction = fraction << 7;
			exponent = exponent - 7;
		end else if (fraction[14] == 1'b1) begin
			fraction = fraction << 8;
			exponent = exponent - 8;
		end else if (fraction[13] == 1'b1) begin
			fraction = fraction << 9;
			exponent = exponent - 9;
		end else if (fraction[12] == 1'b0) begin
			fraction = fraction << 10;
			exponent = exponent - 10;
		end 

舍位:

		mantissa = fraction[21:12];

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

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

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

其他

变量宽度表

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

reg sign;
reg signed [5:0] exponent; 
reg [9:0] mantissa;
reg [10:0] fractionA, fractionB;
reg [21:0] fraction;

always敏感列表

本模块为纯组合逻辑,非时序。因此只要有数据输入,便触发模块功能,开始运算。

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

特殊情况处理

  • 输入两个数都是0时,输出也为0。
if (floatA == 0 || floatB == 0) begin
	product = 0;
end
  • 不允许指数为负数。在半精度浮点数中,指数本来只有5位。但是代码中的变量设置为6位,其中多出的最高一位是符号位。通过这一位,我们来判断指数的正负。

(这一点从上面操作fraction只进行左移也可以看出来。)

if(exponent[5]==1'b1) begin 
	product=16'b0000000000000000;
end

学习文章:二进制浮点数以及二进制浮点数算术运算
开源项目github-URL:CNN-FPGA

你可能感兴趣的:(fpga开发,cnn,开源)