前言

FPGA定点数计算在高效资源利用、运算速度优势、硬件可预测性和成本效益等方面发挥着重要作用。它能节省逻辑和存储资源,实现更快速的运算和更高的时钟频率,保证行为可预测且易于硬件实现和验证,同时降低硬件和开发成本,广泛应用于数字信号处理、工业控制、通信系统等领域。

基本定义

定义有符号写法和无符号数写法,其中定义和说明如下所示

符号类型有符号定点数无符号定点数
简写F/Q10.6UF/UQ10.6
数据位宽10+6=16bit1+9+6=16bit
符号位最高位
说明其中前10位表示整数位宽,后6位为小数位宽。其中整数的取值范围0−450-4^5045,小数位的精度为(1/(26))(1/(2^6))(1/(26))其中最高位为符号位,之后的9位表示整数位宽,最后后6位为小数位宽。
其中整数的取值范围0−290-2^9029,小数位的精度为(1/(26))(1/(2^6))(1/(26))

计算方式

计算流程

  1. 将两个相乘实数RA RB数据转化成对应=>UFa.b/Fa.b 格式数据: 先乘小数2b2^b2b
  2. 将转化后的两个定点数进行相乘
  3. 将相乘的结果进行位移操作,换算QA QB格式数据
  4. 在将QA QB定点数据格式进行移位得到实数RA RB,这个实数会损失精度
任意无符号定点数与任意无符号定点数相乘(无符号)
无符号定点数无符号定点数
QA=UFa.bQB=UFn.m
QA=UF9.7QB=UF1.15
RA=5.3RB=0.2
下面是计算过程
流程定点数计算过程实数计算数值
开始RA=5.3 RB=0.2
1QA=RA×2b=QAQA={RA×2^b=QA}QA=RA×2b=QA | QB=RB×2m=QBQB= {RB×2^m = QB}QB=RB×2m=QBQA=678 QB=6553
2(UQ(a+n).(b+m))=QA∗QB(UQ(a+n).(b+m))=QA*QB(UQ(a+n).(b+m))=QAQBQA×QB=8×6553.6=52428
3QA=QA∗QB>>mQA=QA*QB>>mQA=QAQB>>m | QB=QA∗QB>>bQB=QA*QB>>bQB=QAQB>>bQA=QAQB>>15=135 QB=QAQB>>7=34734
4RA=(UQa.b)>>bRA=(UQa.b)>>bRA=(UQa.b)>>b | RB=(UQn.m)>>mRB=(UQn.m)>>mRB=(UQn.m)>>mRA=(QA=UQ9.7)>>7=1 |RB=(QB=UQ1.15)>>15=1
任意有符号定点数与任意有符号定点数相乘
有符号定点数有符号定点数
QA=Fa.bQB=Fn.m
QA=F3.4QB=F2.3
RA=1.5RB=-2.25
下面是计算过程
实数计算中,若数据为负数,数据左移后将最高位补1,
流程定点数计算过程实数计算数值
开始RA=1.5 RB=-2.25
1QA=RA×2b=QAQA={RA×2^b=QA}QA=RA×2b=QA | QB=RB×2m=QBQB= {RB×2^m = QB}QB=RB×2m=QBQA=24 QB=46
2(Q(a+n).(b+m))=QA∗QB(Q(a+n).(b+m))=QA*QB(Q(a+n).(b+m))=QAQBQA×QB=15952
3QA=QA∗QB>>mQA=QA*QB>>mQA=QAQB>>m | QB=QA∗QB>>bQB=QA*QB>>bQB=QAQB>>bQA=QAQB>3=202 QB=QAQB>>4=37
4RA=(Qa.b)>>bRA=(Qa.b)>>bRA=(Qa.b)>>b | RB=(Qn.m)>>mRB=(Qn.m)>>mRB=(Qn.m)>>mRA=QA>>4=-3.375| RB=QB>>3=-3.375

小技巧

注意整数损失和小数精度损失
在进行定点数据计算前,需要人为考虑定点数计算后数据损失情况,若F(16.0)与F(1,15)相乘,如果使用F(17.15)数据不会损失太多信息
如果将F(17.15)=>F(16.0)则会损失小数精度。
如果将F(17.15)=>F(1.15)则会损失整数16位信息。

小数符号位的计算方法
在整个有符号数据计算时,所有的数据按照补码来计算,其中含负数和符号位乘法如下图所示。
在这里插入图片描述

