使用CORDIC算法求解角度正余弦及Verilog實現


本文是用於記錄在了解和學習CORDIC算法期間的收獲,以供日后自己及他人參考;並且附上了使用Verilog實現CORDIC算法求解角度的正弦和余弦的代碼、簡單的testbench測試代碼、以及在Modelsim下的仿真結果。

 

本文主要參考了:

【1】https://www.cnblogs.com/aikimi7/p/3929592.html (cordic算法的verilog實現及modelsim仿真)

【2】https://www.cnblogs.com/qiweiwang/archive/2010/07/28/1787021.html(CORDIC算法--流水線結構)

【3】https://www.cnblogs.com/yuphone/archive/2010/09/21/1832217.html([文檔].艾米電子 - 算術運算電路,Verilog)

【4】https://wenku.baidu.com/view/54b251aaa98271fe900ef97e(如何理解CORDIC算法)

【5】https://wenku.baidu.com/view/60a0a07831b765ce050814b7(針對正弦余弦計算的CORDIC算法優化及其FPGA實現)

感謝!

-------------------------------------------------------------------------------------------------------------

1、算法簡介

CORDIC(Coordinate Rotation Digital Computer)算法即坐標旋轉數字計算方法,是J.D.Volder1於1959年首次提出,主要用於三角函數、雙曲線、指數、對數的計算。該算法通過基本的加和移位運算代替乘法運算,使得矢量的旋轉和定向的計算不再需要三角函數、乘法、開方、反三角、指數等函數,計算向量長度並能把直角坐標系轉換為極坐標系。因為Cordic 算法只用了移位和加法,很容易用純硬件來實現,非常適合FPGA實現。

CORDIC算法是天平稱重思想在數值運算領域的傑出范例。核心的思想是把非線性的問題變成了線性的迭代問題【4】。

CORDIC算法完成坐標或向量的平面旋轉(下圖以逆時針旋轉為例)。

image

旋轉后,可得如下向量:

image

旋轉的角度θ經過多次旋轉得到的(步步逼近,接近二分查找法),每次旋轉一小角度。單步旋轉定義如下公式:

image

公式(2)提取cosθ,可修改為:

image

修改后的公式把乘法次數從4次改為3次,剩下的乘法運算可以通過選擇每次旋轉的角度去除,將每一步的正切值選為2的指數(二分查找法),除以2的指數可以通過右移操作完成(verilog)。

每次旋轉的角度可以表示為:

image

所有迭代角度累加值等於最終需要的旋轉角度θ:

image

這里Sn為1或者-1,根據旋轉方向確定(后面有確定方法,公式(15)),順時針為-1,逆時針為1。

image

可以得到如下公式:

image

結合公式(3)和(7),得到公式(8):

image

到這里,除了余弦值這個系數,算法只要通過簡單的移位和加法操作完成。而這個系數可以通過預先計算最終值消掉。首先重新重寫這個系數如下:

image

第二步計算所有的余弦值並相乘,這個值K稱為增益系數。

image

由於K值是常量,我們可以先忽略它。

image

image

到這里我們發現,算法只剩下移位和加減法,這就非常適合硬件實現了,為硬件快速計算三角函數提供了一種新的算法。在進行迭代運算時,需要引入一個新的變量Z,表示需要旋轉的角度θ中還沒有旋轉的角度。

image 

這里,我們可以把前面提到確定旋轉方向的方法介紹了,就是通過這個變量Z的符號確定。

image

image

通過公式(5)和(15),將未旋轉的角度變為0。

一個類編程風格的結構如下,反正切值是預先計算好的。

image

1.1 旋轉模式

旋轉模式下,CORDIC算法驅動Z變為0,結合公式(13)和(16),算法的核心計算如下:

image

一種特殊情況是,另初始值如下:

image

因此,旋轉模式下CORDIC算法可以計算一個輸入角度的正弦值和余弦值。

1.2 向量模式

向量模式下,有兩種特例:

image

因此,向量模式下CORDIC算法可以用來計算輸入向量的模和反正切,也能開方計算,並可以將直角坐標轉換為極坐標。

算法介紹:http://en.wikipedia.org/wiki/Cordichttp://blog.csdn.net/liyuanbhu/article/details/8458769

2、硬件算法實現

根據【5】,可以看到CORDIC迭代算法的一種直接實現方式是反饋結構,此結構只設計一級CORDIC運算迭代單元、然后在系統時鍾的驅動下,將本級的輸出作為本級的輸入,通過同一級迭代完成運算。這種方法硬件開銷小、但控制相對復雜。

所以根據【1】、【2】,使用流水線結構實現了CORDIC迭代算法、求取了角度的正弦和余弦值。

下面分段介紹下各部分代碼:

首先是角度的表示,進行了宏定義,360讀用16位二進制表示2^16,每一度為2^16/360。

//360°--2^16,phase_in = 16bits (input [15:0] phase_in)
//1°--2^16/360
`define rot0  16'h2000    //45
`define rot1  16'h12e4    //26.5651
`define rot2  16'h09fb    //14.0362
`define rot3  16'h0511    //7.1250
`define rot4  16'h028b    //3.5763
`define rot5  16'h0145    //1.7899
`define rot6  16'h00a3    //0.8952
`define rot7  16'h0051    //0.4476
`define rot8  16'h0028    //0.2238
`define rot9  16'h0014    //0.1119
`define rot10 16'h000a    //0.0560
`define rot11 16'h0005    //0.0280
`define rot12 16'h0003    //0.0140
`define rot13 16'h0002    //0.0070
`define rot14 16'h0001    //0.0035
`define rot15 16'h0000    //0.0018

然后是流水線級數定義、增益放大倍數以及中間結果位寬定義。流水線級數16,為了滿足精度要求,有文獻指出流水線級數必須大於等於角度位寬16(針對正弦余弦計算的CORDIC算法優化及其FPGA實現)。增益放大2^16,為了避免溢出狀況中間結果(x,y,z)定義為17為,最高位作為符號位判斷,1為負數,0為正數。

module cordic
(
    input clk,
    
    input [15:0] phase_in,
    output reg signed [16:0] eps,
    output reg signed [16:0] sin,
    output reg signed [16:0] cos
);
parameter PIPELINE = 16;

parameter K = 16'h9b74;
//gian k=0.607253*2^16,9b74,n means the number pipeline
//pipeline 16-level    //maybe overflow,matlab result not overflow
//MSB is signed bit,transform the sin and cos according to phase_in[15:14]
reg signed [16:0] x0=0,y0=0,z0=0;
reg signed [16:0] x1=0,y1=0,z1=0;
reg signed [16:0] x2=0,y2=0,z2=0;
reg signed [16:0] x3=0,y3=0,z3=0;
reg signed [16:0] x4=0,y4=0,z4=0;
reg signed [16:0] x5=0,y5=0,z5=0;
reg signed [16:0] x6=0,y6=0,z6=0;
reg signed [16:0] x7=0,y7=0,z7=0;
reg signed [16:0] x8=0,y8=0,z8=0;
reg signed [16:0] x9=0,y9=0,z9=0;
reg signed [16:0] x10=0,y10=0,z10=0;
reg signed [16:0] x11=0,y11=0,z11=0;
reg signed [16:0] x12=0,y12=0,z12=0;
reg signed [16:0] x13=0,y13=0,z13=0;
reg signed [16:0] x14=0,y14=0,z14=0;
reg signed [16:0] x15=0,y15=0,z15=0;
reg signed [16:0] x16=0,y16=0,z16=0;

還需要定義memory型寄存器數組並初始化為0,用於寄存輸入角度高2位的值。

reg [1:0] quadrant [PIPELINE:0];
integer i;
initial
begin
    for(i=0;i<=PIPELINE;i=i+1)
    quadrant[i] = 2'b0;
end

接着,是對輸入角度象限處理,將角度都轉換到第一象限,方便處理。輸入角度值最高兩位賦值0,即轉移到第一象限[0°,90°]。此外,完成x0,y0和z0的初始化,並增加一位符號位。

