CORDIC 算法理論與實踐
CORDIC 算法可以在圓周,雙曲坐標和線性下的用二維向量旋轉後逐漸逼近的方式來計算出某個超越函數的近似值,雖然是近似值,但是如果迭代次數足夠,仍然可以得到非常逼近準確結果的值。
下麵分開討論CORDIC 在圓周,雙曲坐標和線性下的情況。
(1)圓周係統
先通過圓周係統來了解CORDIC 算法的基本思想。該算法的基本原理如圖1
(1)圓周係統
先通過圓周係統來了解CORDIC 算法的基本思想。該算法的基本原理如圖1
所示,現有向量V1,與X 軸夾角為Ф,逆時針旋轉角度θ 後,得到新的向量
V2,
V2,




由上麵的矩陣公司簡化得到:

為了在硬件上實現方便,作如下約定:每一次旋轉的角度θ正切值為2 的倍數,即

並且約定以δi 代表向量的旋轉方向,+1 表示逆時針旋轉,-1 表示順時針旋轉,故第i 步的旋轉可用下式表示:


其中開根號的結果是模校正因子,對於字長一定的運算,它是一個常數,我們用K+1表示。以16bit 字長為例,

這樣可將輸入數據X,Y 校正後再參與運算,避免在運算中增加校正運算。運算迭代式可以簡化成:

上式運算就隻有加或減法和移位了。上麵的公式n 次迭代可以得到:

假設給定X0=K+1;Y0=0, 則迭代結果:

所以,將所需產生的角度值作為z0 輸入,迭代結果輸出xn 和yn 就是需要的浮點超越函數值。采用的迭代方程組為:

CORDIC 算法在圓周係統中還可以用向量模式表達出來。向量模式將輸入向量通過一個特定的角將Y 變為0。該模式下的CORDIC 公式類似於旋轉模式,不同的是旋轉的方向取決於Y 而不是Z 的符號。N 次迭代後CORDIC 公式變為:

