Registrador Série-Paralelo (24 bits)

Registrador Série-Paralelo (24 bits)

[imagem ou vídeo]

O projeto a seguir pretende aumentar a disponibilidade de pinos de saída em projetos, problema muitas vezes encontrado, por meio de um Registrador Série-Paralelo. A ideia deste exemplo surgiu a partir de um artigo da Altera® publicado sobre "Implementing the Top Five Control-Path Applications with Low-Cost, Low-Power CPLDs", no tópico "I/O Expansion". Neste exemplo ampliamos a disponibilidade para 24 pinos, todavia podemos diminuir ou aumentar essa ampliação dependendo da "capacidade de quem recebe os dados".

Este artigo será um exemplo de projeto de circuito sequencial descrito em VHDL no Quartus II.  A simulação será feita no ModelSim-Altera, através de um testbench que implementará o controle da entrada de dados da simulação, sendo também descrito em VHDL. Haverá também, na descrição do funcionamento, uma animação interativa para explicação do funcionamento do mesmo.

Para a implementação do projeto são necessários:

Descrição do Funcionamento

Os Resgistradores de Deslocamento, em geral, funcionam a partir do arranjo de Flip-Flops tipo D, controlados por um sinal de clock, preset e clear.

O princípio neste projeto é o mesmo, sendo que ele recebe a informação em série e transmite a mesma em paralelo. Em outras palavras, o circuito recebe os bits da informação por apenas um fio, bit a bit sequencialmente, e transmite no número de fios igual a quantidade dos bits contidos na informação, onde todos os bits apresentam-se simultaneamente. Por exemplo, se ele receber a informação Eh(1110b) em série,  utilizará 4 fios para transmiti-la em paralelo.

Este registrador possui 5 entradas, sendo EnableN, CLK_REG, ResetN, Serial_In e CLK_SRL, e 24 saídas, sendo a expansão de 24 bits que desejamos. Há também sinais internos, sendo D_INT[23..0] e S_INT[23..0]. A Figura 1 representa o diagrama de blocos desse Registrador Série-Paralelo.

Diagrama de Blocos 2

Figura 1: Diagrama de Blocos

Segue abaixo uma animação interativa no software Proteus, explicando passo a passo como funciona esse exemplo. Para ampliá-lo clique sobre a tela abaixo, ou clique aqui.

"Animação Interativa feita no Wink"

Em resumo, a função de cada pino é:

CLK_SRL: Sinal de clock que controla o deslocamento em série dos bits.

Serial_In: Pino que recebe a informação em série.

ResetN: Reset geral do circuito, sendo ativo baixo. Ou seja, zera todos os sinais do circuito em nível lógico "0".

CLK_REG: Sinal de clock que controla o deslocamento em paralelo dos bits.

EnableN: Habilita as saídas do registrador, sendo ativo baixo. Quando em nível alto ("1"), impõe o estado de alta impedância ("Z") para as saídas, onde desconecta elas do circuito interno temporariamente, ou seja, desabilitando as saídas.

Descrição em VHDL

Primeiramente selecionamos a biblioteca "ieee", juntamente com os pacotes desejados para a implementação do projeto.

-- Declaração da Biblioteca e seus pacotes utilizados
 library ieee;
 use ieee.std_logic_1164.all;
 use ieee.std_logic_unsigned.all;

Nesse projeto serão usadas 5 pinos de entrada, sendo SerialIn, CLR_SRL, CLK_REG, EnableN e ResetN, e 24 pinos de saída, sendo o barramento de bits S, totalizando 29 pinos.

-- Declaração dos Pinos de Entrada e Saída
 entity SerieParalelo_24Bits is
 port (
   SerialIn,  -- Pino que recebe a informação em série.
   CLK_SRL,   -- Clock que controla o deslocamento de bits serial.
   CLK_REG,   -- Clock que controla o armazenamento de bits em paralelo.
   EnableN,   -- Pino que controla a saída "tri-state".
   ResetN      : in std_logic;  -- Pino que "reseta" todo o circuito.

   -- 24 bits de saída sendo o 23 o MSB e o 0 o LSB:
   S                : out std_logic_vector (23 downto 0)
 );
 end entity SerieParalelo_24Bits;

