Conversao Digital Analogica

DAC DELTA SIGMA Prático
para Lógica Programável

Um dos métodos para transformar um valor digital num valor analógico seria usando um conversor digital-analógico Delta-Sigma. A teoria do conceito do Delta-Sigma esta bem explicada em mais de um sitio da Internet dai que vamos focar a nota na implementação prática dum conversor Delta-Sigma em HDL para lógica programável.

O diagrama de blocos a seguir apresenta uma das possíveis soluções. Para os leitores atentos o diagrama de blocos seria, no funcional, semelhante ao apresentado na nota de aplicação da Xilinx. XAPP154 September 23, 1999. Diagrama de bloques

Mas vamos fazer mais prático adicionando um registro de dados para o DeltaSigma salvar o valor do dado e continuar fornecendo a mesma saída até ser colocado um novo dado. Isso simplificará pensando no casso de montar mais um conversor no mesmo barramento de dados. Além disto vamos dimensionar o conversor.
Queremos um range dinâmico teórico de 70dB no pino de saída da FPGA. Diagrama de bloques Com as novas especificações o diagrama de blocos seria : Diagrama de bloques

Descrição Formal

O primeiro passo será a descrição formal da funcionalidade DeltaSigma a que iniciarmos descrevendo entradas e saídas do módulo.

       
         ENTITY DAC IS
            PORT ( CLOCK : IN  std_logic;   -- System clock
                   RESET : IN  std_logic;   -- System reset
                   DI    : IN  std_logic_vector (23 DOWNTO 0); -- dado de entrada 
                   VAL   : IN  std_logic;   -- dado de entrada valido
                   DO    : OUT std_logic    -- saida do modulo.
                  );
         END DAC;
         

