CORDIC(COordinate Rotation Digital Computer) algoritması bir noktayı orjin etrafında döndürerek , , , , gibi fonksiyonları ve hatta , , gibi fonksiyonların da hesaplanmasını sağlayan bir algoritmadır.
Bu yazımda (Magnitude) ve (Faz Açısı) hesaplaması üzerinde duracağım.
Kartezyen koordinat sisteminde bir noktası olsun. Bu noktanın magnitude değeri 'dir.
açısını azalttığımızda değeri olduğundan dolayı 'a yaklaşacaktır. değeri 'a yaklaşınca magnitude değeri de 'ye yaklaşacaktır. açısının değişmesi magnitude değerini etkilemediği için diyebiliriz.
noktasını orjin etrafında(-ekseni etrafında) döndürmek için dönüşüm matrisi kullanılır.
Noktaları saat yönünün tersinde döndürmek istersek kullanacağımız dönüşüm matrisi:
Elimizde değerlerinin tutulduğu bir tablomuz olsun.
işlemi bilgisayarlarda basit bit kaydırma ile yapılabilir.
Koordinatları orjin etrafında saat yönünün tersinde döndürürken açısı yani faz açısının değerini de her bir dönüşümden sonrada çıkarmamız gerekir.
Eğer noktaları orjin etrafında saat yönünde döndürmek istersek:
Noktaları hangi yöne döndüreceğimizi 'nin değeri ile anlayabiliriz. Eğer ise noktası koordinat sisteminin sağ alt çeyreğindedir ve saat yönünün tersinde dönüşüm yapmamız gerekir. ise de saat yönünde dönüşüm yapmamız gerekir.
Genel olarak algoritmayı şu şekilde yazabiliriz:
değeri her iterasyonda gelen kazançtır. Bütün işlemler bittikten sonra değeri bütün iterasyonların ters kazancı olan ile çarpılarak kazanç 'e düşürülür.
FPGA üzerinde uygulanması
Birden çok nokta ile çalışırken, her bir nokta için teker teker algoritmayı uygulamak fazla clock cycle'ı harcamamıza neden olur. Pipeline tasarım ile bu soruna çözüm üretilebilir.
Pipeline yapı CORDIC çekirdeklerinin birbirine seri bağlanması ile yapılır. Her bir çekirdek aşağıdaki gibidir:
---------------------------------------------------------- -- CORDIC_VectorMode.vhd -- Bertan Taşkın -- 2.6.2017 -- -- Pipeline tabanlı çalışmaya tasarlanmış, Vectör modunda -- çalışan CORDIC çekirdeği modülü. İşlenecek veri boyutu -- generic kısmındaki Data_Width ile belirlenir. Pipeline'ın -- hangi adımında olduğu ise PipelineStage ile belirlenir. -- Clock sinyalinin yükselen kenarı ile işlenmiş X,Y,Zin -- verileri pipeline'daki bir sonraki çekirdeğe aktarılır. -- -- Faz açısı 1 işaret biti ve Data_Width-1 kadar fraction -- bitinden oluşur. Radyan cinsinden çıkış verir. -- -- Data_Width değerinin alabileceği en yüksek değer integer -- genişliği olan 32'dir. -- -- ---------------------------------------------------------- --Kütüphaneler library IEEE; use IEEE.STD_LOGIC_1164.ALL; USE ieee.numeric_std.ALL; use ieee.math_real.all; entity CORDIC_VectorMode is generic(Data_Width : integer := 16; --İşlenecek Veri Genişliği PipelineStage : integer := 8); --Çekirdeğin Bulunduğu Pipeline Seviyesi port(Clk : in std_logic; --Clock Girişi Xin, Yin, Zin : in std_logic_vector(Data_Width - 1 downto 0); --Önceki CORDIC Çekirdeğinden Gelen X, Y, Z Girişleri Xout, Yout, Zout : out std_logic_vector(Data_Width - 1 downto 0)); --Sonraki CORDIC Çekirdeğine Gidecek X, Y, Z Çıkışları end CORDIC_VectorMode; architecture Behavioral of CORDIC_VectorMode is signal XS, YS, ZS : signed(Data_Width - 1 downto 0); --Phase hesabında kullanılacak arctanjant değeri --[-0.5, 0.5] aralığına normalize ediliyor constant AtanReal : real := ARCTAN(real(2) ** real(-PipelineStage)) * real(2**(Data_Width - 1)) / MATH_PI; signal Atan : signed(Data_Width - 1 downto 0); begin --İşlemlerin daha kolay yapılabilmesi için giriş --sinyalleri signed tipine dönüştürülüyor XS <= signed(Xin); YS <= signed(Yin); ZS <= signed(Zin); --Arctanjant değeri de signed tipine dönüştürülüyor Atan <= to_signed(integer(AtanReal), Data_Width); --CORDIC işleminin gerçekleştiği process process(Clk) begin --Clock sinyalinin yükselen kenarı ile Pipeline ilerler if rising_edge(Clk) then --Eğer Ys değeri 0'dan küçük ise koordinatlar saat yönünün --tersinde döndürülüyor if Ys < 0 then --Signed tipinden tekrar std_logic_vector tipine dönüştürülüyor Xout <= std_logic_vector(XS - shift_right(YS, PipelineStage)); Yout <= std_logic_vector(YS + shift_right(XS, PipelineStage)); Zout <= std_logic_vector(ZS - Atan); --Değil ise saat yönünde döndürülüyor else Xout <= std_logic_vector(XS + shift_right(YS, PipelineStage)); Yout <= std_logic_vector(YS - shift_right(XS, PipelineStage)); Zout <= std_logic_vector(ZS + Atan); end if; end if; end process; end Behavioral;
Quartus II tarafından oluşturulan RTL şeması:
Çekirdeklerin seri bağlanmış hali:
---------------------------------------------------------- -- CORDIC_MagPha.vhd -- Bertan Taşkın -- 2.6.2017 -- -- CORDIC algoritmasını kullanarak (X,Y) noktası için -- magnitude'u ve faz açısını hesaplayan modül. Pipeline -- yapıya sahiptir. İşlenecek veri boyutu generic kısmındaki -- Data_Width ile belirlenir. Pipeline uzunluğu ise PipelineLength -- ile belirlenir. -- -- Modül pipeline tabanlı olduğundan dolayı, yeni veriler -- PipelineStage kadar gecikmeli hesaplanır. Sıralı giren veri -- sayısı ne kadar fazla olursa verimlilik de o kadar yüksek olur. -- -- N veri için toplam işlem süresi = PipelineStage + N - 1 Cycle -- -- Giriş verilerinde kesinlikle fraction part bırakılmalıdır. -- Toplam pipeline adım sayısı, giriş verilerinin fraction bit -- sayısından 1 fazlasından fazla olması yüksek pipeline -- adımlarının hassasiyetini düşürür. -- -- Magnitude = MagnitudeOut / 2^Fraction_Bit_Sayısı -- -- Faz açısı 1 işaret bitinden ve Data_Width-1 fraction bitinden -- oluşur. [-0.5, 0.5] aralığında radyan cinsindendir. -- -- Faz açısı = PhaseOut / 2^(Data_Width-1) Radyan -- = PhaseOut / 2^(Data_Width-1) * 180 Derece -- ---------------------------------------------------------- --Kütüphaneler library IEEE; use IEEE.STD_LOGIC_1164.ALL; USE ieee.numeric_std.ALL; use ieee.math_real.all; entity CORDIC_MagPha is generic(Data_Width : integer := 16; --İşlenecek Veri Genişliği PipelineLength : integer := 9); --Pipeline Uzunluğu(Derinliği) port(Clk : in std_logic; --Clock Girişi X, Y : in std_logic_vector(Data_Width - 1 downto 0); --X, Y Girişleri MagnitudeOut : out std_logic_vector(Data_Width - 1 downto 0); --Magnitude Çıkışı PhaseOut : out std_logic_vector(Data_Width - 1 downto 0)); --Faz açısı Çıkışı end CORDIC_MagPha; architecture Behavioral of CORDIC_MagPha is --CORDIC algoritmasının kazancını 1'e düşürecek Kn değerini --hesaplayan fonksiyon --Kn = pi i:0->n 1/kök(1+2^(-2i)) function Kn(n : integer) return signed is variable X : signed(Data_Width - 1 downto 0) := (others=>'0'); variable K : real := 1.0; begin --Her bir CORDIC çekirdeğinin kazancının tersi çarpılarak --Kn değeri hesaplanır a : for i in 0 to n loop K := K / SQRT((real(1) + real(2) ** real(-2 * i))); end loop; --K sayısı, Data_Width kadar fraction bit sayısına sahip --fixed-point sayıya dönüştürülüyor K := K * real(2**(Data_Width - 1)); X := to_signed(integer(K), Data_Width); return X; end Kn; --Vectör modundaki CORDIC çekirdeği component CORDIC_VectorMode generic(Data_Width : integer := 16; --İşlenecek Veri Genişliği PipelineStage : integer := 8); --Çekirdeğin Bulunduğu Pipeline Seviyesi port(Clk : in std_logic; --Clcok Girişi Xin, Yin, Zin : in std_logic_vector(Data_Width - 1 downto 0); --Önceki CORDIC Çekirdeğinden Gelen X, Y, Z Girişleri Xout, Yout, Zout : out std_logic_vector(Data_Width - 1 downto 0)); --Sonraki CORDIC Çekirdeğine Gidecek X, Y, Z Çıkışları end component; type VectorArray is array(PipelineLength downto 0) of std_logic_vector(Data_Width - 1 downto 0); signal XBus, YBus, ZBus : VectorArray; begin --CORDIC algoritmasının doğru çalışması için girilen koordinatların --koordinat ekseninin sağ düzleminde(-pi/2 < açı < pi/2) olması gerekir. --Eğer X koordinatı negatif ise (X,Y) noktası koordinat ekseninin sol --tarafındadır. Bunu düzeltmek için sonucu değiştirmeyen (-X,-Y) noktası --ile işleme devam edilir. with X(Data_Width - 1) select XBus(0) <= not X when '1', X when others; with X(Data_Width - 1) select YBus(0) <= not Y when '1', Y when others; --İşlem öncesi (X,Y) noktası döndürülmeyeceğinden Z açısı 0 ile başlar ZBus(0) <= (others=>'0'); --Son CORDIC çekirdeğinden çıkan Magnitude değeri, algoritmadan --kaynaklanan kazancı 1'e çekmek için Kn ile çarpılır MagnitudeOut <= std_logic_vector(shift_right(signed(XBus(PipelineLength)) * Kn(PipelineLength - 1), Data_Width - 1))(Data_Width - 1 downto 0); --Faz açısında herhangi bir ek işleme gerek yoktur PhaseOut <= ZBus(PipelineLength); --CORDIC çekirdeklerinin üretilmesi --Bütün CORDIC çekirdekleri birbirine seri bağlanır(Pipeline yapısı) U1: for i in 0 to PipelineLength - 1 generate CORDIC_Core: CORDIC_VectorMode generic map(Data_Width, i) port map(Clk, XBus(i), YBus(i), ZBus(i), XBus(i + 1), YBus(i + 1), ZBus(i + 1)); end generate; end Behavioral;
RTL şeması:
8 biti fraction olmak üzere 16-bit veri genişliği ve 9 pipeline derinliğine sahip uygulamanın ModelSim ile gerçekleştirilen testi:
Giriş:
Çıkış:
Gerçek değer:
2.Veri:
Giriş:
Çıkış:
Gerçek değer:
3.Veri:
Giriş:
Çıkış:
Gerçek değer:
4.Veri:
Giriş:
Çıkış:
Gerçek değer: