poch路障 2026-01-01 17:05 采纳率: 0%
浏览 1

【fpga】CORDIC算法双曲模式

[FPGA] QAQ 今天写cordic算法,双曲模式中出现一个问题,在进行迭代时,第一次的数据迭代不发生变化,但是第二次迭代是正确的。
我起初以为是开始数据处理出现了问题,但是在cnt = 0 处理后发现最后cnt = 0 和cnt = 1 的数据刚好抵消了

module cordic_sqrt(
    input                           clk,
    input                           rst_n,
    input           signed  [31:0]  theta, //输入为弧度
    input           signed  [31:0]  x, //X0
    input           signed  [31:0]  y, //Y0
    output reg      signed  [31:0]  sinh,
    output reg      signed  [31:0]  cosh,
    output reg      signed  [31:0]  exp,
    output reg      signed  [31:0]  ln,
    output reg                      cs_dv
);
//双曲模式最少进行24次迭代
parameter ITERATION_NUM = 19;       //迭代次数
parameter K = 32'sh0D402;//0.828159860*2^16 
parameter F_K =   32'sd79_134;   
//parameter K = 32'H9B74; //0.6072529350088812
//parameter F_K = 32'H1A592;//1.646760258121066
//状态机
localparam IDLE = 3'b001; //空闲状态
localparam WORK = 3'b010; //迭代计算
localparam DONE = 3'b100; //输出


reg signed [31:0] x_shift;
reg signed [31:0] y_shift;
reg signed [31:0] x_prev;
reg signed [31:0] y_prev;
reg signed [31:0] z_rot;
reg D_sign;
wire D_sign_t;

//查找表
wire [31:0] theta_angle [18:0];
//弧度
//迭代 16次 + 重复2次(4) = 18
//双曲模式查找表(修正重复迭代点)
wire signed [31:0] theta_angle [18:0];
assign theta_angle[0]  = 32'd0;
assign theta_angle[1]  = 32'd35999;  //arctanh(1/2)×65536
assign theta_angle[2]  = 32'd16739;  //arctanh(1/4)×65536
assign theta_angle[3]  = 32'd8235;   //arctanh(1/8)×65536
assign theta_angle[4]  = 32'd4101;   //arctanh(1/16)×65536(重复起点)
assign theta_angle[5]  = 32'd4101;   //arctanh(1/16)×65536(重复迭代)
assign theta_angle[6]  = 32'd2049;   //arctanh(1/32)×65536
assign theta_angle[7]  = 32'd1024;   //arctanh(1/64)×65536
assign theta_angle[8]  = 32'd512;    //arctanh(1/128)×65536
assign theta_angle[9]  = 32'd256;    //arctanh(1/256)×65536
assign theta_angle[10] = 32'd128;    //arctanh(1/512)×65536
assign theta_angle[11] = 32'd64;     //arctanh(1/1024)×65536
assign theta_angle[12] = 32'd32;     //arctanh(1/2048)×65536
assign theta_angle[13] = 32'd16;     //arctanh(1/4096)×65536(重复起点)
assign theta_angle[14] = 32'd16;     //arctanh(1/4096)×65536(重复迭代)
assign theta_angle[15] = 32'd8;      //arctanh(1/8192)×65536
assign theta_angle[16] = 32'd4;      //arctanh(1/16384)×65536
assign theta_angle[17] = 32'd2;      //arctanh(1/32768)×65536
assign theta_angle[18] = 32'd1;      //arctanh(1/65536)×65536

//状态机三段式 

reg [2:0] state_c;
reg [2:0] state_n;

//状态转移
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        state_c <= IDLE;
    else
        state_c <= state_n;
end

//组合逻辑判断状态转移
reg skip_en;

always@(*)begin
    state_n = IDLE;
    case(state_c)
        IDLE : begin
            if(skip_en)
                state_n = WORK;
            else
                state_n = IDLE;
        end
        WORK : begin
            if(skip_en)
                state_n = DONE;
            else
                state_n = WORK;
        end
        DONE : begin
            if(skip_en)
                state_n = IDLE;
            else
                state_n = DONE;
        end
        default : state_n = IDLE;
    endcase
end

//时序逻辑电路判断状态输出
//wire [31:0] theta_t;

//Di  第i时钟的z的正负影响第i+1时钟
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        D_sign <= 1'b0;
    end
    else begin
        D_sign <= D_sign_t;
    end
end

assign D_sign_t = z_rot == theta ? 1'b0 : ~z_rot[31]; 