A seguir definirmos as sinais que iremos usar, a principio pelo diagrama de blocos necessitaríamos 3 sinais : sigma, delta e diReg.

            SIGNAL sigma, delta : unsigned(DI'left + 2 DOWNTO 0) := (OTHERS => '0');
            SIGNAL diReg : unsigned(DI'RANGE) := (OTHERS => '0'); 
         

A descrição do registro de entrada de dados não tem mistério :

         IReg : PROCESS(CLOCK)
         BEGIN
            IF rising_edge(CLOCK) THEN
               IF RESET = '1' THEN 
                  diReg <= (OTHERS => '0');
               ELSIF VAL = '1' THEN 
                  diReg <= unsigned(DI);  
               END IF;
            END IF;
         END PROCESS IReg;
         

Continuarmos montando o delta e as adições,

         delta <= (sigma(sigma'left), sigma(sigma'left), OTHERS => '0');
         adder <= diReg + delta + sigma; 
         

O registrador sigma é simples, apenas colocar a adição no registrador :

         SigmaRegister : PROCESS(CLOCK)
         BEGIN 
            IF rising_edge(CLOCK) THEN 
               IF RESET = '1' THEN 
                  sigma <= (OTHERS => '0');
               ELSE 
                  sigma <= adder;    
               END IF;
            END IF;
         END PROCESS SigmaRegister;   
         

Finalmente ligarmos o dado de saída, não esta pensado usar o DO diretamente ligado no pino da FPGA por isto podermos simplesmente fazer :

         DO <= std_logic(sigma(sigma'left)); 
         

Montando todas as partes apresentadas mais as bibliotecas e estruturas do VHDL a descrição completa do DAC DeltaSigma ficou :

LIBRARY IEEE;
USE ieee.std_logic_1164.ALL, ieee.numeric_std.ALL;

ENTITY DAC IS
   PORT ( CLOCK : IN  std_logic;   -- System clock
          RESET : IN  std_logic;   -- System reset
          DI    : IN  std_logic_vector (23 DOWNTO 0); -- dado de entrada 
          VAL   : IN  std_logic;   -- dado de entrada valido enquanto VAL = '1'
          DO    : OUT std_logic ); -- saida do modulo.      
END DAC;

ARCHITECTURE RTL OF DAC IS  

   SIGNAL sigma, delta : unsigned(DI'left + 2 DOWNTO 0) := (OTHERS => '0');
   SIGNAL diReg : unsigned(DI'RANGE) := (OTHERS => '0');  

BEGIN

   IReg : PROCESS(CLOCK)
   BEGIN
      IF rising_edge(CLOCK) THEN
        IF RESET = '1' THEN 
           diReg <= (OTHERS => '0');
        ELSIF VAL = '1' THEN 
           diReg <= unsigned(DI);  
        END IF;
      END IF;
   END PROCESS IReg;

   delta <= (sigma(sigma'left), sigma(sigma'left), OTHERS => '0');
   
   SigmaRegister : PROCESS(CLOCK)
   BEGIN 
      IF rising_edge(CLOCK) THEN 
         IF RESET = '1' THEN 
            sigma <= (OTHERS => '0');
         ELSE 
            sigma <= sigma + delta + diReg;    
         END IF;
      END IF;
   END PROCESS SigmaRegister; 
   
   DO <= std_logic(sigma(sigma'left)); 
   
END RTL;  

Verificação

Para verificação em simulação criarmos um banco de teste gerando as sinais CLOCK, RESET, DI, VAL.
O DI foi gerado como uma onda escada no range de 0,0 até 1,0 quantificado em 24 bits. Considerando que o tempo em um seria o tempo que aplicarmos energia e os tempos em zero os tempos que desligarmos a energia; esperamos ter 0% do tempo em um na saída se a entrada for 0,0 e 100% do tempo em um na saída se a entrada for 1,0.
E claro esta desejamos uma função de transferência um para um para que o valor colocado na entrada seja o valor da potência fornecida para o circuito controlado pelo DO. Simulacion Zoom

Simulacion Zoom

A principio a saída DO apresenta coerente, mas vamos ir mais no detalhe para verificar se nosso DeltaSigma responde corretamente para os valores inseridos na entrada.
Clique nas capturas da simulação para dar zoom

Verificação A :

Entrada DI = 0,125
Saída DO = Ton / Toff = 10ns / 80 ns = 0,125
Seja colocando 0,125 na entrada a saída fica 0,125 ou 12.5% do período acesso

Simulacion Zoom 0,125

Verificação B :

Entrada DI = 0,5
Saída DO = Ton / Toff = 10ns / 20 ns = 0,5 => 0,5 do período acesso.

Simulacion Zoom 0,500

Verificação C :

Entrada DI = 0,75
Saída DO = Ton / Toff = 30ns / 40 ns = 0,75 => 0,75 do período acesso.

Simulacion Zoom 0,750

O conversor responde como esperado, nos limites temos 0% e 100% e olhando alguns dos valores intermediários a resposta foi também como esperado. Estaria faltando um teste de valores aleatórios apenas para confirmar que não temos algum problema mascarado ainda. Vamos parar aqui por enquanto e retornar sob o código e sua correspondente implementação física.

Otimização

Analisando mais em detalhe a soma do registro de entrada com o delta os 24 bits menos significativos do delta são sempre zero e o registro de dados de entrada tem 24 bits. Alinhando no bit menos significativo a soma seria :

Suma con ceros

Na pratica não estarmos somando só concatenando; seria equivalente fazer :

Concatenacao

O diagrama de blocos modificado fica : Diagrama de bloques

Concatenando resulta mais evidente o que já acontecia com a adição nunca iremos estourar a operação; podermos portanto tirar um bit processo. Resultando no diagrama de blocos a seguir: Diagrama de bloques

A seguir o código da versão otimizada

LIBRARY IEEE;
USE ieee.std_logic_1164.ALL, ieee.numeric_std.ALL;

ENTITY DAC IS
   PORT ( CLOCK : IN  std_logic;   -- System clock
          RESET : IN  std_logic;   -- System reset
          DI    : IN  std_logic_vector (23 DOWNTO 0); -- dado de entrada 
          VAL   : IN  std_logic;   -- dado de entrada valido enquanto VAL = '1'
          DO    : OUT std_logic ); -- saida do modulo.      
END DAC;

ARCHITECTURE RTL OF DAC IS  
 
   SIGNAL sigma, delta : unsigned(DI'left + 1 DOWNTO 0) := (OTHERS => '0');
   SIGNAL diReg : unsigned(DI'RANGE) := (OTHERS => '0');  

BEGIN

   IReg : PROCESS(CLOCK)
   BEGIN
      IF rising_edge(CLOCK) THEN
        IF RESET = '1' THEN 
           diReg <= (OTHERS => '0');
        ELSIF VAL = '1' THEN 
           diReg <= unsigned(DI);  
        END IF;
      END IF;
   END PROCESS IReg;

   delta <= sigma(sigma'left) & diReg;
   
   SigmaRegister : PROCESS(CLOCK)
   BEGIN 
      IF rising_edge(CLOCK) THEN 
         IF RESET = '1' THEN 
            sigma <= (OTHERS => '0');
         ELSE    
           sigma <= sigma + delta;
         END IF;
      END IF;
   END PROCESS SigmaRegister; 
   
   DO <= std_logic(sigma(sigma'left)); 
   
END RTL;  

Verificando a nova versão no mesmo banco de teste

Simulacion Zoom 0,500

Entrada DI = 0,5
Saída DO = Ton / Toff = 10ns / 20 ns = 0,5 => 0,5 do período acesso.

Simulacion Zoom 0,500

Teste em placa

A verificação em simulação esta ótima todo como esperado, não deveríamos esperar problemas em placa. Vamos criar um banco de teste em placa com ajuda do Chipscope da Xilinx e experimentar nos o pontos de teste da simulação.

Teste em placa ponto A :

Entrada DI = 0,125
Saída DO = Ton / Toff = 1 / 8 = 0,125 Correto

Test 0,125

Teste em placa ponto B :

Entrada DI = 0,5
Saída DO = Ton / Toff = 1 / 2 = 0,5 Correto

Test 0,500

Teste em placa ponto C :

Entrada DI = 0,75
Saída DO = Ton / Toff = 3 / 4 = 0,75 Correto

Test 0,750

Como de esperar os resultados em placa são os mesmos da simulação, a metodologia de projeto deu seus frutos. Lembrando, partindo do diagrama de blocos descrevermos a funcionalidade, criarmos um banco de teste em simulação, verificarmos que nosso raciocino foi correto e finalmente sem alterar nada do código testarmos na placa.

Este é um exemplo, simples, da linha metodológica que usarmos para nossos projetos, todo é verificado em simulação só até finalizar a verificação implementamos para teste em placa e compararmos resultados.

Comentário colateral para encerrar; ainda nos processos mais simples e mesmo baseados em documentos acreditáveis apresentando soluções funcionais podem-se mascarar resultados não otimizados.

Seja critico e analise em detalhe as soluções apresentadas.


 
   Sua empresa estaria necessitando uma revisão de projeto ? mais... 
 
   O que acha de fazer um treinamento com a gente ? mais...