Codice DDS (Direct Digital Synthesis)

Sezione dedicata al linguaggio di descrizione hardware per logiche programmabili

Codice DDS (Direct Digital Synthesis)

Postby flz47655 » 25 May 2012, 11:36

Ho implementato un semplice DDS che tramite un contatore incrementa la fase della funzione seno e scrive in uscita i valori secondo una tabella di 16 valori, ecco il codice:

Code: Select all
library ieee;
use ieee.std_logic_1164.all;
-- TODO: Replace with ieee.numeric_std.all
use ieee.std_logic_unsigned.all;

entity dds is
   port
   (
      -- Input ports
      rst      : in   std_logic;
      clk      : in  std_logic;
      -- Output ports
      data_out   : out std_logic_vector(7 downto 0)
   );
end dds;

architecture arc of dds is
   signal counter: std_logic_vector(3 downto 0);
   signal phase: std_logic_vector(3 downto 0);
   signal sine   : std_logic_vector(7 downto 0);
begin
   
   -- Increment phase
   process(clk, rst)
   begin
      if rst = '0' then
         counter <= x"0";
      elsif(rising_edge(clk)) then         
         counter <= counter + 1;
      end if;      
   end process;
   
   phase <= counter;

   process(clk, rst)
   begin
      if rst = '0' then
         --phase <= x"0";
         sine <= x"00";
      elsif(rising_edge(clk)) then         --(sin(x)*127.5)+127.5
         case phase is                     -- Value   Sine(x°)
            when x"0" => sine <= x"7F";   -- 127   0
            when x"1" => sine <= x"B0";   -- 176   22.5
            when x"2" => sine <= x"DA";   -- 218   45         
            when x"3" => sine <= x"F5";   -- 245   67.5
            when x"4" => sine <= x"FF";   -- 255   90
            when x"5" => sine <= x"F5";   -- 245   112.5
            when x"6" => sine <= x"DA";   -- 218   135
            when x"7" => sine <= x"B0";   -- 176   157.5                           
            when x"8" => sine <= x"7F";   -- 127   180
            when x"9" => sine <= x"4F";   -- 79      202.5
            when x"A" => sine <= x"25";   -- 37      225
            when x"B" => sine <= x"0A";   -- 10      247.5
            when x"C" => sine <= x"00";   -- 0      270
            when x"D" => sine <= x"0A";   -- 10      292.5
            when x"E" => sine <= x"25";   -- 37      315
            when x"F" => sine <= x"4F";   -- 79      337.5   
            when others => sine <= x"00";
         end case;
      end if;
   end process;

   data_out <= sine;
end arc;


Vorrei creare un generatore di funzioni simile ai prodotti commerciali con una frequenza impostabile da 0 fino a 10 MHz.

Per il momento il modulo DDS genera una sinusoide alla frequenza CLOCK / 16 poiché per generare un periodo sono necessari 16 colpi di clock per i valori a diversi angoli di fase.

Per generare 10 MHz necessiterei di un clock di 160 MHz, decisamente troppo elevato, avevo pensato a scrivere i valori di uscita sia sui fronti di salita che di discesa per raddoppiare le prestazioni, è una buona idea?

In alternativa potrei ridurre il numero di angoli di fase ma perderei risoluzione, come posso fare per avere la moglie ubriaca e la botte piena?

Per impostare la frequenza attualmente ho creato un modulo divisore di clock programmabile tramite 8 linee di IO esterne:

Code: Select all
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity clk_div is
   port (
      -- Input ports
      rst      : in   std_logic;
      clk_in   : in std_logic;
      clk_div   : in std_logic_vector(7 downto 0);
      -- Output ports
      clk_out   : out std_logic
   );
end entity;

architecture arc_clk_div of clk_div is
   signal counter: std_logic_vector(8 downto 0);
begin      
   process(clk_in, rst)
   begin
      if rst = '0' then
         counter <= "000000000";
      elsif(rising_edge(clk_in)) then         
         counter   <=   std_logic_vector(unsigned(counter) + unsigned(clk_div));
      end if;      
   end process;
   clk_out <= counter(8);
