www.mikrocontroller.net

Forum: FPGA, VHDL & Co. CORDIC in VHDL


Autor: Benedict (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Hi! Ich versuche, einen Cordic Algorithmus in VHDL zu realisieren. Er 
soll später einmal für einen NCO eingesetzt werden. Also brauche ich die 
Sinus- und Cosinuswerte. Den Winkel nähert er gut an (also geht er auch 
in die richtigen Pfade) allerdings scheinen die shift and add 
Operationen nicht zu laufen. Auch den Cordic gain habe ich noch nicht 
rausgerechnet.

Kann mir vielleicht jemand helfen? Ich komme nicht mehr weiter.

Anmerkung: Für die meisten wird es wahrscheinlich sehr dilettantisch 
aussehen, aber ich code VHDL erst seit circa 2 Wochen...

LIBRARY ieee;
    USE ieee.std_logic_1164.all;
    USE ieee.std_logic_arith;
    USE ieee.std_logic_signed.all;

    
-- Eingang: Phase 12 Bits
-- Ausgang: sin cos 10 Bits
-- CORDIC Gain Konstante: 010011011011 ~=0.60725


ARCHITECTURE behaviour OF cordic IS
    signal costemp, sintemp, controlI,controlQ : std_logic_vector (9 DOWNTO 0);
    signal angle : std_logic_vector (breite-1 DOWNTO 0);
    signal count : std_logic_vector (3 DOWNTO 0);
    
    signal L : integer;
    signal operation : std_logic;
       
    BEGIN     
       
       PROCESS(clk,rst)

variable z, z_temp,data : std_logic_vector (breite-1 DOWNTO 0);
variable I_shift,Q_shift,I_temp,Q_temp,I,Q : std_logic_vector (9 DOWNTO 0);

            BEGIN
               IF (rst ='1') THEN
                  
                  -- Anfangswinkel einstellen
                  
                  IF (phase<"110000000000") THEN
                     angle<="110000000000";   -- auf  0 - j1
                     costemp<="0000000000";
                     sintemp<="1000000000";
                
                     
                  ELSIF (phase>"010000000000") THEN
                     angle<="010000000000";   -- auf  0 + j1
                     costemp<="0000000000";
                     sintemp<="0111111111";
               
                   ELSE
                     angle<="000000000000";   -- auf 1 + j0
                     costemp<="0111111111";
                     sintemp<="0000000000";
                  END IF;
                   
                  
                  count<="0000";
                  
                  ELSIF(clk'EVENT AND clk='1') THEN
                      

              
                     --------- Laden des nächsten Winkels
                     --------- shift Werte
                     
                     CASE count IS
                     WHEN "0000" => data := "001000000000"; -- 45.0000°
                                    I_shift:= (costemp(9) & costemp(9 DOWNTO 1));
                                    Q_shift:= (sintemp(9) & sintemp(9 DOWNTO 1));
                                    L<=1;
                     WHEN "0001" => data := "000100101110"; -- 26.5650°
                                    I_shift:= (costemp(9) & costemp(9) & costemp(9 DOWNTO 2));
                                    Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 2));
                                    L<=2;
                     WHEN "0010" => data := "000010011111"; -- 14.0362°
                                    I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 3));
                                    Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 3));
                                    L<=3;
                     WHEN "0011" => data := "000001010001"; --  7.1250°
                                    I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 4));
                                    Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 4));
                                    L<=4;
                     WHEN "0100" => data := "000000101000"; --  3.5763°
                                    I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 5));
                                    Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 5));
                                    L<=5;
                     WHEN "0101" => data := "000000010011"; --  1.7899°
                                    I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 6));
                                    Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 6));
                                    L<=6;
                     WHEN "0110" => data := "000000001010"; --  0.8951°
                                    I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 7));
                                    Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 7));
                                    L<=7;
                     WHEN "0111" => data := "000000000101"; --  0.4476°
                                    I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 8));
                                    Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 8));
                                    L<=8;
                     WHEN OTHERS => data := "000000000000";
                                    I_shift:= costemp;
                                    Q_shift:= sintemp;
                     END CASE;
                      
                     IF count = "1000" THEN
                            
                            -- vorher noch cordic gain!
                            
                            
                            
                            
                            cosout <= costemp;
                            sinout <= sintemp;
                      
                             ELSE
                            
                            I  := costemp;
                            Q  := sintemp;
                            z  := angle;

                               controlI<=I_shift;
                               controlQ<=Q_shift;
                               
                                IF((phase - z)>0)THEN
                           -- rotiere positiv
               
                           -- Ic_neu = Ic - (2^-L)·Qc
                           -- Qc_neu = Qc + (2^-L)·Ic
                           
                           -- addiere die I,Q Werte mit den geshifteten
                           -- gib es auf die Ausgänge
                           I_temp := I - Q_shift;
                           Q_temp := Q + I_shift;
                           z_temp:= z + data;
               
                           costemp<=I_temp;
                           sintemp<=Q_temp;
                           angle<=z_temp;
                           operation<='0';
               
                           ELSIF((phase - z)<0) THEN
                           -- rotiere negativ           
                           
                           -- Ic_neu = Ic + (2^-L)·Qc
                           -- Qc_neu = Qc - (2^-L)·Ic
                           
                           -- addiere die I,Q Werte mit den geshifteten
                           -- gib es auf die Ausgänge
                           
                           I_temp := I + Q_shift;
                           Q_temp := Q - I_shift;
                           z_temp:= z - data;
                           
                           costemp<=I_temp;
                           sintemp<=Q_temp;
                           angle<=z_temp;
                           operation <='1';
               
                           END IF;
                       count  <=    count + 1;
                  END IF;
        END IF;          
