zoukankan      html  css  js  c++  java
  • CORDIC算法原理及硬件实现(FPGA)

    一、CORDIC算法

      CORDIC(Coordinate Rotation DIgital Computer)是一种通过迭代实现快速平面旋转的算法,通过变形扩展,它可以对多种超越函数求值,例如三角/反三角函数、双曲函数等。

      对超越函数求值,常见方法为用多项式近似,例如利用泰勒展开来逼近目标函数,只要阶数取得足够大,就可以无限逼近目标函数。当我们把这个方法运用到某些特殊机器时,很快会发现问题:泰勒展开包括大量复杂浮点运算,对于没有硬件浮点运算单元的机器,只能通过软件浮点实现。

      CORDIC的出现解决了这个问题。该算法利用迭代逼近的方法,仅仅通过加/减和移位操作即可计算,极大的方便了机器实现。

    本文的:

      一、解释CORDIC旋转的原理。

      二、旋转模式出发,解释三角函数(sin, cos, tan)的计算;

      三、旋转模式出发,解释反三角函数(arcsin、arccos)的计算;

      四、从向量模式出发,解释向量模的计算;

      五、介绍定点运算的实现及软件模型的建立

      六、通过Verilog HDL设计硬件。

    本文代码仓库详见:https://github.com/cassuto/CORDIC-all-in-one-verilog 

    二、核心思想

      正如该算法的名字所说,CORDIC最初是为一种用来进行坐标轴旋转的专用计算机开发的(其原型硬件于1959年成功应用于实时导航)。追根溯源,CORDIC的核心就是坐标轴旋转。其后又由不同的旋转方式导出了各类超越函数的计算方法。

      CORDIC包括旋转模式向量模式,下面逐一解释。

    1、旋转模式

    1.1 伪旋转

      假设现要将一个直角坐标系中的点P(x0, y0)绕原点逆时针旋转z0角度,则变换后的点P1(x1, y1)坐标如下:

      我们发现这个转换式中涉及三角函数,但现在假设机器还不能求任意三角函数值,那么能否改进?

      继续观察旋转变换式,可以等价得到:

      上式仍然涉及两个三角函数,但是cos被放到了一边,于是重点研究tan(z0)。将角度z0微分为n个小角度,每一步旋转θn,通过若干步的旋转最终逼近目标如果像下面这样特殊选取θn,使tan θn恰好只与2的幂有关,则原本复杂的乘tan θn运算就可以通过二进制右移n位实现(xn,yn都是整数,相关问题在后面讨论),这对二进制计算机非常自然。

    $$ tan heta_n = frac{1}{2^n} $$

      结合上面的变换公式,令cos θn=Pn,得到如下迭代格式:

      分析计算量。θn = atan(1/2n)可查表得出。同理系数Pn也可以通过查表求出,但它们仍是浮点数。

      到这里,算法原先的4次浮点运算,被成功减少到了2次,我们离CORDIC伪旋转算法已经很近了。

      但问题还没有结束,如果迭代n次的话,仍需要进行2n次浮点运算。引起2n次浮点运算的原因是每次都需要与系数Pn相乘,这样做真的有必要吗?

      构造辅助三角形,可以得出:

      因此Pn的取值只与n有关。完全可以在所有迭代都完成后,再在最终结果上乘P=ΠPn

      对于根据迭代较少的情况,可以将Pn的不同取值固化到表格中,通过查表求出。

      但这仍需查表。进一步分析发现,对于迭代次数较多的情况,可以将Pn看作常数近似处理,因为随着n的增加,Pn逐渐稳定下来:

      即迭代多次时,可取P≈ lim(n)Pn,这成功去除了Pn查找表。不过引入了截断误差。误差分析至关重要,本文有待完善。

      通过数值方法,可以形象地看出Pn随n增大的变化趋势。

      

     

      同理可得顺时针方向的旋转算法。

    对两旋转方向的公式进行归纳:定义符号函数S(n)控制旋转方向,逆时针旋转时S(n)取1;顺时针旋转时S(n)取-1。可以写出更一般的伪旋转公式:

    (式1.1-1)

      至此我们得到了一个完整的旋转算法,一些文献中也称这个过程为“伪旋转”。

     

    1.2 迭代逼近

      伪旋转算法虽然避免了复杂的计算,但结果并不一定精确,原因在于每次旋转的角度θn是严格受限的。例如目标角度Z0取30°,第一次旋转θ0=atan(1)=45°,显然已经远远超过了目标角,因此伪旋转算法不一定收敛于精确值。

      为了确保结果收敛于精确值,需要引入迭代逼近。思路很直观:上例中“超过了目标角”,就以更小的角度向相反方向旋转;若未“超过”,则继续同向旋转,如此反复,经过无穷次迭代后,点(Xn,Yn)将最终收敛于目标点。

    为了实现迭代逼近,首先引入角度累加器Zn,以确定相对于目标角度旋转了多少。Z0取目标角度,规定每步迭代满足:

    (式1.2-2)

      则Zn正是目标角度与累计旋转角度的差值,称为相位误差。如果已经转过的角度大于目标角,则Zn<0,反之Zn>=0。

    回到逼近的原理:如果转过的角度大于目标角,则向相反方向旋转,反之则继续同向旋转。发现,这正是Zn+1和S(n)的关系。

      假设目标角Z0>0表示逆时针旋转,则S(n)与Zn+1可总结如下:

    (式1.2-3)

      联立式1.1-1、1.2-2、1.2-3,便可得到完整的旋转算法。可以证明,当n∞时,Zn→0,(Xn, Yn)将收敛于目标点。

      称这种CORDIC模式为旋转模式

    上述算法可用伪代码描述如下:

     1 For n=0 to i-1
     2     If (Z(n) >= 0) Then
     3         X(n + 1) := X(n) – (Yn >> n)
     4         Y(n + 1) := Y(n) + (Xn >> n)
     5         
     6         Z(n + 1) := Z(n) - atan(1/2^n)
     7     Else
     8         X(n + 1) := X(n) + (Yn >> n)
     9         Y(n + 1) := Y(n) – (Xn >> n)
    10         
    11         Z(n + 1) := Z(n) + atan(1/2^n)
    12     End if
    13 End for
    14 X(i) *=
    0.607253 // limP
    15 Y(i) *= 0.607253 // limP

    1.3 通过旋转模式计算三角函数

      借助单位圆工具,在直角坐标系中,将点(1, 0)以原点为中心逆时针旋转α角,旋转后点的坐标(x',y')正是(cosα, sinα)。

    下图显示了CORDIC算法计算三角函数的迭代过程。

     

    2、向量模式

      向量模式是为了计算向量模和反三角函数而引出的。

    1.1 向量模的计算

      直角坐标系中,设目标向量V(x0, y0),向量模的定义如下:

      向量模的计算:将点P0(x0, y0)作为初始坐标。如果将点P0旋转某个角度,使纵坐标y'=0,则此时横坐标x'即为向量V(x0, y0)的模。

      与CORDIC旋转模式类似,关键在于逼近的目标不同。取S(n):

      则旋转过程中,Zn→0,向量将向Yn=0的方向逼近,其过程如下(注意图中向量模长比例并不精确):

    算法用伪代码描述如下:

     1 limP := 0.607253
     2 For n=0 to i-1
     3     If (Y(n) >= 0) Then
     4         X(n + 1) := X(n) + (Yn >> n)
     5         Y(n + 1) := Y(n) - (Xn >> n)
     6     Else
     7         X(n + 1) := X(n) - (Yn >> n)
     8         Y(n + 1) := Y(n) + (Xn >> n)
     9     End if
    10 End for
    11 X(i) *= limP

    1.2 反正弦函数的计算

      已知正弦函数值S,在直角坐标系中,若当点(1, 0)逆时针旋转某个角度A时纵坐标恰等于S,则A=asinS 即为反正弦函数的值。可见这只是正弦函数计算的逆过程。

      仍为向量模式,只是逼近的目标发生了变化。取Zn = Y(n) - S,Zn为纵坐标相对于S的误差。

      则旋转过程中,Zn向量将不断向竖直分量Yn=S的方向逼近。

    算法用伪代码描述如下:

     1 A := 0
    2 S /= limP
    3 For n=0 to i-1 4 If (Y(n) >= S) Then 5 X(n + 1) := X(n) + (Yn >> n) 6 Y(n + 1) := Y(n) - (Xn >> n) 7 8 A := A + atan(1/2^n) 9 Else 10 X(n + 1) := X(n) - (Yn >> n) 11 Y(n + 1) := Y(n) + (Xn >> n) 12 13 A := A - atan(1/2^n) 14 End if 15 End for

    同理可得反余弦函数的计算。

    总结,CORDIC旋转模式可完成sinA、cosA、asinA、acosA的计算。

    三、定点算法的软件模型建立

    定点数

      定点数可通过整数来表示。

      1、量化角度:在直角范围内,将b位无符号整数的取值区间均分为coeff1 = 2^b / 90 个单位。

    则对应任意给定的浮点角度Z°,其对应的整数可表示为:floor(Z * coeff1);

      2、转换坐标(x, y):将原始坐标放大coeff2倍并取整即可得出定点坐标。coeff2应根据实际问题适当选取,尽量减小舍入误差。

    坐标用整数可表示为:(floor(X * coeff2)、floor(Y * coeff2))。

      3、对于算法的整数输出,只需将整数除以相应coeff即可得到对应的浮点数。

      浮点数到定点数的转换过程将引入舍入误差。

     

    软件模型

           软件的抽象程度高于硬件,可以更加紧凑地对算法进行描述,同时能快速地进行调试。

      通过软件实现CORDIC算法:

        旋转模式的C语言模型:

          https://github.com/cassuto/CORDIC-all-in-one-verilog/blob/master/CORDIC-rotate-fixed-point.c

        向量模式的C语言模型:

          https://github.com/cassuto/CORDIC-all-in-one-verilog/blob/master/CORDIC-anti-rotate-fixed-point.c

       完整软件实现详见:https://github.com/cassuto/CORDIC-all-in-one-verilog 

     

    四、FPGA实现

      CORDIC在硬件中的典型的应用包括DDS(直接数字频率合成)等等。

      本小节主要介绍硬件实现的相关细节,包括象限转换,流水化处理。

     

    象限转换

      根据三角函数的对称性,只需实现一个象限,即可得出其余所有象限的情况。

      由于相位取值范围被象限四等分,可取相位输入的高2位决定象限。得出象限后,即可根据三角函数值在4个象限的符号关系得到正确的输出。

     

    流水化处理

      流水化提高了时钟频率,“以面积换取速度”。

      从流水线为空开始,下游模块需等待PIPE_DEPTH+2个时钟周期才能从流水线得出结果;

      而当流水线填满后,对于相位输入,流水线都可以在单时钟周期内更新输出。

     

    Verilog设计

    输入:相位phase_i。

    输出:三角函数值sin_o,cos_o,相位误差err_o

      1 module cordic_dds # (
      2    parameter DW = 16,               /* Data width */
      3    parameter PIPE_DEPTH = 14,       /* Pipeline depth */
      4    parameter limP = 16'h4dba        /* P = 0.607253 * 2^15 */
      5 )
      6 (/*AUTOARG*/
      7    // Outputs
      8    sin_o, cos_o, err_o,
      9    // Inputs
     10    clk, phase_i
     11 );
     12 
     13    input             clk;
     14    input [DW-1:0]    phase_i;       /* Phase */
     15    output [DW:0]     sin_o, cos_o;  /* Function value output */
     16    output [DW:0]     err_o;         /* Phase Error output */
     17 
     18    reg [DW:0] cos_r=0, sin_o_r=0;
     19    reg [DW:0] x[PIPE_DEPTH:0];
     20    reg [DW:0] y[PIPE_DEPTH:0];
     21    reg [DW:0] z[PIPE_DEPTH:0];
     22 
     23    reg [DW:0] atan_rom[PIPE_DEPTH:0];
     24    
     25    reg [1:0] quadrant [PIPE_DEPTH:0];
     26 
     27    integer i;
     28    initial begin
     29       for(i=0; i<=PIPE_DEPTH; i=i+1) begin
     30          x[i] = 0; y[i] = 0; z[i] = 0;
     31          quadrant[i] = 2'b0;
     32       end
     33    end
     34    
     35    initial begin
     36       atan_rom[0] <= 8189;
     37       atan_rom[1] <= 4834;
     38       atan_rom[2] <= 2554;
     39       atan_rom[3] <= 1296;
     40       atan_rom[4] <= 650;
     41       atan_rom[5] <= 325;
     42       atan_rom[6] <= 162;
     43       atan_rom[7] <= 81;
     44       atan_rom[8] <= 40;
     45       atan_rom[9] <= 20;
     46       atan_rom[10] <= 10;
     47       atan_rom[11] <= 5;
     48       atan_rom[12] <= 2;
     49       atan_rom[13] <= 1;
     50    end
     51    
     52    
     53    // ================= //
     54    // Pipeline stages   //
     55    // ================= //
     56    always @ (posedge clk) begin // stage 0
     57       x[0] <= {1'b0, limP};
     58       y[0] <= 0;
     59       z[0] <= {3'b0, phase_i[DW-1-2:0]}; // control the phase_i to the range[0-Pi/2]
     60    end
     61 
     62    always @ (posedge clk) begin // stage 1
     63       x[1] <= x[0] - y[0];
     64       y[1] <= x[0] + y[0];
     65       z[1] <= z[0] - atan_rom[0]; // reversal 45deg
     66    end
     67 
     68    generate
     69       genvar k;
     70       for(k=1; k<PIPE_DEPTH; k=k+1) begin
     71          always @ (posedge clk) begin
     72             if (z[k][DW]) begin /* the diff is negative on clockwise */
     73                x[k+1] <= x[k] + {{k{y[k][DW]}},y[k][DW:k]}; /* >> k */
     74                y[k+1] <= y[k] - {{k{x[k][DW]}},x[k][DW:k]}; /* >> k */
     75                z[k+1] <= z[k] + atan_rom[k];
     76             end else begin
     77                x[k+1] <= x[k] - {{k{y[k][DW]}},y[k][DW:k]};
     78                y[k+1] <= y[k] + {{k{x[k][DW]}},x[k][DW:k]};
     79                z[k+1] <= z[k] - atan_rom[k];
     80             end
     81          end
     82       end
     83    endgenerate
     84 
     85    // ================= //
     86    // Count quadrant    //
     87    // ================= //
     88    always @ (posedge clk) begin
     89       quadrant[0] <= phase_i[DW-1:DW-2];
     90    end
     91    generate
     92       genvar j;
     93       for(j=0; j<PIPE_DEPTH; j=j+1) begin
     94          always @ (posedge clk) begin
     95             quadrant[j+1] <= quadrant[j];
     96          end
     97       end
     98    endgenerate
     99 
    100    // ================= //
    101    // Adjust quadrant   //
    102    // ================= //
    103    always @ (posedge clk)
    104       case(quadrant[PIPE_DEPTH])
    105          2'b00: begin
    106             cos_r <= x[PIPE_DEPTH]; /* cos */
    107             sin_o_r <= y[PIPE_DEPTH]; /* sin */
    108          end
    109          2'b01: begin
    110             cos_r <= ~(y[PIPE_DEPTH]) + 1'b1; /* -sin */
    111             sin_o_r <= x[PIPE_DEPTH]; /* cos */
    112          end
    113          2'b10: begin
    114             cos_r <= ~(x[PIPE_DEPTH]) + 1'b1; /* -cos */
    115             sin_o_r <= ~(y[PIPE_DEPTH]) + 1'b1; /* -sin */
    116          end
    117          2'b11: begin
    118             cos_r <= y[PIPE_DEPTH]; /* sin */
    119             sin_o_r <= ~(x[PIPE_DEPTH]) + 1'b1; /* -cos */
    120          end
    121       endcase
    122 
    123    assign cos_o = cos_r;
    124    assign sin_o = sin_o_r;
    125    assign err_o = z[PIPE_DEPTH];
    126 
    127 endmodule

     

      通过仿真得出波形如下:

     

  • 相关阅读:
    多线程09-Lock和Condition
    多线程08-Callable和Future
    多线程07-线程池
    在linux环境下搭建java web测试环境(非常详细!)
    Linux下的环境部署和项目发布
    Linux下安装软件命令详解
    Linux下安装Tomcat服务器和部署Web应用
    monkey实战--测试步骤、常用参数、常规monkey命令
    如何做好回归测试
    Apache转发到Tomcat
  • 原文地址:https://www.cnblogs.com/cassuto/p/10463423.html
Copyright © 2011-2022 走看看