注:本文为 “CORDIC 算法” 相关文章合辑。
未整理去重。
如有内容异常,请看原文。
Cordic 算法的原理介绍
乐富道 2014-01-28 23:05
Cordic 算法知道正弦和余弦值,求反正切,即角度。
采用用不断的旋转求出对应的正弦余弦值,是一种近似求解发。
旋转的角度很讲求,每次旋转的角度必须使得 正切值近似等于 1 2 N \frac{1}{2^N} 2N1。旋转的目的是让 Y Y Y 轴趋近与 0 0 0。把每次旋转的角度累加,即得到旋转的角度和即为正切值。
比如 Y Y Y 轴旋转 4 5 ∘ 45^{\circ} 45∘,则值减小 1 2 \frac{1}{2} 21;
再旋转 26.5650 5 ∘ 26.56505^{\circ} 26.56505∘,再减少 1 4 \frac{1}{4} 41;
再旋转角度 14.0362 4 ∘ 14.03624^{\circ} 14.03624∘,再减少 1 8 \frac{1}{8} 81; 依次减少 1 16 \frac{1}{16} 161, 1 32 ⋯ ⋯ \frac{1}{32}\cdots\cdots 321⋯⋯,最后 Y Y Y 轴的值无限小,趋近于 0 0 0 。
比如 X = 1 X = 1 X=1, Y = 1 Y = 1 Y=1,的角度,角度是 4 5 ∘ 45^{\circ} 45∘。经过一次旋转,要使得 Y = 0 Y = 0 Y=0,这个角度必须是 4 5 ∘ 45^{\circ} 45∘。
如上图
如图中,直角坐标系中点( X 0 X_0 X0, Y 0 Y_0 Y0)逆时钟旋转角度 θ \theta θ,变换成坐标( X 1 X_1 X1, Y 1 Y_1 Y1),那么用 X 0 X_0 X0, Y 0 Y_0 Y0,以及 θ \theta θ 的三角函数,如果表示 X 1 X_1 X1, Y 1 Y_1 Y1 呢?
请想象,如果坐标也旋转角度 θ \theta θ,那么 X 1 X_1 X1, Y 1 Y_1 Y1 的坐标依然是( X 0 X_0 X0, Y 0 Y_0 Y0)。接着往下看:
看完以上这副图,就该明白这个等式了:
x 1 = x 0 cos θ − y 0 sin θ x_1 = x_0\cos\theta - y_0\sin\theta x1=x0cosθ−y0sinθ
y 1 = x 0 sin θ + y 0 cos θ y_1 = x_0\sin\theta + y_0\cos\theta y1=x0sinθ+y0cosθ
再把这个式子化成正切函数。
Cordic 算法的思想是通过迭代的方法,不断的旋转特定的角度(这个特定的角度就是使得 Y Y Y 为上次的 1 2 \frac{1}{2} 21),使得累计旋转的角度的和无限接近某一设定的角度,
每次旋转的角度的 θ = arctan ( 1 2 n ) \theta = \arctan(\frac{1}{2^n}) θ=arctan(2n1);
具体迭代如下表: Z 0 = 3 0 ∘ Z_0 = 30^{\circ} Z0=30∘, Y 0 = 0 Y_0 = 0 Y0=0, X 0 = 0.6073 X_0 = 0.6073 X0=0.6073
输入 3 0 ∘ 30^{\circ} 30∘,经过 9 9 9 次迭代后, Z 0 = 0 Z_0 = 0 Z0=0, Y 0 = 0.5006 Y_0 = 0.5006 Y0=0.5006, X 0 = 0.8657 X_0 = 0.8657 X0=0.8657
x ( i + 1 ) ′ = ( x i ′ − y i ′ ( σ i ) 2 − i ) x'_{(i + 1)} = (x'_i - y'_i(\sigma_i)2^{-i}) x(i+1)′=(xi′−yi′(σi)2−i)
y ( i + 1 ) ′ = ( x i ′ ( σ i ) 2 − i + y i ′ ) y'_{(i + 1)} = (x'_i(\sigma_i)2^{-i} + y'_i) y(i+1)′=(xi′(σi)2−i+yi′)
(当 i = 0 i = 0 i=0)
x 1 ′ = 0.607 − 0 ⋅ ( + 1 ) ⋅ 1 = 0.607 x'_1 = 0.607 - 0 \cdot (+1 ) \cdot 1 = 0.607 x1′=0.607−0⋅(+1)⋅1=0.607
y 1 ′ = 0.607 ⋅ ( + 1 ) ⋅ 1 + 0 = 0.607 y'_1 = 0.607 \cdot (+1 ) \cdot 1 + 0 = 0.607 y1′=0.607⋅(+1)⋅1+0=0.607
通过 Cordic 算法后,得到
y 9 = 0.5006 ( = sin ( 3 0 ∘ ) ) y_9 = 0.5006 (=\sin(30^{\circ})) y9=0.5006(=sin(30∘))
x 9 = 0.8657 ( = cos ( 3 0 ∘ ) ) x_9 = 0.8657 (=\cos(30^{\circ})) x9=0.8657(=cos(30∘))
所以也可以用 cordic 算法求出正切值的。
或者求反正切值:
计算公式:
posted @ 2014-01-28 23:05 乐富道
基于 FPGA 的 CORDIC 算法实现 —— Verilog 版
善良的一休君于 2017-08-21 20:07:34 发布
目前,学习与开发 FPGA 的程序员们大多使用的是 Verilog HDL 语言(以下简称为 Verilog),关于 Verilog 的诸多优点一休哥就不多介绍了,在此,我们将重点放在 Verilog 的运算操作上。
我们都知道,在 Verilog 中,运算一般分为逻辑运算(与或非等)与算术运算(加减乘除等)。而在一开始学习 Verilog 时,老司机一定会提醒我们,“切记,千万别用‘/’除、‘%’取模(有的也叫取余)和‘**’幂。” 这话说的不无道理,因为这三个运算是不可综合的。但,需清楚理解的是,不可综合的具体意思为不能综合为简单的模块,当我们在程序中调用了这些运算时,‘/’除和‘%’取模在 Quartus 软件中是可以综合的,因此可以正常调用运行,但是会消耗一些逻辑资源,而且会产生延时,即这两个运算的处理时间会很长,可能会大于时序控制时钟的单周期时间。此时呢,我们会建议你调用 IP 核来实现运算操作,虽然这样也会消耗许多逻辑资源,但产生的延时相对较小满足了你基本的需求。
问题好像迎刃而解了,可是仔细一想,除了这些运算,我们还剩下什么?对呀,三角函数,反三角函数,对数函数,指数函数呢,这些函数我们在高中就学习了的呀,难道在 FPGA 中就没有用武之地吗?有人会说,查找表呗,首先将某个运算的所有可能的输入与输出对一一罗列出来,然后放进 Rom 中,然后根据输入查表得到输出。这个方法虽然有效的避免了延时问题,却是一个十分消耗资源的方法,不适合资源紧张的设计。那么,就真的没有办法了吗?
答案就是咱们今天的标题了,CORDIC,而且 CORDIC 是一个比较全能的算法,通过这一原理,我们可以实现三角函数,反三角函数,对数函数,指数函数等多种运算。接下来,一休哥就带领大家来学习 CORDIC 的原理吧。(题外话:请相信一休哥,本文不会让你感到太多痛苦~)
本文将分三个小部分来展开介绍:
1、CORDIC 的基本原理介绍
2、CORDIC 的具体操作流程介绍
3、CORDIC 的旋转模式 ——Verilog 仿真
本文涉及到的全部资料链接:
链接:http://pan.baidu.com/s/1gfrJzMj
密码:x92u
一、CORDIC 的基本原理介绍
CORDIC 算法是一个 “化繁为简” 的算法,将许多复杂的运算转化为一种 “仅需要移位和加法” 的迭代操作。CORDIC 算法有旋转和向量两个模式,分别可以在圆坐标系、线性坐标系和双曲线坐标系使用,从而可以演算出 8 种运算,而结合这 8 种运算也可以衍生出其他许多运算。下表展示了 8 种运算在 CORDIC 算法中实现的条件。
这里写图片描述
首先,我们先从旋转模式下的圆坐标系讲起,这也是 CORDIC 方法最初的用途。
1、CORDIC 的几何原理介绍
假设在 x y xy xy 坐标系中有一个点 P 1 ( x 1 , y 1 ) P1(x_1,y_1) P1(x1,y1),将 P 1 P1 P1 点绕原点旋转 θ \theta θ 角后得到点 P 2 ( x 2 , y 2 ) P2(x_2,y_2) P2(x2,y2)。
这里图片描述
于是可以得到 P 1 P1 P1 和 P 2 P2 P2 的关系:
x 2 = x 1 cos θ − y 1 sin θ = cos θ ( x 1 − y 1 tan θ ) y 2 = y 1 cos θ + x 1 sin θ = cos θ ( y 1 + x 1 tan θ ) \begin{align*} x_2 &= x_1\cos\theta - y_1\sin\theta = \cos\theta(x_1 - y_1\tan\theta)\\ y_2 &= y_1\cos\theta + x_1\sin\theta = \cos\theta(y_1 + x_1\tan\theta) \end{align*} x2y2=x1cosθ−y1sinθ=cosθ(x1−y1tanθ)=y1cosθ+x1sinθ=cosθ(y1+x1tanθ)
以上就是CORDIC的几何原理部分,而我们该如何深入理解这个几何原理的真正含义呢?
从原理中,我们可以知道,当已知一个点 P 1 P1 P1 的坐标,并已知该点 P 1 P1 P1 旋转的角度 θ \theta θ,则可以根据上述公式求得目标点 P 2 P2 P2 的坐标。然后,麻烦来了,我们需要用FPGA去执行上述运算操作,而FPGA的Verilog语言根本不支持三角函数运算。因此,我们需要对上述式子进行简化操作,将复杂的运算操作转换为一种单一的“迭代位移”算法。那么,接下来我们介绍优化算法部分。
2、CORDIC的优化算法介绍
我们先介绍算法的优化原理:将旋转角 θ \theta θ 细化成若干分固定大小的角度 θ i \theta_i θi,并且规定 θ i \theta_i θi 满足 tan θ i = 2 − i \tan\theta_i = 2^{-i} tanθi=2−i,因此 ∑ θ i \sum\theta_i ∑θi 的值在 [ − 99. 7 ∘ , 99. 7 ∘ ] [-99.7^{\circ},99.7^{\circ}] [−99.7∘,99.7∘] 范围内,如果旋转角 θ \theta θ 超出此范围,则运用简单的三角运算操作即可(加减 π \pi π)。
然后我们需要修改几何原理部分的假设,假设在 x y xy xy 坐标系中有一个点 P 0 ( x 0 , y 0 ) P0(x_0,y_0) P0(x0,y0),将 P 0 P0 P0 点绕原点旋转 θ \theta θ 角后得到点 P n ( x n , y n ) Pn(x_n,y_n) Pn(xn,yn)。
于是可以得到 P 0 P0 P0 和 P n Pn Pn 的关系:
x n = x 0 cos θ − y 0 sin θ = cos θ ( x 0 − y 0 tan θ ) y n = y 0 cos θ + x 0 sin θ = cos θ ( y 0 + x 0 tan θ ) \begin{align*} x_n &= x_0\cos\theta - y_0\sin\theta = \cos\theta(x_0 - y_0\tan\theta)\\ y_n &= y_0\cos\theta + x_0\sin\theta = \cos\theta(y_0 + x_0\tan\theta) \end{align*} xnyn=x0cosθ−y0sinθ=cosθ(x0−y0tanθ)=y0cosθ+x0sinθ=cosθ(y0+x0tanθ)
然后,我们将旋转角 θ \theta θ 细化成 θ i \theta_i θi,由于每次的旋转角度 θ i \theta_i θi 是固定不变的(因为满足 tan θ i = 2 − i \tan\theta_i = 2^{-i} tanθi=2−i),如果一直朝着一个方向旋转则 ∑ θ i \sum\theta_i ∑θi 一定会超过 θ \theta θ(如果 θ \theta θ 在 [ − 99. 7 ∘ , 99. 7 ∘ ] [-99.7^{\circ},99.7^{\circ}] [−99.7∘,99.7∘] 范围内)。因此我们需要对 θ i \theta_i θi 设定一个方向值 d i d_i di。如果旋转角已经大于 θ \theta θ,则 d i d_i di 为 − 1 -1 −1,表示下次旋转为顺时针,即向 θ \theta θ 靠近;如果旋转角已经小于 θ \theta θ,则 d i d_i di 为 1 1 1,表示下次旋转为逆时针,即也向 θ \theta θ 靠近。然后我们可以得到每次旋转的角度值 d i θ i d_i\theta_i diθi,设角度剩余值为 z i + 1 z_{i + 1} zi+1,则有 z i + 1 = z i − d i θ i z_{i + 1} = z_i - d_i\theta_i zi+1=zi−diθi,其中 z 0 z_0 z0 为 θ \theta θ。如此随着 i i i 的增大,角度剩余值 z i + 1 z_{i + 1} zi+1 将会趋近于 0 0 0,此时运算结束。(注:可以发现, d i d_i di 与 z i z_i zi 的符号位相同)
第一次旋转 θ 0 \theta_0 θ0, d 0 d_0 d0 为旋转方向:
x 1 = cos θ 0 ( x 0 − d 0 y 0 tan θ 0 ) y 1 = cos θ 0 ( y 0 + d 0 x 0 tan θ 0 ) \begin{align*} x_1 &= \cos\theta_0(x_0 - d_0y_0\tan\theta_0)\\ y_1 &= \cos\theta_0(y_0 + d_0x_0\tan\theta_0) \end{align*} x1y1