基于FPGA的CORDIC算法的实现(2)

参考文献

[1].碎碎思

项目简述

我们上一篇文章已经讲解了CORDIC的向量模式,通过算法的向量模式我们可以计算向量的幅值与相位角,那么通过算法的旋转模式我们可以计算一个角度的正余弦值。两个模式的不同点就是初始条件与终止条件的设置,相同点都是利用座标旋转来实现的。这篇博客将结合向量模式的推到原理来进一步说明旋转模式的推导过程。

本次实验我们将使用MATLAB与FPGA同时利用CORDIC算法生成正弦波、余弦波,本次实验所用到的软硬件工具如下:
1、VIVADO 2019.1
2、MATLAB 2015b
3、Modelsim 10.7

旋转模式的推导过程

CORDIC算法旋转模式和向量模式都是不停的对座标进行旋转,不一样的是向量模式我们的角度变化Z的初值是0,目标是为了将纵座标轴Y的值经过多次旋转之后变成0,然后通过X的最终值可以求解出x2+y2\sqrt{x^2+y^2},通过Z的最终值可以求解出arctan(y/x)arctan(y/x)。其中包含的数学关系如下:
在这里插入图片描述
上面的式子变化由上一篇博客的推导过程我们可以很容易得到。

与向量模式不同,旋转模式的可以求解的两个量是cos(z0)sin(z0)cos(z_0)、sin(z_0),z的初始条件是z0z_0,终止条件是z经过多次旋转之后变成0。然后我们从整体上看可以把nn次的旋转看成1次旋转,这次旋转就是相当于对原来的座标旋转了z0z_0度,所以对应于数学关系如下:
在这里插入图片描述
所以只要我们的x0=0,y0=1/Anx_0=0,y_0=1/A_n,那么上式就变成了:
在这里插入图片描述
那么我们可以看出经过座标旋转的初始条件和旋转条件的设置,我们最终可以求解到cos(z0)sin(z0)cos(z_0)、sin(z_0)。这里我们没有对旋转模式情况下的推导过程一步步的讲解,而是结合向量模式下的推导过程,把旋转模式下的n次旋转看成1次旋转一步得到n习次旋转之后的表达式,然后设置初始条件左中可以获得cos(z0)sin(z0)cos(z_0)、sin(z_0)的值。

这里需要特别注意旋转模式与向量模式旋转的角度不一致,旋转模式我们是默认逆时针旋转进行推导,向量模式我们默认是顺时针旋转进行推导。

MATLAB仿真

MATLAB代码如下:

clc;
clear all;

Phase = 0:359;
Phase = [Phase,Phase,Phase];

rot = [2949120;1740992;919872;466944;234368;117312;58688;29312;14656;7360;3648;1856;896;448;256;128];

Pipeline = 16;
K = 39796;    		  %K=0.607253*2^16,32'h09b74,
x = zeros(Pipeline+1,1);
y = zeros(Pipeline+1,1);
z = zeros(Pipeline+1,1);

cose = zeros(length(Phase),1);
sine = zeros(length(Phase),1);
zzz = zeros(length(Phase),1);



for i = 1:length(Phase)
	x(1) = K;
	y(1) = 0;
	if(Phase(i) <= 90)
		z(1) = Phase(i)*2^16;
	elseif(Phase(i) > 90 && Phase(i) <= 180)
		z(1) = (Phase(i)-90)*2^16;
	elseif(Phase(i) > 180 && Phase(i) <= 270)
		z(1) = (Phase(i)-180)*2^16;
	else
		z(1) = (Phase(i)-270)*2^16;
	end
	for j = 1:Pipeline
		if(z(j)>0)
			x(j+1) = x(j) - floor(y(j)*(2^-(j-1)));
			y(j+1) = y(j) + floor(x(j)*(2^-(j-1)));
			z(j+1) = z(j) - rot(j);
        else
			x(j+1) = x(j) + floor(y(j)*(2^-(j-1)));
			y(j+1) = y(j) - floor(x(j)*(2^-(j-1)));
			z(j+1) = z(j) + rot(j);
		end
    end
    zzz(i) = z(17);
	if(Phase(i) <= 90)
		cose(i) = x(17);
		sine(i) = y(17);
	elseif(Phase(i) > 90 && Phase(i) <= 180)
		cose(i) = -y(17);
		sine(i) = x(17);
	elseif(Phase(i) > 180 && Phase(i) <= 270)
		cose(i) = -x(17);
		sine(i) = -y(17);
	else
		cose(i) = y(17);
		sine(i) = -x(17);
    end
