crazybird

【原创】CORDIC算法的FPGA实现

0
阅读(4753) 评论(37)

忙了几天的毕业设计,做了256~16M点FFT窗函数的设计。今晚终于可以写下博客了,嘻嘻。在这次的设计中用到了CORDIC算法,接下来开始举一个简单的例子来说明该算法的FPGA实现。根据上一篇博客的CORDIC算法理论基础,本次设计以圆周系统的旋转模式为依据和迭代法为实现方案。为了方便说明该设计,相位输入位宽为8,迭代次数为8。

(1)由上一篇博客可知,为了让复杂的三角函数转换成简单的移位相加,得对θi进行一定的限制,使tanθi=2-i,即θi=tan-12-i(i=0,1,2...)。这样,我们可以算出每次迭代所对应的旋转角度了。为了快速算出这些旋转角度,我们得寻找一种方便易用、计算能力强大的软件来完成,C、C++等随你选。这里以MATLAB为例:

fid = fopen('rot.txt','w');
for i=0:7
    x = atan(2^(-i))/pi*2^7;
    rot_angle = atan(2^(-i))*180/pi;
    y = dec2bin(x,8);
    fprintf(fid,'parameter Rot%d = 8''b',i+1);
    fwrite(fid,y);
    fprintf(fid,';\t//%8.5fdeg\r\n',rot_angle);  
end
fclose(fid)

运行后,产生rot.txt文件,打开后会发现该文件已经包含了本设计中需要的旋转角度:

1

(2)由于每次旋转都会使向量模长增大1/cosθi倍,n次旋转后模长将增大

1/cosθ0*1/cosθ1*...*1/cosθn-1。为了纠正向量模长,最后得乘以因子

cosθ0*cosθ1*...*cosθn-1。该值的计算也交给MATLAB了。

Init_1 = 1;
fid = fopen('Init.txt','w');
for i = 0:7
   cos_data = cos(atan(2^(-i))); 
   Init_1 = Init_1*cos_data;
end
Init_expand = Init_1*2^7;
Init_data = dec2bin(Init_expand,8);
fprintf(fid,'parameter Init  = 8''b');
fwrite(fid,Init_data);
fprintf(fid,'; //%7.5f*2^%d  8次迭代:%7.5f',Init_1,i,Init_1);
fclose(fid)

运行后,产生Init.txt文件,内容如下:

2

(3)有了上面的参数,以及下面的迭代公式和初始值就可以开始设计我们的代码了。

3

其中di为:

4

其Verilog HDL设计代码如下:

