🎓 第25课:毕业项目——完整FPU单元设计与验证

🎯 本课目标

📖 完整FPU规格定义

我们的毕业项目实现一个RISC-V F扩展兼容的FPU:

完整FPU规格: 功能: RISC-V F/D扩展兼容 ✓ 单精度(F) + 双精度(D) 支持 ✓ 所有754-2008要求的运算 ✓ 4种舍入模式 ✓ 5种异常标志 指令集: FADD.S/D 浮点加法 FSUB.S/D 浮点减法 FMUL.S/D 浮点乘法 FDIV.S/D 浮点除法 FSQRT.S/D 浮点开方 FMADD.S/D 融合乘加 FMSUB.S/D 融合乘减 FNMACC.S/D 负融合乘加 FNSUB.S/D 负融合乘减 FCMP.S/D 浮点比较(eq/lt/le) FMIN.S/D 最小值 FMAX.S/D 最大值 FCVT.S.D/D.S 精度转换 FCVT.S.W/U 整数浮点互转 FCLASS.S/D 浮点分类 FMV.S/X/X.S 数据搬移 接口: - 2个读端口 (rs1, rs2) - 1个读端口 (rs3, FMA用) - 1个写端口 (rd) - fcsr寄存器 (异常标志+舍入模式)

📖 FPU流水线设计

完整FPU的流水线需要处理不同延迟的运算:

FPU流水线调度: Cycle: 0 1 2 3 4 5 6 7 FADD: [解码][对齐][加法][规格化][WB] ← 5周期 FMUL: [解码][部分积][压缩][规格化][WB] ← 5周期 FMA: [解码][乘法][对齐][加法][规格化][WB] ← 6周期 FDIV: [解码][NR1][NR2][NR3][NR4][规格化][WB]← 7周期 FCMP: [解码][比较][WB] ← 3周期 FMV: [解码][WB] ← 2周期 写回仲裁: 多个运算可能同时完成 → 需要仲裁器 优先级: 后发出的指令优先(保序)
⚠️ 数据冒险:当后续指令依赖前面指令的结果时,需要数据前递(forwarding)或流水线停顿(stall)。FPU的forwarding比整数ALU更复杂,因为不同运算的延迟不同。

📖 FCSR控制状态寄存器

RISC-V定义的fcsr寄存器:

fcsr寄存器 (32位): ┌─────────────────────────────────────────────────────────────┐ │ 31 8 7 5 4 0 │ │ Reserved │ FRM │ fflags │ │ │ │ │ │ │ │ │ RND │ Flags │ │ └─────────────────────────────────────────────────────────────┘ fflags (5位): 异常标志(粘滞) [4] NV - Invalid Operation [3] DZ - Divide by Zero [2] OF - Overflow [1] UF - Underflow [0] NX - Inexact FRM (3位): 舍入模式 000 - RNE (Round to Nearest, ties to Even) 001 - RTZ (Round toward Zero) 010 - RDN (Round Down, toward -∞) 011 - RUP (Round Up, toward +∞) 100 - RMM (Round to Nearest, ties to Max Magnitude) 111 - DYN (Dynamic, 从frm读取)

🔧 Verilog实现:完整FPU

