基於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要簡單的多。

總結

創作不易,認爲文章有幫助的同學們可以關注、點贊、轉發支持。爲行業貢獻及其微小的一部分。或者對文章有什麼看法或者需要更近一步交流的同學,可以加入下面的羣:
在這裏插入圖片描述

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