详解FPGA定点数计算方法

描述

前言

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

基本定义

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

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

计算方式

计算流程

将两个相乘实数RA RB数据转化成对应=>UFa.b/Fa.b 格式数据: 先乘小数2b2^b2b

将转化后的两个定点数进行相乘

将相乘的结果进行位移操作,换算QA QB格式数据

在将QA QB定点数据格式进行移位得到实数RA RB,这个实数会损失精度

任意无符号定点数与任意无符号定点数相乘(无符号)

无符号定点数 无符号定点数
QA=UFa.b QB=UFn.m
QA=UF9.7 QB=UF1.15
RA=5.3 RB=0.2
下面是计算过程  
流程 定点数计算过程 实数计算数值
开始 RA=5.3 RB=0.2
1 QA=RA×2b=QAQA={RA×2^b=QA}QA=RA×2b=QA  | QB=RB×2m=QBQB= {RB×2^m = QB}QB=RB×2m=QB QA=678 QB=6553
2 (UQ(a+n).(b+m))=QA∗QB(UQ(a+n).(b+m))=QA*QB(UQ(a+n).(b+m))=QA∗QB QA×QB=8×6553.6=52428
3 QA=QA∗QB>>mQA=QA*QB>>mQA=QA∗QB>>m  | QB=QA∗QB>>bQB=QA*QB>>bQB=QA∗QB>>b QA=QAQB>>15=135 QB=QAQB>>7=34734
4 RA=(UQa.b)>>bRA=(UQa.b)>>bRA=(UQa.b)>>b  | RB=(UQn.m)>>mRB=(UQn.m)>>mRB=(UQn.m)>>m RA=(QA=UQ9.7)>>7=1 |RB=(QB=UQ1.15)>>15=1

任意有符号定点数与任意有符号定点数相乘

有符号定点数 有符号定点数
QA=Fa.b QB=Fn.m
QA=F3.4 QB=F2.3
RA=1.5 RB=-2.25
下面是计算过程  
实数计算中,若数据为负数,数据左移后将最高位补1,  
流程 定点数计算过程 实数计算数值
开始 RA=1.5 RB=-2.25
1 QA=RA×2b=QAQA={RA×2^b=QA}QA=RA×2b=QA  | QB=RB×2m=QBQB= {RB×2^m = QB}QB=RB×2m=QB QA=24 QB=46
2 (Q(a+n).(b+m))=QA∗QB(Q(a+n).(b+m))=QA*QB(Q(a+n).(b+m))=QA∗QB QA×QB=15952
3 QA=QA∗QB>>mQA=QA*QB>>mQA=QA∗QB>>m  | QB=QA∗QB>>bQB=QA*QB>>bQB=QA∗QB>>b QA=QAQB>3=202 QB=QAQB>>4=37
4 RA=(Qa.b)>>bRA=(Qa.b)>>bRA=(Qa.b)>>b  | RB=(Qn.m)>>mRB=(Qn.m)>>mRB=(Qn.m)>>m RA=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

 

    

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分