完整的单精度FPU是一个复杂的SoC组件,需要整合前面所有课程实现的运算单元:
我们的FPU支持以下操作码:
| op[3:0] | 指令 | 说明 | 使用单元 |
|---|---|---|---|
| 0000 | FADD | 浮点加法 | 加法器 |
| 0001 | FSUB | 浮点减法 | 加法器 |
| 0010 | FMUL | 浮点乘法 | 乘法器 |
| 0011 | FDIV | 浮点除法 | 除法器 |
| 0100 | FSQRT | 浮点开方 | 开方器 |
| 0101 | FMA | 融合乘加 | FMA |
| 0110 | FCMP | 浮点比较 | 比较器 |
| 0111 | FMIN/FMAX | 最小/最大值 | 比较器 |
| 1000 | F2I | 浮点转整数 | 转换器 |
| 1001 | I2F | 整数转浮点 | 转换器 |
| 1010 | FABS | 绝对值 | 直通 |
| 1011 | FNEG | 取反 | 直通 |
IEEE 754定义了5种异常标志,FPU必须正确设置:
| 标志 | 触发条件 | 默认结果 |
|---|---|---|
| Invalid Operation | 0×∞, ∞-∞, √(-x), NaN运算 | NaN |
| Divide by Zero | 非零/0 | ±∞ |
| Overflow | 结果超出最大可表示值 | ±∞ 或 ±Max (取决于舍入模式) |
| Underflow | 结果低于最小可表示值 | ±0 或 ±MinDenorm |
| Inexact | 结果不精确(舍入发生) | 舍入后的结果 |
//=============================================================
// fpu32_top.sv - 单精度FPU顶层模块
// 整合所有运算单元,统一接口
//=============================================================
module fpu32_top (
input wire clk,
input wire rst_n,
input wire [3:0] op, // 操作码
input wire [31:0] a, // 操作数1
input wire [31:0] b, // 操作数2
input wire [31:0] c, // 操作数3(FMA用)
input wire [1:0] rm, // 舍入模式
output wire [31:0] result, // 运算结果
output wire [4:0] flags // 异常标志
);
// 标志位: [Invalid, DivByZero, Overflow, Underflow, Inexact]
// ========== 各运算单元实例化 ==========
// --- 加减法 ---
wire [31:0] add_result, sub_result;
wire [4:0] add_flags, sub_flags;
// 加法器(复用前课实现)
float_addsub fadd(
.a(a), .b(b), .op_sub(1'b0),
.result(add_result), .flags(add_flags)
);
float_addsub fsub(
.a(a), .b(b), .op_sub(1'b1),
.result(sub_result), .flags(sub_flags)
);
// --- 乘法 ---
wire [31:0] mul_result;
wire [4:0] mul_flags;
float_multiplier fmul(
.a(a), .b(b), .result(mul_result), .flags(mul_flags)
);
// --- 除法 ---
wire [31:0] div_result;
wire [4:0] div_flags;
float_div_nr fdiv(
.a(a), .b(b), .result(div_result), .flags(div_flags)
);
// --- 开方 ---
wire [31:0] sqrt_result;
float_sqrt fsqrt(.a(a), .result(sqrt_result));
// --- FMA ---
wire [31:0] fma_result;
fma_top ffma(.a(a), .b(b), .c(c), .result(fma_result));
// --- 比较 ---
wire cmp_result;
wire [2:0] cmp_op = b[2:0]; // 比较类型编码在b中
float_compare fcmp(.a(a), .b(b), .op(cmp_op), .result(cmp_result));
// --- 简单操作 ---
wire [31:0] abs_result = {1'b0, a[30:0]};
wire [31:0] neg_result = {~a[31], a[30:0]};
// ========== 结果选择 ==========
reg [31:0] result_r;
reg [4:0] flags_r;
always @(*) begin
result_r = 32'd0;
flags_r = 5'b0;
case (op)
4'b0000: begin result_r = add_result; flags_r = add_flags; end
4'b0001: begin result_r = sub_result; flags_r = sub_flags; end
4'b0010: begin result_r = mul_result; flags_r = mul_flags; end
4'b0011: begin result_r = div_result; flags_r = div_flags; end
4'b0100: begin result_r = sqrt_result; flags_r = 5'b0; end
4'b0101: begin result_r = fma_result; flags_r = 5'b0; end
4'b0110: begin result_r = {31'b0, cmp_result}; flags_r = 5'b0; end
4'b1010: begin result_r = abs_result; flags_r = 5'b0; end
4'b1011: begin result_r = neg_result; flags_r = 5'b0; end
default: begin result_r = 32'h7FC00000; flags_r = 5'b10000; end // Invalid
endcase
end
assign result = result_r;
assign flags = flags_r;
endmodule
//=============================================================
// float_addsub.sv - 统一加减法器(带异常标志)
//=============================================================
module float_addsub (
input wire [31:0] a,
input wire [31:0] b,
input wire op_sub,
output wire [31:0] result,
output wire [4:0] flags
);
wire sign_a = a[31], sign_b = b[31] ^ op_sub;
wire [7:0] exp_a = a[30:23], exp_b = b[30:23];
wire [22:0] frac_a = a[22:0], frac_b = b[22:0];
wire a_nan=(exp_a==8'hFF)&(frac_a!=23'b0);
wire b_nan=(exp_b==8'hFF)&(frac_b!=23'b0);
wire a_inf=(exp_a==8'hFF)&(frac_a==23'b0);
wire b_inf=(exp_b==8'hFF)&(frac_b==23'b0);
wire a_zero=(exp_a==8'b0)&(frac_a==23'b0);
wire b_zero=(exp_b==8'b0)&(frac_b==23'b0);
wire [23:0] ma = (exp_a==8'b0) ? {1'b0,frac_a} : {1'b1,frac_a};
wire [23:0] mb = (exp_b==8'b0) ? {1'b0,frac_b} : {1'b1,frac_b};
wire a_larger = (exp_a > exp_b) | ((exp_a==exp_b) & (ma >= mb));
wire [7:0] exp_diff = a_larger ? (exp_a-exp_b) : (exp_b-exp_a);
wire [7:0] max_exp = a_larger ? exp_a : exp_b;
wire [23:0] larger_m = a_larger ? ma : mb;
wire [23:0] smaller_m = a_larger ? mb : ma;
wire larger_sign = a_larger ? sign_a : sign_b;
wire smaller_sign = a_larger ? sign_b : sign_a;
wire [23:0] shifted_m = (exp_diff < 8'd24) ? (smaller_m >> exp_diff) : 24'b0;
wire same_sign = (larger_sign == smaller_sign);
wire [24:0] add_out = same_sign ?
({1'b0,larger_m} + {1'b0,shifted_m}) :
({1'b0,larger_m} - {1'b0,shifted_m});
wire r_sign = same_sign ? larger_sign :
(add_out[24]) ? ~larger_sign : larger_sign;
wire [24:0] abs_out = add_out[24] ? (~add_out+25'b1) : add_out;
// 规格化(简化)
wire [24:0] norm_out = abs_out;
wire [22:0] r_frac = norm_out[22:0];
wire [7:0] r_exp = max_exp;
wire inf_inf_diff = a_inf & b_inf & (sign_a != sign_b);
wire invalid = a_nan | b_nan | inf_inf_diff;
wire is_nan = invalid;
wire is_inf = (a_inf | b_inf) & ~invalid;
wire is_zero = (abs_out == 25'b0) & ~is_nan;
assign result = is_nan ? 32'h7FC00000 :
is_inf ? {larger_sign,8'hFF,23'b0} :
is_zero ? {1'b0,31'b0} :
{r_sign, r_exp, r_frac};
assign flags = {invalid, 1'b0, 1'b0, 1'b0, 1'b0};
endmodule
//=============================================================
// float_div_nr.sv - 除法器接口(简化)
//=============================================================
module float_div_nr (
input wire [31:0] a,
input wire [31:0] b,
output wire [31:0] result,
output wire [4:0] flags
);
wire sign_a=a[31], sign_b=b[31];
wire [7:0] exp_a=a[30:23], exp_b=b[30:23];
wire [22:0] frac_a=a[22:0], frac_b=b[22:0];
wire b_zero=(exp_b==8'b0)&(frac_b==23'b0);
wire a_zero=(exp_a==8'b0)&(frac_a==23'b0);
wire a_nan=(exp_a==8'hFF)&(frac_a!=23'b0);
wire b_nan=(exp_b==8'hFF)&(frac_b!=23'b0);
wire a_inf=(exp_a==8'hFF)&(frac_a==23'b0);
wire b_inf=(exp_b==8'hFF)&(frac_b==23'b0);
wire div_invalid = a_nan|b_nan|(a_zero&b_zero)|(a_inf&b_inf);
wire div_by_zero = b_zero & ~a_zero & ~a_nan & ~a_inf;
wire [23:0] ma = (exp_a==8'b0) ? {1'b0,frac_a} : {1'b1,frac_a};
wire [23:0] mb = (exp_b==8'b0) ? {1'b0,frac_b} : {1'b1,frac_b};
wire r_sign = sign_a ^ sign_b;
wire [7:0] r_exp = exp_a - exp_b + 8'd127;
wire [22:0] r_frac = (ma / mb) < 24'h800000 ? ma[22:0] / mb[22:0] : 23'b0; // 简化
assign result = div_invalid ? 32'h7FC00000 :
div_by_zero ? {r_sign,8'hFF,23'b0} :
(a_inf|a_zero) ? {r_sign, a[30:0]} :
{r_sign, r_exp, r_frac};
assign flags = {div_invalid, div_by_zero, 1'b0, 1'b0, 1'b0};
endmodule
//=============================================================
// tb_fpu32_top.sv - 单精度FPU集成测试
//=============================================================
module tb_fpu32_top;
reg clk=0, rst_n=1;
reg [3:0] op;
reg [31:0] a, b, c;
reg [1:0] rm;
wire [31:0] result;
wire [4:0] flags;
fpu32_top uut(.clk(clk),.rst_n(rst_n),.op(op),.a(a),.b(b),.c(c),
.rm(rm),.result(result),.flags(flags));
localparam ONE=32'h3F800000,TWO=32'h40000000;
localparam ZERO=32'h00000000,MONE=32'hBF800000;
localparam FOUR=32'h40800000;
integer err=0;
task chk; input [3:0] iop; input [31:0] ia,ib,ic,er; input [255:0] nm;
begin op=iop;a=ia;b=ib;c=ic;rm=2'b00;#10;
if(result!==er)begin $display("FAIL %0s:got %h exp %h",nm,result,er);err=err+1;end
else $display("PASS %0s: %h",nm,result); end
endtask
initial begin
// FADD: 1+1=2
chk(4'b0000,ONE,ONE,ZERO,TWO,"1+1=2");
// FSUB: 2-1=1
chk(4'b0001,TWO,ONE,ZERO,ONE,"2-1=1");
// FMUL: 2*2=4
chk(4'b0010,TWO,TWO,ZERO,FOUR,"2*2=4");
// FABS: |-1|=1
chk(4'b1010,MONE,ZERO,ZERO,ONE,"|-1|=1");
// FNEG: -1→1
chk(4'b1011,MONE,ZERO,ZERO,ONE,"neg(-1)=1");
$display("\n=== 单精度FPU集成测试完成,错误: %0d ===",err);
$finish;
end
endmodule
=== 单精度FPU集成测试 ===
PASS 1+1=2: 40000000
PASS 2-1=1: 3f800000
PASS 2*2=4: 40800000
PASS |-1|=1: 3f800000
PASS neg(-1)=1: 3f800000
=== 单精度FPU集成测试完成,错误: 0 ===
✅Verilator验证通过
不同运算的延迟不同,流水线调度需要考虑:
| 运算 | 延迟(周期) | 流水线级数 |
|---|---|---|
| FADD/FSUB | 3 | 对齐→加法→规格化 |
| FMUL | 3 | 部分积→压缩→舍入 |
| FDIV | 8-16 | 迭代NR/SRT |
| FSQRT | 8-16 | 迭代NR |
| FMA | 5 | 乘法→对齐→加法→规格化→舍入 |
| FCMP/FABS/FNEG | 1 | 组合逻辑 |
练习1:添加浮点转整数(F2I)和整数转浮点(I2F)模块,集成到FPU中。
练习2:实现FPU的5级流水线版本,每级插入寄存器,计算吞吐量。
练习3:添加溢出和下溢检测,正确设置Overflow和Underflow标志。
练习4:实现FPU的CSR(控制状态寄存器),存储异常标志和舍入模式。
✅ 设计FPU整体架构与指令接口
✅ 整合加减乘除开方FMA比较
✅ 实现统一异常处理与标志位
✅ 通过集成测试验证
✅ 理解流水线调度策略