為什么會(huì)出現(xiàn)CORDIC算法呢? 對(duì)于三角函數(shù)計(jì)算,理論接觸較多的應(yīng)該就是級(jí)數(shù)展開的方法 ,通過逼近的方式進(jìn)行計(jì)算,比如泰勒級(jí)數(shù)。但是因?yàn)檫@種多項(xiàng)式函數(shù)在計(jì)算的過程中需要大量使用到浮點(diǎn)數(shù)計(jì)算,而對(duì)于缺乏硬件乘法器的器件而言這種計(jì)算方式的實(shí)現(xiàn)是很困難的。有了問題,自然就會(huì)有人出來解決,1959年J. Volder就首次提出了CORDIC算法,只需要進(jìn)行移位和加減運(yùn)算的快速算法。 CORDIC算法能干什么? 這里只介紹關(guān)于三角函數(shù)方面CORDIC的作用: 1、得到坐標(biāo)軸上一點(diǎn)的角度(相位信息); 2、得到坐標(biāo)軸上一點(diǎn)對(duì)應(yīng)的 和 (幅值信息)。 ps:使用cordic計(jì)算幅值的方法不好,可以選擇更好的,比如查找表的方法,因此這里不再過多介紹計(jì)算幅值的方法。 CORDIC算法的原理是什么? 對(duì)于坐標(biāo)軸上點(diǎn)的相位,就是和x軸正半軸(后面統(tǒng)稱x軸)的夾角。求一個(gè)點(diǎn)的相位也就是將要求的點(diǎn)進(jìn)行旋轉(zhuǎn),當(dāng)點(diǎn)旋轉(zhuǎn)到與x軸第一次重合時(shí),旋轉(zhuǎn)過的相位大小就是這個(gè)點(diǎn)的相位。CORDIC算法通俗來講就是把這個(gè)旋轉(zhuǎn)過程變成了“離散”旋轉(zhuǎn),每次旋轉(zhuǎn)特定的角度,旋轉(zhuǎn)N次。在旋轉(zhuǎn)過程中不停的判斷,如果落在x軸就停止,如果發(fā)現(xiàn)在x軸上方就逆時(shí)針旋轉(zhuǎn)(相位增加),如果發(fā)現(xiàn)在x軸下方就順時(shí)針旋轉(zhuǎn)(相位減小),將旋轉(zhuǎn)過程中的相位變化進(jìn)行累加,最終就可以得到一個(gè)點(diǎn)的相位。這里可以總結(jié)這個(gè)旋轉(zhuǎn)過程有兩點(diǎn)要求: 1、旋轉(zhuǎn)過程中能夠?qū)崟r(shí)得到y(tǒng)的值,因?yàn)槲覀冃枰獃的值來判斷我們旋轉(zhuǎn)的點(diǎn)是否到達(dá)了x軸(點(diǎn)在第一象限的話,y=0則表明點(diǎn)落在了x軸上); 2、每次旋轉(zhuǎn)需要知道旋轉(zhuǎn)的角度,以便于計(jì)算相位。 有了上面的原理,現(xiàn)在需要做的就是得到一個(gè)點(diǎn)在坐標(biāo)軸上旋轉(zhuǎn)和相位的關(guān)系式。 首先假設(shè)坐標(biāo)軸上有任意一點(diǎn) 經(jīng)過旋轉(zhuǎn)后得到 ,如下圖所示:  因?yàn)槭切D(zhuǎn),幅值相等,則可以推導(dǎo)公式:  ps:這里介紹一種方便的方法推導(dǎo),使用復(fù)變函數(shù)推導(dǎo)相位旋轉(zhuǎn),有興趣化簡一下即可對(duì)應(yīng)上面的公式:  這里可以把( )看作是輸入點(diǎn),上面的公式的確滿足了旋轉(zhuǎn)的第一個(gè)要求,也就是可以實(shí)時(shí)計(jì)算出旋轉(zhuǎn)后的y值,但是這個(gè)公式中包含了 ,它們計(jì)算起來已經(jīng)很復(fù)雜了,怎么辦呢?接下來就是取巧的過程: 1、對(duì)于 而言,只是影響y值的大小,但是并不影響判斷點(diǎn)是否落在x軸上,因此這里直接將 省略; 2、對(duì)于任意的 的確不好求其結(jié)果,但是我們可以限制每次旋轉(zhuǎn)的角度,使 的值是特殊值,這里選擇的 值是(1,1/2,1/4,...,1/(2^n))。這里值的選擇是方便硬件進(jìn)行計(jì)算,可以直接進(jìn)行算術(shù)右移,最大限度簡化操作。 3、既然 的值有了,那么對(duì)于每次旋轉(zhuǎn)的角度 值而言也就是確定的了(45,26.565051177078,14.0362434679265等等)。對(duì)于實(shí)際工程而言,旋轉(zhuǎn)次數(shù)(迭代)肯定是有限的,這個(gè)次數(shù)是和浮點(diǎn)數(shù)轉(zhuǎn)化為定點(diǎn)數(shù)精度有關(guān)的。比如轉(zhuǎn)化精度是浮點(diǎn)數(shù)1轉(zhuǎn)化為定點(diǎn)數(shù)256,則45°對(duì)應(yīng)的定點(diǎn)數(shù)是45*256=11520,且能分辨的最小 值是1/256=0.00390625°。因?yàn)橛凶钚》直媛氏拗?,如果角度再小也就沒有意義了,因此如果轉(zhuǎn)化倍數(shù)是256,則最大迭代次數(shù)是15次。 ps:CORDIC算法只是一個(gè)近似計(jì)算,也就是說15次迭代后點(diǎn)不一定是落在x軸上的。轉(zhuǎn)化精度也高,迭代次數(shù)越多,結(jié)果越準(zhǔn)確。 有了上面的推論,相位旋轉(zhuǎn)公式可以寫成: 
對(duì)于旋轉(zhuǎn)過程中相位累加的公式可以寫成:  這里的z是有初始值的,初始值和象限有關(guān),第一象限和第四就是z=0(x>0),第二象限就是z=90(y>0),第三象限就是z=-90(y<>在下面給出的示例代碼中,是默認(rèn)輸入值x>0的。如果想將程序擴(kuò)展到四個(gè)象限,判斷輸入點(diǎn)的象限,然后取z的不同初始值和對(duì)輸入點(diǎn)進(jìn)行象限變換(比如輸入點(diǎn)在第二象限(x0,y0),則將點(diǎn)逆時(shí)針旋轉(zhuǎn)90°得到新點(diǎn)(y0,-x0))即可,有興趣的可以進(jìn)行添加。 以下是示例代碼: library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_signed.all; use ieee.std_logic_arith.all; entity cordic is generic( w1 : integer := 16; --幅值位寬 w2 : integer := 16; --角度位寬 w3 : integer := 18 --中間變量 ); port( clk : in std_logic; rstn : in std_logic; data_En : in std_logic; x_In : in std_logic_vector(w1-1 downto 0); y_In : in std_logic_vector(w1-1 downto 0); zn : out std_logic_vector(w2-1 downto 0) ); end cordic;
architecture beha of cordic is type array15x8 is array(0 to 14) of std_logic_vector(w2-1 downto 0); constant z : array15x8 := (x'2D00',x'1A91',x'0E09',x'0720',x'0394',x'01CA',x'00E5', x'0073',x'0039',x'001D',x'000E',x'0007',x'0004',x'0002',x'0001'); signal phi : std_logic_vector(w2-1 downto 0) := (others => '0'); signal x0,y0 : std_logic_vector(w3-1 downto 0); signal im,re : std_logic_vector(w3-1 downto 0); signal n : integer range 0 to 14; type state is (s0,s1); signal Cstate : state := s0; begin process(n,y0,x0) begin im <> to_stdlogicvector(to_bitvector(y0) sra n); re <> to_stdlogicvector(to_bitvector(x0) sra n); end process; process(clk,rstn) begin if rstn='0' then phi <> (others => '0'); elsif rising_edge(clk) then case Cstate is when s0 => if data_En='1' then Cstate <> s1; n <> 0; x0 <> sxt(x_In,w3); y0 <> sxt(y_In,w3); else n <> n; end if; when s1 => if signed(y0)=0 then x0 <> x0; y0 <> y0; elsif (y0(7))='1' then x0 <> x0 - im; y0 <> y0 + re; phi <> phi - z(n); else x0 <> x0 + im; y0 <> y0 - re; phi <> phi + z(n); end if; if n14 then n <> n + 1; else Cstate <> s0; end if; when others => Cstate <> s0; end case; zn <> phi; end if; end process; end beha;
|