`timescale 1ns / 1ps
/*******************************************************
Author       :  CrazyBird
Filename     :  cordic.v
Data         :  2015-3-17
Description  :  cordic 
********************************************************/
module cordic
#(
    parameter DATA_WIDTH = 8
)
(
    input                           clk,
    input                           rst_n,
    input       [DATA_WIDTH-1:0]    phase_in,
    output reg  [DATA_WIDTH-1:0]    sin_out,
    output reg  [DATA_WIDTH-1:0]    cos_out
);
//------------------------------------------------
//定义旋转角度
parameter Rot1 = 8'b00100000;   //45.00000deg
parameter Rot2 = 8'b00010010;   //26.56505deg
parameter Rot3 = 8'b00001001;   //14.03624deg
parameter Rot4 = 8'b00000101;   // 7.12502deg
parameter Rot5 = 8'b00000010;   // 3.57633deg
parameter Rot6 = 8'b00000001;   // 1.78991deg
parameter Rot7 = 8'b00000000;   // 0.89517deg
//parameter Rot8 = 8'b00000000;   // 0.44761deg
parameter Init = 8'b01001101;   // 0.60726*2^7  8次迭代:0.60726
//------------------------------------------------
//定义迭代级数
reg    [DATA_WIDTH-1:0]    x0 ,y0 ,z0 ;
reg    [DATA_WIDTH-1:0]    x1 ,y1 ,z1 ;
reg    [DATA_WIDTH-1:0]    x2 ,y2 ,z2 ;
reg    [DATA_WIDTH-1:0]    x3 ,y3 ,z3 ;
reg    [DATA_WIDTH-1:0]    x4 ,y4 ,z4 ;
reg    [DATA_WIDTH-1:0]    x5 ,y5 ,z5 ;
reg    [DATA_WIDTH-1:0]    x6 ,y6 ,z6 ;
reg    [DATA_WIDTH-1:0]    x7 ,y7 ,z7 ;
reg    [DATA_WIDTH-1:0]    x8 ,y8 ,z8 ;
//------------------------------------------------
//限定区间
reg    [DATA_WIDTH-1:0]   phase_in_reg;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        phase_in_reg <= 8'h0;
    else
    begin
        case(phase_in[7:6])
            2'b00 : phase_in_reg <= phase_in;
            2'b01 : phase_in_reg <= phase_in - 8'h40;
            2'b10 : phase_in_reg <= phase_in - 8'h80;
            2'b11 : phase_in_reg <= phase_in - 8'hc0;
        endcase
    end
end
//------------------------------------------------
//初始值
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        x0 <= 8'h00;
        y0 <= 8'h00;
        z0 <= 8'h00;
        end
    else
        begin
        x0 <= Init;
        y0 <= 8'h00;
        z0 <= phase_in_reg;
        end
end
//------------------------------------------------
//第一级迭代
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        x1 <= 8'h00;
        y1 <= 8'h00;
        z1 <= 8'h00;
        end
    else
        begin
        if(z0[7]==1'b0)
            begin
            x1 <= x0 - y0;
            y1 <= y0 + x0;
            z1 <= z0 - Rot1;
            end
        else
            begin
            x1 <= x0 + y0;
            y1 <= y0 - x0;
            z1 <= z0 + Rot1;
            end
        end
end
//------------------------------------------------
//第二级迭代
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        x2 <= 8'h00;
        y2 <= 8'h00;
        z2 <= 8'h00;
        end
    else
        begin
        if(z1[7]==1'b0)
            begin
            x2 <= x1 - {y1[DATA_WIDTH-1],y1[DATA_WIDTH-1:1]};
            y2 <= y1 + {x1[DATA_WIDTH-1],x1[DATA_WIDTH-1:1]};
            z2 <= z1 - Rot2;
            end
        else
            begin
            x2 <= x1 + {y1[DATA_WIDTH-1],y1[DATA_WIDTH-1:1]};
            y2 <= y1 - {x1[DATA_WIDTH-1],x1[DATA_WIDTH-1:1]};
            z2 <= z1 + Rot2;
            end
        end
end
//------------------------------------------------
//第三级迭代
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        x3 <= 8'h00;
        y3 <= 8'h00;
        z3 <= 8'h00;
        end
    else
        begin
        if(z2[7]==1'b0)
            begin
            x3 <= x2 - {{2{y2[DATA_WIDTH-1]}},y2[DATA_WIDTH-1:2]};
            y3 <= y2 + {{2{x2[DATA_WIDTH-1]}},x2[DATA_WIDTH-1:2]};
            z3 <= z2 - Rot3;
            end
        else
            begin
            x3 <= x2 + {{2{y2[DATA_WIDTH-1]}},y2[DATA_WIDTH-1:2]};
            y3 <= y2 - {{2{x2[DATA_WIDTH-1]}},x2[DATA_WIDTH-1:2]};
            z3 <= z2 + Rot3;
            end
        end
end
//------------------------------------------------
//第四级迭代
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        x4 <= 8'h00000000;
        y4 <= 8'h00000000;
        z4 <= 8'h00000000;
        end
    else
        begin
        if(z3[7]==1'b0)
            begin
            x4 <= x3 - {{3{y3[DATA_WIDTH-1]}},y3[DATA_WIDTH-1:3]};
            y4 <= y3 + {{3{x3[DATA_WIDTH-1]}},x3[DATA_WIDTH-1:3]};
            z4 <= z3 - Rot4;
            end
        else
            begin
            x4 <= x3 + {{3{y3[DATA_WIDTH-1]}},y3[DATA_WIDTH-1:3]};
            y4 <= y3 - {{3{x3[DATA_WIDTH-1]}},x3[DATA_WIDTH-1:3]};
            z4 <= z3 + Rot4;
            end
        end
end
//------------------------------------------------
//第五级迭代
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        x5 <= 8'h00;
        y5 <= 8'h00;
        z5 <= 8'h00;
        end
    else
        begin
        if(z4[7]==1'b0)
            begin
            x5 <= x4 - {{4{y4[DATA_WIDTH-1]}},y4[DATA_WIDTH-1:4]};
            y5 <= y4 + {{4{x4[DATA_WIDTH-1]}},x4[DATA_WIDTH-1:4]};
            z5 <= z4 - Rot5;
            end
        else
            begin
            x5 <= x4 + {{4{y4[DATA_WIDTH-1]}},y4[DATA_WIDTH-1:4]};
            y5 <= y4 - {{4{x4[DATA_WIDTH-1]}},x4[DATA_WIDTH-1:4]};
            z5 <= z4 + Rot5;
            end
        end
end
//------------------------------------------------
//第六级迭代
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        x6 <= 8'h00;
        y6 <= 8'h00;
        z6 <= 8'h00;
        end
    else
        begin
        if(z5[7]==1'b0)
            begin
            x6 <= x5 - {{5{y5[DATA_WIDTH-1]}},y5[DATA_WIDTH-1:5]};
            y6 <= y5 + {{5{x5[DATA_WIDTH-1]}},x5[DATA_WIDTH-1:5]};
            z6 <= z5 - Rot6;
            end
        else
            begin
            x6 <= x5 + {{5{y5[DATA_WIDTH-1]}},y5[DATA_WIDTH-1:5]};
            y6 <= y5 - {{5{x5[DATA_WIDTH-1]}},x5[DATA_WIDTH-1:5]};
            z6 <= z5 + Rot6;
            end
        end
end
//------------------------------------------------
//第七级迭代
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        x7 <= 8'h00;
        y7 <= 8'h00;
        z7 <= 8'h00;
        end
    else
        begin
        if(z6[7]==1'b0)
            begin
            x7 <= x6 - {{6{y6[DATA_WIDTH-1]}},y6[DATA_WIDTH-1:6]};
            y7 <= y6 + {{6{x6[DATA_WIDTH-1]}},x6[DATA_WIDTH-1:6]};
            z7 <= z6 - Rot7;
            end
        else
            begin
            x7 <= x6 + {{6{y6[DATA_WIDTH-1]}},y6[DATA_WIDTH-1:6]};
            y7 <= y6 - {{6{x6[DATA_WIDTH-1]}},x6[DATA_WIDTH-1:6]};
            z7 <= z6 + Rot7;
            end
        end
end
//------------------------------------------------
//第八级迭代
//always @(posedge clk or negedge rst_n)
//begin
//    if(!rst_n)
//        begin
//        x8 <= 8'h00;
//        y8 <= 8'h00;
//        z8 <= 8'h00;
//        end
//    else
//        begin
//        if(z7[7]==1'b0)
//            begin
//            x8 <= x7 - {{7{y7[DATA_WIDTH-1]}},y7[DATA_WIDTH-1:7]};
//            y8 <= y7 + {{7{x7[DATA_WIDTH-1]}},x7[DATA_WIDTH-1:7]};
//            z8 <= z7 - Rot8;
//            end
//        else
//            begin
//            x8 <= x7 + {{7{y7[DATA_WIDTH-1]}},y7[DATA_WIDTH-1:7]};
//            y8 <= y7 - {{7{x7[DATA_WIDTH-1]}},x7[DATA_WIDTH-1:7]};
//            z8 <= z7 + Rot8;
//            end
//        end
//end
//------------------------------------------------
//对界定象限的位进行缓存,为后面的输出正余弦值做区间判断
reg [1:0] phase_in_buf [9:0];
 
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        phase_in_buf[0] <= 2'b0;
        phase_in_buf[1] <= 2'b0;
        phase_in_buf[2] <= 2'b0;
        phase_in_buf[3] <= 2'b0;
        phase_in_buf[4] <= 2'b0;
        phase_in_buf[5] <= 2'b0;
        phase_in_buf[6] <= 2'b0;
        phase_in_buf[7] <= 2'b0;
        phase_in_buf[8] <= 2'b0;
        phase_in_buf[9] <= 2'b0;
        end
    else
        begin
        phase_in_buf[0] <= phase_in[7:6];
        phase_in_buf[1] <= phase_in_buf[0];
        phase_in_buf[2] <= phase_in_buf[1];
        phase_in_buf[3] <= phase_in_buf[2];
        phase_in_buf[4] <= phase_in_buf[3];
        phase_in_buf[5] <= phase_in_buf[4];
        phase_in_buf[6] <= phase_in_buf[5];
        phase_in_buf[7] <= phase_in_buf[6];
        phase_in_buf[8] <= phase_in_buf[7];
        phase_in_buf[9] <= phase_in_buf[8];
        end
end
//------------------------------------------------
//输出正、余弦值
always @ (posedge clk,negedge rst_n)
begin
    if(!rst_n)
    begin
        sin_out <= 8'b0;
        cos_out <= 8'b0;
    end
    else
        begin
        case(phase_in_buf[8])
        2'b00:
            begin
            sin_out <= y7;
            cos_out <= x7;
            end
        2'b01:
            begin
            sin_out <= x7;
            cos_out <= ~(y7)+1'b1;
            end
        2'b10:
            begin
            sin_out <= ~(y7)+1'b1;
            cos_out <= ~(x7)+1'b1;
            end
        2'b11:
            begin
            sin_out <= ~(x7)+1'b1;
            cos_out <= y7;
            end
        endcase
        end
end
 
endmodule


其测试代码:

`timescale 1ns / 1ps
/*******************************************************
Author       :  CrazyBird
Filename     :  cordic_tb.v
Data         :  2015-3-17
Description  :  test of cordic module
********************************************************/
module cordic_tb;