FPGA中截断定点数位宽
在进行定点数据计算时,有可能会出现输出不同精度数据情况,例如若F(8.8)与F(5,11)相乘,产生F(13,19),但是我需要输出F(6,10)这种精度。一般操作过程如下所示
如果将F(13,19)左移9位,变成F(13.9),在截断整数位,变成F(6,10)。
有符号定点数进行截位后注意符号位

FPGA中扩展定点数位宽
在进行定点数据计算时,有可能会出现将低精度数据扩展到高精度数据情况操作,例如若F(8.8)不通过乘法转换成F(9,13)。
整数部分:如果为负数,最高位补1,如果为正数,最高位补0。
小数部分:小数只需要补0即可。
有符号定点数进行截位后注意符号位

无符号与有符号数据实现
1在信号输入端,人为将无符号数通过assign或者always将无符号数据转换成有符号数,在进行运算。

无符号定点数乘法模块
在这里插入图片描述

//无符号数据乘法模块
module unsigned_fixed_point_multiplication#(
    parameter P_A_DATA_DW     = 16,
              P_A_POINT_DW    = 8,
              P_B_DATA_DW     = 16,
              P_B_POINT_DW    = 8 ,
              P_Q_DW          =P_A_DATA_DW +P_B_DATA_DW 

)
(
    input                            i_clk              ,  // 时钟
    input                            i_rst              ,  // 复位
    input   wire [P_A_DATA_DW - 1:0] i_unsgined_a       ,  // 第一个无符号定点数
    input   wire [P_B_DATA_DW - 1:0] i_unsgined_b       ,  // 第二个无符号定点数
    input   wire                     i_unsgined_ab_vld  ,  // 数据有效位
    output  wire [P_A_DATA_DW - 1:0] o_unsgined_cov_a   ,  // 第一个无符号定点数类型结果
    output  wire [P_B_DATA_DW - 1:0] o_unsgined_cov_b   ,  // 第二个无符号定点数类型结果
    output  wire                     o_unsgined_ab_vld  ,  // 数据输出有效位
    output  wire [P_Q_DW - 1     :0] o_unsgined_c       ,  // 不做删减的数据输出
    output  wire                     o_unsgined_c_vld      // 数据输出有效位         

);
    localparam P_POINT_DW = P_A_POINT_DW + P_B_POINT_DW ;
    reg [P_A_DATA_DW - 1:0] ri_unsgined_a       ;
    reg [P_B_DATA_DW - 1:0] ri_unsgined_b       ;
    reg                     ri_unsgined_ab_vld  ;
    reg [P_A_DATA_DW - 1:0] ro_unsgined_cov_a   ;
    reg [P_B_DATA_DW - 1:0] ro_unsgined_cov_b   ;
    reg                     ro_unsgined_ab_vld  ;
    reg [P_Q_DW - 1     :0] ro_unsgined_c       ;
    reg                     ro_unsgined_c_vld   ;


    assign o_unsgined_cov_a   =  ro_unsgined_cov_a   ;
    assign o_unsgined_cov_b   =  ro_unsgined_cov_b   ;
    assign o_unsgined_ab_vld  =  ro_unsgined_ab_vld  ;
    assign o_unsgined_c       =  ro_unsgined_c       ;
    assign o_unsgined_c_vld   =  ro_unsgined_c_vld   ;

    //数据暂存
    always @(posedge i_clk or posedge i_rst) begin
        if (i_rst) begin
            ri_unsgined_a       <= 'd0;
            ri_unsgined_b       <= 'd0;
            ri_unsgined_ab_vld  <= 'd0;
        end else begin
            ri_unsgined_a       <= i_unsgined_a       ;
            ri_unsgined_b       <= i_unsgined_b       ;
            ri_unsgined_ab_vld  <= i_unsgined_ab_vld  ;
        end
    end
 
    // 执行乘法运算
    always @(posedge i_clk or posedge i_rst) begin
        if (i_rst) begin
            ro_unsgined_c <= 'd0;
            ro_unsgined_c_vld <= 'd0;
        end else if(ri_unsgined_ab_vld)begin
            ro_unsgined_c <= ri_unsgined_a * ri_unsgined_b;
            ro_unsgined_c_vld <= ri_unsgined_ab_vld;
        end else begin
            ro_unsgined_c <= ro_unsgined_c; 
            ro_unsgined_c_vld <=  1'd0;
        end
    end
    // 调整小数点位置
    // 注意:这里没有进行舍入处理,根据需要可能需要添加
    // 执行乘法运算
    always @(posedge i_clk or posedge i_rst) begin
        if (i_rst) begin
            ro_unsgined_cov_a <= 'd0;
            ro_unsgined_cov_b <= 'd0;
            ro_unsgined_ab_vld <= 'd0;
        end else if(ro_unsgined_c_vld)begin
            ro_unsgined_cov_a <= ro_unsgined_c>>P_B_POINT_DW;
            ro_unsgined_cov_b <= ro_unsgined_c>>P_A_POINT_DW;
            ro_unsgined_ab_vld <= ro_unsgined_c_vld;
        end else begin
            ro_unsgined_cov_a <= ro_unsgined_cov_a; 
            ro_unsgined_cov_b <=ro_unsgined_cov_b;
            ro_unsgined_ab_vld <=  1'd0;
        end
    end