end arc_clk_div;


per rendere impostabile con un DIP Switch la frequenza direttamente in binario per la comodità del'utente, questo modulo è ancora diciamo in debug poichè ha un piccolo problema ma in linea di principio il funzionamento è quello che si può dedurre dal codice.

Allego lo schematico del DDS, qualsiasi consiglio è ben accetto

Ciao
Attachments
DDSSchematic.png
Schema DDS
DDSSchematic.png (17.24 KiB) Viewed 11364 times
flz47655
 
Posts: 639
Joined: 19 Jan 2012, 21:16

Re: Codice DDS (Direct Digital Synthesis)

Postby deluca » 26 May 2012, 12:09

Tu stai volendo realizzare un DDS con cpld: è improbabile che riuscirai a superare qualche decina di Khz con questa tecnica.
Il limite possiamo dire che lo hai raggiunto.
Per passare a un DDS serio dovresti utilizzare almeno un fpga e sfruttare i suoi PLL interni.
Utilizzando inoltre un fpga potrai insererire i codici DAC in una lpm_ram. I valori (codici DAC) verranno preventivamente generati da un micro che dopo aver elaborato una funzione matematica programmerà la lpm_ram.

Ma non possiamo dimenticare che AD realizza dei bei cippetti che fanno il tutto con una manciata di euro in particolare gli AD98XX etc facilmente interfacciabili a micro e a fpga.

Ad ogni modo quello che hai fatto è un buon inizio: lo so già che è a scopo di studio ;)
Ciao
Il mio sito: http://www.delucagiovanni.com ......e la chat: chat/
User avatar
deluca
Site Admin
 
Posts: 1104
Joined: 19 Jun 2011, 10:44
Location: 95123 - Catania (Italy)

Re: Codice DDS (Direct Digital Synthesis)

Postby flz47655 » 26 May 2012, 15:17

Diciamo che potrei provare a passare al Cyclone IV (sulla DE0-Nano) che sta prendendo polvere.. sarebbe un occasione per imparare cosa sono e come si utilizzano le lpm_ram ma ho utilizzato la CPLD al 39% (25/64 macrocelle) magari smanetto un po' provando a passare da 16 a 256 valori di fase intermedi e vedo cosa succede o si mi vengono idee migliori per generare la sinusoide, magari senza lookup table ma tramite la serie di taylor

Certo i chippetti di AD sono probabilmente n-mila volta migliori e con meno distorsioni ma come mi hai già anticipato..sono qui per imparare le logiche programmabili.

Ciao e grazie
flz47655
 
Posts: 639
Joined: 19 Jan 2012, 21:16

Re: Codice DDS (Direct Digital Synthesis)

Postby flz47655 » 29 May 2012, 11:14

Ecco il nuovo codice che ho sviluppato con le seguenti migliorie:
- la tabella di lookup è stata estesa a 64 valori di 8 bit relativi all'intervallo 0:pi/2
- il resto del periodo è costruito per simmetria
- sintesi con ottimizzazione per area (utilizzazione del 91%, 58/64 macrocelle)

Ho notato però che fmax di CLK_66 è ora di 48.08 MHz mentre clk_div:inst1|counter[8] è di 70.42 MHz.... diciamo che il primo valore è abbastanza chiaro, il clock massimo applicabile al modulo clk_div è quello, non capisco però il secondo valore.. cosa centra clk_div:inst1|counter[8]?

UPDATE: Compilando con l'ottimizzazione bilanciata ottengo l'utilizzazione dell'89% e fMax di 48.31 MHz e 66.67 MHz... i misteri di Quartus..

Code: Select all
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity dds is
   port
   (
      -- Input ports
      rst      : in   std_logic;
      clk      : in  std_logic;
      -- Output ports
      data_out   : out std_logic_vector(7 downto 0)
   );
end dds;

architecture arc of dds is
   signal counter: std_logic_vector(7 downto 0);         -- Increment phase every clock rising edge
   signal phase: std_logic_vector(7 downto 0);            -- Distantiated by (2*pi / 256)
   signal relativePhase: std_logic_vector(7 downto 0);   -- Equivalent phase in 0:(pi/2) interval
   signal sine   : std_logic_vector(7 downto 0);            -- Sine value normalized in 0:64 interval
