與坐標(biāo)旋轉(zhuǎn)的核心原理與FPGA實現(xiàn))
1. 從旋轉(zhuǎn)到計算Cordic算法的核心思想如果你在數(shù)字信號處理、通信或者圖形學(xué)領(lǐng)域摸爬滾打過一陣子大概率會聽說過Cordic這個名字。它不像FFT或者FIR那樣直接以功能命名聽起來有點神秘但它的身影卻無處不在。從你手機(jī)里的GPS定位計算到無線通信中的調(diào)制解調(diào)再到游戲里一個角色的3D旋轉(zhuǎn)背后可能都有Cordic在默默工作。我第一次接觸它是在一個FPGA項目中需要實時計算一個角度的正弦和余弦值。當(dāng)時的第一反應(yīng)是查表或者調(diào)用數(shù)學(xué)庫但前者精度和資源難以平衡后者在硬件里根本行不通。直到同事扔過來一篇論文里面提到了Cordic我才發(fā)現(xiàn)原來有一種方法只用簡單的移位和加法就能逼近這些復(fù)雜的三角函數(shù)。這就像發(fā)現(xiàn)了一把瑞士軍刀看似結(jié)構(gòu)簡單卻能解決一系列棘手的數(shù)學(xué)計算問題。Cordic的全稱是“坐標(biāo)旋轉(zhuǎn)數(shù)字計算機(jī)”。這個名字聽起來很“古老”因為它誕生于1959年由Jack Volder提出最初是為了在早期的航空導(dǎo)航計算機(jī)中高效計算三角函數(shù)和雙曲函數(shù)。它的核心思想非常巧妙通過一系列固定角度的微小旋轉(zhuǎn)來逼近任意角度的旋轉(zhuǎn)從而間接計算出旋轉(zhuǎn)后的坐標(biāo)而這個坐標(biāo)值恰恰就是我們想要的三角函數(shù)值。整個算法避開了復(fù)雜的乘法運算僅用加法、減法和移位即乘除2的冪次這種硬件極易實現(xiàn)的操-作就能達(dá)到很高的精度。這對于那些沒有硬件乘法器或者對計算速度和功耗有極致要求的嵌入式系統(tǒng)、FPGA、ASIC設(shè)計來說簡直是福音。簡單來說你可以把Cordic想象成一種“折紙”或者“走步”的過程。假設(shè)你想從A點走到B點但只能沿著東、南、西、北或者固定的幾個斜方向移動固定的步長比如每次只能向正東走1米或者向東北方向走0.5米。通過精心設(shè)計這些固定方向和步長并按照一定的規(guī)則比如“如果目標(biāo)在我東北方我就先向正東走一步再判斷”一步步迭代最終你就能無限逼近B點。Cordic做的就是類似的事情只不過它“旋轉(zhuǎn)”的是平面上的一個向量。理解了這個你就能明白為什么它如此受硬件工程師的青睞——把“走步”的規(guī)則固化到電路里時鐘每來一次就完成一步移動和判斷流水線一拍結(jié)果就源源不斷地出來了。2. 算法原理深度拆解旋轉(zhuǎn)模式與向量模式要真正掌握Cordic不能只停留在“它能算三角函數(shù)”的層面必須深入其兩種基本工作模式旋轉(zhuǎn)模式和向量模式。這是Cordic應(yīng)用的兩大基石幾乎所有的衍生應(yīng)用都是在這兩種模式上構(gòu)建的。2.1 旋轉(zhuǎn)模式已知角度求三角函數(shù)這是Cordic最經(jīng)典的應(yīng)用。我們的目標(biāo)是已知一個角度 θ求 cosθ 和 sinθ。Cordic的解決方案是將一個初始向量 (x0, y0) 旋轉(zhuǎn) θ 角度得到新向量 (xn, yn)。如果我們巧妙地將初始向量設(shè)置為 (1, 0)那么旋轉(zhuǎn)之后xn 就是 cosθyn 就是 sinθ。關(guān)鍵來了Cordic不是一口氣旋轉(zhuǎn)θ而是把θ分解成一系列預(yù)先定義好的、越來越小的固定角度 δi 的和。這些固定角度滿足tan(δi) 2^{-i}。也就是說δi arctan(2^{-i})。這是一個非常重要的數(shù)列例如i0: δ0 arctan(1) 45°i1: δ1 arctan(0.5) ≈ 26.565°i2: δ2 arctan(0.25) ≈ 14.036°... 以此類推角度越來越小。現(xiàn)在旋轉(zhuǎn)過程變成了一個迭代決策過程。假設(shè)我們已經(jīng)旋轉(zhuǎn)了若干步累計角度是 z_i距離目標(biāo)角度θ還差一個差值。在第i次迭代時我們判斷當(dāng)前累計角度z_i是小于還是大于目標(biāo)θ如果 z_i θ說明我們轉(zhuǎn)得還不夠那么本次迭代就按 δi 方向轉(zhuǎn)。如果 z_i θ說明我們轉(zhuǎn)過頭了那么本次迭代就按 -δi 方向轉(zhuǎn)。這個“旋轉(zhuǎn)”操作在數(shù)學(xué)上對應(yīng)著一次矩陣乘法。旋轉(zhuǎn)δi角度的矩陣是[ cosδi, -sinδi ] [ sinδi, cosδi ]如果直接計算里面涉及cos和sin依然復(fù)雜。但Cordic的魔法在于因為 tanδi 2^{-i}我們可以把這個旋轉(zhuǎn)矩陣“提”出一個cosδi因子變成cosδi * [ 1, -tanδi ] [ tanδi, 1 ]而 tanδi 2^{-i}乘法變成了移位操作于是每次迭代的核心操作簡化為x_{i1} x_i - d_i * (y_i i) y_{i1} y_i d_i * (x_i i) z_{i1} z_i - d_i * δi其中d_i 是旋轉(zhuǎn)方向取1或-1由 z_i 與 θ 的比較結(jié)果決定 i表示向右移位i位即乘以2^{-i}??吹?jīng)]有整個迭代過程只剩下加法、減法和移位經(jīng)過N次迭代后我們得到x_N K * (x_0 * cosθ - y_0 * sinθ) y_N K * (y_0 * cosθ x_0 * sinθ) z_N ≈ 0這里的 K 是一個累計的縮放因子K ∏ cosδi ≈ 0.607253。如果我們?nèi)〕跏枷蛄?(x0, y0) (1/K, 0)那么經(jīng)過迭代后x_N 就直接等于 cosθy_N 就直接等于 sinθ。在實際應(yīng)用中我們通常初始化 x01y00迭代完成后得到的結(jié)果再乘以K或者事先將K補(bǔ)償?shù)捷斎?輸出中就能得到正確的三角函數(shù)值。注意這里的移位是算術(shù)右移對于有符號數(shù)的處理需要特別注意尤其是在Verilog等硬件描述語言中要確保符號位正確擴(kuò)展否則在迭代后期會導(dǎo)致嚴(yán)重的計算錯誤。這是一個常見的實現(xiàn)坑。2.2 向量模式已知坐標(biāo)求角度和模長旋轉(zhuǎn)模式是“給角度算坐標(biāo)”向量模式則反過來是“給坐標(biāo)算角度和模長”。即已知一個向量 (x0, y0)求其與X軸的夾角 θ 和其模長 R sqrt(x0^2 y0^2)。這個模式的應(yīng)用同樣廣泛比如直角坐標(biāo)到極坐標(biāo)的轉(zhuǎn)換、求信號的相位和幅度等。迭代過程與旋轉(zhuǎn)模式類似但決策邏輯相反。我們的目標(biāo)是把給定的向量旋轉(zhuǎn)到X軸正向上。在第i次迭代時我們判斷當(dāng)前向量的y分量如果 y_i 0說明向量在X軸上方或下方取決于約定那么我們就朝負(fù)方向旋轉(zhuǎn) δi 角使其向X軸靠攏。如果 y_i 0則朝正方向旋轉(zhuǎn) δi 角。迭代公式如下x_{i1} x_i d_i * (y_i i) y_{i1} y_i - d_i * (x_i i) z_{i1} z_i - d_i * δi注意這里x和y的更新公式與旋轉(zhuǎn)模式相比符號發(fā)生了變化這是因為旋轉(zhuǎn)的方向判斷依據(jù)從“角度差”變成了“y坐標(biāo)的符號”。經(jīng)過N次迭代后向量被旋轉(zhuǎn)到X軸上此時y_N ≈ 0 x_N ≈ K * R (R是初始向量的模長) z_N ≈ θ (初始向量與X軸的夾角)同樣這里的K是相同的縮放因子。如果我們想直接得到模長R可以在初始化時傳入 (x0, y0)迭代結(jié)束后將 x_N 除以 K 即可。2.3 兩種模式的統(tǒng)一與擴(kuò)展仔細(xì)觀察你會發(fā)現(xiàn)旋轉(zhuǎn)模式和向量模式的迭代公式非常對稱。它們可以被一個統(tǒng)一的公式描述通過一個模式控制信號mode來區(qū)分mode0(旋轉(zhuǎn)模式): d_i sign(z_i)目標(biāo)是驅(qū)動 z - 0。mode1(向量模式): d_i -sign(y_i)目標(biāo)是驅(qū)動 y - 0。這種統(tǒng)一性使得用同一套硬件電路來實現(xiàn)兩種模式成為可能大大提高了硬件資源的利用率。此外Cordic算法不僅可以用于圓周坐標(biāo)系計算sin/cos/atan通過改變每次迭代的旋轉(zhuǎn)角度δi的定義它還可以擴(kuò)展到線性坐標(biāo)系和雙曲坐標(biāo)系從而用于計算乘法、除法、開方、指數(shù)、對數(shù)等更廣泛的函數(shù)。這體現(xiàn)了Cordic算法框架的強(qiáng)大與優(yōu)美。3. 硬件實現(xiàn)關(guān)鍵從迭代到流水線理解了算法原理下一步就是如何把它變成硬件電路。Cordic的硬件實現(xiàn)主要有三種結(jié)構(gòu)迭代結(jié)構(gòu)、展開結(jié)構(gòu)和流水線結(jié)構(gòu)。選擇哪種結(jié)構(gòu)取決于你對速度、面積和功耗的要求。3.1 迭代結(jié)構(gòu)面積最優(yōu)速度最慢這是最直觀的實現(xiàn)方式。只用一套計算單元加法器、移位器和一個狀態(tài)機(jī)。狀態(tài)機(jī)控制迭代次數(shù)i每來一個時鐘周期完成一次迭代計算更新x y z寄存器。需要N個時鐘周期才能完成一次N階的Cordic計算。優(yōu)點硬件資源占用最少面積最小。缺點延遲高吞吐率低每N個周期輸出一個結(jié)果。適用場景對速度要求不高但對芯片面積或功耗極其敏感的應(yīng)用例如一些低功耗的傳感器節(jié)點。在實現(xiàn)迭代結(jié)構(gòu)時關(guān)鍵設(shè)計點是移位器的實現(xiàn)。因為每次迭代的移位位數(shù)i是變化的所以需要一個桶形移位器。桶形移位器雖然靈活但比固定移位數(shù)的移位器要消耗更多的邏輯資源。另一個細(xì)節(jié)是角度查找表ROM。δi arctan(2^{-i}) 這些固定角度值需要預(yù)先計算好存儲在一個小的ROM中根據(jù)迭代次數(shù)i來讀取。3.2 展開結(jié)構(gòu)速度最快面積最大與迭代結(jié)構(gòu)相反展開結(jié)構(gòu)將N次迭代全部展開用N級完全相同的硬件電路串聯(lián)起來。數(shù)據(jù)從第一級流入依次經(jīng)過每一級每一級都是一個完整的Cordic迭代單元包含加法器、固定移位器和角度值常數(shù)。數(shù)據(jù)流過后直接輸出結(jié)果。優(yōu)點延遲極低一個時鐘周期就能輸出結(jié)果組合邏輯路徑很長或者流水線打拍后吞吐率是每個周期一個結(jié)果。缺點硬件資源占用巨大是迭代結(jié)構(gòu)的N倍。而且由于每一級的移位位數(shù)是固定的第i級就固定移位i位所以不需要桶形移位器但需要N個不同的固定移位器。適用場景對實時性要求極高且不在乎面積的場合例如某些高性能雷達(dá)信號處理的前端。3.3 流水線結(jié)構(gòu)性能與面積的折衷這是在實際工程中最常用也是最經(jīng)典的結(jié)構(gòu)。它本質(zhì)上是展開結(jié)構(gòu)的流水線化。將N級展開的電路在每一級之間插入寄存器。這樣整個計算過程被分割成N個流水段。工作流程第一個時鐘周期初始數(shù)據(jù)進(jìn)入第一級第二個時鐘周期第一級的計算結(jié)果存入寄存器同時進(jìn)入第二級計算而新的初始數(shù)據(jù)可以進(jìn)入第一級……以此類推。經(jīng)過N個時鐘周期的“填充”后流水線達(dá)到穩(wěn)定狀態(tài)此后每個時鐘周期都會輸出一個計算結(jié)果。優(yōu)點高吞吐率穩(wěn)定后每個周期輸出一個結(jié)果非常適合處理連續(xù)的數(shù)據(jù)流。高時鐘頻率因為每一級之間的組合邏輯路徑很短只是一次加法、移位和比較所以電路可以運行在很高的時鐘頻率下。面積適中雖然用了N級硬件但因為是流水線在實際的FPGA或ASIC中可以通過資源共享和精細(xì)優(yōu)化面積小于完全的展開結(jié)構(gòu)。缺點仍然需要N套計算單元面積大于迭代結(jié)構(gòu)。并且有N個時鐘周期的初始延遲。對于FPGA設(shè)計流水線Cordic是絕配。FPGA內(nèi)部有豐富的寄存器資源和布線資源可以非常高效地實現(xiàn)這種深度流水線結(jié)構(gòu)。Xilinx和IntelAltera的IP核庫中提供的Cordic IP其核心通常就是高度優(yōu)化的流水線結(jié)構(gòu)。3.4 精度、位寬與迭代次數(shù)的權(quán)衡在硬件實現(xiàn)中有幾個關(guān)鍵參數(shù)需要仔細(xì)考量輸出精度最終結(jié)果需要多少位這決定了內(nèi)部計算所需的位寬。Cordic的誤差主要來源于兩個方面角度近似誤差由有限迭代次數(shù)引起和舍入誤差由有限數(shù)據(jù)位寬引起。通常迭代次數(shù)N每增加1精度大約增加1位二進(jìn)制位或0.3位十進(jìn)制位。要達(dá)到16位精度通常需要16次左右的迭代。數(shù)據(jù)位寬由于迭代過程中數(shù)值可能放大縮放因子K的倒數(shù)約為1.647輸入位寬需要預(yù)留增長空間防止溢出。同時在迭代后期進(jìn)行右移時低位信息會丟失因此初始位寬需要比輸出精度多幾位保護(hù)位來保證最終精度。一個經(jīng)驗公式是內(nèi)部位寬 輸出精度位數(shù) log2(N) 2~3。迭代次數(shù)NN越大精度越高但硬件消耗也越大計算延遲也越長。需要在精度和資源/速度之間取得平衡。對于大多數(shù)應(yīng)用N16是一個常用值它能提供足夠好的精度。角度格式Cordic處理的角度范圍通常在[-π, π)或[-99.9°, 99.9°]之間因為arctan(2^0)45°兩次旋轉(zhuǎn)才到90°。對于全圓周的角度需要先進(jìn)行象限預(yù)處理將任意角度映射到第一象限然后使用Cordic計算最后再根據(jù)象限恢復(fù)符號。這個預(yù)處理和后處理模塊是必不可少的。4. 在FPGA中的實戰(zhàn)Verilog實現(xiàn)與IP核使用紙上得來終覺淺絕知此事要躬行。我們以一個具體的例子在FPGA上實現(xiàn)一個計算sin/cos的流水線Cordic核并對比手寫代碼與使用官方IP核的差異。4.1 自定義流水線Cordic Verilog實現(xiàn)假設(shè)我們需要一個計算sin/cos的模塊輸入角度為16位有符號整數(shù)-32768~32767對應(yīng) -π~π輸出cos和sin為16位有符號整數(shù)-32768~32767對應(yīng) -1~1。迭代次數(shù)N16。首先我們需要預(yù)計算角度查找表arctan_lut。這里將π映射為32768所以arctan(2^{-i})也需要按此比例量化。// 預(yù)定義迭代次數(shù)和位寬 parameter N 16; parameter WIDTH 16; // 輸入輸出位寬 parameter INTERNAL_WIDTH 20; // 內(nèi)部計算位寬留出保護(hù)位 // 角度查找表存儲 arctan(2^{-i}) * (32768/π)已取整 localparam logic signed [INTERNAL_WIDTH-1:0] arctan_lut [0:N-1] { 20sd25735, // i0, arctan(1) 45° π/4 ≈ 0.7854, 0.7854*(32768/π)≈8192實際需精確計算 20sd15192, // i1, arctan(0.5) 20sd8027, // i2 20sd4075, // i3 // ... 此處省略中間項實際需全部列出 20sd1 // i15 };接下來是核心的流水線級模塊。每一級完成一次迭代。module cordic_stage #( parameter STAGE_IDX 0, parameter WIDTH 16, parameter INTERNAL_WIDTH 20 )( input wire clk, input wire rst_n, // 上一級的結(jié)果 input wire signed [INTERNAL_WIDTH-1:0] x_in, input wire signed [INTERNAL_WIDTH-1:0] y_in, input wire signed [INTERNAL_WIDTH-1:0] z_in, // 本級結(jié)果 output reg signed [INTERNAL_WIDTH-1:0] x_out, output reg signed [INTERNAL_WIDTH-1:0] y_out, output reg signed [INTERNAL_WIDTH-1:0] z_out ); // 本級固定的旋轉(zhuǎn)角度 wire signed [INTERNAL_WIDTH-1:0] angle arctan_lut[STAGE_IDX]; // 移位操作根據(jù)級數(shù)進(jìn)行算術(shù)右移 wire signed [INTERNAL_WIDTH-1:0] x_shifted x_in STAGE_IDX; wire signed [INTERNAL_WIDTH-1:0] y_shifted y_in STAGE_IDX; // 旋轉(zhuǎn)方向判斷旋轉(zhuǎn)模式下看z的符號 wire direction (z_in 0); always (posedge clk or negedge rst_n) begin if (!rst_n) begin x_out 0; y_out 0; z_out 0; end else begin if (direction) begin // z_in 0 順時針旋轉(zhuǎn)負(fù)方向 x_out x_in - y_shifted; y_out y_in x_shifted; z_out z_in - angle; end else begin // z_in 0 逆時針旋轉(zhuǎn)正方向 x_out x_in y_shifted; y_out y_in - x_shifted; z_out z_in angle; end end end endmodule最后將N個stage實例化并串聯(lián)起來構(gòu)成完整的流水線。還需要注意初始化為了補(bǔ)償縮放因子K我們通常將初始x0設(shè)為1/K的量化值也稱為“增益補(bǔ)償”。一個常見的技巧是直接將x0初始化為一個較大的常數(shù)如0.607253的量化值這樣迭代結(jié)束后x和y輸出值本身就近似是cos和sin無需后乘?;蛘咴谧詈笠患壿敵龊蟪艘砸粋€固定的補(bǔ)償系數(shù)。實操心得在Verilog中實現(xiàn)移位時強(qiáng)烈建議使用算術(shù)右移操作符而不是邏輯右移這對于有符號數(shù)的正確處理至關(guān)重要。邏輯右移會補(bǔ)0會破壞負(fù)數(shù)的表示。這是我早期實現(xiàn)時踩過的一個坑導(dǎo)致在計算第二象限和第三象限角度時結(jié)果完全錯誤。4.2 使用Vivado/Xilinx Cordic IP核對于大多數(shù)項目除非有極致的定制化需求否則直接使用FPGA廠商提供的IP核是更高效、更可靠的選擇。以Xilinx Vivado中的Cordic IP核6.0版本為例其配置非常靈活。關(guān)鍵配置步驟選擇功能在“Functional Selection”中選擇“Sin and Cos”。IP核也支持其他多種函數(shù)如平移、雙曲函數(shù)等。選擇架構(gòu)在“Architectural Configuration”中選擇“Parallel”并行即展開或流水線結(jié)構(gòu)。還可以選擇“Word Serial”字串行類似迭代結(jié)構(gòu)。對于高性能應(yīng)用必定選擇“Parallel”。流水線模式在“Pipelining Mode”中選擇“Optimal”或“Maximum”。Optimal會在保證性能的前提下優(yōu)化資源Maximum則會最大化流水線級數(shù)以獲得最高時鐘頻率。數(shù)據(jù)格式這是配置的核心。Input/Output Width設(shè)置輸入輸出位寬如16位。Phase Format選擇“Radians”或“Scaled Radians”。對于硬件常用“Scaled Radians”即用定點數(shù)表示例如將π映射為1或者映射為2^N。IP核會自動處理縮放。Data Format選擇“Signed Fraction”有符號小數(shù)。這意味著輸入輸出的范圍是[-1, 1)。這對于表示三角函數(shù)值非常自然。精度設(shè)置通過“Iterations”和“Precision”選項來控制??梢赃x擇“Automatic”讓工具決定也可以手動指定迭代次數(shù)和輸出舍入模式。配置完成后IP核會生成一個包含phase_in輸入角度、sine、cosine輸出以及握手信號valid_in/valid_out的模塊。其內(nèi)部已經(jīng)完美處理了象限映射、增益補(bǔ)償和流水線控制。IP核 vs 手寫代碼優(yōu)勢IP核經(jīng)過高度優(yōu)化在時序、資源利用率和可靠性上通常優(yōu)于手寫代碼。它自動處理了所有邊界情況和精度細(xì)節(jié)并提供標(biāo)準(zhǔn)的AXI-Stream接口易于集成到更大的系統(tǒng)中。劣勢靈活性稍差如果遇到非常特殊的定制化需求比如非標(biāo)準(zhǔn)的旋轉(zhuǎn)序列、混合模式控制可能不如手寫代碼方便。另外IP核通常是加密或網(wǎng)表形式的不利于學(xué)習(xí)和深度調(diào)試。對于產(chǎn)品開發(fā)我強(qiáng)烈推薦使用IP核。對于學(xué)習(xí)、研究或者有極端定制需求則可以從手寫實現(xiàn)開始。5. 性能優(yōu)化與高級話題當(dāng)你掌握了基本的Cordic實現(xiàn)后可能會追求更高的性能或更特殊的應(yīng)用。這里有幾個進(jìn)階方向。5.1 擴(kuò)展計算范圍與精度提升基本的圓周Cordic輸入角度范圍有限。為了計算全圓周的函數(shù)必須進(jìn)行象限預(yù)處理。算法如下將輸入角度θ映射到[0, π/2]區(qū)間并記錄原始象限信息。利用三角函數(shù)的周期性sin(π/2 - θ) cosθ等將第一象限的[0, π/2]映射到Cordic核心能處理的[0, π/4]或[-π/4, π/4]范圍。Cordic核心計算映射后小角度的sin/cos值。根據(jù)記錄的象限信息對核心計算結(jié)果進(jìn)行符號恢復(fù)和值交換sin變cos等。精度方面除了增加迭代次數(shù)還可以采用雙步Cordic或角度重編碼技術(shù)。傳統(tǒng)的Cordic每一步旋轉(zhuǎn)方向只有1或-1。雙步Cordic允許每一步旋轉(zhuǎn)方向在{-1, 0, 1}中選擇其中0意味著跳過該次旋轉(zhuǎn)。通過更靈活的角度逼近策略可以用更少的迭代次數(shù)達(dá)到相同的精度或者用相同的迭代次數(shù)獲得更高的精度但控制邏輯會變得復(fù)雜。5.2 線性與雙曲坐標(biāo)系下的應(yīng)用Cordic的威力不止于三角函數(shù)。通過修改旋轉(zhuǎn)角度δi的定義它可以工作在另外兩種坐標(biāo)系下線性坐標(biāo)系令 δi 2^{-i}。在這種模式下Cordic可以實現(xiàn)乘法和除法運算。例如在向量模式下給定(x, y)經(jīng)過線性Cordic迭代最終z的收斂值就是y/x除法而x的收斂值與初始值有關(guān)可用于乘法。雙曲坐標(biāo)系令 tanh(δi) 2^{-i}。這時Cordic可以計算雙曲函數(shù) sinh, cosh, tanh進(jìn)而通過數(shù)學(xué)變換計算平方根、指數(shù)函數(shù)和對數(shù)函數(shù)。例如計算 sqrt(A) 時可以設(shè)置初始向量為 (A1/4, A-1/4)在雙曲Cordic的向量模式下迭代最終的x值就會收斂到 sqrt(A)。計算 ln(w) 也有類似的巧妙設(shè)置。這些擴(kuò)展使得Cordic成為一個真正的“數(shù)學(xué)函數(shù)計算工具箱”在需要多種超越函數(shù)計算的專用硬件中極具價值。5.3 在具體系統(tǒng)中的應(yīng)用實例讓我們看兩個具體的例子感受Cordic如何融入真實系統(tǒng)。實例一數(shù)字下變頻中的數(shù)控振蕩器在軟件無線電或通信接收機(jī)中需要產(chǎn)生一個本振信號來與接收信號混頻實現(xiàn)下變頻。這個本振信號就是一個頻率可調(diào)的正弦波和余弦波對cos(2πft) 和 sin(2πft)。用Cordic實現(xiàn)NCO是最經(jīng)典的應(yīng)用之一。用一個相位累加器生成線性遞增的相位字對應(yīng)角度θ。將這個相位字作為Cordic旋轉(zhuǎn)模式的輸入。Cordic輸出連續(xù)的cosθ和sinθ樣本即所需的正交本振信號。 這種方法產(chǎn)生的信號純度SFDR高且頻率分辨率極細(xì)由相位累加器位寬決定非常適合高性能數(shù)字調(diào)制解調(diào)。實例二電機(jī)控制中的Park/Clarke逆變換在電機(jī)的矢量控制FOC中需要將旋轉(zhuǎn)坐標(biāo)系下的電壓矢量轉(zhuǎn)換回靜止坐標(biāo)系。這涉及到角度θ的sin和cos值。由于電機(jī)控制環(huán)路對實時性要求極高控制周期通常在幾十到幾百微秒且常在DSP或FPGA中實現(xiàn)使用Cordic來計算sin/cos比查表法占用內(nèi)存大或級數(shù)展開計算復(fù)雜更具優(yōu)勢。將電角度作為輸入Cordic能在固定的、很短的延遲內(nèi)輸出高精度的sin/cos值確??刂扑惴ǖ目焖賵?zhí)行。5.4 資源評估與選型建議在FPGA項目中決定是否使用以及如何使用Cordic時需要進(jìn)行簡單的資源評估。查找表法需要存儲一個周期的正弦波樣點。對于16位精度、16位地址深度的查找表需要2^16 * 16bit ≈ 128KB的存儲空間。這對于FPGA內(nèi)部的Block RAM來說是一筆不小的開銷且精度受表大小限制。多項式近似如泰勒展開或切比雪夫逼近需要多個乘法器和加法器級聯(lián)。計算一個sin值可能需要4-5次乘加操作在高速下會成為時序瓶頸。Cordic法主要消耗邏輯資源加法器、移位器、寄存器和少量ROM存儲arctan表。一個16級流水線Cordic在中等規(guī)模FPGA上實現(xiàn)可能消耗幾百個LUT和寄存器。選型建議需要同時計算sin和cosCordic具有天然優(yōu)勢它幾乎“免費”地同時給出兩個值。數(shù)據(jù)流連續(xù)要求高吞吐率流水線Cordic是最佳選擇。資源極度緊張速度要求低可以考慮迭代結(jié)構(gòu)的Cordic或者精度較低的查找表。需要計算多種函數(shù)考慮可配置的Cordic IP核它可以通過模式切換來計算多種函數(shù)比實現(xiàn)多個專用模塊更節(jié)省資源。最后無論采用哪種實現(xiàn)充分的仿真測試都必不可少。必須覆蓋輸入角度的全范圍特別是邊界和象限切換點驗證輸出精度是否滿足系統(tǒng)要求例如計算信噪比SNR或有效位數(shù)ENOB。Cordic是一個優(yōu)雅而強(qiáng)大的工具理解其內(nèi)核善用其模式就能在數(shù)字設(shè)計的世界里用最簡單的操作解決復(fù)雜的數(shù)學(xué)問題。