end
figure(1)
plot(cose,'r');
hold on
plot(sine,'g');

MATLAB仿真结果如下:
在这里插入图片描述
从上面结果可以看出我们实验的正确性。我们利用了CORDIC算法通过计算累加的相位完成了正余弦波形的计算。

FPGA代码

我们将完全按照FPGA代码的书写进行Verilog代码的编写,并且在Modelsim中进行仿真验证。
Cordic_Test模块:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : https://blog.csdn.net/zhangningning1996
// Module Name  : Cordic_Test.v
// Create Time  : 2020-06-15 16:06:58
// Editor     : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************
module Cordic_Test(
    input                   sclk            ,
    input                   rst_n           ,
    input           [31:0]  phase           ,
    input                   phase_en        ,
    output  reg     [31:0]  cose            ,
    output  reg     [31:0]  sine            ,
    output  wire            data_en 

);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
parameter           PIPELINE    =       16-1;
parameter           K           =       39796;      

reg     signed      [31:0]  rot[15:0]       ;
reg     signed      [31:0]  x[16:0]         ;
reg     signed      [31:0]  y[16:0]         ;
reg     signed      [31:0]  z[16:0]         ;
reg                 [17:0]  phase_en_delay  ;
reg                 [ 1:0]  quad[17:0]      ;


 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
initial begin
    rot[0]                  <=                  32'd2949120;
    rot[1]                  <=                  32'd1740992;
    rot[2]                  <=                  32'd919872;
    rot[3]                  <=                  32'd466944;
    rot[4]                  <=                  32'd234368;
    rot[5]                  <=                  32'd117312;
    rot[6]                  <=                  32'd58688;
    rot[7]                  <=                  32'd29312;
    rot[8]                  <=                  32'd14656;
    rot[9]                  <=                  32'd7360;
    rot[10]                 <=                  32'd3648;
    rot[11]                 <=                  32'd1856;
    rot[12]                 <=                  32'd896;
    rot[13]                 <=                  32'd448;
    rot[14]                 <=                  32'd256;
    rot[15]                 <=                  32'd128;
end

