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.14 | 16 bit | [-2, 2) | 2⁻¹⁴≈0.000061 | 角度z、输出x/y |
| Q1.15 | 16 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
运行与验证方法
- 仿真运行:使用Vivado/ModelSim/Verilator运行testbench,观察
$display输出。 - 通过条件:所有
PASS标记的测试均通过,无FAIL输出。 - 精度验证:π/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后截断)。