從上麵的推導可以看出,CORDIC 算法在圓周係統下的向量模式可以用來計算給定向量(X,Y)的長度和角。這顯然就是從迪卡爾坐標到極坐標的轉換。
下麵給出一個CORDIC算法在圓周係統下的向量模式下獲取角度的Verilog 程序:
/*==============================================================================*\
Filename : Cordic.v
Discription : 坐標旋轉數字計算方法。通過該算法,對輸入的向量坐標進行9次迭代
計算,得到該向量的模值和相角。
\*==============================================================================*/
Filename : Cordic.v
Discription : 坐標旋轉數字計算方法。通過該算法,對輸入的向量坐標進行9次迭代
計算,得到該向量的模值和相角。
\*==============================================================================*/
module CORDIC
(
Clk_20m,
_Rst,
Cordic_start,
Ug_d,
Ug_q,
Ug,
Delta
(
Clk_20m,
_Rst,
Cordic_start,
Ug_d,
Ug_q,
Ug,
Delta
);
input Clk_20m,
_Rst,
Cordic_start; //CORDIC變換啟動標誌
input[15:0] Ug_d, //輸出電壓的d軸分量
Ug_q; //輸出電壓的q軸分量
output[15:0] Ug; //輸出電壓向量的模值
output[13:0] Delta; //輸出電壓向量的相角
wire[31:0] Ug_tmp;
reg[3:0] Times; //迭代次數累加器
reg[15:0] Ug_d_tmp, //輸出電壓d軸分量的中間迭代結果
Ug_q_tmp; //輸出電壓q軸分量的中間迭代結果
reg[13:0] //Delta,
Delta_tmp; //相位角旋轉累加寄存器
// assign Ug = ( Ug_d_tmp>>1 ) + ( Ug_d_tmp>>3 ) - ( Ug_d_tmp>>6 ) - ( Ug_d_tmp>>9 );
//對電壓模值進行比例係數調整,得到實際模值的32倍
// assign Ug_tmp[31:0] = Ug_d_tmp[15:0] * 16'd48224;//d39797;
assign Ug_tmp[31:0] = Ug_d_tmp[15:0] * 16'd45208;
assign Ug[15:0] = Ug_tmp[31:16];
_Rst,
Cordic_start; //CORDIC變換啟動標誌
input[15:0] Ug_d, //輸出電壓的d軸分量
Ug_q; //輸出電壓的q軸分量
output[15:0] Ug; //輸出電壓向量的模值
output[13:0] Delta; //輸出電壓向量的相角
wire[31:0] Ug_tmp;
reg[3:0] Times; //迭代次數累加器
reg[15:0] Ug_d_tmp, //輸出電壓d軸分量的中間迭代結果
Ug_q_tmp; //輸出電壓q軸分量的中間迭代結果
reg[13:0] //Delta,
Delta_tmp; //相位角旋轉累加寄存器
// assign Ug = ( Ug_d_tmp>>1 ) + ( Ug_d_tmp>>3 ) - ( Ug_d_tmp>>6 ) - ( Ug_d_tmp>>9 );
//對電壓模值進行比例係數調整,得到實際模值的32倍
// assign Ug_tmp[31:0] = Ug_d_tmp[15:0] * 16'd48224;//d39797;
assign Ug_tmp[31:0] = Ug_d_tmp[15:0] * 16'd45208;
assign Ug[15:0] = Ug_tmp[31:16];
//輸出電壓向量的相角即為CORDIC算法輸出的旋轉角
assign Delta = Delta_tmp;
/*
always @( posedge Clk_20m or negedge _Rst )
begin
if ( !_Rst )
Delta <= 14'h0;
else if ( Delta_tmp <= 14'h6 )
Delta <= Delta_tmp;
else if ( Delta_tmp <= 14'h1fff )
Delta <= 14'h6;
else if ( Delta_tmp <= 14'h3ffa )
Delta <= 14'h3ffa;
else
Delta <= Delta_tmp;
assign Delta = Delta_tmp;
/*
always @( posedge Clk_20m or negedge _Rst )
begin
if ( !_Rst )
Delta <= 14'h0;
else if ( Delta_tmp <= 14'h6 )
Delta <= Delta_tmp;
else if ( Delta_tmp <= 14'h1fff )
Delta <= 14'h6;
else if ( Delta_tmp <= 14'h3ffa )
Delta <= 14'h3ffa;
else
Delta <= Delta_tmp;
else
Delta <= 14'h6;
end
*/
always @( posedge Clk_20m or negedge _Rst )
begin
if ( !_Rst )
begin
Times[3:0] <= 4'hf;
Ug_d_tmp[15:0] <= 16'h0;
Ug_q_tmp[15:0] <= 16'h0;
Delta_tmp[13:0] <= 14'h0;
end
else if ( Cordic_start ) //啟動CORDIC變換
begin
Times[3:0] <= 4'h0;
Ug_d_tmp <= Ug_d;
Ug_q_tmp <= Ug_q;
Delta_tmp <= 14'h0;
end
else if ( Times <= 4'd9 ) //開始迭代計算
begin
Times[3:0] <= Times[3:0] + 4'h1; //迭代次數加1
case ( Times )
4'h0:
//Ug_q_tmp[15] 符號位
if ( Ug_q_tmp[15] ) //旋轉的目標是使Ug_q_tmp趨近於0,
//根據對Ug_q_tmp符號的判斷,決定正向旋轉還是反向旋轉
begin
Ug_d_tmp <= Ug_d_tmp - Ug_q_tmp; //重新計算新的d軸分量
Ug_q_tmp <= Ug_q_tmp + Ug_d_tmp; //重新計算新的q軸分量
Delta_tmp <= Delta_tmp - 14'hB40; //對相位角進行累加計算
end
else
begin
Ug_d_tmp <= Ug_d_tmp + Ug_q_tmp;
Ug_q_tmp <= Ug_q_tmp - Ug_d_tmp;
Delta_tmp <= Delta_tmp + 14'hB40; //(2880/64)=45
end
4'h1:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { Ug_q_tmp[15], Ug_q_tmp[15:1] };
Ug_q_tmp <= Ug_q_tmp + { Ug_d_tmp[15], Ug_d_tmp[15:1] };
Delta_tmp <= Delta_tmp - 14'h6A4; // (1700/64)=26.5625
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { Ug_q_tmp[15], Ug_q_tmp[15:1] };
Ug_q_tmp <= Ug_q_tmp - { Ug_d_tmp[15], Ug_d_tmp[15:1] };
Delta_tmp <= Delta_tmp + 14'h6A4; //
end
4'h2:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {2{Ug_q_tmp[15]}}, Ug_q_tmp[15:2] };
Ug_q_tmp <= Ug_q_tmp + { {2{Ug_d_tmp[15]}}, Ug_d_tmp[15:2] };
Delta_tmp <= Delta_tmp - 14'h382; // (382/64=14.03125)
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {2{Ug_q_tmp[15]}}, Ug_q_tmp[15:2] };
Ug_q_tmp <= Ug_q_tmp - { {2{Ug_d_tmp[15]}}, Ug_d_tmp[15:2] };
Delta_tmp <= Delta_tmp + 14'h382;
end
4'h3:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {3{Ug_q_tmp[15]}}, Ug_q_tmp[15:3] };
Ug_q_tmp <= Ug_q_tmp + { {3{Ug_d_tmp[15]}}, Ug_d_tmp[15:3] };
Delta_tmp <= Delta_tmp - 14'h1c8; // (382/64=14.03125)
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {3{Ug_q_tmp[15]}}, Ug_q_tmp[15:3] };
Ug_q_tmp <= Ug_q_tmp - { {3{Ug_d_tmp[15]}}, Ug_d_tmp[15:3] };
Delta_tmp <= Delta_tmp + 14'h1c8; // (456/64=7.125)
end
4'h4:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {4{Ug_q_tmp[15]}}, Ug_q_tmp[15:4] };
Ug_q_tmp <= Ug_q_tmp + { {4{Ug_d_tmp[15]}}, Ug_d_tmp[15:4] };
Delta_tmp <= Delta_tmp - 14'hE5; //(229/64=3.578125)
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {4{Ug_q_tmp[15]}}, Ug_q_tmp[15:4] };
Ug_q_tmp <= Ug_q_tmp - { {4{Ug_d_tmp[15]}}, Ug_d_tmp[15:4] };
Delta_tmp <= Delta_tmp + 14'hE5;
end
4'h5:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {5{Ug_q_tmp[15]}}, Ug_q_tmp[15:5] };
Ug_q_tmp <= Ug_q_tmp + { {5{Ug_d_tmp[15]}}, Ug_d_tmp[15:5] };
Delta_tmp <= Delta_tmp - 14'h72; //(114/64=1.78125)
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {5{Ug_q_tmp[15]}}, Ug_q_tmp[15:5] };
Ug_q_tmp <= Ug_q_tmp - { {5{Ug_d_tmp[15]}}, Ug_d_tmp[15:5] };
Delta_tmp <= Delta_tmp + 14'h72;
end
4'h6:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {6{Ug_q_tmp[15]}}, Ug_q_tmp[15:6] };
Ug_q_tmp <= Ug_q_tmp + { {6{Ug_d_tmp[15]}}, Ug_d_tmp[15:6] };
Delta_tmp <= Delta_tmp - 14'h39;//(57/64=0.890625)
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {6{Ug_q_tmp[15]}}, Ug_q_tmp[15:6] };
Ug_q_tmp <= Ug_q_tmp - { {6{Ug_d_tmp[15]}}, Ug_d_tmp[15:6] };
Delta_tmp <= Delta_tmp + 14'h39;
end
4'h7:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {7{Ug_q_tmp[15]}}, Ug_q_tmp[15:7] };
Ug_q_tmp <= Ug_q_tmp + { {7{Ug_d_tmp[15]}}, Ug_d_tmp[15:7] };
Delta_tmp <= Delta_tmp - 14'h1C;//(28/64=0.4375)
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {7{Ug_q_tmp[15]}}, Ug_q_tmp[15:7] };
Ug_q_tmp <= Ug_q_tmp - { {7{Ug_d_tmp[15]}}, Ug_d_tmp[15:7] };
Delta_tmp <= Delta_tmp + 14'h1C;
end
4'h8:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {8{Ug_q_tmp[15]}}, Ug_q_tmp[15:8] };
Ug_q_tmp <= Ug_q_tmp + { {8{Ug_d_tmp[15]}}, Ug_d_tmp[15:8] };
Delta_tmp <= Delta_tmp - 14'hE;//(14/64=0.21875)
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {8{Ug_q_tmp[15]}}, Ug_q_tmp[15:8] };
Ug_q_tmp <= Ug_q_tmp - { {8{Ug_d_tmp[15]}}, Ug_d_tmp[15:8] };
Delta_tmp <= Delta_tmp + 14'hE;
end
4'h9:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {9{Ug_q_tmp[15]}}, Ug_q_tmp[15:9] };
Ug_q_tmp <= Ug_q_tmp + { {9{Ug_d_tmp[15]}}, Ug_d_tmp[15:9] };
Delta_tmp <= Delta_tmp - 14'h7;
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {9{Ug_q_tmp[15]}}, Ug_q_tmp[15:9] };
Ug_q_tmp <= Ug_q_tmp - { {9{Ug_d_tmp[15]}}, Ug_d_tmp[15:9] };
Delta_tmp <= Delta_tmp + 14'h7; //(7/64=0.109375)
end
default: //缺省情況下所有寄存器清零
begin
Ug_d_tmp <= 16'h0;
Ug_q_tmp <= 16'h0;
Delta_tmp <= 14'h0;
end
// ;
endcase
end
else
Times[3:0] <= 4'hf; //迭代計算完畢,結束CORDIC算法,迭代次數置複位值
end
if ( Ug_q_tmp[15] ) //旋轉的目標是使Ug_q_tmp趨近於0,
//根據對Ug_q_tmp符號的判斷,決定正向旋轉還是反向旋轉
begin
Ug_d_tmp <= Ug_d_tmp - Ug_q_tmp; //重新計算新的d軸分量
Ug_q_tmp <= Ug_q_tmp + Ug_d_tmp; //重新計算新的q軸分量
Delta_tmp <= Delta_tmp - 14'hB40; //對相位角進行累加計算
end
else
begin
Ug_d_tmp <= Ug_d_tmp + Ug_q_tmp;
Ug_q_tmp <= Ug_q_tmp - Ug_d_tmp;
Delta_tmp <= Delta_tmp + 14'hB40; //(2880/64)=45
end
4'h1:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { Ug_q_tmp[15], Ug_q_tmp[15:1] };
Ug_q_tmp <= Ug_q_tmp + { Ug_d_tmp[15], Ug_d_tmp[15:1] };
Delta_tmp <= Delta_tmp - 14'h6A4; // (1700/64)=26.5625
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { Ug_q_tmp[15], Ug_q_tmp[15:1] };
Ug_q_tmp <= Ug_q_tmp - { Ug_d_tmp[15], Ug_d_tmp[15:1] };
Delta_tmp <= Delta_tmp + 14'h6A4; //
end
4'h2:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {2{Ug_q_tmp[15]}}, Ug_q_tmp[15:2] };
Ug_q_tmp <= Ug_q_tmp + { {2{Ug_d_tmp[15]}}, Ug_d_tmp[15:2] };
Delta_tmp <= Delta_tmp - 14'h382; // (382/64=14.03125)
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {2{Ug_q_tmp[15]}}, Ug_q_tmp[15:2] };
Ug_q_tmp <= Ug_q_tmp - { {2{Ug_d_tmp[15]}}, Ug_d_tmp[15:2] };
Delta_tmp <= Delta_tmp + 14'h382;
end
4'h3:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {3{Ug_q_tmp[15]}}, Ug_q_tmp[15:3] };
Ug_q_tmp <= Ug_q_tmp + { {3{Ug_d_tmp[15]}}, Ug_d_tmp[15:3] };
Delta_tmp <= Delta_tmp - 14'h1c8; // (382/64=14.03125)
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {3{Ug_q_tmp[15]}}, Ug_q_tmp[15:3] };
Ug_q_tmp <= Ug_q_tmp - { {3{Ug_d_tmp[15]}}, Ug_d_tmp[15:3] };
Delta_tmp <= Delta_tmp + 14'h1c8; // (456/64=7.125)
end
4'h4:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {4{Ug_q_tmp[15]}}, Ug_q_tmp[15:4] };
Ug_q_tmp <= Ug_q_tmp + { {4{Ug_d_tmp[15]}}, Ug_d_tmp[15:4] };
Delta_tmp <= Delta_tmp - 14'hE5; //(229/64=3.578125)
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {4{Ug_q_tmp[15]}}, Ug_q_tmp[15:4] };
Ug_q_tmp <= Ug_q_tmp - { {4{Ug_d_tmp[15]}}, Ug_d_tmp[15:4] };
Delta_tmp <= Delta_tmp + 14'hE5;
end
4'h5:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {5{Ug_q_tmp[15]}}, Ug_q_tmp[15:5] };
Ug_q_tmp <= Ug_q_tmp + { {5{Ug_d_tmp[15]}}, Ug_d_tmp[15:5] };
Delta_tmp <= Delta_tmp - 14'h72; //(114/64=1.78125)
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {5{Ug_q_tmp[15]}}, Ug_q_tmp[15:5] };
Ug_q_tmp <= Ug_q_tmp - { {5{Ug_d_tmp[15]}}, Ug_d_tmp[15:5] };
Delta_tmp <= Delta_tmp + 14'h72;
end
4'h6:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {6{Ug_q_tmp[15]}}, Ug_q_tmp[15:6] };
Ug_q_tmp <= Ug_q_tmp + { {6{Ug_d_tmp[15]}}, Ug_d_tmp[15:6] };
Delta_tmp <= Delta_tmp - 14'h39;//(57/64=0.890625)
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {6{Ug_q_tmp[15]}}, Ug_q_tmp[15:6] };
Ug_q_tmp <= Ug_q_tmp - { {6{Ug_d_tmp[15]}}, Ug_d_tmp[15:6] };
Delta_tmp <= Delta_tmp + 14'h39;
end
4'h7:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {7{Ug_q_tmp[15]}}, Ug_q_tmp[15:7] };
Ug_q_tmp <= Ug_q_tmp + { {7{Ug_d_tmp[15]}}, Ug_d_tmp[15:7] };
Delta_tmp <= Delta_tmp - 14'h1C;//(28/64=0.4375)
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {7{Ug_q_tmp[15]}}, Ug_q_tmp[15:7] };
Ug_q_tmp <= Ug_q_tmp - { {7{Ug_d_tmp[15]}}, Ug_d_tmp[15:7] };
Delta_tmp <= Delta_tmp + 14'h1C;
end
4'h8:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {8{Ug_q_tmp[15]}}, Ug_q_tmp[15:8] };
Ug_q_tmp <= Ug_q_tmp + { {8{Ug_d_tmp[15]}}, Ug_d_tmp[15:8] };
Delta_tmp <= Delta_tmp - 14'hE;//(14/64=0.21875)
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {8{Ug_q_tmp[15]}}, Ug_q_tmp[15:8] };
Ug_q_tmp <= Ug_q_tmp - { {8{Ug_d_tmp[15]}}, Ug_d_tmp[15:8] };
Delta_tmp <= Delta_tmp + 14'hE;
end
4'h9:
if ( Ug_q_tmp[15] )
begin
Ug_d_tmp <= Ug_d_tmp - { {9{Ug_q_tmp[15]}}, Ug_q_tmp[15:9] };
Ug_q_tmp <= Ug_q_tmp + { {9{Ug_d_tmp[15]}}, Ug_d_tmp[15:9] };
Delta_tmp <= Delta_tmp - 14'h7;
end
else
begin
Ug_d_tmp <= Ug_d_tmp + { {9{Ug_q_tmp[15]}}, Ug_q_tmp[15:9] };
Ug_q_tmp <= Ug_q_tmp - { {9{Ug_d_tmp[15]}}, Ug_d_tmp[15:9] };
Delta_tmp <= Delta_tmp + 14'h7; //(7/64=0.109375)
end
default: //缺省情況下所有寄存器清零
begin
Ug_d_tmp <= 16'h0;
Ug_q_tmp <= 16'h0;
Delta_tmp <= 14'h0;
end
// ;
endcase
end
else
Times[3:0] <= 4'hf; //迭代計算完畢,結束CORDIC算法,迭代次數置複位值
end
endmodule
最後更新:2017-04-03 14:53:43