END PROCESS;

END behaviour;


Autor: Duke Scarring (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hast Du eine VHDL Testbench dazu? Sonst mag ich das gar nicht erst 
dem Simulator geben...


Duke

Autor: Benedict (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Antwort!
Sowas?
LIBRARY ieee  ; 
USE ieee.std_logic_1164.all  ; 
USE ieee.std_logic_arith  ; 
USE ieee.std_logic_signed.all  ; 
ENTITY cordic_tb  IS 
  GENERIC (breite  : integer   := 12 ); 
END ; 
 
ARCHITECTURE cordic_tb_arch OF cordic_tb IS
  SIGNAL cosout   :  std_logic_vector (9 downto 0)  ; 
  SIGNAL rst   :  std_logic  ; 
  SIGNAL phase   :  std_logic_vector (breite - 1 downto 0)  ; 
  SIGNAL sinout   :  std_logic_vector (9 downto 0)  ; 
  SIGNAL clk   :  std_logic  ; 
  COMPONENT cordic  
    GENERIC ( 
      breite  : integer  );  
    PORT ( 
      cosout  : out std_logic_vector (9 downto 0) ; 
      rst  : in std_logic ; 
      phase  : in std_logic_vector (breite - 1 downto 0) ; 
      sinout  : out std_logic_vector (9 downto 0) ; 
      clk  : in std_logic ); 
  END COMPONENT ; 
BEGIN
  DUT  : cordic  
    GENERIC MAP ( 
      breite  => breite   )
    PORT MAP ( 
      cosout   => cosout  ,
      rst   => rst  ,
      phase   => phase  ,
      sinout   => sinout  ,
      clk   => clk   ) ; 

PROCESS
    BEGIN
        clk<='1';
        wait for 50 ns;
        clk<='0';
        wait for 50 ns;
END PROCESS;

PROCESS
    BEGIN
        rst<='1';
        wait for 20 ns;
        rst<='0';
        wait for 10000 ns;
END PROCESS;

PROCESS
    BEGIN
phase<="000101010101";
wait for 10000 ns;
END PROCESS;

END ; 


Autor: Benedict (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe gerade gelesen, dass man den CORDIC Gain damit kompensieren 
kann, indem man den Anfangswert der Vektorlänge auf den reziproken Wert 
setzt.

Also, statt 0 + j1 auf 0 + j0.607

Hier ist nochmal die source als Datei.

Autor: Christoph Kessler (db1uq) (christoph_kessler)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es gibt hier die VHDL-und Verilog-Beispiele aus einem Lehrbuch, da ist 
auch ein Cordic drin:
http://uisprocesadores2008.wdfiles.com/local--file...
http://www.mikrocontroller.net/articles/AVR-CORDIC...

Autor: Duke Scarring (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
@Benedict:

wichtiger Tipp: verwende numeric_std!
Sonst bekommst Du folgende Warnungen (und die sind noch harmlos, nutze 
die Suchfunktion!):
# vcom -work work -2002 cordic.vhd
# Model Technology ModelSim XE III vcom 6.2g Compiler 2007.02 Feb 22 2007
# -- Loading package standard
# -- Loading package std_logic_1164
# -- Loading package std_logic_arith
# -- Loading package std_logic_signed
# -- Compiling entity cordic
# -- Compiling architecture nochmal of cordic
# ** Error: cordic.vhd(48): Subprogram '<' is ambiguous. Suitable definitions exist in packages 'std_logic_1164' and 'std_logic_signed'.
# ** Error: cordic.vhd(48):    (Use the '-explicit' option to disable the previous error check.)
# ** Error: cordic.vhd(54): Subprogram '>' is ambiguous. Suitable definitions exist in packages 'std_logic_1164' and 'std_logic_signed'.
# ** Error: cordic.vhd(54):    (Use the '-explicit' option to disable the previous error check.)
# ** Error: cordic.vhd(113): Subprogram '=' is ambiguous. Suitable definitions exist in packages 'std_logic_1164' and 'std_logic_signed'.
# ** Error: cordic.vhd(113):    (Use the '-explicit' option to disable the previous error check.)
# ** Error: cordic.vhd(173): VHDL Compiler exiting

So. Jetzt hab ich erstmal eine compilierbare/lauffähige Version 
(Anhang). Was ist jetzt Dein Problem?

Duke

Autor: Benedict (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Also, wenn ich statt USE ieee.std_logic_arith;    USE 
ieee.std_logic_signed.all; einfach das numeric verwende, kompiliert er 
bei mir nicht mehr...
# ** Error: /home/heyerben/cordic-beh-2.vhd(117): No feasible entries for infix operator "-".
# ** Error: /home/heyerben/cordic-beh-2.vhd(117): Bad expression in left operand of infix expression.
# ** Error: /home/heyerben/cordic-beh-2.vhd(117): Type error resolving infix expression ">" as type boolean.
# ** Error: /home/heyerben/cordic-beh-2.vhd(125): No feasible entries for infix operator "-".
# ** Error: /home/heyerben/cordic-beh-2.vhd(125): Bad right hand side (infix expression) in variable assignment.
# ** Error: /home/heyerben/cordic-beh-2.vhd(126): No feasible entries for infix operator "+".
# ** Error: /home/heyerben/cordic-beh-2.vhd(126): Bad right hand side (infix expression) in variable assignment.
# ** Error: /home/heyerben/cordic-beh-2.vhd(127): No feasible entries for infix operator "+".
# ** Error: /home/heyerben/cordic-beh-2.vhd(127): Bad right hand side (infix expression) in variable assignment.
# ** Error: /home/heyerben/cordic-beh-2.vhd(134): No feasible entries for infix operator "-".
# ** Error: /home/heyerben/cordic-beh-2.vhd(134): Bad expression in left operand of infix expression.
# ** Error: /home/heyerben/cordic-beh-2.vhd(134): Type error resolving infix expression "<" as type boolean.
# ** Error: /home/heyerben/cordic-beh-2.vhd(143): No feasible entries for infix operator "+".
# ** Error: /home/heyerben/cordic-beh-2.vhd(143): Bad right hand side (infix expression) in variable assignment.
# ** Error: /home/heyerben/cordic-beh-2.vhd(144): No feasible entries for infix operator "-".
# ** Error: /home/heyerben/cordic-beh-2.vhd(144): Bad right hand side (infix expression) in variable assignment.
# ** Error: /home/heyerben/cordic-beh-2.vhd(145): No feasible entries for infix operator "-".
# ** Error: /home/heyerben/cordic-beh-2.vhd(145): Bad right hand side (infix expression) in variable assignment.
# ** Error: /home/heyerben/cordic-beh-2.vhd(153): No feasible entries for infix operator "+".
# ** Error: /home/heyerben/cordic-beh-2.vhd(153): Type error resolving infix expression "+" as type std_logic_vector.
# ** Error: /home/heyerben/cordic-beh-2.vhd(158): VHDL Compiler exiting



Ich benutze ModelSim 6.1


Naja, mein Problem ist, dass er nicht die richtigen Sinus und 
Cosinuswerte ausgibt, wenn der Algorithmus durchgelaufen ist. Ich 
vermute, dass der Fehler in den Shiftings steckt, aber ich weiß nicht, 
wo.

Wie du vielleicht gesehen hast, nähert sich der Winkel schon an. 
("angle" ist annähernd gleich zu "phase", wenn das Programm 
durchgelaufen ist.)

Nur die Sinus- und Cosinuswerte werden anscheinend falsch aufaddiert

Autor: Benedict (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Übrigens: In dem Code sind noch allerhand überflüssige Signale, die ich 
zur Fehlersuche eingebaut hatte. (controli, controlq, L, operation).

Autor: Duke Scarring (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Benedict:

Vesuch mal die Version aus meinem Anhang. Da habe ich die Datentypen 
angepasst.

Duke

Autor: Benedict (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Achso, dankeschön!

Könntest du das vielleicht nochmal als Code hier im Forum schreiben, 
damit ich das als Copy-Paste einfügen kann? Ich arbeite über Remote 
Desktop und kann leider die Datei nicht einfügen...

Autor: Benedict (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ah sorry, habs doch mit nem "Word-Umweg" hinbekommen.

Autor: Benedict (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hab's hinbekommen :D Der Fehler war, dass ich die geshifteten Werte 
schon eine Iteration zu früh hinzuaddiert habe. Da musste noch ein 
ungeshifteter Wert hinzuaddiert werden. Naja, hier ist jedenfalls die 
funktionierende Version, als Inspiration für Leute, die auch an dem 
Problem hängen:

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;

--------------------------------------------------------------    
--------------------- CORDIC Algorithmus ---------------------
--------------------------------------------------------------
--                                                          --
-- CORDIC Algorithmus zum Berechnen von Sinus- und          --
-- Cosinuswerten                                            --
--                                                          --
-- Geschrieben von: Benedict Heyer                          --
--                                                          --
-- Eingang: Phasenwert: 12 Bits                             --
-- 100000000000 = -180°                                     --
-- 011111111111 = 180° - (180° / 2048) = 179.9121094°       --
--                                                          --
-- => Schrittweite: 0.0878°                                 --
--                                                          --
-- Ausgang: sin und cos: 10 Bits                            --
-- reziproker Wert der CORDIC Gain Konstante                --
-- bei 7 Iterationen: 010011011011 ~=0.60725                --
--------------------------------------------------------------
--------------------------------------------------------------
--------------------------------------------------------------


ENTITY cordic IS
    
    GENERIC( breite : integer := 12);
    
    PORT( phase : IN signed (breite-1 DOWNTO 0);
          clk,rst : In std_logic;
          sinout, cosout : OUT signed(9 DOWNTO 0)
         );
        
   END cordic;



ARCHITECTURE behaviour OF cordic IS
signal costemp, sintemp : signed(9 DOWNTO 0);
signal angle : signed(breite-1 DOWNTO 0);
signal count : unsigned(3 DOWNTO 0);
  
BEGIN     
  
PROCESS(clk,rst)

variable z, z_temp,data : signed(breite-1 DOWNTO 0);
variable I_shift,Q_shift,I_temp,Q_temp,I,Q : signed(9 DOWNTO 0);

       BEGIN
          IF (rst ='1') THEN
             
             -- Anfangswinkel einstellen
             
             IF (phase<"110000000000") THEN
                angle<="110000000000";   -- eigentlich auf  0 - j1, aber durch
                costemp<="0000000000";   -- Kompensieren von cordic gain: 0+j0.607
                sintemp<="1011001010";   -- <--- wäre eigentlich -1
           
                
             ELSIF (phase>"010000000000") THEN
                angle<="010000000000";   -- eigentlich auf  0 + j1, aber durch
                costemp<="0000000000";   -- Kompensieren von cordic gain: 0+j0.607
                sintemp<="0100110110";   -- <--- wäre eigentlich +1
          
              ELSE
                angle<="000000000000";   -- auf 1 + j0
                costemp<="0100110110";   -- <--- wäre eigentlich +1
                sintemp<="0000000000";
             END IF;
              
             
             count<="0000";
             
             ELSIF(clk'EVENT AND clk='1') THEN
                 

         
                --------- Laden des nächsten Winkels
                --------- shift Werte
                
                CASE count IS
                
                WHEN "0000" => data := "001000000000"; -- 45.0000°
                               I_shift:= costemp;
                               Q_shift:= sintemp;
                               
                WHEN "0001" => data := "000100101110"; -- 26.5650°
                               I_shift:= (costemp(9) & costemp(9 DOWNTO 1));
                               Q_shift:= (sintemp(9) & sintemp(9 DOWNTO 1));
                               
                WHEN "0010" => data := "000010011111"; -- 14.0362°
                               I_shift:= (costemp(9) & costemp(9) & costemp(9 DOWNTO 2));
                               Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 2));
                               
                WHEN "0011" => data := "000001010001"; --  7.1250°
                               I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 3));
                               Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 3));
                               
                WHEN "0100" => data := "000000101000"; --  3.5763°
                               I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 4));
                               Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 4));
                               
                WHEN "0101" => data := "000000010011"; --  1.7899°
                               I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 5));
                               Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 5));
                               
                WHEN "0110" => data := "000000001010"; --  0.8951°
                               I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 6));
                               Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 6));
                               
                WHEN "0111" => data := "000000000101"; --  0.4476°
                               I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 7));
                               Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 7));
                               
                
                WHEN OTHERS => data := "000000000000";
                               I_shift:= costemp;
                               Q_shift:= sintemp;
                END CASE;
                 
                IF count = "1000" THEN
                       
                       cosout <= costemp;
                       sinout <= sintemp;
                 
                        ELSE
                       
                          I  := costemp;
                          Q  := sintemp;
                          z  := angle;
                           
                          IF((phase - z)>0)THEN
                           -- rotiere positiv
                            
                           -- Ic_neu = Ic - (2^-L)?Qc
                           -- Qc_neu = Qc + (2^-L)?Ic
                      
                           -- addiere die I,Q Werte mit den geshifteten
                           -- gib es auf die Ausgänge
                           I_temp := I - Q_shift;
                           Q_temp := Q + I_shift;
                           z_temp:= z + data;
                            
                           costemp<=I_temp;
                           sintemp<=Q_temp;
                           angle<=z_temp;
                           
                           ELSIF((phase - z)<0) THEN
                           -- rotiere negativ           
                            
                           -- Ic_neu = Ic + (2^-L)?Qc
                           -- Qc_neu = Qc - (2^-L)?Ic
                            
                           -- addiere die I,Q Werte mit den geshifteten
                           -- gib es auf die Ausgänge
                            
                           I_temp := I + Q_shift;
                           Q_temp := Q - I_shift;
                           z_temp:= z - data;
                      
                           costemp<=I_temp;
                           sintemp<=Q_temp;
                           angle<=z_temp;
                            
                          END IF;
                          count  <=    count + 1;
                   END IF;                 -- count="1000" If-Clause
         END IF;                           -- clk,rst If-Clause
END PROCESS;

END behaviour;

Autor: marcus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich bin gerade dabei den winkel zwischen einem cosinus und sinus signal 
aus einem resolver zu ermitteln, also genau das gegenstück zu diesem 
quellcode beispiel. hat da eventuell jemand einen satz für mich bzw. 
sogar den code. ich bin leider noch nicht hinter die funktionsweise des 
algorithmus gekommenen (mein englisch ist leider zu schlecht um die 
literatur zu verstehen)

danke schon einmal

Autor: René D. (Firma: www.dossmatik.de) (dose)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
marcus schrieb:
> Hallo,
>
> ich bin gerade dabei den winkel zwischen einem cosinus und sinus signal
> aus einem resolver zu ermitteln, also genau das gegenstück zu diesem
> quellcode beispiel. hat da eventuell jemand einen satz für mich bzw.
> sogar den code. ich bin leider noch nicht hinter die funktionsweise des
> algorithmus gekommenen (mein englisch ist leider zu schlecht um die
> literatur zu verstehen)
>
> danke schon einmal

Ein mathemaitscher Ansatz ist, das Sinus mit dem Cosinus Signal zu 
ermitteln. Dabei entsteht ein Signal mit der Doppelten Frequenz und dem 
Gleichanteil. Der Gleichanteil entspricht dem Winkel zwischen den 
Signalen. Die Doppelte Frequenz lässt sich mit einem Tiefpassfilter 
entfernen.

