首页 > 编程语言 >CORDIC算法解释及FPGA实现(圆坐标系)

CORDIC算法解释及FPGA实现(圆坐标系)

时间:2024-08-18 17:27:16浏览次数:8  
标签:FPGA 16 32 CORDIC assign theta cr rot 坐标系

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 $ 倍(角度保持一致)。
image

角度累加器

为便于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]\) 可进行拆分为多个类似矩阵乘积,即旋转角度 θ ,可拆分为多次小的旋转(下图为对应的反正切角度表)。
image

由于旋转角度 θ 可为任意值,故将旋转变换采用迭代算法实现,即多次角度迭代关系无限趋近于目标θ角度(以 θ 旋转角度限制)。以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$。对于范围外的角度,需要使用三角恒等式转化进行“预处理”,即象限判断。

image

因此,原始算法规整为使用向量的伪旋转来表示迭代移位-相加算法,即:\(\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硬件结构如下:

image

在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


设定多种角度值,仿真如下图:

image

在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值,仿真如下图:

image


本篇文章中使用的Verilog程序模块,若有需见网页左栏Gitee仓库链接:https://gitee.com/silly-big-head/little-mouse-funnyhouse/tree/FPGA-Verilog/

标签:FPGA,16,32,CORDIC,assign,theta,cr,rot,坐标系
From: https://www.cnblogs.com/handat/p/18365670

相关文章

  • fpga图像处理实战-对数变换
    对数变换        图像对数变换(LogarithmicTransformation)是一种非线性灰度变换方法,常用于增强图像中的细节,特别是在图像中存在较大的亮度范围时。对数变换通过压缩亮度范围,使得较暗区域的细节更加明显,同时抑制过亮区域的影响。它在图像处理中的应用非常广泛,特别是在......
  • fpga图像处理实战-图像缓存(FIFO)
    FPGA实现`timescale1ns/1ps////Company://Engineer:////CreateDate:2024/08/1813:47:22//DesignName://ModuleName:line_buffer//ProjectName://TargetDevices://ToolVersions://Description:////Dependencies:////Revision......
  • 详解Xilinx FPGA高速串行收发器GTX/GTP(9)--TX/RX通道
    目录1、TX端的剩余模块1.1、TXPIPEControl1.2、TXGearbox1.3、PCIEBeacon1.4、SATAOOB1.5、PhaseAdjustFIFO1.6、Polarity1.7、PISO1.8、TXPre/PostEmp和10、TXDriver1.9、TXOOBandPCIE1.10、TXDriver1.11、TXPhaseInterpolatorController(包括12......
  • 基于三帧差算法的运动目标检测系统FPGA实现,包含testbench和MATLAB辅助验证程序
    目录1.算法运行效果图预览2.算法运行软件版本3.部分程序4.算法理论概述5.算法完整程序工程1.算法运行效果图预览(完整程序运行后无水印)将FPGA的仿真结果导入到MATLAB中,分别得到MATLAB的结果和FPGA的结果:2.算法运行软件版本vivado2019.2matlab2022a3.部分程序......
  • FPGA实现HDMI传输(一)
    1.HDMIHDMI全称(HighDefinitionMultimediaInterface)高清多媒体接口,支持在单线缆上传输全数字高清视频和多声道音频。HDMI标准的发展历史:日立、松下、飞利浦、SiliconImage、索尼、汤姆逊、东芝七家公司共同组建了HDMI高清多媒体接口组织,着手制定一种符合高清时代标准的全新......
  • 国产FPGA/CPLD芯片设计企业
    1、紫光同创 紫光同创是紫光集团旗下紫光国威的子公司。成立于2013年,具备大规模fpga工艺开发设计能力。布局涵盖高、中、低端fpga产品。半导体曾被台媒称为“国产fpg三驾马车”。2015年紫光同创成功推出国内拥有数千万门自主知识产权的高性能fpga芯片tin系列;2019年10月,紫光......
  • MoseEvent 中的几种坐标系
    本文主要内容:screenX和screenY,clientX和clientY/x和ypageX和pageYlayerX和layerY在处理鼠标事件(MouseEvent)时,了解不同的坐标系是非常重要的。常见的坐标系包括屏幕坐标(screen)、客户端坐标(client)、页面坐标(page)、图层坐标(layer),以及偏移坐标(offset)。这些坐标系各......
  • 【TES807】基于 XCKU115 FPGA 的双 FMC 接口万兆光纤传输信号处理平台
     板卡概述TES807是一款基于千兆或者万兆以太网传输的双FMC接口信号处理平台。该平台采用XILINX的KintexUltraSacle系列FPGA:XCKU115-2FLVF1924I作为主处理器,FPGA外挂两组72位DDR4SDRAM,用来实现超大容量数据缓存,DDR4的最高数据缓存带宽可以达到2400MHz,DDR4的缓......
  • Qt/C++地图坐标纠偏/地球坐标系/火星坐标系/百度坐标系/互相转换/离线函数
    一、前言说明为什么需要地球坐标纠偏这个功能,因为国家安全需要,不允许使用国际标准的地球坐标系,也并不是咱们这边这样,很多国家都是这样处理的,就是本国的地图经纬度坐标都是按照国家标准来的,所以就需要一个转换。国内主要有三种坐标系,国际标准用的地球坐标系WGS-84,国家标准用的火星......
  • 【xilinx】Xilinx最强FPGA VU系列简介
    在高性能计算和数据处理领域,FPGA扮演着日益重要的角色。Xilinx的Virtex™UltraScale+™(VU)系列以其卓越的性能和灵活性,为各种高端应用提供了强有力的支持。以下是关于VU系列的详细介绍。AMDVirtex™UltraScale+™产品优势Xilinx的VirtexUltraScale+™FPGA系列产品,在14n......