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. Java面试知识点(六)hashmap深度理解

    1.hashmap 的数据结构 要知道 hashmap 是什么,首先要搞清楚它的数据结构,在 java 编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用 ...

  2. spring cloud 上云的情况下,Ribbon 客户端负载均衡 与 ALB 服务端负载均衡的选择

    在云环境(例如AWS)中,由于云提供商通常提供强大的负载均衡服务(如AWS的ALB),一般不再需要使用Ribbon这种客户端负载均衡方案.云环境中的负载均衡器通常能够提供更高的可靠性.可扩展性和简化的 ...

  3. ComfyUI基础篇:为什么要学 ComfyUI?

    前言: 在AI生成图像领域,有许多产品,例如 Midjourney 和 Stability AI 等.为什么要学习 ComfyUI 呢?我斗胆带大家一起分析一下. 目录 1.Midjourney VS ...

  4. JS -- Ajax -- 手稿

  5. Nunjucks

    Nunjucks是什么东东?其实它是一个模板引擎. 那什么是模板引擎? 模板引擎就是基于模板配合数据构造出字符串输出的一个组件.比如下面的函数就是一个模板引擎: function examResult ...

  6. SpringBoot整合EasyPoi 封装Excel导出通用工具类,行高自适应,导出图片

    导读 下午抽空封装一个通用导出Excel工具类.之前还写过一篇EasyPoi导入参数校验,批注导出,点我直达 添加依赖 <!-- easy poi --> <dependency&g ...

  7. SpringBoot 解决跨域问题

    今天遇到一个很神奇的问题,之前写的项目,后端跨域都处理好的,按部就班使用原来的方式,前后端都开发完之后,部署本地后,跨域没起效,一脸懵逼,然后使用公司另外一个同事的跨域解决方案,具体我也没深入研究到底 ...

  8. SpringCloud连接远程nacos报错,一直提示连接本地的localhost:8848

    application.properties spring.cloud.nacos.discovery.server-addr=xxx.xxx.xxx.xxx:8848 spring.applicat ...

  9. iOS开发基础100 - MDM证书申请流程

    申请成为MDM Vendor 首先需要拥有一个 iOS Developer Enterprise Program 帐号; 申请成为MDM Vendor,iOS企业开发帐号默认不支持MDM功能,需要向苹 ...

  10. 用这开源小书学 Docker,香!

    > 最新.全面.通俗.可多端阅读的 Docker 教程小书.>> 编程导航开源仓库:https://github.com/liyupi/code-navDocker 可以说是一个改变 ...