begin
   
   -- Increment phase
   process(clk, rst)
   begin
      if rst = '0' then
         counter <= x"00";
      elsif(rising_edge(clk)) then         
         counter <= std_logic_vector(unsigned(counter) + 1);
      end if;      
   end process;
   
   phase <= counter;

   process(clk, rst)
   begin
      if rst = '0' then         
         sine <= x"00";
      elsif(rising_edge(clk)) then                     
         
         -- Calculate relativePhase (or equivalent phase) from phase          
         if(unsigned(phase) > 191) then                                 -- 192-255
            relativePhase <= std_logic_vector(255 - unsigned(phase));
         elsif(unsigned(phase) > 126) AND (unsigned(phase) < 192)  then   -- 127-191
            relativePhase <= std_logic_vector(unsigned(phase) - 127);                        
         elsif(unsigned(phase) > 63) AND (unsigned(phase) < 127) then   -- 64-126   y=(126-x)
            relativePhase <= std_logic_vector(126 - unsigned(phase));
         else                                                            -- 0-63      y=x            
            relativePhase <= phase;
         end if;      
                                            --               (sin(x)*127.5)+127.5
         case relativePhase is              -- Value         Sine(x rad)
            when x"00" => sine <= x"80";    -- 0             0
            when x"01" => sine <= x"83";    -- 0.0245       0.0245
            when x"02" => sine <= x"86";    -- 0.0491       0.0491
            when x"03" => sine <= x"89";    -- 0.0736       0.0736
            when x"04" => sine <= x"8C";    -- 0.098        0.0982
            when x"05" => sine <= x"8F";    -- 0.1224       0.1227
            when x"06" => sine <= x"92";    -- 0.1467       0.1473
            when x"07" => sine <= x"95";    -- 0.171        0.1718
            when x"08" => sine <= x"98";    -- 0.1951       0.1963
            when x"09" => sine <= x"9B";    -- 0.2191       0.2209
            when x"0A" => sine <= x"9E";    -- 0.243        0.2454
            when x"0B" => sine <= x"A2";    -- 0.2667       0.27
            when x"0C" => sine <= x"A5";    -- 0.2903       0.2945
            when x"0D" => sine <= x"A7";    -- 0.3137       0.3191
            when x"0E" => sine <= x"AA";    -- 0.3369       0.3436
            when x"0F" => sine <= x"AD";    -- 0.3599       0.3682
            when x"10" => sine <= x"B0";    -- 0.3827       0.3927
            when x"11" => sine <= x"B3";    -- 0.4052       0.4172
            when x"12" => sine <= x"B6";    -- 0.4276       0.4418
            when x"13" => sine <= x"B9";    -- 0.4496       0.4663
            when x"14" => sine <= x"BC";    -- 0.4714       0.4909
            when x"15" => sine <= x"BE";    -- 0.4929       0.5154
            when x"16" => sine <= x"C1";    -- 0.5141       0.54
            when x"17" => sine <= x"C4";    -- 0.535        0.5645
            when x"18" => sine <= x"C6";    -- 0.5556       0.589
            when x"19" => sine <= x"C9";    -- 0.5758       0.6136
            when x"1A" => sine <= x"CB";    -- 0.5957       0.6381
            when x"1B" => sine <= x"CE";    -- 0.6152       0.6627
            when x"1C" => sine <= x"D0";    -- 0.6344       0.6872
            when x"1D" => sine <= x"D3";    -- 0.6532       0.7118
            when x"1E" => sine <= x"D5";    -- 0.6716       0.7363
            when x"1F" => sine <= x"D7";    -- 0.6895       0.7609
            when x"20" => sine <= x"DA";    -- 0.7071       0.7854
            when x"21" => sine <= x"DC";    -- 0.7242       0.8099
            when x"22" => sine <= x"DE";    -- 0.741        0.8345
            when x"23" => sine <= x"E0";    -- 0.7572       0.859
            when x"24" => sine <= x"E2";    -- 0.773        0.8836
            when x"25" => sine <= x"E4";    -- 0.7883       0.9081
            when x"26" => sine <= x"E6";    -- 0.8032       0.9327
            when x"27" => sine <= x"E8";    -- 0.8176       0.9572
            when x"28" => sine <= x"EA";    -- 0.8315       0.9817
            when x"29" => sine <= x"EB";    -- 0.8449       1.0063
            when x"2A" => sine <= x"ED";    -- 0.8577       1.0308
            when x"2B" => sine <= x"EE";    -- 0.8701       1.0554
            when x"2C" => sine <= x"F0";    -- 0.8819       1.0799
            when x"2D" => sine <= x"F1";    -- 0.8932       1.1045
            when x"2E" => sine <= x"F3";    -- 0.904        1.129
            when x"2F" => sine <= x"F4";    -- 0.9142       1.1536
            when x"30" => sine <= x"F5";    -- 0.9239       1.1781
            when x"31" => sine <= x"F6";    -- 0.933        1.2026
            when x"32" => sine <= x"F8";    -- 0.9415       1.2272
            when x"33" => sine <= x"F9";    -- 0.9495       1.2517
            when x"34" => sine <= x"FA";    -- 0.9569       1.2763
            when x"35" => sine <= x"FA";    -- 0.9638       1.3008
            when x"36" => sine <= x"FB";    -- 0.97          1.3254
            when x"37" => sine <= x"FC";    -- 0.9757       1.3499
            when x"38" => sine <= x"FD";    -- 0.9808       1.3744
            when x"39" => sine <= x"FD";    -- 0.9853       1.399
            when x"3A" => sine <= x"FE";    -- 0.9892       1.4235
            when x"3B" => sine <= x"FE";    -- 0.9925       1.4481
            when x"3C" => sine <= x"FE";    -- 0.9952       1.4726
            when x"3D" => sine <= x"FF";    -- 0.9973       1.4972
            when x"3E" => sine <= x"FF";    -- 0.9988       1.5217
            when x"3F" => sine <= x"FF";    -- 0.9997       1.5463
            when x"40" => sine <= x"FF";    -- 1             1.5708         
            when others => sine <= x"00";
         end case;
               
      end if;
   end process;
   
   -- Adjust sine from pi to 2pi
   data_out <= std_logic_vector(255 - unsigned(sine)) when unsigned(phase) > 127 else
               sine;
