CORDIC Algoritması ve FPGA Üzerinde Uygulaması

CORDIC yani COordinate Rotation DIgital Computer, bilgisayarlarda kullanılan ve trigonometrik fonksiyonları kısa yoldan en az hata ile hesaplamada kullanılan bir algoritmadır. Algoritmanın temeli, bir noktanın orjin etrafında gitgide azalan bir açı ile döndürülmesinden gelir. \(\mathbf{R}\), dönüşüm matrisi olmak üzere yapılan işlemleri matematiksel olarak şu şekilde yazabiliriz:

\(\mathbf{v_i}=\begin{bmatrix}x_i\\y_i\end{bmatrix}\)

\(\begin{align*}\mathbf{v_{i+1}}=\mathbf{R}(\theta)\mathbf{v_i}=\begin{bmatrix}\cos(\theta)&-\sin(\theta)\\\sin(\theta)&\cos(\theta)\end{bmatrix}\begin{bmatrix}x_i\\y_i\end{bmatrix}\end{align*}\)

Dönüşüm matrisindeki \(\sin\) ve \(\cos\) ifadelerini \(\tan\) cinsinden yazalım.

\(\begin{align*}\sin(\theta)=\frac{\tan(\theta)}{\sqrt{1+\tan^2(\theta)}}\end{align*}\)

\(\begin{align*}\cos(\theta)=\frac{1}{\sqrt{1+\tan^2(\theta)}}\end{align*}\)

\(\begin{align*}\mathbf{R}(\theta)=\frac{1}{\sqrt{1+\tan^2(\theta)}}\begin{bmatrix}1&&-\tan(\theta)\\\tan(\theta)&&-1\end{bmatrix}\end{align*}\)

Elimizde \(i=0,1,2,\dots\) olmak üzere \(\tan(2^{-i})\) ve \(\frac{1}{\sqrt{1+\tan^2(2^{-i})}}\) değerlerinden oluşan tablolarımız olsun. Bu tabloları kullanarak istediğimiz değerdeki \(\sin\) ve \(\cos\) değerlerini bulabiliriz.

\(\begin{align*}\mathbf{v_{i+1}}=K_{i}\mathbf{R}(2^{-i})\mathbf{v_{i}}\end{align*}\)

\(\begin{align*}\mathbf{v_{i+1}}=\frac{1}{\sqrt{1+\tan^2({\sigma}2^{-i})}}\begin{bmatrix}1&&-\tan({\sigma}2^{-i})\\\tan({\sigma}2^{-i})&&-1\end{bmatrix}\begin{bmatrix}x_{i}\\y_{i}\end{bmatrix}\end{align*}\)

\(\begin{align*}\mathbf{v_{i+1}}=\frac{1}{\sqrt{1+\tan^2(2^{-i})}}\begin{bmatrix}1&&-{\sigma}\tan(2^{-i})\\{\sigma}\tan(2^{-i})&&-1\end{bmatrix}\begin{bmatrix}x_{i}\\y_{i}\end{bmatrix}\end{align*}\)

\(\begin{align*}x_{i+1}=K_ix_{i}-{\sigma}K_i\tan(2^{-i})y_{i}\end{align*}\)

\(\begin{align*}y_{i+1}=K_iy_{i}+{\sigma}K_i\tan(2^{-i})x_{i}\end{align*}\)

Burada \(\sigma\) dönmenin yönünü belirtir. \(-1\) ya da \(1\) değerlerinden birini alabilir. Bu işlemler ne kadar tekrar edilirse istenilen \(\sin\) ve \(\cos\) değerlerine o kadar yaklaşılır.

Bilgisayarlarda çarpma işlemi yapmak, kaydırma işlemi yapmaktan daha fazla zaman harcar. Bu yüzden \(tan(2^{-i})\) yerine işlemleri daha kolay hesaplanabilecek hale getiren \(2^{-i}\) ifadesini yazalım ve \(tan^{-1}(2^{-i})\) değerlerini bir tabloda saklayalım.

\(\begin{align*}\mathbf{R}(\gamma)=\frac{1}{\sqrt{1+2^{-2i}}}\begin{bmatrix}1&&-2^{-i}\\2^{-i}&&-1\end{bmatrix}\end{align*}\)

\(\begin{align*}\gamma=tan^{-1}(2^{-i})\end{align*}\)

\(\begin{align*}\mathbf{v_{i+1}}=2^{-i}K_{i}\,\mathbf{v_{i}}\end{align*}\)

\(\begin{align*}x_{i+1}=K_i\,x_{i}-{\sigma}\,K_i\,2^{-i}\,y_{i}\end{align*}\)

\(\begin{align*}y_{i+1}=K_i\,y_{i}+{\sigma}\,K_i\,2^{-i}\,x_{i}\end{align*}\)

Her işlemde \(K_i\) değerini tekrar tekrar çarpmak yerine en son belirli iterasyon değeri için hesaplanmış \(K\) ile çarparak işlemleri bitirebiliriz.

\(\theta=27^{\circ}\) değeri için \(sin\) ve \(cos\) fonksiyonlarını \(n=4\) iterasyonda hesaplamaya çalışalım.

\(n=0:\)
\(x_0=1\,,\,y_0=0\)
\(\alpha=0^{\circ}\)

\(n=1:\)
\(i=0\)
\(\sigma=sgn(\theta-\alpha)=sgn(27^{\circ}-0^{\circ})=1\)
\(x_1=x_0-2^{-0}\,y_0\)
\(y_1=y_0+2^{-0}\,x_0\)
\(x_1=1-0=1\)
\(y_1=0+1=1\)
\(\alpha=0^{\circ}+\tan^{-1}(0)=45^{\circ}\)

\(n=2:\)
\(i=1\)
\(\sigma=sgn(27^{\circ}-45^{\circ})=-1\)
\(x_2=x_1+2^{-1}\,y_1\)
\(y_2=y_1-2^{-1}\,x_1\)
\(x_2=1+0.5=1.5\)
\(y_2=1-0.5=0.5\)
\(\alpha=45^{\circ}-26.57^{\circ}=18.43^{\circ}\)

\(n=3:\)
\(i=2\)
\(\sigma=sgn(27^{\circ}-18.43^{\circ})=1\)
\(x_3=x_2-2^{-2}\,y_2\)
\(y_3=y_2+2^{-2}\,x_2\)
\(x_3=1.5-0.125=1.375\)
\(y_3=0.5+0.375=0.875\)
\(\alpha=18.43^{\circ}+14.04=32.47^{\circ}\)

\(n=4:\)
\(i=3\)
\(\sigma=sgn(27^{\circ}-32.47^{\circ})=-1\)
\(x_4=x_3+2^{-3}\,y_3\)
\(y_4=y_3-2^{-3}\,x_3\)
\(x_4=1.375+0.125\,\times\,0.875=1.484375\)
\(y_4=0.875-0.125\,\times\,1.375=0.703125\)
\(\alpha=32.47^{\circ}-7.125=25.345^{\circ}\)

\(\sin(27^{\circ})\,{\approx}\,K\,y_4=0.60883\,\times\,0.703125\)
\(\sin(27^{\circ})\,{\approx}\,0.4280\)

\(\cos(27^{\circ})\,{\approx}\,K\,x_4=0.60883\,\times\,1.484375\)
\(\cos(27^{\circ})\,{\approx}\,0.9037\)

Burada \(\alpha\) hesaplanan gerçek açıdır. Yaptığımız işlemler koordinat sisteminde aşağıdaki gibi olacaktır:

FPGA Üzerinde Uygulama:

Kullanılacak sayı formatı \(\sin\) ve \(\cos\) için \(UQ15\), açı için \(Q1.14\) olarak seçilmiştir. Donanımın tasarımı aşağıdaki gibi olacaktır:

Top-Level VHDL kodu:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity CORDIC is
    port(Clk, Reset, S : in std_logic;                   --Clock, Reset ve S girişleri
        Angle : in std_logic_vector(15 downto 0);      --Hesaplanacak yeni açı girişi
        Sin, Cos : out std_logic_vector(15 downto 0)); --Sin, Cos çıkışları
end CORDIC; 

architecture Behavioral of CORDIC is

--Shifter    
component ArithmeticShifter
    port(DataIn : in std_logic_vector(15 downto 0);
        i : in std_logic_vector(3 downto 0);
        DataOut : out std_logic_vector(15 downto 0));
end component;    

--Toplayıcı-Çıkarıcı    
component Adder
    generic(W : integer := 8);
    port(Sigma : in std_logic;
        A, B : in std_logic_vector(W - 1 downto 0);
        O : out std_logic_vector(W - 1 downto 0));