assign  data_en             =                   phase_en_delay[16];

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        z[0]                <=                  32'd0;
    else if(phase <= 90 && phase_en == 1'b1)
        z[0]                <=                  phase << 16;
    else if(phase > 90 && phase <= 180 && phase_en == 1'b1)
        z[0]                <=                  (phase - 90) << 16;
    else if(phase > 180 && phase <= 270 && phase_en == 1'b1)
        z[0]                <=                  (phase - 180) << 16;
    else if(phase_en == 1'b1)
        z[0]                <=                  (phase - 270) << 16;
    else
        z[0]                <=                  z[0];

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        quad[0]             <=                  2'b00;
    else if(phase <= 90 && phase_en == 1'b1)
        quad[0]             <=                  2'b00;
    else if(phase > 90 && phase <= 180 && phase_en == 1'b1)
        quad[0]             <=                  2'b01;
    else if(phase > 180 && phase <= 270 && phase_en == 1'b1)
        quad[0]             <=                  2'b10;
    else if(phase_en == 1'b1)
        quad[0]             <=                  2'b11;
    else
        quad[0]             <=                  quad[0];

genvar i;
generate
    for (i=0; i < 17; i=i+1)
        always @(posedge sclk or negedge rst_n)
            quad[i+1]      <=                   quad[i];  
endgenerate

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)begin
        x[0]                <=                  K;
        y[0]                <=                  0;
    end else if(phase_en == 1'b1)begin
        x[0]                <=                  K;
        y[0]                <=                  0;
    end  else begin
        x[0]                <=                  x[0];
        y[0]                <=                  y[0];    
    end
        
generate
    for (i=0; i < 16; i=i+1)
        always @(posedge sclk or negedge rst_n)
            if(rst_n == 1'b0) begin
                x[i+1]      <=                  32'd0;              
                y[i+1]      <=                  32'd0; 
                z[i+1]      <=                  32'd0;
            end else if(z[i] > 0)begin
                x[i+1]      <=                  x[i] - (y[i] >>> i);              
                y[i+1]      <=                  y[i] + (x[i] >>> i); 
                z[i+1]      <=                  z[i] - rot[i];
            end else begin
                x[i+1]      <=                  x[i] + (y[i] >>> i);              
                y[i+1]      <=                  y[i] - (x[i] >>> i); 
                z[i+1]      <=                  z[i] + rot[i];
            end        
endgenerate
    
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0) begin
        cose                <=                  32'd0;
        sine                <=                  32'd0;
    end else if(quad[16] == 2'b00) begin
        cose                <=                  x[16];
        sine                <=                  y[16];
    end else if(quad[16] == 2'b01)begin
        cose                <=                  -y[16];
        sine                <=                  x[16];
    end else if(quad[16] == 2'b10)begin
        cose                <=                  -x[16];
        sine                <=                  -y[16];       
    end else if(quad[16] == 2'b11) begin
        cose                <=                  y[16];
        sine                <=                  -x[16];
    end else begin
        cose                <=                  cose;
        sine                <=                  sine;
    end      

always @(posedge sclk)
    phase_en_delay          <=                  {phase_en_delay[16:0],phase_en};
    
endmodule

测试模块的代码,
Cordic_Test_tb:

`timescale 1 ps/ 1 ps

module Cordic_Test_tb;

// Inputs
reg                         CLK_50M;
reg                         RST_N;
reg             [15:0]      cnt;
reg             [15:0]      cnt_n;
reg             [31:0]      Phase;
reg             [31:0]      Phase_n;
wire            [31:0]      Sin;
wire            [31:0]      Cos;
wire            [31:0]      Error;
wire                        data_en;


Cordic_Test  Cordic_Test_inst(
    .sclk                   (CLK_50M    ),
    .rst_n                  (RST_N      ),
    .phase                  (Phase      ),
    .phase_en               (RST_N      ),
    .cose                   (Cos        ),
    .sine                   (Sin        ),
    .data_en                (data_en    )

);
initial
begin
    #0 CLK_50M = 1'b0;
    #10000 RST_N = 1'b0;
    #10000 RST_N = 1'b1;
    #1000000000 $stop;
end 

always #10000 
begin
    CLK_50M = ~CLK_50M;
end

always @ (posedge CLK_50M or negedge RST_N)
begin
    if(!RST_N)
        cnt <= 1'b0;
    else
        cnt <= cnt_n;
end

always @ (*)
begin
    if(cnt == 16'd359)
        cnt_n = 1'b0;
    else
        cnt_n = cnt + 1'b1;
end

//生成相位0-359度,Phase[17:16]为相位的象限,Phase[15:10]为相位的值
always @ (posedge CLK_50M or negedge RST_N)
begin
    if(!RST_N)
        Phase <= 1'b0;
    else
        Phase <= cnt;
end



endmodule


仿真结果如下:
在这里插入图片描述
从上面MATLAB与Modelsim的联合仿真验证,从而证明了我们实验的正确性。

小结

至此为止,我们利用两篇文章介绍了圆周系统的向量模式与旋转模式,至于线性系统与双曲线系统的两种模式在这就不做过多的介绍,相信大家从这两篇文章再结合参考文献中的文章可以很轻松的写出来这两种系统的座标旋转算法。

大家有没有发现使用CORDIC产生正弦波比DDS要简单的多。

总结

创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。为行业贡献及其微小的一部分。或者对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群:
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章