end arc;


Con ModelSim ho però notato un problema che non sono riuscito a risolvere, tra pi e 2pi sono presenti dei valori errati.. secondo voi cosa può essere?

Ciao e grazie a tutti

PS: E.. anche il codice C# che ho usato per generare i valori, anzi diciamo proprio le righe di VHDL :D
Code: Select all
    static void Main(string[] args)
    {
      string hexCase, hexSin, sinValue, sinPhase;

      int xCounter = 0;
      for (double phase = 0; phase < (Math.PI / 2); phase += ((Math.PI / 2) / 64))
      {       
        hexCase = xCounter.ToString("X2");

        // (sin(x) * 127.5) + 127.5
        double NormalizedSin = (Math.Sin(phase) * 127.5) + 127.5;
        // Console.WriteLine(NormalizedSin);
        hexSin = ((int)Math.Round(NormalizedSin, 0)).ToString("X2");

        sinValue = Math.Round(Math.Sin(phase), 4).ToString();
        sinPhase = Math.Round(phase, 4).ToString();
        Console.WriteLine(string.Format("when x\"{0}\" => sine <= x\"{1}\";   -- {2}\t{3}", hexCase, hexSin, sinValue, sinPhase));
        xCounter++;
      }
    }
Attachments
ModelSimSineProblem.png
Problema all'onda generata, sono presenti delle sorte di spike..
ModelSimSineProblem.png (9.58 KiB) Viewed 11339 times
ModelSimSine.png
Sinusoidi generate
ModelSimSine.png (22.43 KiB) Viewed 11339 times
flz47655
 
Posts: 639
Joined: 19 Jan 2012, 21:16

