cordic的FPGA实现(三)sin、cos函数运算与源码分析
在旋转模式中曾提到:由于每次伪旋转都导致向量模长发生了变化,以Ki表示第i次伪旋转模长补偿因子,所以第i次伪旋转真实旋转的结果应该为:
经过n次伪旋转,得到的伪旋转点最终结果可以表示为:
当n趋近于无穷大时,An逼近1.646760258,令xo=1/An且yo=0即可得到目标旋转角度的正弦、余弦值。
那么,我们现在就让xo=1/An且yo=0且(即x0=1/1.646760258=0.6073),就可以得到z0(旋转角所对应的正弦、余弦值)。
我在想:为什么常量定义中定义的是反正切函数的一半、一半、一半逐个减半,而不是角度一半、一半、一半的减小呢?没想通,反正就是反正切函数的值(角度)逐个减半吧?不对!当然是使用反正切函数的变量值逐个减半更好!因为这样的话逐个将tan>>>(带符号右移)一位就好,要是角度的话一半一半的话绝逼有小数!想出这个CORDIC求解tan、sin、cos的人真的好厉害!!!
verilog代码:
module cordic
(
input CLOCK,RESET,
input iCall,
output oDone,
input [31:0]iData,
output [31:0]cos,
output [31:0]sin,
/*
输出的x为cos值,输出的y为sin值,x=qx,y=qy
*/
output [31:0]q_deg,q_y,q_x
);
reg [31:0] atan [15:0];
//声明atan反正切函数常量表,该常量表从tan(45)度开始每次减小一半儿
initial begin
atan[0] = 32'd2949120; atan[1] = 32'd1740992; atan[2] = 32'd919872; atan[3] = 32'd466944;
atan[4] = 32'd234368; atan[5] = 32'd117312; atan[6] = 32'd58688; atan[7] = 32'd29312;
atan[8] = 32'd14656; atan[9] = 32'd7360; atan[10] = 32'd3648; atan[11] = 32'd1856;
atan[12] = 32'd896; atan[13] = 32'd448; atan[14] = 32'd256; atan[15] = 32'd128;
end
//带符号位的寄存器声明
reg signed [31:0]x,y,tx,ty,deg;
reg [7:0]i;
reg isDone;
always @ ( posedge CLOCK or negedge RESET )
if( !RESET )
begin
{ x,y,tx,ty,deg } <= { 32'd0,32'd0,32'd0,32'd0,32'd0 };
i <= 8'd0;
isDone <= 1'b0;
end
else if( iCall )
case( i )
0:
begin x <= 0.607253 * 65536; y <= 32'd0; deg <= iData << 16; i <= i + 1'b1; end
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16:
/*
在剩余角度大于0时,需要继续逆时针旋转,在单位圆上逆时针旋转的话横坐标会减小,纵坐标会增大
*/
if( deg > 0 )
begin
ty = y >>> i-1;
tx = x >>> i-1;
x <= x - ty;
y <= y + tx;
deg <= deg - atan[i-1];
i <= i + 1'b1;
end
else
/*
在剩余角度小于0时候,需要顺时针旋转,顺时针旋转的话纵坐标会减小,横坐标会增大
*/
begin
ty = y >>> i-1;
tx = x >>> i-1;
x <= x + ty ;
y <= y - tx ;
deg <= deg + atan[i-1];
i <= i + 1'b1;
end
17:
begin isDone <= 1'b1; i <= i + 1'b1; end
18:
begin isDone <= 1'b0; i <= 8'd0; end
endcase
assign oDone = isDone;
assign cos = x;
assign sin = y;
assign { q_deg,q_y,q_x } = { deg,y,x };
endmodule
仿真代码:
`timescale 1ns/1ns
module cordic_tb;
reg clk; //系统时钟
reg rst_n;//复位信号
reg iCall;//模块调用信号
reg [31:0]iData; //待求角度
wire oDone; //迭代完成标志
wire [31:0]cos; //cos值
wire [31:0]sin; //sin值
wire [31:0]deg_left; //剩余角度
cordic u0(
.CLOCK(clk),
.RESET(rst_n),
.iCall(iCall),
.oDone(oDone),
.iData(iData),
.cos(cos),
.sin(sin),
/*
输出的x为cos值,输出的y为sin值,x=qx,y=qy
*/
.q_deg(deg_left)
);
initial begin
clk=0;
forever #5 clk=~clk;
end
initial begin
rst_n=0;
#10;
rst_n=1;
end
reg [7:0]i;
always@(posedge clk or negedge rst_n)
if(~rst_n)
begin
i<=8'd0;
iCall<=1'b0;
iData<=32'd0;
end
else
case(i)
0: //z=30
begin
if(oDone)
begin
iCall<=1'b0;
i<=i+1'b1;
end
else
begin
iCall<=1'b1;
iData<=32'd30;
end
end
1: //z=45
begin
if(oDone)
begin
iCall<=1'b0;
i<=i+1'b1;
end
else
begin
iCall<=1'b1;
iData<=32'd45;
end
end
2: //z=60
begin
if(oDone)
begin
iCall<=1'b0;
i<=i+1'b1;
end
else
begin
iCall<=1'b1;
iData<=32'd60;
end
end
3: //z=75
begin
if(oDone)
begin
iCall<=1'b0;
i<=i+1'b1;
end
else
begin
iCall<=1'b1;
iData<=32'd75;
end
end
4: //z=90
begin
if(oDone)
begin
iCall<=1'b0;
i<=i+1'b1;
end
else
begin
iCall<=1'b1;
iData<=32'd90;
end
end
5:
i<=i;
endcase
endmodule
仿真结果:
真实结果 迭代结果
sin30=0.5000 32769/65536=0.5000
cos30=0.8660 56759/65536=0.8661
sin45=0.7071 46341/65536=0.7071
cos45=0.7071 46342/65536=0.7071
sin60=0.8660 56759/65536=0.8661
cos60=0.5000 32769/65536=0.5000
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人