4. CORDIC定点数格式设计

任务目标

为CORDIC算法设计定点数格式,明确整数位宽与小数位宽的选择依据,并实现一个通用的定点数转换模块,支持十进制浮点数与定点数二进制补码之间的双向转换,用于后续CORDIC迭代计算。

定点数格式选择

CORDIC算法要求输入角度和输出结果均满足:|x| ≤ 1,|y| ≤ 1,|z| ≤ π/2 ≈ 1.5708。因此采用Q1.14格式(1位符号位 + 1位整数位 + 14位小数位),可表示范围[-2, 2),精度为2⁻¹⁴ ≈ 0.000061。该格式同时覆盖角度输入(最大π/2)与正余弦输出(最大1),且14位小数精度足以支撑10轮CORDIC迭代的误差需求。

格式位宽范围精度适用场景
Q1.1416 bit[-2, 2)2⁻¹⁴≈0.000061角度z、输出x/y
Q1.1516 bit[-1, 1)2⁻¹⁵≈0.000031仅当输入严格|v|≤1时

本案例选用Q1.14,因为CORDIC旋转角度z可能达到π/2≈1.5708,超过Q1.15的表示上限1.0,而Q1.14的整数位可覆盖到2.0,避免溢出。

转换模块实现

以下Verilog模块实现浮点→定点(float2fix)与定点→浮点(fix2float)双向转换。定点数采用16位二进制补码,小数位宽14位。

module fixed_point_convert #(
    parameter IW = 2,   // 整数位宽(含符号位)
    parameter FW = 14   // 小数位宽
)(
    input  wire        clk,
    input  wire        rst_n,
    // 浮点→定点接口
    input  wire        req_f2f,
    input  wire signed [31:0] float_in,  // 32位定点表示浮点(高16位整数,低16位小数)
    output reg  signed [IW+FW-1:0] fixed_out,
    output reg         f2f_done,
    // 定点→浮点接口
    input  wire        req_fix2f,
    input  wire signed [IW+FW-1:0] fixed_in,
    output reg  signed [31:0] float_out, // 32位定点表示浮点
    output reg         fix2f_done
);

    // 浮点输入格式:32位,其中[31:16]为整数部分,[15:0]为小数部分(无符号)
    // 实际浮点值 = int_part + frac_part / 65536

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            fixed_out <= 0;
            f2f_done <= 0;
            float_out <= 0;
            fix2f_done <= 0;
        end else begin
            // 默认清除完成标志
            f2f_done <= 0;
            fix2f_done <= 0;

            if (req_f2f) begin
                // 浮点→定点:乘以2^FW后取整
                // float_in[31:16]为整数部分,float_in[15:0]为小数部分(/65536)
                // 定点值 = (int_part + frac_part/65536) * 2^14
                //        = int_part * 16384 + frac_part * 16384 / 65536
                //        = int_part * 16384 + frac_part / 4
                fixed_out <= (float_in[31:16] * (1 << FW)) +
                              (float_in[15:0] >> (16 - FW));
                f2f_done <= 1;
            end

            if (req_fix2f) begin
                // 定点→浮点:除以2^FW
                // 整数部分 = fixed_in >> FW(算术右移)
                // 小数部分 = fixed_in[FW-1:0] 扩展为16位
                float_out[31:16] <= fixed_in >>> FW;  // 符号扩展右移
                float_out[15:0]  <= fixed_in[FW-1:0] << (16 - FW);
                fix2f_done <= 1;
            end
        end
    end

endmodule

测试平台与验证

编写testbench验证转换正确性。测试向量:1.5、-0.75、0.1、1.5708(π/2)。

module tb_fixed_point;
    reg clk = 0, rst_n = 0;
    reg req_f2f = 0, req_fix2f = 0;
    reg signed [31:0] float_in;
    reg signed [15:0] fixed_in;
    wire signed [15:0] fixed_out;
    wire signed [31:0] float_out;
    wire f2f_done, fix2f_done;

    fixed_point_convert dut(.*);

    // 生成时钟
    always #5 clk = ~clk;

    // 浮点输入编码:整数部分<<16 | 小数部分(0~65535)
    // 例如1.5 → 1<<16 | 32768 = 98304
    // -0.75 → 补码表示:-0.75*65536 = -49152 → 32位补码 = 0xFFFF4000

    initial begin
        #10 rst_n = 1;

        // 测试1: 1.5 → 定点 = 1.5 * 16384 = 24576
        @(posedge clk);
        float_in = 32'd98304;  // 1.5
        req_f2f = 1;
        @(posedge clk);
        req_f2f = 0;
        @(posedge clk);
        if (fixed_out === 16'sd24576)
            $display("PASS: 1.5 → fixed = %d", fixed_out);
        else
            $display("FAIL: 1.5 → fixed = %d, expected 24576", fixed_out);

        // 测试2: -0.75 → 定点 = -0.75 * 16384 = -12288
        @(posedge clk);
        float_in = 32'hFFFF4000;  // -0.75
        req_f2f = 1;
        @(posedge clk);
        req_f2f = 0;
        @(posedge clk);
        if (fixed_out === -16'sd12288)
            $display("PASS: -0.75 → fixed = %d", fixed_out);
        else
            $display("FAIL: -0.75 → fixed = %d, expected -12288", fixed_out);

        // 测试3: 定点→浮点,输入24576 → 应输出1.5
        @(posedge clk);
        fixed_in = 16'sd24576;
        req_fix2f = 1;
        @(posedge clk);
        req_fix2f = 0;
        @(posedge clk);
        // 期望float_out = 1<<16 | 32768 = 98304
        if (float_out === 32'sd98304)
            $display("PASS: fixed 24576 → float = %d", float_out);
        else
            $display("FAIL: fixed 24576 → float = %d, expected 98304", float_out);

        // 测试4: 定点→浮点,输入-12288 → 应输出-0.75
        @(posedge clk);
        fixed_in = -16'sd12288;
        req_fix2f = 1;
        @(posedge clk);
        req_fix2f = 0;
        @(posedge clk);
        if (float_out === 32'hFFFF4000)
            $display("PASS: fixed -12288 → float = %d", float_out);
        else
            $display("FAIL: fixed -12288 → float = %d, expected 0xFFFF4000", float_out);

        // 测试5: 边界值 π/2 ≈ 1.5708 → 定点 = 1.5708*16384 ≈ 25736
        @(posedge clk);
        float_in = 32'd102944;  // 1.5708 * 65536 ≈ 102944
        req_f2f = 1;
        @(posedge clk);
        req_f2f = 0;
        @(posedge clk);
        $display("INFO: π/2 → fixed = %d (期望约25736)", fixed_out);

        #50 $finish;
    end

    // 监控转换完成
    always @(posedge f2f_done)
        $display("f2f_done at time %t", $time);
    always @(posedge fix2f_done)
        $display("fix2f_done at time %t", $time);

endmodule

运行与验证方法

  1. 仿真运行:使用Vivado/ModelSim/Verilator运行testbench,观察$display输出。
  2. 通过条件:所有PASS标记的测试均通过,无FAIL输出。
  3. 精度验证:π/2转换结果与理论值25736的误差应≤1个LSB(即≤0.000061),表明14位小数位宽满足CORDIC角度输入精度要求。

常见问题

  • 负浮点输入编码:testbench中负浮点值需以32位补码形式给出(如-0.75 → 0xFFFF4000),不能直接写十进制负数。
  • 溢出风险:若输入值≥2.0,Q1.14格式将溢出。CORDIC输入应保证|x|,|y|≤1,角度z≤π/2,否则需扩展整数位宽。
  • 舍入误差:浮点→定点转换采用截断(truncation),最大误差1 LSB。如需更精确可改为四舍五入(加0.5后截断)。