reg [5:0] cnt; //当前迭代次数

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        x_shift <= 32'sd0;
        y_shift <= 32'sd0;
        x_prev  <= 32'sd0;
        y_prev  <= 32'sd0;
        skip_en <= 1'b0;
        cnt     <= 6'd0 ;
        sinh    <= 32'd0;
        cosh    <= 32'd0;
        exp     <= 32'd0;
        ln      <= 32'd0;
        cs_dv <= 1'b0;
        z_rot <= 32'd0;
    end
    else begin
        //为x,y赋初值
        skip_en <= 1'b0;
        x_prev  <= x_shift;
        y_prev  <= y_shift;
        case(state_n)
            IDLE : begin
                x_shift <= x;
                y_shift <= y;
                z_rot <= theta;
                if(cnt == 0) //如果迭代次数小于16,此时进行迭代
                    skip_en <= 1'b1;
                else
                    skip_en <= 1'b0;
            end
            WORK : begin //WORK状态进行迭代 共迭代16次
                //第i次迭代,右移i位
                skip_en <= 1'b0;
                if(cnt == ITERATION_NUM - 1)
                    skip_en <= 1'b1;
                //双曲计算    
                else if(cnt != 0)begin
                    if(D_sign_t) begin //di 为1
                        x_shift       <= $signed(x_prev) + $signed( (y_prev>>>cnt) );
                        y_shift       <= $signed(y_prev) + $signed( (x_prev>>>cnt) );
                        z_rot         <= z_rot   - theta_angle[cnt];
                    end
                    else begin //di 为0
                        x_shift       <= $signed(x_prev) - $signed( (y_prev>>>cnt) );
                        y_shift       <= $signed(y_prev) - $signed( (x_prev>>>cnt) );
                        z_rot         <= z_rot + theta_angle[cnt];
                    end
                    cnt <= cnt + 1;
                end
                else 
                    cnt <= cnt + 1; 
            end
            DONE : begin //计算赋值
                if(cnt == ITERATION_NUM - 1)begin // 迭代完成,输出
                    sinh <= y_shift * F_K;
                    cosh <= x_shift * F_K;
                    ln <= z_rot;
                    exp <= sinh + cosh;
                    skip_en <= 1'b1;
                end
                else ;
            end
        endcase
    end
end

endmodule

TESTBENCH

