• 【CNN-FPGA开源项目解析】卷积层01--floatMult16模块


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

    16位半精度浮点数

    在这里插入图片描述

    浮点数的乘运算

    根本原理式:
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    floatMult16代码逐步解析

    符号位sign判断

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

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

    指数exponent计算

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

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

    尾数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;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    尾数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 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    舍位:

    		mantissa = fraction[21:12];
    
    • 1

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

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

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

    其他

    变量宽度表

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    always敏感列表

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

    always @ (floatA or floatB)	begin
       /* -------------------- */ 
    end
    
    • 1
    • 2
    • 3

    特殊情况处理

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

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

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

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

  • 相关阅读:
    NNDL 实验六 卷积神经网络(2)LeNet & ResNet
    spark:热门品类中每个品类活跃的SessionID统计TOP10(案例)
    向日葵全面科普,为你的远程控制设备及时规避漏洞
    uni-app进阶之生命周期【day8】
    【从零开始学微服务】02.初识微服务
    高性能MySQL实战第05讲:高性能索引该如何设计(上)
    008. 子集
    js的同步执行
    uni-app 使用uCharts-进行图表展示(折线图带单位)
    MismatchedInlineShardingAlgorithmExpressionAndColumnException
  • 原文地址:https://blog.csdn.net/GalaxyerKw/article/details/132975470