reg             clk;
reg             rst_n;
reg     [7:0]   phase_in;

wire    [7:0]   sin_out;
wire    [7:0]   cos_out;

cordic u_cordic 
(
    .clk        (clk        ), 
    .rst_n      (rst_n      ), 
    .phase_in   (phase_in   ), 
    .sin_out    (sin_out    ), 
    .cos_out    (cos_out    )
);
    
parameter PERIOD = 10;
    
initial
begin
     clk = 0;
     forever #(PERIOD/2)
        clk = ~clk;
end

initial 
begin
    rst_n = 0;
    repeat(2)@(negedge clk);
    rst_n = 1;
end
    
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        phase_in <= 8'b0;
    else
        phase_in <= phase_in + 1'b1;
end
      
endmodule

(4)测试结果

5

由仿真结果可以知道功能基本实现,但精度不够高。为了提高精度,可以增加输出相位的位宽和迭代次数。

好,总算讲完了。该睡了,晚安*^_^*

  1. 我的工程上存在一个这样的问题,做atan反正切的x,y数据是64位的,这个位数与迭代次数有关系么

  2. @匿名用户(123.138.79.11 )
    有时间一定搞
  3. 匿名用户匿名用户
    林老弟,有机会吧arctan的做一做,我做的精度不够高
  4. 谢谢了  我好像知道为什么了 要调那个数据类型
  5. 277166022 QQ邮箱
  6. 要不你把整个工程发给我?行么
  7. 为什么我这边不对啊  真的一模一样的代码
  8. @Tino

    正确的。我截图给你看。

  9. 波形不对啊
  10. @Tino
    改好了。是用modelsim仿真的
  11. 博主知道为什么会这样吗?你用的是什么仿真软件,我用的是modelsim
  12. 博主知道为什么会这样吗?你用的是什么仿真软件,我用的是modelsim
  13. @Tino

    哈哈,上次不小心删掉部分代码,我现在补回去,谢谢指出


  14. 就完全跟你上面的代码粘贴的 我刚才自己写的代码也是出不来正常的正余弦波,然后想说用你的试试看能不能出正确的波形,结果还是不行。。还有,你给z0赋值时好像写错了phase_in_reg这个未定义,应该是phase_in?
  15. @Tino
    你是有符号数显示的吗?