Dentro da architecture será necessário declarar dois sinais, sendo D_INT e S_INT, cada um com 24 bits para armazenar o deslocamento de bits interno.

-- Sinais Internos do Projeto, sendo os dois de 24 bits cada.
 architecture comportamento of SerieParalelo_24Bits is

   -- Barramento entre o registrador de deslocamento e o registrador paralelo:
   signal D_INT    : std_logic_vector (23 downto 0);

   -- Barramento entre o registrador paralelo e a saída "tri-state":
   signal S_INT    : std_logic_vector (23 downto 0);

O primeiro processo será o deslocamento de bits, descrito e comentado abaixo.

begin
 -- Processo de Deslocamento de bits.
 Deslocamento:
 process (ResetN, CLK_SRL)                        
 begin
   if (ResetN = '0') then       -- Se ResetN for 0, então
      D_INT <= (others => '0'); -- D_INT é "resetada".
   elsif (CLK_SRL'event) and (CLK_SRL = '1') then
   -- Se ResetN for 1, e um evento de subida de "Clock Serial",
      flip_flops: -- os flip_flops serão acionados.
      for cont in 1 to 23 loop -- Atuação: Para contagem de 1 à 23...
          D_INT(cont - 1) <= D_INT (cont);
          -- O que está em "cont - 1" receberá o bit que esta em cont,
          -- por exemplo, com cont = 1, D_INT[0] recebe o que está em D_INT[1],
          -- e assim sucessivamente até D_INT[22] <= D_INT[23].
     end loop;

      D_INT(23) <= SerialIn;
      -- D_INT[23] receberá o bit de SerialIn, completando o deslocamento dos 24 bits.
   end if;
 end process Deslocamento; -- Final do Processo de Deslocamento.

O segundo processo é utilizado para controlar a saída "tri-state".

-- Processo para Controle de Saída (Tri-State)
 Registro_Saida:
 process (ResetN, CLK_REG, EnableN, S_INT)    
 begin
   if (ResetN = '0') then        -- Se ResetN for 0, então
       S_INT <= (others => '0'); -- S_INT é "resetada".
   elsif (CLK_REG'event) and (CLK_REG = '1') then
   -- Se ResetN for 1, e um evento de subida de "Clock Registrador",
       S_INT <= D_INT; -- S_INT registra o que está em D_INT.
   end if;                                                           
   if (EnableN = '0') then    -- Se EnableN for 0, então
       S <= S_INT;            -- S ficará ativa, exibindo o que está em S_INT
   else                       -- Se não (ou seja, se EnableN for 1),
       S <= (others => 'Z');  -- S ficará em alta impedância (desabilitada).
   end if;     
 end process Registro_Saida;  -- Final do Processo de Controle de Saída.
end architecture;

Depois de estruturar todo projeto, faça uma Análise e Síntese do mesmo para o programa procurar algum erro ou aviso no VHDL. Se não ocorreu nenhum equívoco na implementação do projeto, somente o aviso de  compilação paralela (disponível apenas na versão paga) aparecerá.

Um resumo da Análise e Síntese está logo abaixo, na Figura 2.

Resumo da Análise e Sintese

Figura 2: Resumo da Análise e Síntese

Simulação

Utilizamos um testbench para simular esse projeto. Primeiro selecionamos a biblioteca "ieee", juntamente com os pacotes desejados para a simulação do projeto, e a entidade a ser simulada.

-- Declaração das Bibliotecas e seus pacotes utilizados:
 library ieee;
 use ieee.std_logic_1164.all;
 use ieee.std_logic_unsigned.all;
-- Declaração da entidade a ser simulada.
 entity tb_SerieParalelo_24Bits is
 end entity tb_SerieParalelo_24Bits;

Na arquitetura da simulaçao, além dos sinais para interfaceamento do testbench com a entity do projeto, criamos um novo sinal chamado "dado_a_receber", para atuar como a informação enviada de outro dispositivo para nosso registrador.

architecture teste of tb_SerieParalelo_24Bits is

   -- Sinal criado para simulação
   signal dado_a_receber            : std_logic_vector (23 downto 0);

   -- Sinais para interfaceamento do testbench com a entity do projeto.
   -- Para simplificar, utilizamos os mesmos nomes para os sinais, embora
   -- isto não seja necessário.
   signal SerialIn, CLK_SRL, CLK_REG,
          EnableN, ResetN                : std_logic;
   signal S                              : std_logic_vector (23 downto 0);

Ainda na arquitetura, atribuímos uma informação fixa para o "dado_a_receber", além do port map para o testbench.

begin
     -- Atribuímos um dado fixo para simular a informação recebida,
     -- sendo 6 algarismos hexadecimais, totalizando 24 bits.
     dado_a_receber <= X"6FAA55";
 
 uut : entity work.SerieParalelo_24Bits -- Nome da entidade a ser testada.
    port map (
         SerialIn, CLK_SRL, CLK_REG, -- Declaração dos pinos, sendo na mesma sequencia
         EnableN, ResetN, S          -- apresentada na "entity" do projeto.
         );

Feito todas as descrições acima, partimos para atribuição dos sinais de entrada. Nosso registrador é um escravo, então os sinais de entrada serão controlados por outro dipositivo. Nesse caso, o testbench emulará esse controle externo, atribuindo níveis lógicos nas variáveis de controle e através do ModelSim-Altera analisaremos o funcionamento do projeto.

-- Começam os comandos para simulação...
 SIMULACAO:
 process
 begin
    ResetN   <= '0'; -- Primeiramente atribuímos "0" em ResetN,
    SerialIn <= '0'; -- para resetar "informações anteriores".
    CLK_REG  <= '0'; --
    EnableN  <= '1'; -- A saída inicialmente desabilitada (alta impedância).
    CLK_SRL  <= '1';
    wait for 100 ns; -- Tempo de permanência do estado     

    ResetN   <= '1'; -- Atribuição do ResetN para "1" para podermos atribuir
    wait for 100 ns; -- entradas, caso contrário todo circuito ficaria em "0".          

    for cont in 0 to 23 loop                 -- "Loop" para simular o processo de deslocamento em série.
       CLK_SRL  <= '0';                      -- Atribuímos inicialmente "0" no CLK_SRL.    

       SerialIn <= dado_a_receber(cont);     -- SerialIn receberá o bit na posição
                                             -- (contagem) de "dado_a_receber".     
 
       wait for 100 ns;                      -- Tempo de permanência de estado no pulso "baixo".
 
       CLK_SRL  <= '1';                      -- Mudança do CLK_SRL para 1, completando o sinal de clock
       wait for 100 ns;                      -- Tempo de permanência de estado no pulso "alto".    

    end loop;                                -- Término do loop.   

    wait for 400 ns;
    CLK_REG <= '1';                          -- CLK_REG trava a informação no registrador paralelo.   

    wait for 100 ns;        
    EnableN <= '0';                          -- EnableN habilita a saída.   

    wait for 1000 ns;                                  
    wait;                                                        

 end process SIMULACAO;                      -- Término do processo de SIMULAÇÃO

 end architecture teste;

Segue abaixo, na Figura 3, o resultado da simulação no ModelSim-Altera. Clique sobre ela para ampliá-la.

Figura 3: Formas de ondas obtidas na simulação

Figura 3: Formas de ondas obtidas na simulação

Observe que a mudança do estado de SerialIn, quando necessário, acontece na descida do CLK_SRL. Além disso, o registrador de deslocamento, representado por D_INT, funciona na subida do CLK_SRL. Ambas situações são apresentadas na Figura 4, sendo a primeira grifada em vermelho e a segunda em amarelo.

Figura 4: Mudança de estado de SerialIn e D_INT

Figura 4: Mudança do estado de SerialIn e D_INT

Analise na Figura 5 que após o deslocamento do último bit (amarelo), CLK_SRL não é mais acionado, permanecendo no estado alto. Como programado, depois de 400 ns, CLK_REG é ativado, atualizando o que estava em D_INT para S_INT (vermelho), porém a saída S ainda permanece em alta impedância ("Z"). Somente com EnableN em "0", S recebe o que está em S_INT (azul).

Figura 5: Mudança do estado de S_INT e S

Projeto no uCduino

Como relatado, esse projeto funciona como escravo, ou seja, há um mestre controlando-o e consequentemente expandindo, nessa aplicação, seus sinais de saída. Essa expansão de pinos E/S é uma aplicação típica de dispositivos de lógica programável (PLD). Nesse exemplo, o módulo de CPLD está sendo controlado pelo módulo uCduino (microcontrolador similar ao Arduino).

O protocolo dos sinais de comando do projeto no CPLD é compatível com o da interface SPI, bastante usado na indústria, que é suportado pelo Arduino. A Figura 6 mostra a conexão entre um dispositivo SPI mestre e um SPI escravo. Pelos pinos SCLK e SS\, respectivamente, o mestre sincroniza a transmissão e seleciona o escravo. A transferência de dados, em modo serial, ocorre pelos pinos MOSI (do mestre para o escravo) e MISO (do escravo para o mestre). Para saber mais sobre o padrão SPI, e sobre a implementação da SPI no Arduino, leia em "Serial Peripheral Interface Bus" (WIKIPEDIA, 2012)  e em  "SPI library - Arduino" (IGOE; MELLIS, 2010).

Figura 6: Conexão entre mestre e escravo no protocolo SPI

A tabela seguinte mostra as conexões entre os módulos uCduino e de CPLD, prevendo as linhas da comunicação SPI e os sinais de RESET, de habilitação (ENABLEn) e de clock do registrador paralelo.

Num. de pino Arduíno Sinal Arduino Sinal CPLD Pino CPLD
8 CLK_REG CLK_REG 43
9 RESETn ResetN 1
10 SSn_SPI ----- N.C.
11 MOSI SerialIn 41
12 MISO ----- N.C.
13 SCK CLK_SRL 2
A0 ENABLEn EnableN 44

O código-fonte do projeto no uCduino está disponível na seção de download, mais abaixo.  Destacamos aqui o trecho que mostra as instruções de transferência de dados (fixos, neste caso) pelo modo SPI, para o CPLD, e o registro da informação na saída, ao final do processo.

// Transmite três bytes (valores fixos) e pára
 digitalWrite(CLK_REG, LOW);
 dado = 0x55;
 SPI.transfer (dado);
 dado = 0xAA;
 SPI.transfer (dado);
 dado = 0x6F;
 SPI.transfer (dado);
 digitalWrite(CLK_REG, HIGH);

Estrutura Física

O projeto é composto pelo módulo de CPLD e três placas de LEDs de catodo comum conectados respectivamente no CON1, CON2 e CON3 do CPLD, como mostra a Figura 6. Lembre-se de que o CPLD usado é o EPM7064, da família MAX7000S.

Figura 6: Estrutura Física do Projeto

Figura 6: Estrutura Física do Projeto

No projeto, as entradas são implementadas por outro dispositivo que controle-as, nesse caso utilizamos o kit de uCduíno (microcontrolador), mostrado na Figura 7. Já as saídas são implementadas pelas placas de LEDs e o deslocamento e armazenamento de bits pelo CPLD.

Figura 7: Microncontrolador uCduíno

Figura 7: Microncontrolador uCduíno

A Figura 8 dá uma visão superior da estrutura física, com o kit de CPLD e uCduíno.

Figura 8: Visão superior da Estrutura Física

Figura 8: Visão superior da Estrutura Física

Montagem e Roteamento

Fique atento para que o Vcc e o GND dos periféricos fiquem alinhados com o do CPLD. Veja na Figura 9 a ilustração da montagem do projeto com o kit CPLD, feita no Fritzing.

Observe que selecionamos o GCLK2, GCLK1 e o GCLRN para pinos externos com os jumpers (verde) na posição E, pois os mesmos serão controlados por outro dispositivo. Não esqueça de interligar os GNDs do mestre e do escravo. Caso utilize o uCduíno, você também pode alimentá-lo e interligar o Vcc dele com o nosso kit, pois ele fornece alimentação de 5 V.

Figura 8: Ligação na Matriz de Contatos

Figura 9: Ligação na Matriz de Contatos para o kit CPLD

A Figura 10 mostra a pinagem atribuida no Pin Planner, no Quartus II, de acordo com a ligação na matriz de contatos.

Figura 9: Atribuição de Pinos no Pin Planner

Figura 10: Atribuição de Pinos no Pin Planner

Como existem pinos específicos pré-definidos para a comunicação SPI do uCduíno, precisa-se prestar bastante atenção na montagem do mesmo na matriz de contatos. A Figura 11, feita no Fritzing, ilustra a ligação  do uCduino na matriz de contatos. Observe que os pinos 10 e 12 são do padrão SPI, mas não serão usados, pois possuímos apenas um escravo e é preciso somente enviar os dados, sem que o escravo precise informar algo.

Figura 11: Ligação na Matriz de Contatos para o kit uCduino

Figura 11: Ligação na Matriz de Contatos para o kit uCduino

A Figura 12 mostra um pedaço de código, da plataforma de programação para o uCduino, onde fez-se a definição dos pinos do projeto.

Figura 12: Plataforma de programação do uCduino

Figura 12: Plataforma de programação do uCduino

A ilustração da Figura 13 mostra a ligação entre os módulos, onde os pinos estão nomeados com os mesmos pinos do CPLD e do uCduino, para facilitar a montagem.

Figura 12: Ligação na Matriz entre CPLD (escravo) - uCduino (mestre)

Figura 13: Ligação na Matriz entre CPLD (escravo) - uCduino (mestre)

Gravação e Teste

Depois de implementar e atribuir os pinos do projeto, faça uma "Compilação Completa" para gerar o arquivo de gravação do projeto, além do programa procurar algum erro ou aviso no mesmo.

Para gravar o projeto siga o Tutorial de Criação de Projeto em Modo Esquemático na etapa de "Gravação do CPLD" clicando aqui. Lembre-se sempre de gravar o módulo de CPLD sem os periféricos, evitando problemas na pinagem atribuída em projetos anteriores. Também não esqueça de configurar as saídas não utilizadas como entradas tri-state.

Para testar o funcionamento desse projeto, monta-se a periferia como relatada na "Estrutura Física" juntamente com o dispositivo que controlará as entradas, em nosso caso o uCduíno. Feito isso, basta conferir se a informação enviada é a mesma que está em paralelo. A Figura 8 ilustra o teste realizado com a transmissão dos bytes 0x6FAA55 do mestre para o escravo.

Arquivos do Projeto

Para o download do projeto completo, clique aqui.

O arquivo está no formato ".zip", e inclui  duas pastas, sendo uma para o escravo e outra para o mestre. Os arquivos do escravo estão no formato de projeto do Quartus (".qpf"), de modo VHDL (".vhd"), o testbench já configurado (".vhd"), de gravação do CPLD (".pof"), de símbolo para usar em projetos mais avançados (".bsf"), entre outros. Já o arquivo do mestre está na plataforma do Arduino (".ino").

Referências

IGOE, Tom; MELLIS, David A.. SPI library. Disponível em: < http://arduino.cc/en/Reference/SPI > . Acesso em: 13 dezembro 2010.

SHULTE, Thomas. Implementing the Top Five Control-Path Applications with Low-Cost, Low-Power CPLDs. Altera Corporation. Disponível em: < http://www.altera.com/literature/wp/wp-01146-control-path-apps.pdf?GSA_pos=1&WT.oss_r=1&WT.oss=Implementing%20de%20Top%20Five > Acesso em: 10 de abril de 2012.

WIKIPEDIA (Org.). Serial Peripheral Interface Bus. Disponível em: < http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus > . Acesso em: 13 maio 2012.