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 ...
随机推荐
- STP生成树协议详解
看了网上关于STP生成树的解释感觉不是很懂,随即自己研究了一番 如有错误,欢迎指正,欢迎留言 ----------------------------------------------------- ...
- C++判断当前程序是否运行在Windows展台(Kiosk)模式下
Windows有一个展台(Kiosk)模式.展台模式可以使Windows作为数字标牌进行使用.具体请参考Windows 展台 配置完展台模式,重启设备后,Windows会以全屏的方式运行展台应用,无法 ...
- .NET Core WebApi接口ip限流实践
.NET Core WebApi接口ip限流实践 前言 之前一直想实现接口限流,但一直没去实现,然后刚好看到一篇文章是基于AspNetCoreRateLimit 组件的限流策略.这个组件不做多的介绍, ...
- Linux内核:regmap机制
背景 在学习SPI框架的时候,看到了有一个rtc驱动用到了regmap,本想通过传统方式访问spi接口的我,突然有点不适应,翻了整个驱动,愣是没有找到读写spi的范式:因此了解了regmap以后,才发 ...
- Linux内核驱动:cdev、misc以及device三者之间的联系和区别
Linux内核驱动:cdev.misc以及device三者之间的联系和区别 背景 我想在cdev中使用dev_err等log打印函数,但是跟踪了一下cdev中的原型,发现并不是我想要的. 常见的驱动是 ...
- 在高通Fastmmi模式中增强交互方式
在高通Fastmmi模式中增强交互方式 背景 由于之前工厂抱怨 FCT模式不好用. 之前的FCT测试是这样子的:PCBA上夹具,连接USB. 同时,使用上位机程序(ATE)发送指令,人工判断结果以后, ...
- 【基础推导】MPC控制器及其车辆模型详细推导 (附代码链接及详细推导说明)
0. 参考与前言 Python 代码:github AtsushiSakai/PythonRobotics C++ 代码:github jchengai/gpir/mpc_controller 相关参 ...
- AVCODEC_MAX_AUDIO_FRAME_SIZE 未定义标识符
调用ffmpeg接口时,出现了这个问题:未定义标识符"AVCODEC_MAX_AUDIO_FRAME_SIZE" 在网上搜了一下,可能的解决方案是: 添加: #define AVC ...
- WTM的项目中EFCore如何适配人大金仓数据库
一.WTM是什么 WalkingTec.Mvvm框架(简称WTM)最早开发与2013年,基于Asp.net MVC3 和 最早的Entity Framework, 当初主要是为了解决公司内部开发效率低 ...
- 拆解LangChain的大模型记忆方案
之前我们聊过如何使用LangChain给LLM(大模型)装上记忆,里面提到对话链ConversationChain和MessagesPlaceholder,可以简化安装记忆的流程.下文来拆解基于Lan ...