end component;

--Çarpıcı(Q15 x Q15 = Q30)
component Multiplier
    port(A, B : in std_logic_vector(15 downto 0);
        O : out std_logic_vector(31 downto 0));
end component;

--Register
component Reg
    generic(ResetValue : std_logic_vector(15 downto 0) := X"00");
    port(Clk, Reset : in std_logic;
        DataIn : in std_logic_vector(15 downto 0);
        DataOut : out std_logic_vector(15 downto 0));
end component;
    
    signal XiIn, XiOut : std_logic_vector(15 downto 0);
    signal XiShifterOut : std_logic_vector(15 downto 0);
    signal XiMultiplierOut : std_logic_vector(31 downto 0);
    signal YiIn, YiOut : std_logic_vector(15 downto 0);
    signal YiShifterOut : std_logic_vector(15 downto 0);
    signal YiMultiplierOut : std_logic_vector(31 downto 0);
    
    signal atanLUTOut : std_logic_vector(15 downto 0);
    signal AngleOut, AngleIn : std_logic_vector(15 downto 0);
    signal AngleAdderOut : std_logic_vector(15 downto 0);
    
    signal Sigma : std_logic;
    signal i : std_logic_vector(3 downto 0) := X"0";
    signal CounterAdderOut : std_logic_vector(3 downto 0);
    
begin

--Xi Registeri
--Reset Değeri = 1 (UQ1.15)
    Xi : Reg
        generic map(X"8000")
        port map(Clk, Reset, XiIn, XiOut);

--Xi için shifter        
    XShifter : ArithmeticShifter
        port map(XiOut, i, XiShifterOut);

--Xi için Toplayıcı çıkarıcı        
    Adder1 : Adder
        generic map(16)
        port map(Sigma, YiOut, XiShifterOut, YiIn);

--Yi Registeri
--Reset Değeri = 0 (UQ1.15)        
    Yi : Reg
        generic map(X"0000")
        port map(Clk, Reset, YiIn, YiOut);

--Yi için shifter                
    YShifter : ArithmeticShifter
        port map(YiOut, i, YiShifterOut);

--Yi için Toplayıcı çıkarıcı            
    Adder2 : Adder
        generic map(16)
        port map(not Sigma, XiOut, YiShifterOut, XiIn);

--Xi ve Yi'yi K katsayısı ile çarp        
    XiMultiplier : Multiplier
        port map(XiOut, X"4DBA", XiMultiplierOut);
        
    YiMultiplier : Multiplier
        port map(YiOut, X"4DBA", YiMultiplierOut);
        
--Q30 -> Q15 için shift işlemi    
    Shift : for j in 0 to 15 generate
        Sin(j) <= YiMultiplierOut(j + 15);
        Cos(j) <= XiMultiplierOut(j + 15);
    end generate;
    
--Açı Registeri
    AngleRegister : Reg
        generic map(X"0000")
        port map(Clk, Reset, AngleIn, AngleOut);
        
    AngleAdder : Adder
        generic map(16)
        port map(not Sigma, AngleOut, atanLUTOut, AngleAdderOut);
    
--Yeni açı girmek için multiplexer
    with S select
        AngleIn <= AngleAdderOut when '0',
                      Angle when others;
    
--Açının işaret bitini
    Sigma <= AngleOut(15);

--Atan tablosu (Q1.14)
    with i select
        atanLUTOut <= X"3201" when X"0",
                      X"1DAC" when X"1",
                      X"0FAD" when X"2",
                      X"07F5" when X"3",
                      X"03FE" when X"4",
                      X"0200" when X"5",
                      X"0100" when X"6",
                      X"0080" when X"7",
                      X"0040" when X"8",
                      X"0020" when X"9",
                      X"0010" when X"A",
                      X"0008" when X"B",
                      X"0004" when X"C",
                      X"0002" when X"D",
                      X"0001" when X"E",
                      X"0000" when others;

--Counter                          
    CounterAdder : Adder
        generic map(4)
        port map('0', i, X"1", CounterAdderOut);
    
    process(Reset, Clk)
    begin
        if Reset = '1' then
            i <= X"0";
        elsif rising_edge(Clk) then
            i <= CounterAdderOut;
        end if;
    end process;
    
end Behavioral;

Tasarımın diğer dosyalarına buradan ulaşabilirsiniz.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir