3. CORDIC流水线设计

CORDIC流水线设计 学习卡片

任务目标

设计一个16级流水线结构的CORDIC处理器,在旋转模式下计算给定角度对应的正弦/余弦值。通过流水线寄存器将迭代运算在时间上展开,使每个时钟周期都能启动一次新的角度计算,从而提升系统吞吐率。最终与单次迭代(非流水)结构进行资源与延迟对比。

实现方案

采用16级流水线,每级执行一次CORDIC微旋转。输入角度为16位定点数(格式:1位符号 + 15位小数,表示范围[-π, π)),输出为16位正弦/余弦定点值(1位符号 + 14位整数 + 1位小数,实际输出范围[-1,1])。每级包含:角度累加器、X/Y坐标旋转、以及对应的反正切常数ROM。流水线寄存器在每级之间插入,确保数据同步推进。

关键完整代码

// 文件: cordic_pipeline.v
module cordic_pipeline #(
    parameter DATA_WIDTH = 16,
    parameter STAGES     = 16
)(
    input  wire                     clk,
    input  wire                     rst_n,
    input  wire                     valid_in,
    input  wire [DATA_WIDTH-1:0]    angle_in,   // 定点角度输入
    output reg                      valid_out,
    output reg  [DATA_WIDTH-1:0]    sin_out,
    output reg  [DATA_WIDTH-1:0]    cos_out
);

    // 反正切常数表 (弧度 * 2^15 / π * 2^15 归一化)
    // 实际存储值 = atan(2^-i) * 2^15 / (π/2) 的定点近似
    localparam [DATA_WIDTH-1:0] ATAN_TABLE [0:STAGES-1] = '{
        16'd8192, 16'd4836, 16'd2555, 16'd1297,
        16'd651,  16'd326,  16'd163,  16'd81,
        16'd41,   16'd20,   16'd10,   16'd5,
        16'd3,    16'd1,    16'd1,    16'd0
    };

    // 流水线寄存器组
    reg [DATA_WIDTH-1:0] x_reg [0:STAGES];
    reg [DATA_WIDTH-1:0] y_reg [0:STAGES];
    reg [DATA_WIDTH-1:0] z_reg [0:STAGES];
    reg                  valid_reg [0:STAGES];

    integer i;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (i = 0; i <= STAGES; i = i + 1) begin
                x_reg[i] <= 0;
                y_reg[i] <= 0;
                z_reg[i] <= 0;
                valid_reg[i] <= 0;
            end
        end else begin
            // 输入级
            x_reg[0] <= 16'd19898;  // 增益补偿常数 K = 0.60725 * 2^15 ≈ 19898
            y_reg[0] <= 0;
            z_reg[0] <= angle_in;
            valid_reg[0] <= valid_in;

            // 16级流水线处理
            for (i = 0; i < STAGES; i = i + 1) begin
                // 根据z的符号决定旋转方向
                wire [DATA_WIDTH-1:0] x_shift = x_reg[i] >>> i;
                wire [DATA_WIDTH-1:0] y_shift = y_reg[i] >>> i;
                wire [DATA_WIDTH-1:0] atan_val = ATAN_TABLE[i];

                if (z_reg[i][DATA_WIDTH-1] == 1'b0) begin  // z为正,顺时针旋转
                    x_reg[i+1] <= x_reg[i] - y_shift;
                    y_reg[i+1] <= y_reg[i] + x_shift;
                    z_reg[i+1] <= z_reg[i] - atan_val;
                end else begin  // z为负,逆时针旋转
                    x_reg[i+1] <= x_reg[i] + y_shift;
                    y_reg[i+1] <= y_reg[i] - x_shift;
                    z_reg[i+1] <= z_reg[i] + atan_val;
                end
                valid_reg[i+1] <= valid_reg[i];
            end

            // 输出级
            valid_out <= valid_reg[STAGES];
            cos_out   <= x_reg[STAGES];
            sin_out   <= y_reg[STAGES];
        end
    end

endmodule

测试平台与验证

// 文件: tb_cordic_pipeline.v
`timescale 1ns/1ps
module tb_cordic_pipeline;
    reg clk = 0, rst_n = 0, valid_in = 0;
    reg [15:0] angle_in;
    wire valid_out;
    wire [15:0] sin_out, cos_out;

    cordic_pipeline dut (
        .clk(clk), .rst_n(rst_n), .valid_in(valid_in),
        .angle_in(angle_in), .valid_out(valid_out),
        .sin_out(sin_out), .cos_out(cos_out)
    );

    always #5 clk = ~clk;

    // 测试角度:30° = π/6 ≈ 0.5236 rad
    // 定点表示:0.5236 * 2^15 ≈ 17157
    initial begin
        #20 rst_n = 1;
        #10 valid_in = 1; angle_in = 16'd17157;
        #10 valid_in = 0;

        // 等待流水线输出(16个周期 + 1个周期输出寄存器)
        #340;
        if (valid_out) begin
            $display("sin(30°) = %d (期望约8192)", sin_out);
            $display("cos(30°) = %d (期望约14199)", cos_out);
        end
        #50 $finish;
    end
endmodule

运行与对比方法

  1. 仿真运行:使用Vivado/ModelSim运行上述测试平台,观察valid_out拉高时sin_out和cos_out的值。
  2. 判定标准:sin(30°)输出应在8192±50范围内(对应0.5±0.0015),cos(30°)输出应在14199±50范围内(对应0.866±0.0015)。
  3. 性能对比:与迭代结构(单次计算需16个周期)相比,流水线结构在连续输入时每个周期均可输出一次结果,吞吐率提升16倍;代价是寄存器资源增加约16倍(每级需3个16位寄存器+1个有效位)。

常见问题

  • 增益补偿:CORDIC迭代后幅度会放大约1.647倍,因此输入X初始值需预乘1/K(K≈0.60725),代码中已用19898(=0.60725×2^15)补偿。
  • 角度范围:本设计仅支持[-π/2, π/2]输入,超出范围需先进行象限转换(将角度映射到第一/四象限)。
  • 流水线延迟:从输入到输出共需17个时钟周期(16级+1级输出寄存器),但吞吐率为每周期1个结果。