endmodule

有符号定点数乘法模块
在这里插入图片描述

//有符号数据乘法模块
module signed_fixed_point_multiplication#(
    parameter P_A_DATA_DW     = 8,
              P_A_POINT_DW    = 4,
              P_B_DATA_DW     = 6,
              P_B_POINT_DW    = 3 ,
              P_Q_DW          =P_A_DATA_DW +P_B_DATA_DW 

)
(
input                            i_clk              ,
input                            i_rst              ,
input   wire signed [P_A_DATA_DW - 1:0] i_sgined_a       ,
input   wire signed [P_B_DATA_DW - 1:0] i_sgined_b       ,
input   wire                            i_sgined_ab_vld  ,
output  wire signed [P_A_DATA_DW - 1:0] o_sgined_cov_a   ,
output  wire signed [P_B_DATA_DW - 1:0] o_sgined_cov_b   ,
output  wire                            o_sgined_ab_vld  ,
output  wire signed [P_Q_DW - 1     :0] o_sgined_c       ,
output  wire                            o_sgined_c_vld   
);

    reg signed [P_A_DATA_DW - 1:0] ri_sgined_a       ;
    reg signed [P_B_DATA_DW - 1:0] ri_sgined_b       ;
    reg                            ri_sgined_ab_vld  ;
    reg signed [P_A_DATA_DW - 1:0] ro_sgined_cov_a   ;
    reg signed [P_B_DATA_DW - 1:0] ro_sgined_cov_b   ;
    reg                            ro_sgined_ab_vld  ;
    reg signed [P_Q_DW - 1     :0] ro_sgined_c       ;
    reg                            ro_sgined_c_vld   ;


    assign o_sgined_cov_a   =  ro_sgined_cov_a   ;
    assign o_sgined_cov_b   =  ro_sgined_cov_b   ;
    assign o_sgined_ab_vld  =  ro_sgined_ab_vld  ;
    assign o_sgined_c       =  ro_sgined_c       ;
    assign o_sgined_c_vld   =  ro_sgined_c_vld   ;

    //数据暂存
    always @(posedge i_clk or posedge i_rst) begin
        if (i_rst) begin
            ri_sgined_a       <= 'd0;
            ri_sgined_b       <= 'd0;
            ri_sgined_ab_vld  <= 'd0;
        end else begin
            ri_sgined_a       <= i_sgined_a       ;
            ri_sgined_b       <= i_sgined_b       ;
            ri_sgined_ab_vld  <= i_sgined_ab_vld  ;
        end
    end
 
    // 执行乘法运算
    always @(posedge i_clk or posedge i_rst) begin
        if (i_rst) begin
            ro_sgined_c <= 'd0;
            ro_sgined_c_vld <= 'd0;
        end else if(ri_sgined_ab_vld)begin
            ro_sgined_c <= ri_sgined_a * ri_sgined_b;
            ro_sgined_c_vld <= ri_sgined_ab_vld;
        end else begin
            ro_sgined_c <= ro_sgined_c; 
            ro_sgined_c_vld <=  1'd0;
        end
    end
    // 调整小数点位置
    // 注意:这里没有进行舍入处理,根据需要可能需要添加
    // 执行乘法运算
    always @(posedge i_clk or posedge i_rst) begin
        if (i_rst) begin
            ro_sgined_cov_a <= 'd0;
            ro_sgined_cov_b <= 'd0;
            ro_sgined_ab_vld <= 'd0;
        end else if(ro_sgined_c_vld)begin
            ro_sgined_cov_a <= ro_sgined_c>>P_B_POINT_DW;
            ro_sgined_cov_b <= ro_sgined_c>>P_A_POINT_DW;
            ro_sgined_ab_vld <= ro_sgined_c_vld;
        end else begin
            ro_sgined_cov_a <= ro_sgined_cov_a; 
            ro_sgined_cov_b <=ro_sgined_cov_b;
            ro_sgined_ab_vld <=  1'd0;
        end
    end

endmodule


Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