`timescale 1ns/1ns

module sqrt_tb;
    localparam CLK_PERIOD  = 20;
    parameter ITERATION_NUM = 16; //迭代次数
    //parameter K = 32'H9B74; //0.6072529350088812
    //parameter F_K = 32'H1A592; //1.646760258121066
    parameter K = 32'sh0D402;//0.828159860*2^16 
    parameter F_K =   32'sd79_134; //1.207496340138968
    reg         clk   ;
    reg         rst_n ;
    reg [31:0]  theta ;
    reg [31:0]  x     ;
    reg [31:0]  y     ;
    wire [31:0]  sinh  ;
    wire [31:0]  cosh  ;
    wire [31:0]  exp   ;
    wire [31:0]  ln    ;
    wire         cs_dv ;

    // 1. 定义文件句柄
    integer data_file; // 用于写入数据的文件句柄

    always #(CLK_PERIOD/2) clk = ~clk;

    initial begin
        // 2. 打开文件(仿真开始前)
        data_file = $fopen("simulation_data.txt", "w"); // "w"表示写入模式,会覆盖已有文件
        if (!data_file) begin
            $display("错误:无法打开文件!");
            $stop;
        end
        // 可选:写入文件头
        $fdisplay(data_file, "时间\tx_shift\ty_shift\tz_rot\tcnt\tD_sign\tD_sign_t"); // 使用制表符分隔

        clk = 1'b0;
        rst_n = 1'b0;
        x = 32'sd0;
        y = 32'sd0;
        #5 rst_n = 1'b1;
        //计算sinh,cosh
        x = 32'sd54274; 
        y = 32'sd0; 
        theta =  32'd32_768; //0.5 * 65536
    end

    cordic_sqrt u_cordic_sqrt(
        .clk  (clk  ),
        .rst_n(rst_n),
        .theta(theta),
        .x    (x    ),
        .y    (y    ),
        .sinh (sinh ),
        .cosh (cosh ),
        .exp  (exp  ),
        .ln   (ln   ),
        .cs_dv(cs_dv)
    );

    // 3. 捕获和写入数据
    always @(posedge clk) begin
        if (rst_n) begin // 确保在复位完成后才记录数据
            // 使用层次化路径访问DUT内部信号
            // 关键:使用 $fstrobe 确保采样到非阻塞赋值后的稳定值
            $fstrobe(data_file, "%0t\t%d\t%d\t%d\t%b\t%b\t%b", 
                     $time, 
                     u_cordic_sqrt.x_shift, // 访问内部reg
                     u_cordic_sqrt.y_shift, 
                     u_cordic_sqrt.z_rot, 
                     u_cordic_sqrt.cnt,
                     u_cordic_sqrt.D_sign_t,
                     u_cordic_sqrt.D_sign); // 访问内部wire
        end
    end

    // 4. 控制仿真时长并关闭文件
    initial begin
        #5000; // 根据实际需要调整仿真时间
        $fclose(data_file);
        $display("仿真完成。数据已写入 simulation_data.txt");
        $finish;
    end

endmodule

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2026-01-01 17:06
    关注

    下午好🌅🌅🌅
    本答案参考通义千问

    你提到的问题是:在使用CORDIC算法的双曲模式时,第一次迭代不发生变化,而第二次迭代是正确的。这可能是因为在第一次迭代时,z_rottheta 的值相等,导致没有进行任何旋转操作。


    问题分析

    在双曲模式下,CORDIC算法的迭代过程如下:

    1. 每次迭代中,根据当前角度 z_rot 与目标角度 theta 的关系,决定旋转方向(正向或反向)。
    2. 在每次迭代中,会根据旋转方向对 xy 进行更新,并调整 z_rot
    3. 如果 z_rot == theta,则不会发生任何变化。

    在你的代码中,D_sign_t = z_rot == theta ? 1'b0 : ~z_rot[31]; 表示如果 z_rot 等于 theta,则 D_sign 为 0,表示不需要旋转。

    因此,在第一次迭代中,如果 z_rot == theta,那么就不会进行任何操作,导致第一次迭代没有变化。


    解决方案

    1. 检查初始条件

    确保在第一次迭代时,z_rot 不等于 theta,否则将跳过第一次迭代。

    2. 修改状态机逻辑

    确保在第一次迭代时不跳过任何操作。

    3. 修正 D_sign_t 逻辑

    避免因 z_rot == theta 而跳过第一次迭代。


    修改后的代码(关键部分)

    // 修改 D_sign_t 逻辑,避免第一次迭代被跳过
    assign D_sign_t = (z_rot == theta) ? 1'b0 : ~z_rot[31];
    
    // 修改状态转移逻辑,确保第一次迭代正常执行
    always@(*) begin
        state_n = IDLE;
        case(state_c)
            IDLE : begin
                if (!rst_n) begin
                    state_n = IDLE;
                end else if (cnt == 0) begin
                    state_n = WORK; // 确保第一次迭代进入 WORK 状态
                end
            end
            WORK : begin
                if (cnt >= ITERATION_NUM) begin
                    state_n = DONE;
                end else begin
                    state_n = WORK;
                end
            end
            DONE : begin
                state_n = IDLE;
            end
            default : state_n = IDLE;
        endcase
    end
    
    // 修改迭代逻辑,确保第一次迭代正确
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            x_shift <= x;
            y_shift <= y;
            z_rot <= theta;
            cnt <= 6'd0;
            skip_en <= 1'b0;
            cs_dv <= 1'b0;
        end else begin
            if (state_c == WORK && cnt < ITERATION_NUM) begin
                // 第一次迭代不跳过
                if (D_sign_t) begin
                    // 反向旋转
                    x_shift <= x_prev - (y_prev >> cnt);
                    y_shift <= y_prev + (x_prev >> cnt);
                end else begin
                    // 正向旋转
                    x_shift <= x_prev + (y_prev >> cnt);
                    y_shift <= y_prev - (x_prev >> cnt);
                end
                z_rot <= z_rot - theta_angle[cnt];
                cnt <= cnt + 1;
            end else if (cnt >= ITERATION_NUM) begin
                // 计算 sinh, cosh, exp, ln
                sinh <= y_shift * K;
                cosh <= x_shift * K;
                exp <= x_shift * K + y_shift * K;
                ln <= z_rot;
                cs_dv <= 1'b1;
            end
        end
    end
    

    总结

    • 第一次迭代不发生变化的原因z_rot == theta 导致未进行旋转。
    • 解决方法
      • 检查初始条件,确保 z_rot != theta
      • 修改状态机逻辑,确保第一次迭代正常执行。
      • 修正 D_sign_t 逻辑,避免跳过第一次迭代。

    建议调试步骤

    1. 使用仿真工具(如 ModelSim 或 Vivado Sim)观察 z_rottheta 的值是否相等。
    2. 打印 cntD_sign_t 的值,确认第一次迭代是否被跳过。
    3. 确保 theta_angle 中的角度值合理,避免重复或错误。

    如有更多代码片段或具体调试信息,可以进一步帮助定位问题。

    评论

报告相同问题?

问题事件

  • 创建了问题 1月1日