赞
踩
语言 :System Verilg
EDA工具:ISE、Vivado、Quartus II
在FPGA中实现浮点运算是一个复杂但有用的过程,因为浮点运算在许多应用中都是必需的,如数字信号处理、图像处理和科学计算等,上篇博客中介绍了在FPGA中使用浮点数运算的背景以及实现方式,传送链接:0315 FPGA的浮点数处理 I,以及浮点数在FPGA和有符号整数之间的转换关于FPGA的浮点数处理 II。 本篇博客注重描述单精度浮点数在FPGA中乘、加、开方、绝对值等操作
module FpAdd (
input iCLK,
input [26:0] iA,
input [26:0] iB,
output reg [26:0] oSum
);
// Extract fields of A and B.
wire A_s;
wire [7:0] A_e;
wire [17:0] A_f;
wire B_s;
wire [7:0] B_e;
wire [17:0] B_f;
assign A_s = iA[26];
assign A_e = iA[25:18];
assign A_f = {1'b1, iA[17:1]};
assign B_s = iB[26];
assign B_e = iB[25:18];
assign B_f = {1'b1, iB[17:1]};
wire A_larger;
// Shift fractions of A and B so that they align.
wire [7:0] exp_diff_A;
wire [7:0] exp_diff_B;
wire [7:0] larger_exp;
wire [36:0] A_f_shifted;
wire [36:0] B_f_shifted;
assign exp_diff_A = B_e - A_e; // if B bigger
assign exp_diff_B = A_e - B_e; // if A bigger
assign larger_exp = (B_e > A_e) ? B_e : A_e;
assign A_f_shifted = A_larger ? {1'b0, A_f, 18'b0} :
(exp_diff_A > 9'd35) ? 37'b0 :
({1'b0, A_f, 18'b0} >> exp_diff_A);
assign B_f_shifted = ~A_larger ? {1'b0, B_f, 18'b0} :
(exp_diff_B > 9'd35) ? 37'b0 :
({1'b0, B_f, 18'b0} >> exp_diff_B);
// Determine which of A, B is larger
assign A_larger = (A_e > B_e) ? 1'b1 :
((A_e == B_e) && (A_f > B_f)) ? 1'b1 :
1'b0;
// Calculate sum or difference of shifted fractions.
wire [36:0] pre_sum;
assign pre_sum = ((A_s^B_s) & A_larger) ? A_f_shifted - B_f_shifted :
((A_s^B_s) & ~A_larger) ? B_f_shifted - A_f_shifted :
A_f_shifted + B_f_shifted;
// buffer midway results
reg [36:0] buf_pre_sum;
reg [7:0] buf_larger_exp;
reg buf_A_e_zero;
reg buf_B_e_zero;
reg [26:0] buf_A;
reg [26:0] buf_B;
reg buf_oSum_s;
always @(posedge iCLK) begin
buf_pre_sum <= pre_sum;
buf_larger_exp <= larger_exp;
buf_A_e_zero <= (A_e == 8'b0);
buf_B_e_zero <= (B_e == 8'b0);
buf_A <= iA;
buf_B <= iB;
buf_oSum_s <= A_larger ? A_s : B_s;
end
// Convert to positive fraction and a sign bit.
wire [36:0] pre_frac;
assign pre_frac = buf_pre_sum;
// Determine output fraction and exponent change with position of first 1.
wire [17:0] oSum_f;
wire [7:0] shft_amt;
assign shft_amt = pre_frac[36] ? 8'd0 : pre_frac[35] ? 8'd1 :
pre_frac[34] ? 8'd2 : pre_frac[33] ? 8'd3 :
pre_frac[32] ? 8'd4 : pre_frac[31] ? 8'd5 :
pre_frac[30] ? 8'd6 : pre_frac[29] ? 8'd7 :
pre_frac[28] ? 8'd8 : pre_frac[27] ? 8'd9 :
pre_frac[26] ? 8'd10 : pre_frac[25] ? 8'd11 :
pre_frac[24] ? 8'd12 : pre_frac[23] ? 8'd13 :
pre_frac[22] ? 8'd14 : pre_frac[21] ? 8'd15 :
pre_frac[20] ? 8'd16 : pre_frac[19] ? 8'd17 :
pre_frac[18] ? 8'd18 : pre_frac[17] ? 8'd19 :
pre_frac[16] ? 8'd20 : pre_frac[15] ? 8'd21 :
pre_frac[14] ? 8'd22 : pre_frac[13] ? 8'd23 :
pre_frac[12] ? 8'd24 : pre_frac[11] ? 8'd25 :
pre_frac[10] ? 8'd26 : pre_frac[9] ? 8'd27 :
pre_frac[8] ? 8'd28 : pre_frac[7] ? 8'd29 :
pre_frac[6] ? 8'd30 : pre_frac[5] ? 8'd31 :
pre_frac[4] ? 8'd32 : pre_frac[3] ? 8'd33 :
pre_frac[2] ? 8'd34 : pre_frac[1] ? 8'd35 :
pre_frac[0] ? 8'd36 : 8'd37;
wire [53:0] pre_frac_shft, uflow_shift;
// the shift +1 is because high order bit is not stored, but implied
assign pre_frac_shft = {pre_frac, 17'b0} << (shft_amt+1); //? shft_amt+1
assign uflow_shift = {pre_frac, 17'b0} << (shft_amt); //? shft_amt for overflow
assign oSum_f = pre_frac_shft[53:36];
wire [7:0] oSum_e;
assign oSum_e = buf_larger_exp - shft_amt + 8'b1;
// Detect underflow
wire underflow;
// this incorrectly sets uflow for 10-10.1
//assign underflow = ~oSum_e[7] && buf_larger_exp[7] && (shft_amt != 8'b0);
// if top bit of matissa is not set, then denorm
assign underflow = ~uflow_shift[53];
always @(posedge iCLK) begin
oSum <= (buf_A_e_zero && buf_B_e_zero) ? 27'b0 :
buf_A_e_zero ? buf_B :
buf_B_e_zero ? buf_A :
underflow ? 27'b0 :
(pre_frac == 0) ? 27'b0 :
{buf_oSum_s, oSum_e, oSum_f};
end //output update
endmodule
FpAdd模块的作用是实现浮点数的加法运算。浮点数的表示通常由三个部分组成:符号位(sign bit)、指数(exponent)和尾数(fraction)。这个模块将两个浮点数相加,并将结果存储在输出寄存器oSum中。
对代码分析:
1、输入输出定义:
iCLK:时钟信号。
iA和iB:输入的两个浮点数,每个数27位,包括1位符号位、8位指数位和17位尾数位。
oSum:输出的浮点数结果,也是27位。
2、提取输入浮点数的各部分:
A_s和B_s:分别表示iA和iB的符号位。
A_e和B_e:分别表示iA和iB的指数位。
A_f和B_f:分别表示iA和iB的尾数位,其中最高位隐含为1。
3、对齐尾数:
计算两个指数的差值,确定哪个指数更大。
根据差值,将尾数右移或左移,使它们对齐。
4、确定哪个数更大:
通过比较指数和尾数来确定A和B中哪个数更大,并赋值给A_larger。
5、计算求和或差:
根据符号位和A_larger的值,计算两个浮点数的和或差。
6、缓冲中间结果:
使用always块在时钟上升沿更新中间结果。
7、处理结果的尾数和指数:
根据预处理的尾数pre_frac,确定输出尾数oSum_f和输出指数oSum_e。
8、检测下溢:
检查是否发生下溢,并在必要时将结果设置为0。
9、输出结果:
在时钟上升沿更新输出寄存器oSum,根据条件设置结果。
module FpMul (
input [26:0] iA, // First input
input [26:0] iB, // Second input
output [26:0] oProd // Product
);
// Extract fields of A and B.
wire A_s;
wire [7:0] A_e;
wire [17:0] A_f;
wire B_s;
wire [7:0] B_e;
wire [17:0] B_f;
assign A_s = iA[26];
assign A_e = iA[25:18];
assign A_f = {1'b1, iA[17:1]};
assign B_s = iB[26];
assign B_e = iB[25:18];
assign B_f = {1'b1, iB[17:1]};
// XOR sign bits to determine product sign.
wire oProd_s;
assign oProd_s = A_s ^ B_s;
// Multiply the fractions of A and B
wire [35:0] pre_prod_frac;
assign pre_prod_frac = A_f * B_f;
// Add exponents of A and B
wire [8:0] pre_prod_exp;
assign pre_prod_exp = A_e + B_e;
// If top bit of product frac is 0, shift left one
wire [7:0] oProd_e;
wire [17:0] oProd_f;
assign oProd_e = pre_prod_frac[35] ? (pre_prod_exp-9'd126) : (pre_prod_exp - 9'd127);
assign oProd_f = pre_prod_frac[35] ? pre_prod_frac[34:17] : pre_prod_frac[33:16];
// Detect underflow
wire underflow;
assign underflow = pre_prod_exp < 9'h80;
// Detect zero conditions (either product frac doesn't start with 1, or underflow)
assign oProd = underflow ? 27'b0 :
(B_e == 8'd0) ? 27'b0 :
(A_e == 8'd0) ? 27'b0 :
{oProd_s, oProd_e, oProd_f};
endmodule
FpMul的模块,它的作用是实现了两个浮点数的乘法运算。代码的目的是将两个27位的浮点数相乘,并将结果存储在输出oProd中。以下是对代码的详细解释:
1、模块输入输出:
(1)iA和iB:分别是乘法操作的两个输入浮点数,每个数27位,包括1位符号位、8位指数位和17位尾数位。
(2)oProd:乘法操作的结果,也是27位。
2、提取浮点数的各个字段:
(1)A_s和B_s:分别表示iA和iB的符号位。
(2)A_e和B_e:分别表示iA和iB的指数位。
(3)A_f和B_f:分别表示iA和iB的尾数位,其中最高位隐含为1。
3、确定乘积的符号:
使用异或操作A_s ^ B_s来确定乘积的符号位oProd_s。
4、相乘尾数:
(1)将A_f和B_f相乘,得到36位的中间结果pre_prod_frac。
相加指数:
(2)将A_e和B_e相加,得到9位的中间结果pre_prod_exp。
5、调整指数和尾数:
如果pre_prod_frac的最高位为0,说明乘积需要左移一位,指数减去126(而不是127),否则直接使用减去127的指数。
根据最高位是否为1,选择正确的尾数部分。
6、处理下溢:
如果pre_prod_exp小于9’h80(即小于十进制的128),则认为发生了下溢,此时输出结果为0。
7、处理零条件:
如果B_e或A_e为0,表示其中一个输入数为0,乘积也为0。
如果没有发生下溢,且两个输入数的指数都不为0,将计算得到的符号位、指数和尾数组合成最终的乘积oProd。
代码中有几个需要注意的地方:
尾数相乘的结果pre_prod_frac是36位,这意味着乘法结果可能会超出浮点数尾数的表示范围,需要进行舍入和规范化处理。
指数调整时,如果乘积的尾数需要左移(即pre_prod_frac[35]为0),则指数减去127,否则减去126。这是为了将尾数规范化,使其最高位为1。
下溢条件的检测使用了pre_prod_exp < 9’h80,这个条件可能需要根据具体的实现和标准进行调整。
本篇博客注重描述单精度浮点数在FPGA中乘、加操作。上述两段verilog代码分别实现了浮点数的加法和乘法操作。浮点加法模块FpAdd通过提取输入浮点数的符号位、指数和尾数,对齐尾数,计算和或差,并考虑了下溢情况来生成结果。浮点乘法模块FpMul则通过提取输入浮点数的相应字段,计算尾数的乘积和指数的和,处理可能的下溢,并根据结果的规范化要求调整指数和尾数。两个模块都考虑了符号位的处理,确保了结果的符号正确,并在输出时组合了符号位、指数和尾数来形成最终的浮点数结果。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。