前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >cordic的FPGA实现(三) sin、cos函数运算与源码分析

cordic的FPGA实现(三) sin、cos函数运算与源码分析

作者头像
数字芯片社区
发布2020-07-20 15:59:03
1.4K0
发布2020-07-20 15:59:03
举报
文章被收录于专栏:数字芯片数字芯片

在旋转模式中曾提到:由于每次伪旋转都导致向量模长发生了变化,以Ki表示第i次伪旋转模长补偿因子,所以第i次伪旋转真实旋转的结果应该为:

经过n次伪旋转,得到的伪旋转点最终结果可以表示为:

当n趋近于无穷大时,An逼近1.646760258,令xo=1/An且yo=0即可得到目标旋转角度的正弦、余弦值。

那么,我们现在就让xo=1/An且yo=0且(即x0=1/1.646760258=0.6073),就可以得到z0(旋转角所对应的正弦、余弦值)。

我在想:为什么常量定义中定义的是反正切函数的一半、一半、一半逐个减半,而不是角度一半、一半、一半的减小呢?没想通,反正就是反正切函数的值(角度)逐个减半吧?不对!当然是使用反正切函数的变量值逐个减半更好!因为这样的话逐个将tan>>>(带符号右移)一位就好,要是角度的话一半一半的话绝逼有小数!想出这个CORDIC求解tan、sin、cos的人真的好厉害!!!

verilog代码:
代码语言:javascript
复制
module cordic
(
    input CLOCK,RESET,
   input iCall, 
   output oDone,
   input [31:0]iData,
   output [31:0]cos,
   output [31:0]sin,
   /*
   输出的x为cos值,输出的y为sin值,x=qx,y=qy
   */
   output [31:0]q_deg,q_y,q_x
);
   reg [31:0] atan [15:0];
   //声明atan反正切函数常量表,该常量表从tan(45)度开始每次减小一半儿
   initial begin
       atan[0] = 32'd2949120; atan[1] = 32'd1740992; atan[2] = 32'd919872; atan[3] = 32'd466944;
      atan[4] = 32'd234368; atan[5] = 32'd117312; atan[6] = 32'd58688; atan[7] = 32'd29312; 
      atan[8] = 32'd14656; atan[9] = 32'd7360; atan[10] = 32'd3648; atan[11] = 32'd1856; 
      atan[12] = 32'd896;  atan[13] = 32'd448; atan[14] = 32'd256; atan[15] = 32'd128;
   end
   //带符号位的寄存器声明
   reg signed [31:0]x,y,tx,ty,deg;
   reg [7:0]i;
   reg isDone;
   
   always @ ( posedge CLOCK or negedge RESET )
       if( !RESET )
           begin
            { x,y,tx,ty,deg } <= { 32'd0,32'd0,32'd0,32'd0,32'd0 };
            i <= 8'd0;
            isDone <= 1'b0;
         end
      else if( iCall )
         case( i )
        
             0:
           begin x <= 0.607253 * 65536; y <= 32'd0; deg <= iData << 16; i <= i + 1'b1; end
           
           1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16:
           /*
           在剩余角度大于0时,需要继续逆时针旋转,在单位圆上逆时针旋转的话横坐标会减小,纵坐标会增大
           
           */
           if( deg > 0 )
              begin
                  ty = y >>> i-1;
                  tx = x >>> i-1;
                   x <= x - ty;
                  y <= y + tx;
                  deg <= deg - atan[i-1];
                  i <= i + 1'b1;
              end
           else
           /*
           在剩余角度小于0时候,需要顺时针旋转,顺时针旋转的话纵坐标会减小,横坐标会增大
           */
              begin
                  ty = y >>> i-1;
                  tx = x >>> i-1;
                   x <= x + ty ;
                  y <= y - tx ;
                  deg <= deg + atan[i-1];
                  i <= i + 1'b1;
              end
          
          17:
          begin isDone <= 1'b1; i <= i + 1'b1; end
          
          18:
          begin isDone <= 1'b0; i <= 8'd0; end
             
        endcase
        
    assign oDone = isDone;
    assign cos = x;
    assign sin = y;
    assign { q_deg,q_y,q_x } = { deg,y,x };
    
endmodule

仿真代码:

代码语言:javascript
复制
`timescale 1ns/1ns
module cordic_tb;

reg clk;  //系统时钟
reg rst_n;//复位信号
reg iCall;//模块调用信号
reg [31:0]iData; //待求角度
wire oDone;      //迭代完成标志
wire [31:0]cos;  //cos值
wire [31:0]sin;  //sin值
wire [31:0]deg_left; //剩余角度
cordic u0(
.CLOCK(clk),
.RESET(rst_n),
.iCall(iCall), 
.oDone(oDone),
.iData(iData),
.cos(cos),
.sin(sin),
   /*
   输出的x为cos值,输出的y为sin值,x=qx,y=qy
   */
.q_deg(deg_left)
);

initial begin 
clk=0;
forever #5 clk=~clk;
end 

initial begin 
rst_n=0;
#10;
rst_n=1; 
end 

reg [7:0]i;
always@(posedge clk or negedge rst_n)
if(~rst_n)
begin
 i<=8'd0;
 iCall<=1'b0;
 iData<=32'd0;
end 
else 
case(i) 
0: //z=30
 begin
  if(oDone)
   begin
   iCall<=1'b0;
   i<=i+1'b1;
  end 
   else 
   begin
    iCall<=1'b1;
    iData<=32'd30;
   end 
 end 
1: //z=45
 begin
  if(oDone)
   begin
   iCall<=1'b0;
   i<=i+1'b1;
  end 
   else 
   begin
    iCall<=1'b1;
    iData<=32'd45;
   end  
 end 
2: //z=60
 begin
  if(oDone)
   begin
   iCall<=1'b0;
   i<=i+1'b1;
  end 
   else 
   begin
    iCall<=1'b1;
    iData<=32'd60;
   end  
 end 
3: //z=75
 begin
  if(oDone)
   begin
   iCall<=1'b0;
   i<=i+1'b1;
  end 
   else 
   begin
    iCall<=1'b1;
    iData<=32'd75;
   end  
 end 
4: //z=90
 begin
  if(oDone)
   begin
   iCall<=1'b0;
   i<=i+1'b1;
  end 
   else 
   begin
    iCall<=1'b1;
    iData<=32'd90;
   end 
 end 
 5:
 i<=i;
 endcase  
endmodule

仿真结果:

真实结果 迭代结果

sin30=0.5000 32769/65536=0.5000

cos30=0.8660 56759/65536=0.8661

sin45=0.7071 46341/65536=0.7071

cos45=0.7071 46342/65536=0.7071

sin60=0.8660 56759/65536=0.8661

cos60=0.5000 32769/65536=0.5000

END

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数字ICer 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • verilog代码:
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档