• FPGA基本算术运算


    FPGA基本算术运算

      FPGA相对于MCU有并行计算、算法效率较高等优势,但同样由于没有成型的FPU等MCU内含的浮点数运算模块,导致一些基本的符号数、浮点数运算需要我们自己进行管理。因此需要我们对基本的运算法则进行了解。基本类别如下,即:

    在这里插入图片描述

    1 有符号数与无符号数

       无符号数即为没有符号的数,简单点就是全部为正数运算的数没有负数。有符号数字便是有正与负数的数。那么在同样位数的条件下,表示的有符号数范围更加少,如下。

    reg [7:0] unsigned_a; //定义一个无符号8位数 
    					//既然无符号,则可表示 0 - 0xFF,即 0 -255;
    					//256 - 0x100 即超出了8位
    
    • 1
    • 2
    • 3

      以上是无符号数的概念,而有符号数则为了表示符号,将补码的概念引入了基本数体系,建立了符号数体系

    符号体系中,补码即将数分为符号位和基本数位;最高位表示符号位,1为负,0为正数,其他基本数位则表示正常数码。

    reg signed [7:0] sign_b; //8位有符号数 bit7:符号位; bit6-bit0: 数位
    						 //此时数位,最大只能表示0-127,较8位无符号数可表示范围减少一半
    					 
    
    • 1
    • 2
    • 3

      同时,为了方便运算减法,建立了补码运算体系,即a-b可转换为 a + b[补码]。

    ​ 因此,定义,正数的原码、反码、补码均相同,负数原码、反码、补码经过以下运算得到:

    //正数:8’d82 - 8'b0101_0010
    //原码:8'b0101_0010
    //反码:8'b0101_0010
    //补码:8'b0101_0010
    
    //负数: -8’d82 
    //原码:8'b0101_0010
    //反码:8'b1010_1101 (原码取反)
    //补码:8'b1010_1110 (反码 + 1)
    
    //8位有符号数:
    //正数:  8’d127 补码: 8'hff
    //....
    //正数:  8’d2  补码: 8'h02
    //正数:  8’d1  补码: 8'h01
    //      8'd0   补码: 8'd0
    //负数: -8’d1  补码: 8'hff
    //负数: -8’d2  补码: 8'hfe
    // ...
    //负数: -8’d127  补码: 8'h81
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

      根据以上规则,发现0是个特殊的数字,+0的补码即是8‘h0,而-0的补码是8’h80,而0 仅仅就是0。于是一般规定,8’h80为-128。因此8位有符号数范围为[-128,127],相对于无符号数[0,255],数值大小降低了一半。

    2 浮点数及定点数

      浮点数主要是遵守 IEEE754 浮点数标准这里不再缀述。而定点数,即是固定小数点的一种方式,如

    //十进制小数: 32.15 - 二进制:100000.00100110011001100110011001100110011001100110011
    //保留10位二进制小数后 - 100000.0010011001 (32.1494140625) - 有截断误差
    //定义定点数为Q10     - 即将原来的数乘以2^10 - 1000000010011001(32921)
    //Qn  表示小数点在第几位前面
    
    • 1
    • 2
    • 3
    • 4

    I、定点数的加减法

      不同定点数的加减要转换为同一定点格式才能相加减,即

    //32.15 - 32921(Q10)
    //15.62 - 7997(Q9)
    //32.15 + 15.62 = 47.77
    // 32921 + 7997* 2^(10-9) = 48915(Q10)
    // 48915/ 2^10 = 47.7685546875 [有截断误差]
    
    • 1
    • 2
    • 3
    • 4
    • 5

      Qm的定点数 +(-) Qn位的定点数{m>n},需要将n位数定点扩展到同一位置,然后进行运算,结果为Qm。

      因此m位的定点数 +(-) n位的定点数,首先需要将结果变量扩展到m+1位(防止溢出)

    II、定点数的乘除法

    //32.15 - 32921(Q10)
    //15.62 - 7997(Q9)
    //32.15 * 15.62 = 502.183
    // 32921 * 7997 = 263269237(Q19)
    // 263269237/ 2^19 = 502.1462192535400390625 [有截断误差]
    
    • 1
    • 2
    • 3
    • 4
    • 5

     Qm的定点数 *(/) Qn位的定点数{m>n},结果为Q(m+n)。

      因此m位的定点数 *(/) n位的定点数,首先需要将结果变量扩展到m+n位(防止溢出)

    3 仿真验证

    i、加减法验证

    对如下module进行激励测试:

    module add(
    
    	input [7:0] a,
    	input [7:0] b,
    	
    	output [7:0] c
    
    );
    
    wire [8:0] add_out;
    wire [8:0] sub_out;
    
    wire [7:0] add_out_2;
    wire [7:0] sub_out_2;
    
    assign add_out = a+b;
    assign sub_out = a-b;
    
    assign add_out_2 = a+b;
    assign sub_out_2 = a-b;
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    第一阶段,a->8’d12; a->8’d35。

    在这里插入图片描述

    可以看到8位及9位的结果输出均正常,这时12-35=-23的结果自动变为补码。

    第二阶段,a->-8’d12; a->8’d35。

    在这里插入图片描述

    第二阶段这里就发现了9位的输出结果明显不对,这里0xf4(-12) + 0x23 = 0x117;结果溢出了。正常情况,有符号数符号位不会参与运算,这里参与了运算导致了错误[主要是结果溢出导致]

    有两种办法进行修改:

    1)将input 改为:

    ​ input signed [7:0] a,
    ​ input signed [7:0] b,

    2)将加法运算进行扩展后计算

    ​ assign add_out = {a[7],a} + {b[7],b}; //扩展符号位
    ​ assign sub_out = {a[7],a} - {b[7],b};

    结论:对于是否定义有无符号数,即使是无符号数,运算产生负数后,也会使用补码进行表示。但是如果计算结果存在溢出,则会产生错误,所以即使不使用signed类型也可,但需要注意位宽。

    ii、乘除法验证

    module chengc(
    
    	input [7:0] c1,
    	input [7:0] c2,
    	
    	output [7:0] d2
    
    );
    
    wire [15:0] mux_out;
    wire [15:0] divid_out;
    
    wire [7:0] mux_out_2;
    wire [7:0] divid_out_2;
    
    
    assign mux_out	 = c1 * c2;
    assign divid_out = c1 / c2;
    
    assign mux_out_2   = c1 * c2;
    assign divid_out_2 = c1 / c2;
    
    
    
    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

    与加减法相似,在溢出时,仍旧出现了符号位参与运算导致的错误。

    在这里插入图片描述

    这个时候进行如下修改:

    module chengc(
    
    	input signed [7:0] c1,
    	input signed [7:0] c2,
    	
    	output [7:0] d2
    
    );
    
    wire signed [15:0] mux_out;
    wire signed [15:0] divid_out;
    
    wire [15:0] mux_out_2;
    wire [15:0] divid_out_2;
    
    
    assign mux_out	 = ({ {8{c1[7]}},c1}) * ( { {8{c2[7]}}, c2});
    assign divid_out = ({ {8{c1[7]}},c1}) / ( { {8{c2[7]}}, c2});
    
    
    assign mux_out_2   = c1 * c2;
    assign divid_out_2 = c1 / c2;
    
    
    
    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

    此时输出结果如下:

    在这里插入图片描述

    在除法运算中,符号位仍旧参与了运算,导致结果失常。因此这里并不推荐直接使用 * / ,FPGA最好使用片上自带的DSP模块进行运算,也可使用移位及加减进行近似运算。如:

    //a * 0.625 = a * (0.10011101011100001010001111010111000010100011110101111)b
    
    //a * 1/2 + a*1/8 + a*1/16 + a*1/32 + ... +.. 进行近似运算
    
    
    • 1
    • 2
    • 3
    • 4

    定点数乘除法,因已将定点数转为了整数,因此也可参考该过程。

  • 相关阅读:
    linux文件输入输出的重定向
    Redis源码与设计剖析 -- 11.哈希对象
    亚马逊鲲鹏系统可全自动化批量操作亚马逊买家号
    以detectron2了解maskrcnn实现源码(0)--开篇
    pwm呼吸灯
    DRF-(4)
    第29节-PhotoShop基础课程-滤镜库
    牛客网刷题-(1)
    (九)学习笔记:动手深度学习(多层感知机 + 代码实现)
    ES6的Promise详解
  • 原文地址:https://blog.csdn.net/qq_42151264/article/details/132699717