[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