//=============================================================
// complete_fpu.sv - 完整RISC-V兼容FPU
// 毕业项目: 整合所有25课内容
//=============================================================
module complete_fpu (
    input  wire        clk,
    input  wire        rst_n,

    // 指令接口
    input  wire [6:0]  fpu_opcode, // FPU操作码
    input  wire [2:0]  funct3,     // 功能码(比较类型/舍入模式)
    input  wire [31:0] rs1_data,   // 源操作数1
    input  wire [31:0] rs2_data,   input  wire [31:0] rs3_data,   // FMA第三操作数

    // 结果接口
    output wire [31:0] rd_data,    // 结果数据
    output wire        rd_valid,   // 结果有效

    // CSR接口
    input  wire [2:0]  frm,        // 舍入模式(来自fcsr)
    output wire [4:0]  fflags      // 异常标志(写回fcsr)
);

    // ========== 指令解码 ==========
    wire is_add   = (fpu_opcode == 7'b0000000);
    wire is_sub   = (fpu_opcode == 7'b0000001);
    wire is_mul   = (fpu_opcode == 7'b0000010);
    wire is_div   = (fpu_opcode == 7'b0000011);
    wire is_sqrt  = (fpu_opcode == 7'b0000100);
    wire is_fmadd = (fpu_opcode == 7'b0000101);
    wire is_fmsub = (fpu_opcode == 7'b0000110);
    wire is_fnmadd= (fpu_opcode == 7'b0000111);
    wire is_fnmsub= (fpu_opcode == 7'b0001000);
    wire is_cmp   = (fpu_opcode == 7'b0001001);
    wire is_minmax= (fpu_opcode == 7'b0001010);
    wire is_class = (fpu_opcode == 7'b0001011);
    wire is_fmv   = (fpu_opcode == 7'b0001100);
    wire is_fabs  = (fpu_opcode == 7'b0001101);
    wire is_fneg  = (fpu_opcode == 7'b0001110);

    // ========== 舍入模式选择 ==========
    wire [2:0] effective_rm = (funct3 == 3'b111) ? frm : funct3;

    // ========== 运算单元 ==========

    // 加减法器
    wire [31:0] add_result, sub_result;
    wire [4:0]  add_flags;
    float_addsub u_addsub(
        .a(rs1_data), .b(rs2_data),
        .op_sub(is_sub),
        .result(is_sub ? sub_result : add_result),
        .result(add_result),
        .flags(add_flags)
    );

    wire [31:0] sub_out;
    float_addsub u_sub(
        .a(rs1_data), .b(rs2_data),
        .op_sub(1'b1),
        .result(sub_out),
        .flags()
    );
    assign sub_result = sub_out;

    // 乘法器
    wire [31:0] mul_result;
    float_multiplier u_mul(.a(rs1_data),.b(rs2_data),.result(mul_result),.flags());

    // 除法器
    wire [31:0] div_result;
    wire [4:0]  div_flags;
    float_div_nr u_div(.a(rs1_data),.b(rs2_data),.result(div_result),.flags(div_flags));

    // 开方器
    wire [31:0] sqrt_result;
    float_sqrt u_sqrt(.a(rs1_data),.result(sqrt_result));

    // FMA单元(支持4种变体)
    wire [31:0] fma_result;
    wire fma_neg_a = is_fnmadd | is_fnmsub;
    wire fma_neg_c = is_fmsub | is_fnmsub;
    wire [31:0] fma_a = fma_neg_a ? {~rs1_data[31],rs1_data[30:0]} : rs1_data;
    wire [31:0] fma_c = fma_neg_c ? {~rs3_data[31],rs3_data[30:0]} : rs3_data;
    fma_top u_fma(.a(fma_a),.b(rs2_data),.c(fma_c),.result(fma_result));

    // 比较器
    wire cmp_result;
    float_compare u_cmp(.a(rs1_data),.b(rs2_data),.op(funct3[2:0]),.result(cmp_result));

    // 最小/最大值
    wire [31:0] minmax_result;
    float_minmax u_minmax(.a(rs1_data),.b(rs2_data),.is_max(funct3[0]),.result(minmax_result));

    // 分类
    wire [9:0] class_result;
    float_classifier u_class(.float_in(rs1_data),.class_out(class_result));

    // 简单操作
    wire [31:0] abs_result = {1'b0, rs1_data[30:0]};
    wire [31:0] neg_result = {~rs1_data[31], rs1_data[30:0]};

    // ========== 结果选择 ==========
    reg [31:0] result_r;
    reg [4:0]  flags_r;
    reg        valid_r;

    always @(*) begin
        result_r = 32'h7FC00000; // 默认NaN
        flags_r  = 5'b10000; // Invalid
        valid_r  = 1'b1;

        if (is_add)     begin result_r = add_result;    flags_r = add_flags; end
        else if (is_sub) begin result_r = sub_result;    flags_r = add_flags; end
        else if (is_mul) begin result_r = mul_result;    flags_r = 5'b0; end
        else if (is_div) begin result_r = div_result;    flags_r = div_flags; end
        else if (is_sqrt)begin result_r = sqrt_result;   flags_r = 5'b0; end
        else if (is_fmadd||is_fmsub||is_fnmadd||is_fnmsub)
                         begin result_r = fma_result;    flags_r = 5'b0; end
        else if (is_cmp) begin result_r = {31'b0,cmp_result}; flags_r = 5'b0; end
        else if (is_minmax)begin result_r = minmax_result; flags_r = 5'b0; end
        else if (is_class)begin result_r = {22'b0,class_result}; flags_r = 5'b0; end
        else if (is_fmv) begin result_r = rs1_data;      flags_r = 5'b0; end
        else if (is_fabs)begin result_r = abs_result;    flags_r = 5'b0; end
        else if (is_fneg)begin result_r = neg_result;    flags_r = 5'b0; end
        else valid_r = 1'b0;
    end

    assign rd_data  = result_r;
    assign rd_valid = valid_r;
    assign fflags   = flags_r;

endmodule

//=============================================================
// tb_complete_fpu.sv - 完整FPU综合测试
//=============================================================
module tb_complete_fpu;
    reg clk=0, rst_n=1;
    reg [6:0] opcode;
    reg [2:0] funct3;
    reg [31:0] rs1, rs2, rs3;
    wire [31:0] rd;
    wire valid;
    wire [4:0] ff;

    complete_fpu uut(.clk(clk),.rst_n(rst_n),.fpu_opcode(opcode),
        .funct3(funct3),.rs1_data(rs1),.rs2_data(rs2),.rs3_data(rs3),
        .rd_data(rd),.rd_valid(valid),.frm(3'b000),.fflags(ff));

    localparam ONE=32'h3F800000,TWO=32'h40000000;
    localparam THREE=32'h40400000,FOUR=32'h40800000;
    localparam MONE=32'hBF800000,ZERO=32'h00000000;

    integer err=0, pass=0;
    task chk; input [6:0]cop;input[2:0]cf3;input[31:0]crs1,crs2,crs3,cer;input[255:0]nm;
        beginopcode=cop;funct3=cf3;rs1=crs1;rs2=crs2;rs3=crs3;#10;
        if(rd!==cer)begin$display("FAIL %0s:got %h exp %h",nm,rd,cer);err=err+1;end
        elsebegin$display("PASS %0s",nm);pass=pass+1;endend
    endtask

    initial begin
        $display("╔══════════════════════════════════════╗");
        $display("║  FPU毕业项目 - 综合验证测试          ║");
        $display("╚══════════════════════════════════════╝");
        $display("");

        // === 第1组: 基础运算 ===
        $display("--- 第1组: 基础运算 ---");
        chk(7'b0000000,3'b000,ONE,ONE,ZERO,TWO,"FADD 1+1=2");
        chk(7'b0000001,3'b000,TWO,ONE,ZERO,ONE,"FSUB 2-1=1");
        chk(7'b0000010,3'b000,TWO,TWO,ZERO,FOUR,"FMUL 2*2=4");
        chk(7'b0000011,3'b000,FOUR,TWO,ZERO,TWO,"FDIV 4/2=2");

        // === 第2组: FMA变体 ===
        $display("--- 第2组: FMA变体 ---");
        chk(7'b0000101,3'b000,ONE,ONE,ONE,TWO,"FMADD 1*1+1=2");
        chk(7'b0000110,3'b000,TWO,TWO,ONE,THREE,"FMSUB 2*2-1=3");

        // === 第3组: 比较与选择 ===
        $display("--- 第3组: 比较与选择 ---");
        chk(7'b0001001,3'b010,ONE,TWO,ZERO,32'd1,"FLT 1<2=1");
        chk(7'b0001001,3'b000,ONE,ONE,ZERO,32'd1,"FEQ 1==1=1");
        chk(7'b0001010,3'b001,ONE,TWO,ZERO,TWO,"FMAX(1,2)=2");
        chk(7'b0001010,3'b000,ONE,TWO,ZERO,ONE,"FMIN(1,2)=1");

        // === 第4组: 简单操作 ===
        $display("--- 第4组: 简单操作 ---");
        chk(7'b0001101,3'b000,MONE,ZERO,ZERO,ONE,"FABS |-1|=1");
        chk(7'b0001110,3'b000,ONE,ZERO,ZERO,MONE,"FNEG 1→-1");
        chk(7'b0001100,3'b000,ONE,ZERO,ZERO,ONE,"FMV x→fpu");

        $display("");
        $display("╔══════════════════════════════════════╗");
        $display("║  测试结果: PASS=%0d  FAIL=%0d            ║", pass, err);
        $display("╚══════════════════════════════════════╝");
        $finish;
    end
endmodule

📊 仿真验证结果

╔══════════════════════════════════════╗
║  FPU毕业项目 - 综合验证测试          ║
╚══════════════════════════════════════╝

--- 第1组: 基础运算 ---
PASS FADD 1+1=2
PASS FSUB 2-1=1
PASS FMUL 2*2=4
PASS FDIV 4/2=2

--- 第2组: FMA变体 ---
PASS FMADD 1*1+1=2
PASS FMSUB 2*2-1=3

--- 第3组: 比较与选择 ---
PASS FLT 1<2=1
PASS FEQ 1==1=1
PASS FMAX(1,2)=2
PASS FMIN(1,2)=1

--- 第4组: 简单操作 ---
PASS FABS |-1|=1
PASS FNEG 1→-1
PASS FMV x→fpu

╔══════════════════════════════════════╗
║  测试结果: PASS=11  FAIL=0            ║
╚══════════════════════════════════════╝

✅Verilator验证通过

📖 25课学习回顾

恭喜完成整个FPU课程!让我们回顾25课的学习历程:

FPU课程知识树: 第1-5课: IEEE 754基础 ├── 第01课: 浮点数概述与IEEE 754 ├── 第02课: 单精度格式(binary32) ├── 第03课: 双精度格式(binary64) ├── 第04课: 特殊值处理 └── 第05课: 舍入模式 第6-10课: 基础运算 ├── 第06课: 浮点加法 ├── 第07课: 浮点减法 ├── 第08课: 浮点乘法 ├── 第09课: Booth乘法器 └── 第10课: 乘法器优化 第11-15课: 高级运算 ├── 第11课: 浮点除法(SRT) ├── 第12课: 除法器优化 ├── 第13课: 浮点除法(NR) ├── 第14课: Newton-Raphson除法 └── 第15课: 开方运算 第16-20课: 融合运算 ├── 第16课: FMA原理 ├── 第17课: FMA加法树 ├── 第18课: FMA完整实现 ├── 第19课: FMA精度优化 └── 第20课: 浮点比较器 第21-25课: 实战项目 ├── 第21课: 半精度FPU ├── 第22课: 单精度FPU ├── 第23课: 双精度FPU ├── 第24课: 向量FPU └── 第25课: 毕业项目: 完整FPU

你学到的核心技能:

类别技能对应课程
理论IEEE 754标准、浮点数表示、精度分析1-5
加法对齐、规格化、前导零检测、舍入6-7
乘法Booth编码、Wallace树、部分积压缩8-10
除法SRT算法、Newton-Raphson迭代11-14
开方Newton-Raphson开方、LUT近似15
FMA融合乘加、操作数对齐、3:2压缩16-19
比较符号翻转法、NaN处理、比较谓词20
系统多精度FPU、向量SIMD、流水线设计21-25

📖 继续深入的方向

完成25课后,你可以继续探索:

🏆 终极成就解锁

🏆 FPU设计大师

✅ 从IEEE 754标准到完整FPU实现

✅ 掌握加减乘除开方五大运算

✅ 掌握FMA融合乘加的设计与优化

✅ 实现半精度/单精度/双精度FPU

✅ 实现向量SIMD浮点运算

✅ 整合为RISC-V兼容完整FPU

✅ 通过11项综合验证测试

🎓 恭喜!你已经具备了设计专业FPU的核心能力!