CORDIC算法解释及FPGA实现(圆坐标系)
CORDIC算法解释及Verilog仿真(圆坐标系)
CORDIC算法原理阐述
CORDIC(Coordinate Rotation Digital Computer)算法,即坐标旋转数字计算方法,是J.D.Volder1于1959年首次提出,主要用于三角函数、双曲线、指数、对数的计算。
伪旋转
在笛卡尔坐标平面(下方左图)由 \(({x_1},{y_1})\) 旋转 θ 角度至 \(({x_2},{y_2})\) 得到:\(({\hat x_2},{\hat y_2})\) ;
提出因数 \(\cos \theta\) ,方程转化为:\(\left\{ {\matrix{
{{x_2} = \cos \theta ({x_1} - {y_1}\tan \theta )} \cr
{{y_2} = \cos \theta ({y_1} + {x_1}\tan \theta )} \cr
} } \right.\);
待去除 \(\cos \theta\) 项,得到“伪旋转”公式\(\left\{ {\matrix{
{{{\hat x}_2} = {x_1} - {y_1}\tan \theta } \cr
{{{\hat y}_2} = {y_1} + {x_1}\tan \theta } \cr
} } \right.\)。
经“伪旋转”后,向量 R 模值将增加 $1/\cos \theta $ 倍(角度保持一致)。
角度累加器
为便于FPGA硬件实现(正切项需改为移位操作):以 $ \tan {\theta ^i} = {2^{ - i}}$ 设定旋转角度 θ ;
故方程可转换为\(\left\{ {\matrix{
{{{\hat x}_{_2}} = {x_1} - {y_1}{2^{ - i}}} \cr
{{{\hat y}_{_2}} = {y_1} + {x_1}{2^{ - i}}} \cr
} } \right.\) 或 \(\left[ {\matrix{
{{{\hat x}_{_2}}} \cr
{{{\hat y}_{_2}}} \cr
} } \right] = \left[ {\matrix{
1 & { - {2^{ - i}}} \cr
{{2^{ - i}}} & 1 \cr
} } \right]\left[ {\matrix{
{{x_1}} \cr
{{y_1}} \cr
} } \right]\)。
其中矩阵 \(\left[ {\matrix{
1 & { - {2^{ - i}}} \cr
{{2^{ - i}}} & 1 \cr
} } \right]\) 可进行拆分为多个类似矩阵乘积,即旋转角度 θ ,可拆分为多次小的旋转(下图为对应的反正切角度表)。
由于旋转角度 θ 可为任意值,故将旋转变换采用迭代算法实现,即多次角度迭代关系无限趋近于目标θ角度(以 θ 旋转角度限制)。以55°度旋转角为例逼近55° = 45.0° + 26.6° -14.0°- 7.1° + 3.6° + 1.8° - 0.9°。
旋转过程需引入一个判决因子 \({d_i}\) ,用于确定角度旋转的方向。
根据判决因子 \({d_i}\) 来设定一个角度累加器:$\eqalign{
& {z^{(i + 1)}} = {z^{(i)}} - {d_i}{\theta ^{(i)}} \cr
& where:{d_i} = \pm 1 \cr} $,其中z(旋转角度差)无限趋近于0。
并且伪旋转可表示为\(\left\{ {\matrix{
{{x^{(i + 1)}} = {x^{(i)}} - {d_i}({2^{ - i}}{y^{(i)}})} \cr
{{y^{(i + 1)}} = {y^{(i)}} + {d_i}({2^{ - i}}{x^{(i)}})} \cr
} } \right.\)。
象限预处理
当然,每次旋转的方向都影响到最终要旋转的累积角度,角度范围大致为: $ - 99.7 \le \theta \le 99.7$。对于范围外的角度,需要使用三角恒等式转化进行“预处理”,即象限判断。
因此,原始算法规整为使用向量的伪旋转来表示迭代移位-相加算法,即:\(\left\{ {\matrix{
{{x^{(i + 1)}} = {x^{(i)}} - {d_i}({2^{ - i}}{y^{(i)}})} \cr
{{y^{(i + 1)}} = {y^{(i)}} + {d_i}({2^{ - i}}{x^{(i)}})} \cr
{{z^{(i + 1)}} = {z^{(i)}} - {d_i}{\theta ^{(i)}}} \cr
} } \right.\),
前面提到了,在进行“伪旋转”操作时,每次迭代运算都忽略了\(\cos \theta\)项,最终得到的 \({x^{(n)}},{y^{(n)}}\) 被伸缩了 \({k_n}\)倍
${k_n} = \prod\limits_n {({1 \over {\cos {\theta ^{(i)}}}})} = \prod\limits_n {(\sqrt {1 + {2^{( - 2i)}}} )} $ (伸缩因子)。
对 \({k_n}\) 求无限积,${k_n} = \prod\limits_n {(\sqrt {1 + {2^{( - 2i)}}} )} \to 1.6476,as:n \to \infty $( \(1/{k_n} = 0.6073\) )
若已知执行的迭代次数,便可直接求得 \({k_n}\) 最终值。
关于圆坐标系下,CORDIC算法应用包括旋转模式和向量模式两种:
旋转模式
应用场景:已知相角angle,用Cordic算法计算其正弦和余弦值。
具体过程:判决因子\({d_i}{\rm{ = sign}}({z^{(i)}}) \Rightarrow {z^{(i)}} \to 0\),N次迭代后得到\(\left\{ {\matrix{
{{x^{(n)}} = {k_n}({x^{(0)}}\cos {z^{(0)}} - {y^{(0)}}\sin {z^{(0)}})} \cr
{{y^{(n)}} = {k_n}({y^{(0)}}\cos {z^{(0)}} + {x^{(0)}}\sin {z^{(0)}})} \cr
{{z^{(n)}} = 0} \cr
} } \right.\)( \({z^{(0)}}\) = θ)通过设置 \({x^{(0)}} = {1 \over {{k_n}}}{{\rm{y}}^{(0)}} = 0\),可最终求到 $\cos \theta、 \sin \theta $ 。
向量模式
应用场景:已知坐标,用cordic算法计算相角和幅值。
具体过程:直角坐标系转换的极坐标系,迭代过程变化为\(\left\{ {\matrix{
{{x^{(i + 1)}} = {x^{(i)}} - {d_i}({2^{ - i}}{y^{(i)}})} \cr
{{y^{(i + 1)}} = {y^{(i)}} + {d_i}({2^{ - i}}{x^{(i)}})} \cr
{{z^{(i + 1)}} = {z^{(i)}} - {d_i}{\theta ^{(i)}}} \cr
} } \right.\),
其中判决因子 \({d_i}{\rm{ = - sign}}({x^{(i)}}{y^{(i)}}) \Rightarrow {y^{(i)}} \to 0\),N次迭代得到:\(\left\{ {\matrix{
{{x^{(n)}} = {k^{(n)}}\sqrt {x_0^2 + y_0^2} } \cr
{{y^{(n)}} = 0} \cr
{{z^{(n)}} = {z^{(0)}} + {{\tan }^{ - 1}}({y_0}/{x_0})} \cr
{{k^{(n)}} = \prod\limits_n {\sqrt {1 + {2^{ - 2i}}} } } \cr
} } \right.\),
通过设定\({x^{(0)}} = 1,{z^{(0)}} = 0\),可最终求得 \({\tan ^{ - 1}}{y^{(0)}}\)。
Verilog HDL实现CORDIC
针对\(\left\{ {\matrix{
{{x^{(i + 1)}} = {x^{(i)}} - {d_i}({2^{ - i}}{y^{(i)}})} \cr
{{y^{(i + 1)}} = {y^{(i)}} + {d_i}({2^{ - i}}{x^{(i)}})} \cr
{{z^{(i + 1)}} = {z^{(i)}} - {d_i}{\theta ^{(i)}}} \cr
} } \right.\) ,每次迭代计算需要2次移位 \(({x^{(i)}{,y^{(i)}}})\) 、1次查找表\({\theta ^{(i)}}\)、3次加法(x、y、z累加)。
对应的CORDIC硬件结构如下:
在Cordic—旋转模式下,Matlab代码实现:
点击查看代码
%% ***********************************************************************************
% 圆坐标系下:Cordic—旋转模式
% 已知相角angle,计算其正弦和余弦值。基本公式如下:
% x(k+1) = x(k) - d(k)*y(k)*2^(-k)
% y(k+1) = y(k) + d(k)*x(k)*2^(-k)
% z(k) = z(k) - d(k)*actan(2^(-k))
%% ***********************************************************************************
clear;close all;clc;
angle = 30; %设定旋转角度
% 初始化-------------------------------
N = 16; %迭代次数
tan_table = 2.^-(0 : N-1);
angle_LUT = atan(tan_table); %建立arctan&angle查找表
An = 1;
for k = 0 : N-1
An = An*(1/sqrt(1 + 2^(-2*k)));
end
Kn = 1/An;%计算归一化伸缩因子参数:Kn = 1.6476,1/Kn = 0.6073
Xn = 1/Kn; %相对于X轴上开始旋转
Yn = 0;
Zi = angle/180*pi; %角度转化为弧度
% cordic算法计算-------------------------------
if (Zi > pi/2) % 先做象限判断,得到相位补偿值
Zi = Zi - pi;
sign_x = -1;
sign_y = -1;
elseif (Zi < -pi/2)
Zi = Zi + pi;
sign_x = -1;
sign_y = -1;
else
sign_x = 1;
sign_y = 1;
end
for k = 0 : N-1 % 迭代开始
Di = sign(Zi);
x_temp = Xn;
Xn = x_temp - Di*Yn*2^(-k);
Yn = Yn + Di*x_temp*2^(-k);
Zi = Zi - Di*angle_LUT(k+1);
end
cos_out = sign_x*Xn; %余弦输出
sin_out = sign_y*Yn; %正弦输出
Verilog HDL在旋转模式下,程序:
点击查看代码
module Cordic_rotate_mode(
input sys_clk ,
input sys_rst ,
input signed [31:0] angle ,
output reg [31:0] cosout ,
output reg [31:0] sinout
);
//旋转角度查找表
wire [31:0]rot[15:0];
assign rot[0] = 32'd2949120 ; //45.0000度*2^16
assign rot[1] = 32'd1740992 ; //26.5651度*2^16
assign rot[2] = 32'd919872 ; //14.0362度*2^16
assign rot[3] = 32'd466944 ; //7.1250度*2^16
assign rot[4] = 32'd234368 ; //3.5763度*2^16
assign rot[5] = 32'd117312 ; //1.7899度*2^16
assign rot[6] = 32'd58688 ; //0.8952度*2^16
assign rot[7] = 32'd29312 ; //0.4476度*2^16
assign rot[8] = 32'd14656 ; //0.2238度*2^16
assign rot[9] = 32'd7360 ; //0.1119度*2^16
assign rot[10] = 32'd3648 ; //0.0560度*2^16
assign rot[11] = 32'd1856 ; //0.0280度*2^16
assign rot[12] = 32'd896 ; //0.0140度*2^16
assign rot[13] = 32'd448 ; //0.0070度*2^16
assign rot[14] = 32'd256 ; //0.0035度*2^16
assign rot[15] = 32'd128 ; //0.0018度*2^16
//FSM_parameter
localparam IDLE = 2'd0;
localparam WORK = 2'd1;
localparam ENDO = 2'd2;
reg [1:0] state ;
reg [1:0] next_state ;
reg [3:0] cnt;
always @(posedge sys_clk or negedge sys_rst)begin
if(!sys_rst)
next_state <= IDLE;
else begin
state <= next_state;
case(state)
IDLE:next_state <= WORK;
WORK:next_state <= cnt == 15 ? ENDO:WORK;
ENDO:next_state <= IDLE;
default:next_state <= IDLE;
endcase
end
end
reg signed [31:0] x_shift;
reg signed [31:0] y_shift;
reg signed [31:0] z_rot;
wire D_sign;
assign D_sign= z_rot[31];
always @(posedge sys_clk) begin
case(state)
IDLE:
begin
x_shift <= 32'd39800;
y_shift <= 32'd0;
z_rot <= (angle<<16);
end
WORK:
if(D_sign)begin
x_shift <= x_shift + (y_shift>>>cnt);
y_shift <= y_shift - (x_shift>>>cnt);
z_rot <= z_rot + rot[cnt];
end
else begin
x_shift <= x_shift - (y_shift>>>cnt);
y_shift <= y_shift + (x_shift>>>cnt);
z_rot <= z_rot - rot[cnt];
end
ENDO:
begin
cosout <= x_shift;
sinout <= y_shift;
end
default :;
endcase
end
always @(posedge sys_clk or negedge sys_rst) begin
if(!sys_rst)
cnt <= 4'd0;
else if(state == IDLE && next_state == WORK)
cnt <= 4'd0;
else if(state==WORK)begin
if(cnt<4'd15)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
end
else
cnt <= 4'd0;
end
endmodule
设定多种角度值,仿真如下图:
在Cordic—向量模式下,Matlab代码实现:
点击查看代码
%% ***********************************************************************************
% 圆坐标系下:Cordic—向量模式
% 已知坐标,用cordic算法计算相角和幅值。基本公式如下:
% x(k+1) = x(k) - d(k)*y(k)*2^(-k)
% y(k+1) = y(k) + d(k)*x(k)*2^(-k)
% z(k) = z(k) - d(k)*actan(2^(-k))
%% ***********************************************************************************
clear;close all;clc;
% 初始化----------------------------------------
Xn = -1;
Yn = sqrt(3);
Zi = 0;
Di = 0;
N = 16; %迭代次数
tan_table = 2.^-(0 : N-1);
angle_LUT = atan(tan_table);
An = 1;
for k = 0 : N-1
An = An*(1/sqrt(1 + 2^(-2*k)));
end
Kn = 1/An;%计算归一化伸缩因子参数:Kn = 1.6476,1/Kn = 0.6073
% cordic算法计算-------------------------------
if (Xn==0 && Yn==0) %移至原点,未旋转角度
radian_out = 0;
amplitude_out = 0;
else % 先做象限判断,得到相位补偿值
if (Xn > 0) %第一、四象限:(-pi/2,0)/(0,pi/2)-->Zn
phase_shift = 0;
elseif (Yn < 0) %第三象限:(-pi,-pi/2)-->预旋转-pi,Zn+pi/2
phase_shift = -pi;
else %第二象限:(pi/2,pi)-->预旋转pi,Zn-pi/2
phase_shift = pi;
end
for k = 0 : N-1 % 迭代开始
Di = -sign(Xn*Yn);
x_temp = Xn;
Xn = x_temp - Di*Yn*2^(-k);
Yn = Yn + Di*x_temp*2^(-k);
Zi = Zi - Di*angle_LUT(k+1);
end
radian_out = Zi + phase_shift; %弧度输出
amplitude_out = abs(Xn)/Kn; %幅值输出
end
angle_out = radian_out*180/pi; %相角输出:角度(度)=角度(弧度)x pi/180
Verilog HDL在向量模式下,程序:
点击查看代码
module Cordic_vector_mode(
input sys_clk ,
input sys_rst ,
input signed [31:0] x ,
input signed [31:0] y ,
output reg [31:0] phase ,
output reg [31:0] mo_value
);
//旋转角度查找表
wire [31:0]rot[15:0];
assign rot[0] = 32'd2949120 ; //45.0000度*2^16
assign rot[1] = 32'd1740992 ; //26.5651度*2^16
assign rot[2] = 32'd919872 ; //14.0362度*2^16
assign rot[3] = 32'd466944 ; //7.1250度*2^16
assign rot[4] = 32'd234368 ; //3.5763度*2^16
assign rot[5] = 32'd117312 ; //1.7899度*2^16
assign rot[6] = 32'd58688 ; //0.8952度*2^16
assign rot[7] = 32'd29312 ; //0.4476度*2^16
assign rot[8] = 32'd14656 ; //0.2238度*2^16
assign rot[9] = 32'd7360 ; //0.1119度*2^16
assign rot[10] = 32'd3648 ; //0.0560度*2^16
assign rot[11] = 32'd1856 ; //0.0280度*2^16
assign rot[12] = 32'd896 ; //0.0140度*2^16
assign rot[13] = 32'd448 ; //0.0070度*2^16
assign rot[14] = 32'd256 ; //0.0035度*2^16
assign rot[15] = 32'd128 ; //0.0018度*2^16
//FSM_parameter
localparam IDLE = 2'd0;
localparam WORK = 2'd1;
localparam ENDO = 2'd2;
reg [1:0] state ;
reg [1:0] next_state ;
reg [3:0] cnt;
reg signed [31:0] x_shift;
reg signed [31:0] y_shift;
reg signed [31:0] z_rot;
always @(posedge sys_clk or negedge sys_rst)begin
if(!sys_rst)
next_state <= IDLE;
else begin
state <= next_state;
case(state)
IDLE:next_state <= WORK;
WORK:next_state <= cnt == 15 ? ENDO:WORK;
ENDO:next_state <= IDLE;
default:next_state <= IDLE;
endcase
end
end
wire D_sign;
assign D_sign=~y_shift[31];
always @(posedge sys_clk) begin
case(state)
IDLE:
begin
x_shift <= x;
y_shift <= y;
z_rot <= 0;
end
WORK:
if(D_sign)begin
x_shift <= x_shift + (y_shift>>>cnt);
y_shift <= y_shift - (x_shift>>>cnt);
z_rot <= z_rot + rot[cnt];
end
else begin
x_shift <= x_shift - (y_shift>>>cnt);
y_shift <= y_shift + (x_shift>>>cnt);
z_rot <= z_rot - rot[cnt];
end
ENDO:
begin
phase <= z_rot>>>16;
mo_value <= (x_shift>>>16)*0.6073;
end
default :;
endcase
en
always @(posedge sys_clk or negedge sys_rst) begin
if(!sys_rst)
cnt <= 4'd0;
else if(state == IDLE && next_state == WORK)
cnt <= 4'd0;
else if(state==WORK)begin
if(cnt<4'd15)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
end
else
cnt <= 4'd0;
end
endmodule
设定三种不同x,y值,仿真如下图:
本篇文章中使用的Verilog程序模块,若有需见网页左栏Gitee仓库链接:https://gitee.com/silly-big-head/little-mouse-funnyhouse/tree/FPGA-Verilog/
CORDIC算法解释及FPGA实现(圆坐标系)的更多相关文章
- 学习cordic算法所得(流水线结构、Verilog标准)
最近学习cordic算法,并利用FPGA实现,在整个学习过程中,对cordic算法原理.FPGA中流水线设计.Verilog标准有了更加深刻的理解. 首先,cordic算法的基本思想是通过一系列固定的 ...
- cordic算法的verilog实现及modelsim仿真
1. 算法介绍 CORDIC(Coordinate Rotation Digital Computer)算法即坐标旋转数字计算方法,是J.D.Volder1于1959年首次提出,主要用于三角函数.双曲 ...
- 使用CORDIC算法求解角度正余弦及Verilog实现
本文是用于记录在了解和学习CORDIC算法期间的收获,以供日后自己及他人参考:并且附上了使用Verilog实现CORDIC算法求解角度的正弦和余弦的代码.简单的testbench测试代码.以及在Mod ...
- 基于FPGA的Cordic算法实现
CORDIC(Coordinate Rotation Digital Computer)算法即坐标旋转数字计算方法,是J.D.Volder1于1959年首次提出,主要用于三角函数.双曲线.指数.对数的 ...
- 使用帅气的cordic算法进行坐标系互转及log10的求解
参考博客 https://blog.csdn.net/u010712012/article/details/77755567 https://blog.csdn.net/Reborn_Lee/arti ...
- 定点CORDIC算法求所有三角函数及向量模的原理分析、硬件实现(FPGA)
一.CORDIC算法 CORDIC(Coordinate Rotation DIgital Computer)是一种通过迭代对多种数学函数求值的方法,它可以对三角函数.双曲函数和平面旋转问题进行求解. ...
- 基于FPGA的cordic算法的verilog初步实现
最近在看cordic算法,由于还不会使用matlab,真是痛苦,一系列的笔算才大概明白了这个算法是怎么回事.于是尝试用verilog来实现.用verilog实现之前先参考软件的程序,于是先看了此博文h ...
- FPGA之CORDIC算法实现_理论篇(上)
关于cordic的算法原理核心思想就是规定好旋转角度,然后通过不停迭代逐步逼近的思想来实现数学求解,网上关于这部分的资料非常多,主要可以参考: 1)https://blog.csdn.net/qq_3 ...
- FPGA之CORDIC算法实现_代码实现(下)
关于FPGA之CORDIC算法的纯逻辑实现,博主洋葱洋葱“https://www.cnblogs.com/cofin/p/9188629.html”以及善良的一休军“https://blog.csdn ...
- [黑金原创教程] FPGA那些事儿《数学篇》- CORDIC 算法
简介 一本为完善<设计篇>的书,教你CORDIC算法以及定点数等,内容请看目录. 贴士 这本教程难度略高,请先用<时序篇>垫底. 目录 Experiment 01:认识CORD ...
随机推荐
- 通俗易懂的路径搜索之A-star算法
A-star算法 搜索技术 搜索技术是一种通用的问题求解技术,可以将待解决的问题转化为可搜索的问题空间,然后在该空间中搜索求解.搜索技术在人工智能领域有着非常广泛的应用. 盲目搜索 盲目搜索是最简单的 ...
- post基础错误注入
Burpsuite抓取HTTP请求 Burpsuite是一款Web安全测试的利器,集成了几乎Web安全测试中所有需要用到的功能. 运行前提: 需要安装Java https://www.java.com ...
- Linux系统基本介绍
一.Linux系统基本介绍 [1].Linux操作系统特点 多任务的操作系统 多任务操作系统 严格区分大小写 Linux一切皆文件 所有文件的起点都是"/"根目录 对于Linux操 ...
- 全新发布!桌面端效率工具RunFlow
RunFlow是一款跨平台的生产力工具,可以启动应用程序和搜索文件等,类似于Windows平台的Wox和PowerToys,同样也类似于Mac平台的Alfred和Raycast.但我们并不与这些工具相 ...
- linux系统&自动清理日志实现脚本
文章来源:https://blog.csdn.net/lakelise/article/details/93711932 编写清理脚本,添加到定时任务中:创建可执行文件cd /hometouch cl ...
- yarn -D和-S区别
-D和-S区别 安装的环境不同 -D是--save-dev的简写,会安装在开发环境中(production)中的devPendencies中 -S是--save的简写,会安装在生产环境中(develo ...
- [oeasy]python0048_取整_int_float_浮点型_cast_扮演_tab_制表键_制表符
转化为10进制 回忆上次内容 上次 把其他进制 转化回 十进制 用的是 int 函数 int 来自于 integer 同源词 还有 integrate entire 意思都是完整的 完整的 和 零散的 ...
- 暑假java自学进度总结02
一.今日所学: 1.配置环境变量 在系统内配置java路径后,再在path中 利用系统路径配置Java编译工具和运行工具路径. 2.下载并安装Natepad++,并且配置相关设置 3.初步了解了Jav ...
- 栈—顺序栈(C实现)
// Code file created by C Code Develop // 顺序栈 #include "ccd.h" #include "stdio.h" ...
- Django 不通过外键实现多表关联查询
Django不通过外键实现多表关联查询 by:授客 QQ:1033553122 测试环境 Win 10 Python 3.5.4 Django-2.0.13.tar.gz 需求 不通过外键,使 ...