Autor: René D. (Firma: www.dossmatik.de) (dose)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Benedict
für 14 Tage VHDL sieht das schon ganz gut aus.
Ich habe keinen Simulator, jedoch habe ich versucht deinen Code zu 
verstehen.

Hinweise:
Der Name von RST ist unglücklich gewählt. Es ist besser "load" für diese 
Signal geeignet.

An Anfang des Prozesse prüfst du in welchen Quadranten dein Winkel 
liegt. Da sehe ich nur drei Quadranten. Es gibt aber vier Quadranten. 
Was ist mit dem Fall -1+j0?

Für das Schieben gibt es seit VHDL-93 die Befehle srl, sll, sra, sla
 shift (left, right) (logic, arithmetic).

Ansonsten viel Spaß mit VHDL

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Koennte mir mal bitte jemand erklären, wie dieser Core angesteuert 
werden soll? Ich habe mir eine TB geschrieben, die den core antriggert 
und bekomme uach daten raus. Allerdings sind es immer dieselben. Ich 
sehe nicht, dass die Ergebnisse mitlaufen mit der eingespeisten phase.

Autor: René D. (Firma: www.dossmatik.de) (dose)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe das Teil nicht geschrieben, trotzdem kann ich dir es erklären.

Mit rst='1' wird die Phase(Winkel) geladen. Nachdem rst='0' wieder ist, 
werden mit jedem Takt der Cosinus- und Sinuswert genauer.

So sollte es sein ich habe schon bemerkt, dass rst hier als Namensgebung 
unglücklich gewählt ist.
Da ich keine Simulation habe, ist es interessant was hier rauskommt. 
Interessiert mich auch.

Bis bald.

Autor: Benedict (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hi, Leute! Kam durch Zufall wieder hierhin, als ich nach was Anderem 
gesucht habe :D

Also, zuerst: slr und sll hatte ich mal probiert, ging aber nicht. Weiß 
nicht mehr, wieso :)

Zu den Quadranden: Der CORDIC kann von seinem Startpunkt +/- 90° 
ansteuern, von daher reichen drei Startpunkte, womit 2 Quadranten sogar 
doppelt angesteuert werden könnten.

Ich habe hier mal eine kleine Testbench angefügt, die auch Ergebnisse 
auspuckt. Die Ergebnisseder ModelSim Simulation habe ich als Bild 
angefügt.

TB:

LIBRARY ieee  ; 
USE ieee.std_logic_1164.all  ; 
USE ieee.std_logic_arith  ; 
USE ieee.std_logic_signed.all  ; 
ENTITY cordic_tb  IS 
  GENERIC (
    breite  : integer   := 12 ); 
END ; 
 
ARCHITECTURE cordic_tb_arch OF cordic_tb IS
  SIGNAL cosout   :  std_logic_vector (9 downto 0)  ; 
  SIGNAL rst   :  std_logic  ; 
  SIGNAL phase   :  std_logic_vector (breite - 1 downto 0):="000000000000"  ; 
  SIGNAL sinout   :  std_logic_vector (9 downto 0)  ; 
  SIGNAL clk   :  std_logic  ; 
  COMPONENT cordic  
    GENERIC ( 
      breite  : integer  );  
    PORT ( 
      cosout  : inout std_logic_vector (9 downto 0) ; 
      rst  : in std_logic ; 
      phase  : in std_logic_vector (breite - 1 downto 0) ; 
      sinout  : inout std_logic_vector (9 downto 0) ; 
      clk  : in std_logic ); 
  END COMPONENT ; 
BEGIN
  DUT  : cordic  
    GENERIC MAP ( 
      breite  => breite   )
    PORT MAP ( 
      cosout   => cosout  ,
      rst   => rst  ,
      phase   => phase  ,
      sinout   => sinout  ,
      clk   => clk   ) ; 

process
begin
    clk<='1';
    wait for 20 ns;
    clk<='0';
    wait for 20 ns;
end process;

process
begin
    rst<='1';
    wait for 50 ns;
    rst<='0';
    wait for 400 ns;
end process;

process
    begin
    phase <= phase + "000000100000";
    wait for 450 ns;
end process;

END ; 


Autor: Benedict (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ach ja: Ich hatte damals noch eine kleine Änderung gemacht: Sollte das 
hier jemand ausprobieren, wird er sehen, dass der Wert vom Cosinus auf 
seinem Maximum einen Einknick hat. Das hatte mit dem Anfangswert zur 
CORDIC-Gain Kompensation zu tun. Da habe ich noch eine kleine Abfrage 
eingefügt (im Code unter dem shifting Teil):
IF count = "1000" THEN
                       
                       if(costemp = "0100110110" AND sintemp = "0000000000")then
                          cosout <= "0111111111";
                          sinout <= sintemp;
                       else
                          cosout <= costemp;
                          sinout <= sintemp;
                       end if;

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke! das probiere ich gleich aus.

Autor: René D. (Firma: www.dossmatik.de) (dose)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Bendict,

wenn du hier angemeldest bist, kannst du einen Haken setzen bei E-Mail 
Benachrichtigung.

Dann bekommst du automatisch eine Email, wenn jemand hier antwortet.



Was baust du als nächstes?

Du hattes eine interessante Idee.

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
irgendwie ist das nicht komplett!

was ist z.B. mit dem Fall

ELSIF((phase - z)=0) THEN

der ist nicht formuliert

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
die testbench benutzt auch inout, passt also nicht

das hast du im leben nicht simuliert, denn dann hättest du das sofort 
gemerkt

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ausserdem hat der core cordic die typen signed waehrend die testbench 
stdlogic verwendet

vielleicht postest du nochmal core und testbench komplett zueinnder 
passend

Autor: Benedict (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
TB
LIBRARY ieee  ; 
USE ieee.std_logic_1164.all  ; 
USE ieee.std_logic_arith  ; 
USE ieee.std_logic_signed.all  ; 
ENTITY cordic_tb  IS 
  GENERIC (
    breite  : integer   := 12 ); 
END ; 
 
ARCHITECTURE cordic_tb_arch OF cordic_tb IS
  SIGNAL cosout   :  std_logic_vector (9 downto 0)  ; 
  SIGNAL rst   :  std_logic  ; 
  SIGNAL phase   :  std_logic_vector (breite - 1 downto 0):="000000000000"  ; 
  SIGNAL sinout   :  std_logic_vector (9 downto 0)  ; 
  SIGNAL clk   :  std_logic  ; 
  COMPONENT cordic  
    GENERIC ( 
      breite  : integer  );  
    PORT ( 
      cosout  : inout std_logic_vector (9 downto 0) ; 
      rst  : in std_logic ; 
      phase  : in std_logic_vector (breite - 1 downto 0) ; 
      sinout  : inout std_logic_vector (9 downto 0) ; 
      clk  : in std_logic ); 
  END COMPONENT ; 
BEGIN
  DUT  : cordic  
    GENERIC MAP ( 
      breite  => breite   )
    PORT MAP ( 
      cosout   => cosout  ,
      rst   => rst  ,
      phase   => phase  ,
      sinout   => sinout  ,
      clk   => clk   ) ; 

process
begin
    clk<='1';
    wait for 20 ns;
    clk<='0';
    wait for 20 ns;
end process;

process
begin
    rst<='1';
    wait for 50 ns;
    rst<='0';
    wait for 400 ns;
end process;

process
    begin
    phase <= phase + "000000100000";
    wait for 450 ns;
end process;

END ; 


Code
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith;
USE ieee.std_logic_signed.all;
    

ENTITY cordic IS
    
    GENERIC( breite : integer := 12);
    
    PORT( phase : IN std_logic_vector (breite-1 DOWNTO 0);
          clk,rst : In std_logic;
          sinout, cosout : INOUT std_logic_vector(9 DOWNTO 0)
         );
        
   END cordic;

ARCHITECTURE behaviour OF cordic IS
signal costemp, sintemp : std_logic_vector(9 DOWNTO 0);
signal angle : std_logic_vector(breite-1 DOWNTO 0);
signal count : std_logic_vector(3 DOWNTO 0);
 
begin 
PROCESS(clk,rst)

variable z, z_temp,data : std_logic_vector(breite-1 DOWNTO 0);
variable I_shift,Q_shift,I_temp,Q_temp,I,Q : std_logic_vector(9 DOWNTO 0);

       BEGIN
          IF (rst ='1') THEN
             
             -- Anfangswinkel einstellen
             
             IF (phase<"110000000000") THEN
                angle<="110000000000";   -- eigentlich auf  0 - j1, aber durch
                costemp<="0000000000";   -- Kompensieren von cordic gain: 0+j0.607
                sintemp<="1011001010";   -- <--- wäre eigentlich -1
           
                
             ELSIF (phase>"010000000000") THEN
                angle<="010000000000";   -- eigentlich auf  0 + j1, aber durch
                costemp<="0000000000";   -- Kompensieren von cordic gain: 0+j0.607
                sintemp<="0100110110";   -- <--- wäre eigentlich +1
          
              ELSE
                angle<="000000000000";   -- auf 1 + j0
                costemp<="0100110110";   -- <--- wäre eigentlich +1
                sintemp<="0000000000";
             END IF;
              
             
             count<="0000";
             
             ELSIF(clk'EVENT AND clk='1') THEN
                 

         
                --------- Laden des nächsten Winkels
                --------- shift Werte
                
                CASE count IS
                
                WHEN "0000" => data := "001000000000"; -- 45.0000°
                               I_shift:= costemp;
                               Q_shift:= sintemp;
                               
                WHEN "0001" => data := "000100101110"; -- 26.5650°
                               I_shift:= (costemp(9) & costemp(9 DOWNTO 1));
                               Q_shift:= (sintemp(9) & sintemp(9 DOWNTO 1));
                               
                WHEN "0010" => data := "000010011111"; -- 14.0362°
                               I_shift:= (costemp(9) & costemp(9) & costemp(9 DOWNTO 2));
                               Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 2));
                               
                WHEN "0011" => data := "000001010001"; --  7.1250°
                               I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 3));
                               Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 3));
                               
                WHEN "0100" => data := "000000101000"; --  3.5763°
                               I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 4));
                               Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 4));
                               
                WHEN "0101" => data := "000000010011"; --  1.7899°
                               I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 5));
                               Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 5));
                               
                WHEN "0110" => data := "000000001010"; --  0.8951°
                               I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 6));
                               Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 6));
                               
                WHEN "0111" => data := "000000000101"; --  0.4476°
                               I_shift:= (costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9) & costemp(9 DOWNTO 7));
                               Q_shift:= (sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9) & sintemp(9 DOWNTO 7));
                               
                
                WHEN OTHERS => data := "000000000000";
                               I_shift:= costemp;
                               Q_shift:= sintemp;
                END CASE;
                 
                IF count = "1000" THEN
                       
                       if(costemp = "0100110110" AND sintemp = "0000000000")then
                          cosout <= "0111111111";
                          sinout <= sintemp;
                       else
                          cosout <= costemp;
                          sinout <= sintemp;
                       end if;
                 
                        ELSE
                       
                          I  := costemp;
                          Q  := sintemp;
                          z  := angle;
                           
                          IF((phase - z)>0)THEN
                           -- rotiere positiv
                            
                           -- Ic_neu = Ic - (2^-L)?Qc
                           -- Qc_neu = Qc + (2^-L)?Ic
                      
                           -- addiere die I,Q Werte mit den geshifteten
                           -- gib es auf die Ausgänge
                           I_temp := I - Q_shift;
                           Q_temp := Q + I_shift;
                           z_temp:= z + data;
                            
                           costemp<=I_temp;
                           sintemp<=Q_temp;
                           angle<=z_temp;
                           
                           ELSIF((phase - z)<0) THEN
                           -- rotiere negativ           
                            
                           -- Ic_neu = Ic + (2^-L)?Qc
                           -- Qc_neu = Qc - (2^-L)?Ic
                            
                           -- addiere die I,Q Werte mit den geshifteten
                           -- gib es auf die Ausgänge
                            
                           I_temp := I + Q_shift;
                           Q_temp := Q - I_shift;
                           z_temp:= z - data;
                      
                           costemp<=I_temp;
                           sintemp<=Q_temp;
                           angle<=z_temp;
                            
                          END IF;
                          count  <=    count + 1;
                   END IF;                 -- count="1000" If-Clause
         END IF;                           -- clk,rst If-Clause
END PROCESS;


END behaviour;


So funktioniert's.

Momentan arbeite ich an nem BPSK Demodulator. Um die Carrier Frequenz zu 
strippen, benutze ich aber nicht den CORDIC, da der viel zu viele 
Iterationen brauch und ich nicht auf eine genaue Phase angewiesen bin. 
Deswegen hab ichs mit Lookup realisiert.

Der CORDIC war sowieso nur zur Übung. Gibt es ja auch im Xilinx CoreGEN 
und der hat viel bessere Werte.

Autor: CS (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

kann jemand bitte erklären, woher die Binärzahlen kommen?

IF (phase<"110000000000") THEN
                angle<="110000000000";
                costemp<="0000000000";
                sintemp<="1011001010"; -- das hier?

und für jede Winkel

WHEN "0001" => data := "000100101110"; -- 26.5650°??
                I_shift:= (costemp(9) & costemp(9 DOWNTO 1));
                Q_shift:= (sintemp(9) & sintemp(9 DOWNTO 1));

Danke für die Hilfe

viele Grüsse

Autor: berndl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
CS schrieb:
> IF (phase<"110000000000") THEN
>                 angle<="110000000000";
>                 costemp<="0000000000";
>                 sintemp<="1011001010"; -- das hier?
>
> und für jede Winkel
>
> WHEN "0001" => data := "000100101110"; -- 26.5650°??
>                 I_shift:= (costemp(9) & costemp(9 DOWNTO 1));
>                 Q_shift:= (sintemp(9) & sintemp(9 DOWNTO 1));

Hi,
kein Problem, bin an sowas auch seit 2 Tagen dran.

Oberer Teil:
Du selektierst den Quadranten in dem du dich gerade aufhaelst 
(phase(11:10)=00 ist 'rechts oben', 01 ist links oben und so weiter)

Das 'case' statement ist auch einfach: Das sind die Faelle fuer arctan 
1, 1/2, 1/4, 1/8, usw... Das ergibt halt bestimmte Winkel. Und die 
werden jeweils berechnet und addiert oder subtrahiert.

@originalposter: Dein Problem mit den Nullstellen/Uebergaengen kannst du 
ganz einfach loesen: Anstatt 'IF' und 'ELSIF' zu schreiben reicht es, 
anstatt ELSIF einfach ein ELSE zu verwenden. Die Sonderbehandlung geht 
dann naemlich automatisch.

Ausserdem ist der Code ziemlich grottig, sowas wuerde ich mich nicht 
abzuliefern trauen...

... Aber prinzipiell funktioniert es, also nicht schlecht

Autor: Sigurt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist es denn so schlimm, wenn der Code schlecht ist? Wird das nicht durch 
die Synthese gltt gebügelt?

Autor: berndl (Gast)
Datum:
Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
Hi, der Thread ist ja jahrealt mittlerweile. Aber ich habe damals den 
Code hier als Basis benutzt, ihn dann doch nicht benoetigt. Aber jetzt 
brauche ich ihn...

Hier mal als Anhang, was ich aus dem 'rotating' CORDIC gerade gemacht 
habe.

Ich habe den redundanten Code mal eingedampft, dabei das Timing deutlich 
verbessern koennen.

Ich bin deshalb wieder auf diesen alten Thread gestossen, weil ich jetzt 
was mit 'CORDIC vectoring mode' machen muss, also von per ADC gelesenen 
Sinuessen und Cosinuessen den Winkel berechnen... Deshalb hab' ich den 
alten 'CORDIC' hier nochmal ausgegraben. Die TB dreht nur den CORDIC 
kurz in positive Richtung, dann in negative Richtung. Bei 'sinout' und 
'cosout' sollte man also passende Signale finden...

Nur falls es jemanden hier interessiert...

Autor: Klaus L. (klausi5000)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ein thread von 2009 - wow, der Zombie lebt :-)

Finde ich aber gut, dass mal jemand ein Thema zum Abschluss bringt. Habe 
den thread abboniert, weil mich das auch interessiert. Ich bastle 
nämlich auch gerade mit CORDIC.

Läuft Deine Version denn so, wie oben gepostet?

Autor: berndl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
im cordic.vhd muss noch was geaendert werden:
when "11" =>                           -- 270..360 degree, start with 270
   sintemp <= '1' & gainy_n; -- Signbit korrigiert
   costemp <= '0' & VAL_0;   -- Signbit korrigiert

Autor: W.S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus L. schrieb:
> Ein thread von 2009 - wow, der Zombie lebt :-)

Ja, ist auch dringend nötig.

Schau dich doch mal um, was es denn sonst so gibt. Firmen wie Xilinx 
haben in ihrem IP Portfolio zwar auch sowas, aber ohne Einblick in den 
echten Quellcode.

Und anderweitig sucht man sich die Augen wund, ohne wirklich was 
Brauchbares zu finden - es gibt nur Zeugs zuhauf, wo sich jemand "über" 
den CORDIC-Algo ausläßt, aber ohne ihn jemals tatsächlich konkret 
durchzuexerzieren. Das ist genau so wie z.B. das Gelaber über den 
DES-Algo, der ja auch seit langem veröffentlicht ist, aber auch (wie 
CORDIC) ohne daß es konkrete und zugleich verständliche (quasi 
"knuffige") Realisierungen gibt.

Natürlich kann man sich hinsetzen, das Ganze an den theoretischen 
Formeln durchackern und sich daraus seinen Code selbst schreiben. Jaja, 
das wäre die selbständigste Methode, aber gelegentlich sucht man einfach 
nur nach einer fertigen Teillösung, damit der Aufwand für ein Projekt 
noch vom Einzelnen stemmbar bleibt und man nicht bei Adam&Eva beginnen 
muß.

W.S.

Autor: berndl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus L. schrieb:
> Läuft Deine Version denn so, wie oben gepostet?

ja, simuliert sowohl mit GHDL/Gtkwave als auch mit Modelsim. Und mit 
einem anderen cordic_top gebe ich ueber DACs die Ergebnisse aus, 
Plattform ist S3E und S6 von Xilinx sowie CycIII von Altera.

Autor: Klaus L. (klausi5000)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich komme nochmal drauf zurück:

Läuft der Code korrekt? Liesse der sich als Basis nehmen, um ihn zu 
pipelinen und schneller zu machen?

Ich suche eine Lösung, die schneller ist, als die von Xilinx!

Der Core hat eine enorme Latenz!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.