Re: Codice DDS (Direct Digital Synthesis)

Postby deluca » 29 May 2012, 11:54

Non ho analizzato il codice,,,

Il problema è visibile solo con ModelSim o anche a livello hw analizzando l'uscita analogica con l'oscillo ?

Hai provato a far macinare graficamente i valori a Excel ? Cosa succede ? (la semplice classica prova del 9 che qualche volta mi capita di applicare).
Ciao
Il mio sito: http://www.delucagiovanni.com ......e la chat: chat/
User avatar
deluca
Site Admin
 
Posts: 1104
Joined: 19 Jun 2011, 10:44
Location: 95123 - Catania (Italy)

Re: Codice DDS (Direct Digital Synthesis)

Postby flz47655 » 29 May 2012, 15:00

Il difetto della simulazione non è stato misurabile con l'oscilloscopio ma...

Ignorando l'fmax restituita da Quartus ed usando un oscillatore da 100 MHz ho raggiunto sinusoidi di 100 MHz / 256 = 390,625 KHz ma sono rimasto molto deluso, a parte la bassa frequenza, la qualità delle sinusoidi che speravo migliore avendo 256 valori per periodo invece di 16 è veramente bassa, ecco le immagini:

390,6 kHz
Image

Ad occhio non sembra una sinusoide molto armonica... ecco anche una sinusoide a frequenza minore:

96,53 kHz
Image

Non so se è meglio o peggio della precedente! Ci sono delle sorte di "spigoli"

Devo dire che per basse frequenze non si nota più la "quadrettatura" (vedi http://www.delucagiovanni.com/public/phpbb3/viewtopic.php?f=5&t=261&p=688#p688) ma per frequenze maggiori il risultato è anche peggio!

Ho fatto le prove anche con un oscillatore da 66 MHz ma la qualità delle sinusoidi non cambia.. spero mi arrivi presto il cristallo da 40 e 50 MHz che ho ordinato.
flz47655
 
Posts: 639
Joined: 19 Jan 2012, 21:16

Re: Codice DDS (Direct Digital Synthesis)

Postby deluca » 29 May 2012, 15:09

Ho un grande sospetto (uhm) non penso che il problema sia il clk

Ma vorrei sapere :
Il Dac in uscita è realizzato con resistenze o altro? se si

Di che tipo film-metallico o carbone ? e quanti ppm ?

Tolleranza 20, 10, 5 o migliore dell' 1% ?

(per realizzare un buon DDS intanto devi procurarti un DAC con una buona accuratezza e monotonicità)
Ciao
Il mio sito: http://www.delucagiovanni.com ......e la chat: chat/
User avatar
deluca
Site Admin
 
Posts: 1104
Joined: 19 Jun 2011, 10:44
Location: 95123 - Catania (Italy)

Re: Codice DDS (Direct Digital Synthesis)

Postby flz47655 » 29 May 2012, 15:46

Il DAC è autocostruito su millefori, basato su rete R-2R con resistenze (penso a carbone, sono esternamente di colore blu) dalla precisione dell'1%, selezionate ulteriormente con un tester.

Secondo me a frequenze così basse la distorsione "spigolosa" non può essere causata del DAC, a frequenze d 3 MHz non presentava questa distorsione..
flz47655
 
Posts: 639
Joined: 19 Jan 2012, 21:16

Re: Codice DDS (Direct Digital Synthesis)

Postby flz47655 » 29 May 2012, 17:34

Il problema era quello predetto da Giovanni ma non inerente le resistenze ma bensi... l'operazionale sulla schedina DAC (foto schedina http://arduino.cc/forum/index.php/topic,104750.0.html).

Avevo predisposto sulla schedina un LM358 DIP su socket, per queste prove non avevo collegato la sua alimentazione e pensavo che non influisse sul segnale preso direttamente sulla resistenza.. non ho ancora capito il perchè ma sembra invece influire anche a IC senza alimentazione.

Togliendo dal socket o alimentando l'op-amp i disturbi spariscono e si ottiene finalmente una sinusoide carina carina, alla fine dei test pubblicherò qualche altra schermata dell'oscilloscopio.
flz47655
 
Posts: 639
Joined: 19 Jan 2012, 21:16

Re: Codice DDS (Direct Digital Synthesis)

Postby deluca » 29 May 2012, 17:51

Tante volte io le cose le do per scontato.

Bisogna imparare a non darle ! E' capitato tante volte di dire :"non funziona !!!" e poi cos'era .... La spina staccata !! :lol:

Regola fondamentale : Non dimenticare di attaccare la spina
Il tuo DAC è fatto cosi?
Attachments
8_bit_dac.jpg
8_bit_dac.jpg (32.34 KiB) Viewed 11317 times
Ciao
Il mio sito: http://www.delucagiovanni.com ......e la chat: chat/
User avatar
deluca
Site Admin
 
Posts: 1104
Joined: 19 Jun 2011, 10:44
Location: 95123 - Catania (Italy)

Re: Codice DDS (Direct Digital Synthesis)

Postby flz47655 » 29 May 2012, 18:22

In questo caso non mi ero scordato ma avevo volutamente lasciato scollegata l'alimentazione dell'op-amp perché l'LM358 non amplifica a frequenze elevate (e tosa il segnale se alla stessa alimentazione), non sapevo però che anche da spento in qualche modo influiva sul circuito :o

Lo schema è proprio quello, con l'unica aggiunta che visto che l'IC contiene due op-amp, il secondo op-amp è collegato alla penultima resistenza per ottenere Vout / 2 se l'ottavo bit è collegato a GND in modo da evitare una seconda alimentazione.

Per le misure mi agganciavo direttamente alla resistenza e pensavo di non introdurre distorsioni con l'IC spento..

Dopo aver tolto l'IC ecco una misurazione:

Image

Il rumore cala velocemente sotto i 40-50 db e la sinusoide è molto ben fatta. Ho notato che c'è un lieve ripple alla frequenza del clock (66 MHz) sovrapposto al segnale ma è talmente debole che per il momento non importa.

Ciao e grazie
flz47655
 
Posts: 639
Joined: 19 Jan 2012, 21:16

Re: Codice DDS (Direct Digital Synthesis)

Postby flz47655 » 06 Jul 2012, 19:00

Ho finalmente trovato un'alternativa all' LM358, si chiama MCP6022 e sembra fatto proprio per questo, ha una banda di 10 MHz ed è rail-to-rail e quindi utilissimo per i miei esperimenti.

Ecco un'immagine con il nuovo IC catturata dall'oscilloscopio dall'uscita dell'op-amp (sinusoide a 195.3kHz):

Image

La brutta notizia è che ora è evidente il difetto nella sinusoide trovato con la simulazione.. (vedi prima pagina del thread)
La vedrò come un occasione per ripassare VHDL prima che scordo tutto quello che ho imparato, vi terrò aggiornati! :)
flz47655
 
Posts: 639
Joined: 19 Jan 2012, 21:16

Re: Codice DDS (Direct Digital Synthesis)

Postby flz47655 » 06 Jul 2012, 22:05

Ho trovato una cosa che non mi piace e che mi era sfuggita:

Code: Select all
         -- Calculate relativePhase from phase         
         if(unsigned(phase) > 191) then    -- 192-255
            relativePhase <= std_logic_vector(255 - unsigned(phase));
         elsif(..)
            -- ..
         end if;     

         case relativePhase is
            when x"00" => sine <= x"80";
            -- ...


Durante il "case" il valore relativePhase non è stato calcolato correttamente in quanto riferito alla phase precedente, nei process infatti i segnali non vengono modificati immediatamente.
Modificando relativePhase in una variable il problema sembra sparire, perlomeno elimino il salto che era evidente nella simulazione :D

Piccole soddisfazioni di VHDL

Ciao a tutti e alla prossima

PS: Sull'oscilloscopio si vede ancora il piccolo salto ma diciamo che mi accontento di simulare correttamente per il momento
flz47655
 
Posts: 639
Joined: 19 Jan 2012, 21:16


Return to VHDL x FPGA

Who is online

Users browsing this forum: No registered users and 14 guests

cron