前言
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
我要赚赏金