always @ (posedge clk)//stage 0,not pipeline
begin
    x0 <= {1'b0,K}; //add one signed bit,0 means positive
    y0 <= 17'b0;
    z0 <= {3'b0,phase_in[13:0]};//control the phase_in to the range[0-Pi/2]
end

接下來根據剩余待旋轉角度z的符號位進行16次迭代處理,即完成16級流水線處理。

always @ (posedge clk)//stage 1
begin
  if(z0[16])//the diff is negative so clockwise
  begin
      x1 <= x0 + y0;
      y1 <= x0 - y0;
      z1 <= z0 + `rot0;
  end
  else
  begin
      x1 <= x0 - y0;//x1 <= x0;
      y1 <= x0 + y0;//y1 <= x0;
      z1 <= z0 - `rot0;//reversal 45
  end
end

always @ (posedge clk)//stage 2
begin
    if(z1[16])//the diff is negative so clockwise
    begin
        x2 <= x1 + (y1>>>4'd1);
        y2 <= y1 - (x1>>>4'd1);
        z2 <= z1 + `rot1;//clockwise 26
    end
    else
    begin
        x2 <= x1 - (y1>>>4'd1);
        y2 <= y1 + (x1>>>4'd1);
        z2 <= z1 - `rot1;//anti-clockwise 26
    end
end

always @ (posedge clk)//stage 3
begin
    if(z2[16])//the diff is negative so clockwise
    begin
        x3 <= x2 + (y2>>>4'd2); //right shift n bits,divide 2^n
        y3 <= y2 - (x2>>>4'd2); //left adds n bits of MSB,in first quadrant x or y are positive,MSB =0 ??
        z3 <= z2 + `rot2;//clockwise 14    //difference of positive and negtive number and no round(4,5)
    end
    else
    begin
        x3 <= x2 - (y2>>>4'd2);
        y3 <= y2 + (x2>>>4'd2);
        z3 <= z2 - `rot2;//anti-clockwise 14
    end
end

always @ (posedge clk)//stage 4
begin
    if(z3[16])
    begin
        x4 <= x3 + (y3>>>4'd3);
        y4 <= y3 - (x3>>>4'd3);
        z4 <= z3 + `rot3;//clockwise 7
    end
    else
    begin
        x4 <= x3 - (y3>>>4'd3);
        y4 <= y3 + (x3>>>4'd3);
        z4 <= z3 - `rot3;//anti-clockwise 7
    end
end

always @ (posedge clk)//stage 5
begin
    if(z4[16])
    begin
        x5 <= x4 + (y4>>>4'd4);
        y5 <= y4 - (x4>>>4'd4);
        z5 <= z4 + `rot4;//clockwise 3
    end
    else
    begin
        x5 <= x4 - (y4>>>4'd4);
        y5 <= y4 + (x4>>>4'd4);
        z5 <= z4 - `rot4;//anti-clockwise 3
    end
end

always @ (posedge clk)//STAGE 6
begin
    if(z5[16])
    begin
        x6 <= x5 + (y5>>>4'd5);
        y6 <= y5 - (x5>>>4'd5);
        z6 <= z5 + `rot5;//clockwise 1
    end
    else
    begin
        x6 <= x5 - (y5>>>4'd5);
        y6 <= y5 + (x5>>>4'd5);
        z6 <= z5 - `rot5;//anti-clockwise 1 
    end
end

always @ (posedge clk)//stage 7
begin
    if(z6[16])
    begin
        x7 <= x6 + (y6>>>4'd6);
        y7 <= y6 - (x6>>>4'd6);
        z7 <= z6 + `rot6;
    end
    else
    begin
        x7 <= x6 - (y6>>>4'd6);
        y7 <= y6 + (x6>>>4'd6);
        z7 <= z6 - `rot6;
    end
end

always @ (posedge clk)//stage 8
begin
    if(z7[16])
    begin
        x8 <= x7 + (y7>>>4'd7);
        y8 <= y7 - (x7>>>4'd7);
        z8 <= z7 + `rot7;
    end
    else
    begin
        x8 <= x7 - (y7>>>4'd7);
        y8 <= y7 + (x7>>>4'd7);
        z8 <= z7 - `rot7;
    end
end

always @ (posedge clk)//stage 9
begin
    if(z8[16])
    begin
        x9 <= x8 + (y8>>>4'd8);
        y9 <= y8 - (x8>>>4'd8);
        z9 <= z8 + `rot8;
    end
    else
    begin
        x9 <= x8 - (y8>>>4'd8);
        y9 <= y8 + (x8>>>4'd8);
        z9 <= z8 - `rot8;
    end
end

always @ (posedge clk)//stage 10
begin
    if(z9[16])
    begin
        x10 <= x9 + (y9>>>4'd9);
        y10 <= y9 - (x9>>>4'd9);
        z10 <= z9 + `rot9;
    end
    else
    begin
        x10 <= x9 - (y9>>>4'd9);
        y10 <= y9 + (x9>>>4'd9);
        z10 <= z9 - `rot9;
    end
end

always @ (posedge clk)//stage 11
begin
    if(z10[16])
    begin
        x11 <= x10 + (y10>>>4'd10);
        y11 <= y10 - (x10>>>4'd10);
        z11 <= z10 + `rot10;
    end
    else
    begin
        x11 <= x10 - (y10>>>4'd10);
        y11 <= y10 + (x10>>>4'd10);
        z11 <= z10 - `rot10;
    end
end

always @ (posedge clk)//stage 12
begin
    if(z11[16])
    begin
        x12 <= x11 + (y11>>>4'd11);
        y12 <= y11 - (x11>>>4'd11);
        z12 <= z11 + `rot11;
    end
    else
    begin
        x12 <= x11 - (y11>>>4'd11);
        y12 <= y11 + (x11>>>4'd11);
        z12 <= z11 - `rot11;
    end
end

always @ (posedge clk)//stage 13
begin
    if(z12[16])
    begin
        x13 <= x12 + (y12>>>4'd12);
        y13 <= y12 - (x12>>>4'd12);
        z13 <= z12 + `rot12;
    end
    else
    begin
        x13 <= x12 - (y12>>>4'd12);
        y13 <= y12 + (x12>>>4'd12);
        z13 <= z12 - `rot12;
    end
end

always @ (posedge clk)//stage 14
begin
    if(z13[16])
    begin
        x14 <= x13 + (y13>>>4'd13);
        y14 <= y13 - (x13>>>4'd13);
        z14 <= z13 + `rot13;
    end
    else
    begin
        x14 <= x13 - (y13>>>4'd13);
        y14 <= y13 + (x13>>>4'd13);
        z14 <= z13 - `rot13;
    end
end

always @ (posedge clk)//stage 15
begin
    if(z14[16])
    begin
        x15 <= x14 + (y14>>>4'd14);
        y15 <= y14 - (x14>>>4'd14);
        z15 <= z14 + `rot14;
    end
    else
    begin
        x15 <= x14 - (y14>>>4'd14);
        y15 <= y14 + (x14>>>4'd14);
        z15 <= z14 - `rot14;
    end
end

always @ (posedge clk)//stage 16
begin
    if(z15[16])
    begin
        x16 <= x15 + (y15>>>4'd15);
        y16 <= y15 - (x15>>>4'd15);
        z16 <= z15 + `rot15;
    end
    else
    begin
        x16 <= x15 - (y15>>>4'd15);
        y16 <= y15 + (x15>>>4'd15);
        z16 <= z15 - `rot15;
    end
end
View Code

其中使用到了算數右移(>>>)運算、所以在之前聲明時將相應的reg/wire均聲明為signed類型。這一點在【1】的最后也有說明。

需要注意的是這里的算數移位運算(這一運算的詳細過程在【3】中進行了說明),與之區分的是邏輯移位運算。

二者規則為:

邏輯左移和右移,空出的位均補零。

算數左移與邏輯左移相同,都在低位補零;算數右移、移出的高位比特使用符號位填充(0正1負)

舉例說明,對8'b_1011_0111進行邏輯、算數移位的結果如下圖所示:

比如這里的原數值是8'b10110111、為負數(補碼形式)、換算成十進制為-73.

算數右移一位之后的結果是8'b11011011、由補碼換算成原碼再換算為十進制為-37.

 

由於進行了象限的轉換,最終流水結果需要根據象限進行轉換為正確的值。這里寄存17次高2位角度輸入值,配合流水線結果用於象限判斷,並完成轉換。

//according to the pipeline,register phase_in[15:14]
always @ (posedge clk)
begin
quadrant[0] <= phase_in[15:14];
quadrant[1] <= quadrant[0];
quadrant[2] <= quadrant[1];
quadrant[3] <= quadrant[2];
quadrant[4] <= quadrant[3];
quadrant[5] <= quadrant[4];
quadrant[6] <= quadrant[5];
quadrant[7] <= quadrant[6];
quadrant[8] <= quadrant[7];
quadrant[9] <= quadrant[8];
quadrant[10] <= quadrant[9];
quadrant[11] <= quadrant[10];
quadrant[12] <= quadrant[11];
quadrant[13] <= quadrant[12];
quadrant[14] <= quadrant[13];
quadrant[15] <= quadrant[14];
quadrant[16] <= quadrant[15];
end

最后,根據寄存的高2位角度輸入值,利用三角函數關系,得出最后的結果,其中負數進行了補碼操作。

//alter register, according to quadrant[16] to transform the result to the right result
always @ (posedge clk)
    eps <= z16;

always @ (posedge clk) begin
case(quadrant[16]) //or 15
2'b00:begin //if the phase is in first quadrant,the sin(X)=sin(A),cos(X)=cos(A)
        cos <= x16;
        sin <= y16;
        end
2'b01:begin //if the phase is in second quadrant,the sin(X)=sin(A+90)=cosA,cos(X)=cos(A+90)=-sinA
        cos <= ~(y16) + 1'b1;//-sin
        sin <= x16;//cos
        end
2'b10:begin //if the phase is in third quadrant,the sin(X)=sin(A+180)=-sinA,cos(X)=cos(A+180)=-cosA
        cos <= ~(x16) + 1'b1;//-cos
        sin <= ~(y16) + 1'b1;//-sin
        end
2'b11:begin //if the phase is in forth quadrant,the sin(X)=sin(A+270)=-cosA,cos(X)=cos(A+270)=sinA
        cos <= y16;//sin
        sin <= ~(x16) + 1'b1;//-cos
        end
endcase
end

完整代碼:

//360°--2^16,phase_in = 16bits (input [15:0] phase_in)
//1°--2^16/360
`define rot0  16'h2000    //45
`define rot1  16'h12e4    //26.5651
`define rot2  16'h09fb    //14.0362
`define rot3  16'h0511    //7.1250
`define rot4  16'h028b    //3.5763
`define rot5  16'h0145    //1.7899
`define rot6  16'h00a3    //0.8952
`define rot7  16'h0051    //0.4476
`define rot8  16'h0028    //0.2238
`define rot9  16'h0014    //0.1119
`define rot10 16'h000a    //0.0560
`define rot11 16'h0005    //0.0280
`define rot12 16'h0003    //0.0140
`define rot13 16'h0002    //0.0070
`define rot14 16'h0001    //0.0035
`define rot15 16'h0000    //0.0018

module cordic
(
    input clk,
    //input rst_n,
    //input ena,
    input [15:0] phase_in,
    output reg signed [16:0] eps,
    output reg signed [16:0] sin,
    output reg signed [16:0] cos
);
parameter PIPELINE = 16;
//parameter K = 16'h4dba;//k=0.607253*2^15
parameter K = 16'h9b74;//gian k=0.607253*2^16,9b74,n means the number pipeline
//pipeline 16-level    //maybe overflow,matlab result not overflow
//MSB is signed bit,transform the sin and cos according to phase_in[15:14]
reg signed [16:0] x0=0,y0=0,z0=0;
reg signed [16:0] x1=0,y1=0,z1=0;
reg signed [16:0] x2=0,y2=0,z2=0;
reg signed [16:0] x3=0,y3=0,z3=0;
reg signed [16:0] x4=0,y4=0,z4=0;
reg signed [16:0] x5=0,y5=0,z5=0;
reg signed [16:0] x6=0,y6=0,z6=0;
reg signed [16:0] x7=0,y7=0,z7=0;
reg signed [16:0] x8=0,y8=0,z8=0;
reg signed [16:0] x9=0,y9=0,z9=0;
reg signed [16:0] x10=0,y10=0,z10=0;
reg signed [16:0] x11=0,y11=0,z11=0;
reg signed [16:0] x12=0,y12=0,z12=0;
reg signed [16:0] x13=0,y13=0,z13=0;
reg signed [16:0] x14=0,y14=0,z14=0;
reg signed [16:0] x15=0,y15=0,z15=0;
reg signed [16:0] x16=0,y16=0,z16=0;

reg [1:0] quadrant [PIPELINE:0];
integer i;
initial
begin
    for(i=0;i<=PIPELINE;i=i+1)
    quadrant[i] = 2'b0;
end

always @ (posedge clk)//stage 0,not pipeline
begin
    x0 <= {1'b0,K}; //add one signed bit,0 means positive
    y0 <= 17'b0;
    z0 <= {3'b0,phase_in[13:0]};//control the phase_in to the range[0-Pi/2]
end

always @ (posedge clk)//stage 1
begin
  if(z0[16])//the diff is negative so clockwise
  begin
      x1 <= x0 + y0;
      y1 <= x0 - y0;
      z1 <= z0 + `rot0;
  end
  else
  begin
      x1 <= x0 - y0;//x1 <= x0;
      y1 <= x0 + y0;//y1 <= x0;
      z1 <= z0 - `rot0;//reversal 45
  end
end

always @ (posedge clk)//stage 2
begin
    if(z1[16])//the diff is negative so clockwise
    begin
        x2 <= x1 + (y1>>>4'd1);
        y2 <= y1 - (x1>>>4'd1);
        z2 <= z1 + `rot1;//clockwise 26
    end
    else
    begin
        x2 <= x1 - (y1>>>4'd1);
        y2 <= y1 + (x1>>>4'd1);
        z2 <= z1 - `rot1;//anti-clockwise 26
    end
end

always @ (posedge clk)//stage 3
begin
    if(z2[16])//the diff is negative so clockwise
    begin
        x3 <= x2 + (y2>>>4'd2); //right shift n bits,divide 2^n
        y3 <= y2 - (x2>>>4'd2); //left adds n bits of MSB,in first quadrant x or y are positive,MSB =0 ??
        z3 <= z2 + `rot2;//clockwise 14    //difference of positive and negtive number and no round(4,5)
    end
    else
    begin
        x3 <= x2 - (y2>>>4'd2);
        y3 <= y2 + (x2>>>4'd2);
        z3 <= z2 - `rot2;//anti-clockwise 14
    end
end

always @ (posedge clk)//stage 4
begin
    if(z3[16])
    begin
        x4 <= x3 + (y3>>>4'd3);
        y4 <= y3 - (x3>>>4'd3);
        z4 <= z3 + `rot3;//clockwise 7
    end
    else
    begin
        x4 <= x3 - (y3>>>4'd3);
        y4 <= y3 + (x3>>>4'd3);
        z4 <= z3 - `rot3;//anti-clockwise 7
    end
end

always @ (posedge clk)//stage 5
begin
    if(z4[16])
    begin
        x5 <= x4 + (y4>>>4'd4);
        y5 <= y4 - (x4>>>4'd4);
        z5 <= z4 + `rot4;//clockwise 3
    end
    else
    begin
        x5 <= x4 - (y4>>>4'd4);
        y5 <= y4 + (x4>>>4'd4);
        z5 <= z4 - `rot4;//anti-clockwise 3
    end
end

always @ (posedge clk)//STAGE 6
begin
    if(z5[16])
    begin
        x6 <= x5 + (y5>>>4'd5);
        y6 <= y5 - (x5>>>4'd5);
        z6 <= z5 + `rot5;//clockwise 1
    end
    else
    begin
        x6 <= x5 - (y5>>>4'd5);
        y6 <= y5 + (x5>>>4'd5);
        z6 <= z5 - `rot5;//anti-clockwise 1 
    end
end

always @ (posedge clk)//stage 7
begin
    if(z6[16])
    begin
        x7 <= x6 + (y6>>>4'd6);
        y7 <= y6 - (x6>>>4'd6);
        z7 <= z6 + `rot6;
    end
    else
    begin
        x7 <= x6 - (y6>>>4'd6);
        y7 <= y6 + (x6>>>4'd6);
        z7 <= z6 - `rot6;
    end
end

always @ (posedge clk)//stage 8
begin
    if(z7[16])
    begin
        x8 <= x7 + (y7>>>4'd7);
        y8 <= y7 - (x7>>>4'd7);
        z8 <= z7 + `rot7;
    end
    else
    begin
        x8 <= x7 - (y7>>>4'd7);
        y8 <= y7 + (x7>>>4'd7);
        z8 <= z7 - `rot7;
    end
end

always @ (posedge clk)//stage 9
begin
    if(z8[16])
    begin
        x9 <= x8 + (y8>>>4'd8);
        y9 <= y8 - (x8>>>4'd8);
        z9 <= z8 + `rot8;
    end
    else
    begin
        x9 <= x8 - (y8>>>4'd8);
        y9 <= y8 + (x8>>>4'd8);
        z9 <= z8 - `rot8;
    end
end

always @ (posedge clk)//stage 10
begin
    if(z9[16])
    begin
        x10 <= x9 + (y9>>>4'd9);
        y10 <= y9 - (x9>>>4'd9);
        z10 <= z9 + `rot9;
    end
    else
    begin
        x10 <= x9 - (y9>>>4'd9);
        y10 <= y9 + (x9>>>4'd9);
        z10 <= z9 - `rot9;
    end
end

always @ (posedge clk)//stage 11
begin
    if(z10[16])
    begin
        x11 <= x10 + (y10>>>4'd10);
        y11 <= y10 - (x10>>>4'd10);
        z11 <= z10 + `rot10;
    end
    else
    begin
        x11 <= x10 - (y10>>>4'd10);
        y11 <= y10 + (x10>>>4'd10);
        z11 <= z10 - `rot10;
    end
end

always @ (posedge clk)//stage 12
begin
    if(z11[16])
    begin
        x12 <= x11 + (y11>>>4'd11);
        y12 <= y11 - (x11>>>4'd11);
        z12 <= z11 + `rot11;
    end
    else
    begin
        x12 <= x11 - (y11>>>4'd11);
        y12 <= y11 + (x11>>>4'd11);
        z12 <= z11 - `rot11;
    end
end

always @ (posedge clk)//stage 13
begin
    if(z12[16])
    begin
        x13 <= x12 + (y12>>>4'd12);
        y13 <= y12 - (x12>>>4'd12);
        z13 <= z12 + `rot12;
    end
    else
    begin
        x13 <= x12 - (y12>>>4'd12);
        y13 <= y12 + (x12>>>4'd12);
        z13 <= z12 - `rot12;
    end
end

always @ (posedge clk)//stage 14
begin
    if(z13[16])
    begin
        x14 <= x13 + (y13>>>4'd13);
        y14 <= y13 - (x13>>>4'd13);
        z14 <= z13 + `rot13;
    end
    else
    begin
        x14 <= x13 - (y13>>>4'd13);
        y14 <= y13 + (x13>>>4'd13);
        z14 <= z13 - `rot13;
    end
end

always @ (posedge clk)//stage 15
begin
    if(z14[16])
    begin
        x15 <= x14 + (y14>>>4'd14);
        y15 <= y14 - (x14>>>4'd14);
        z15 <= z14 + `rot14;
    end
    else
    begin
        x15 <= x14 - (y14>>>4'd14);
        y15 <= y14 + (x14>>>4'd14);
        z15 <= z14 - `rot14;
    end
end

always @ (posedge clk)//stage 16
begin
    if(z15[16])
    begin
        x16 <= x15 + (y15>>>4'd15);
        y16 <= y15 - (x15>>>4'd15);
        z16 <= z15 + `rot15;
    end
    else
    begin
        x16 <= x15 - (y15>>>4'd15);
        y16 <= y15 + (x15>>>4'd15);
        z16 <= z15 - `rot15;
    end
end


//according to the pipeline,register phase_in[15:14]
always @ (posedge clk)
begin
  quadrant[0] <= phase_in[15:14];
  quadrant[1] <= quadrant[0];
  quadrant[2] <= quadrant[1];
  quadrant[3] <= quadrant[2];
  quadrant[4] <= quadrant[3];
  quadrant[5] <= quadrant[4];
  quadrant[6] <= quadrant[5];
  quadrant[7] <= quadrant[6];
  quadrant[8] <= quadrant[7];
  quadrant[9] <= quadrant[8];
  quadrant[10] <= quadrant[9];
  quadrant[11] <= quadrant[10];
  quadrant[12] <= quadrant[11];
  quadrant[13] <= quadrant[12];
  quadrant[14] <= quadrant[13];
  quadrant[15] <= quadrant[14];
  quadrant[16] <= quadrant[15];
end

//alter register, according to quadrant[16] to transform the result to the right result
always @ (posedge clk)
    eps <= z16;

always @ (posedge clk) begin
case(quadrant[16]) //or 15
2'b00:begin //if the phase is in first quadrant,the sin(X)=sin(A),cos(X)=cos(A)
        cos <= x16;
        sin <= y16;
        end
2'b01:begin //if the phase is in second quadrant,the sin(X)=sin(A+90)=cosA,cos(X)=cos(A+90)=-sinA
        cos <= ~(y16) + 1'b1;//-sin
        sin <= x16;//cos
        end
2'b10:begin //if the phase is in third quadrant,the sin(X)=sin(A+180)=-sinA,cos(X)=cos(A+180)=-cosA
        cos <= ~(x16) + 1'b1;//-cos
        sin <= ~(y16) + 1'b1;//-sin
        end
2'b11:begin //if the phase is in forth quadrant,the sin(X)=sin(A+270)=-cosA,cos(X)=cos(A+270)=sinA
        cos <= y16;//sin
        sin <= ~(x16) + 1'b1;//-cos
        end
endcase
end
 
endmodule
Whole Code

 testbench測試代碼:

`timescale 1 ps/ 1 ps
module cordic_tb;
// test vector input registers
reg arst;
reg clk;

reg [15:0] phase = 16'h2000;
// wires                                               
wire signed [16:0] cosine_out;
wire signed [7:0] eps_out;
wire signed [16:0] sine_out;
//
localparam coef=1000;
// assign statements (if any)                          
cordic i1 (
// port map - connection between master ports and signals/registers   
    
    .clk(clk),
    .cos(cosine_out),
    .eps(eps_out),
    .phase_in(phase),
    .sin(sine_out)
);
initial                                               
begin
  clk=0;
  #(10000*coef) $stop;                                            
end                                                   
always #(5*coef) clk=~clk;  
//
always @(negedge clk)
begin
    phase=phase+16'h0100;
end                                
endmodule
Testbench

3、Modelsim仿真結果

仿真結果的補充說明:

(1)程序全程未使用復位信號,testbench中第一個計算的角度為16'h2000也就是45度,如果以圖示時刻為0時刻、仿真結果對應的波形即分別為sin(x+π/4)和cos(x+π/4)的波形。作為參考,0.5*√2*65535≈46340.

(2)關於運算過程中的位數溢出

根據仿真結果,本測試例下,x4出現過16位位數溢出。

 (3)關於流水線設計的理解

前文提到過實現CORDIC迭代算法時可以使用反饋結構(只使用一級)、也可以使用流水線結構(多級),如果任務是只單獨計算一個角度的正弦或者余弦值,那么所需要的迭代次數或者說消耗的時鍾周期數量其實是相同的,本設計中為16個時鍾。

流水線結構的威力是在連續、源源不斷地計算一組多個角度的正余弦值的時候才展現出來,當初始流水線被填滿之后,每經過一個時鍾周期、都會在輸出上獲得一個更新的角度的正余弦結果值,上圖仿真結果圖中黃色cursor左側的時間段內、流水線即被逐步填滿。

換句話說,如果現在的任務是要計算n個角度的正余弦值、計算一個角度需要的迭代次數為x,反饋結構需要的時長為(n*x)個時鍾周期,流水線結構只需要(n+x-1)個時鍾周期。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM