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实现(圆坐标系)的更多相关文章

  1. 学习cordic算法所得(流水线结构、Verilog标准)

    最近学习cordic算法,并利用FPGA实现,在整个学习过程中,对cordic算法原理.FPGA中流水线设计.Verilog标准有了更加深刻的理解. 首先,cordic算法的基本思想是通过一系列固定的 ...

  2. cordic算法的verilog实现及modelsim仿真

    1. 算法介绍 CORDIC(Coordinate Rotation Digital Computer)算法即坐标旋转数字计算方法,是J.D.Volder1于1959年首次提出,主要用于三角函数.双曲 ...

  3. 使用CORDIC算法求解角度正余弦及Verilog实现

    本文是用于记录在了解和学习CORDIC算法期间的收获,以供日后自己及他人参考:并且附上了使用Verilog实现CORDIC算法求解角度的正弦和余弦的代码.简单的testbench测试代码.以及在Mod ...

  4. 基于FPGA的Cordic算法实现

    CORDIC(Coordinate Rotation Digital Computer)算法即坐标旋转数字计算方法,是J.D.Volder1于1959年首次提出,主要用于三角函数.双曲线.指数.对数的 ...

  5. 使用帅气的cordic算法进行坐标系互转及log10的求解

    参考博客 https://blog.csdn.net/u010712012/article/details/77755567 https://blog.csdn.net/Reborn_Lee/arti ...

  6. 定点CORDIC算法求所有三角函数及向量模的原理分析、硬件实现(FPGA)

    一.CORDIC算法 CORDIC(Coordinate Rotation DIgital Computer)是一种通过迭代对多种数学函数求值的方法,它可以对三角函数.双曲函数和平面旋转问题进行求解. ...

  7. 基于FPGA的cordic算法的verilog初步实现

    最近在看cordic算法,由于还不会使用matlab,真是痛苦,一系列的笔算才大概明白了这个算法是怎么回事.于是尝试用verilog来实现.用verilog实现之前先参考软件的程序,于是先看了此博文h ...

  8. FPGA之CORDIC算法实现_理论篇(上)

    关于cordic的算法原理核心思想就是规定好旋转角度,然后通过不停迭代逐步逼近的思想来实现数学求解,网上关于这部分的资料非常多,主要可以参考: 1)https://blog.csdn.net/qq_3 ...

  9. FPGA之CORDIC算法实现_代码实现(下)

    关于FPGA之CORDIC算法的纯逻辑实现,博主洋葱洋葱“https://www.cnblogs.com/cofin/p/9188629.html”以及善良的一休军“https://blog.csdn ...

  10. [黑金原创教程] FPGA那些事儿《数学篇》- CORDIC 算法

    简介 一本为完善<设计篇>的书,教你CORDIC算法以及定点数等,内容请看目录. 贴士 这本教程难度略高,请先用<时序篇>垫底. 目录 Experiment 01:认识CORD ...

随机推荐

  1. 通俗易懂的路径搜索之A-star算法

    A-star算法 搜索技术 搜索技术是一种通用的问题求解技术,可以将待解决的问题转化为可搜索的问题空间,然后在该空间中搜索求解.搜索技术在人工智能领域有着非常广泛的应用. 盲目搜索 盲目搜索是最简单的 ...

  2. post基础错误注入

    Burpsuite抓取HTTP请求 Burpsuite是一款Web安全测试的利器,集成了几乎Web安全测试中所有需要用到的功能. 运行前提: 需要安装Java https://www.java.com ...

  3. Linux系统基本介绍

    一.Linux系统基本介绍 [1].Linux操作系统特点 多任务的操作系统 多任务操作系统 严格区分大小写 Linux一切皆文件 所有文件的起点都是"/"根目录 对于Linux操 ...

  4. 全新发布!桌面端效率工具RunFlow

    RunFlow是一款跨平台的生产力工具,可以启动应用程序和搜索文件等,类似于Windows平台的Wox和PowerToys,同样也类似于Mac平台的Alfred和Raycast.但我们并不与这些工具相 ...

  5. linux系统&自动清理日志实现脚本

    文章来源:https://blog.csdn.net/lakelise/article/details/93711932 编写清理脚本,添加到定时任务中:创建可执行文件cd /hometouch cl ...

  6. yarn -D和-S区别

    -D和-S区别 安装的环境不同 -D是--save-dev的简写,会安装在开发环境中(production)中的devPendencies中 -S是--save的简写,会安装在生产环境中(develo ...

  7. [oeasy]python0048_取整_int_float_浮点型_cast_扮演_tab_制表键_制表符

    转化为10进制 回忆上次内容 上次 把其他进制 转化回 十进制 用的是 int 函数 int 来自于 integer 同源词 还有 integrate entire 意思都是完整的 完整的 和 零散的 ...

  8. 暑假java自学进度总结02

    一.今日所学: 1.配置环境变量 在系统内配置java路径后,再在path中 利用系统路径配置Java编译工具和运行工具路径. 2.下载并安装Natepad++,并且配置相关设置 3.初步了解了Jav ...

  9. 栈—顺序栈(C实现)

    // Code file created by C Code Develop // 顺序栈 #include "ccd.h" #include "stdio.h" ...

  10. Django 不通过外键实现多表关联查询

    Django不通过外键实现多表关联查询 by:授客 QQ:1033553122 测试环境 Win 10   Python 3.5.4   Django-2.0.13.tar.gz 需求 不通过外键,使 ...