segunda-feira, 30 de junho de 2014

[FPGA] Tutorial 3 - Shift Registers em VHDL

Olá pessoal,
Nesse tutorial a gente vai falar sobre shift registers, o que são? Para que serve? E ainda montar um projeto com um resultado muito interessante, o resultado vocês podem ver no vídeo abaixo.


- O que é um shift registers?

Shift registers, como o próprio nome propõe, é um circuito digital que possui dados que são armazenados e shifted, ou seja, "substituidos" como se saltassem a cada clock, juntos. 
Eu recomendo ao leitor dar uma olhada na página do wikipedia sobre shift registers e conferir todas as imagens e entedê-las.

Esse tipo de circuito é bem útil e usado para os vários propósitos, e em projetos maiores ele pode ser conveniente.


- O nosso projeto

Como de costume, abra o Quartus (aqui estamos utilizando a v. 13.1) e crie um novo projeto. 


Figura 1: Criando um novo projeto.

Qualquer maior dificuldades nesse passo, consulte o tutorial 01. Em File > New > VHDL File, crie um arquivo VHDL em branco.


Figura 2: Criando um novo arquivo VHDL.

Agora é só descrever o nosso hardware.

-- declara ao compilador a biblioteca usada
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
                                              
-- entidade shift_register           
ENTITY shift_register IS             
-- listagem de entradas e saidas 
PORT(                                      
LEDG: OUT STD_LOGIC_VECTOR (9 downto 0);
SW:         IN STD_LOGIC_VECTOR (9 downto 0);
CLOCK_50:                 IN STD_LOGIC                                
);                           
END shift_register;   
                             
-- descrever o comportamento do circuito
ARCHITECTURE behavioral OF shift_register IS
-- contador para o tempo (aprx. um quarto de segundo)
SIGNAL temporal: INTEGER RANGE 0 TO 12500000 := 0; 
-- inicialmente nenhum LED é ligado.
SIGNAL vetor_temp: STD_LOGIC_VECTOR (9 downto 0) := (OTHERS => '0');
-- indice para deslocar o vetor
SIGNAL i: INTEGER RANGE 0 TO 9 := 0;
BEGIN
-- LEDs possuirao o valor de vetor_temp
LEDG <= vetor_temp;
-- CLOCK_50 na lista de sensibilidade do process
PROCESS (CLOCK_50)
BEGIN
-- na borda de subida do clock
IF rising_edge(CLOCK_50) THEN
-- caso tenha percorrido 1/4 segundo
IF(temporal = 12500000) THEN
-- reseta a variavel
temporal <= 0;
-- desloca os valores
vetor_temp(9 downto 1) <= vetor_temp (8 downto 0);
-- transfere valor da chave compassadamente para o vetor_temp(0)           
vetor_temp(0) <= SW(9-i);
-- reseta o indice quando chegar no limite
IF (i = 9) THEN
i <= 0;
ELSE            
i <= i + 1;
END IF;        
ELSE                    
-- senao, continua contando (tempo correndo...)
temporal <= temporal + 1;
END IF;
END IF;
END PROCESS;
END behavioral;

Antes de compilar, importamos (ou declaramos) os pinos da placa. Para isso, Assignments -> Import Assignments.


Figura 3: Declarando os pinos da placa.

E finalmente, Processing> Start Compilation

Nota: qualquer dificuldade em algum desses passos, leia os tutoriais anteriores.


- Existem outras maneiras de fazê-lo?

Claro que sim. Assim como existem várias maneiras de descrever um objeto, existem várias maneiras de descrever um shift register em VHDL. Talvez uma possível solução seria utilizando o for loop e executar a mudança dos valores dentro do loop... seria um ótimo exercício tentar uma outra aproximação para arranjar o shift register.

É isso pessoal, e não esqueçam de ler a página do wikipédia :-) .



segunda-feira, 23 de junho de 2014

[FPGA] Tutorial 2 - Relógio Digital em VHDL


Olá pessoal, 
Mais um novo projeto com VHDL, nesse post iremos criar um simples e pequeno relógio digital utilizando VHDL.
Primeiramente, abra o Quartus II (a versão utilizada aqui é a 13.1)...
em File > New > New Quartus II Project crie um novo projeto (caso tenha alguma dúvida de como fazer, leia o primeiro tutorial)



Figura 1: Criando um novo projeto para o relógio digital.

Logo após, crie dois novos documentos VHDL File > New > VHDL File
Dai você pergunta, por que dois? Bom, nesse tutorial a gente vai criar uma função que irá converter números inteiros em um STD_LOGIC_VECTOR com valores correspondente a sua imagem no display de 7 segmentos, e para isso, precisaremos de um novo arquivo VHDL.

Antes de começar a programar, vamos entender como funciona o display de 7 segmentos... primeiramente, vale lembrar que cada display é composto por 7 LEDs (light emitting diode). No nosso caso, o display é conectado em ânodo comum, ou seja, para ligar o LED devemos atribuir um sinal de tensão baixo (zero) e para desligar devemos atribuir um sinal alto (um). Caso o display seja ligado em cátodo comum, basta inverter a lógica.



Figura 2: Esquemático do display de 7 segmentos.

Por exemplo, para "escrever" o número 5, escreveriamos um vetor com o seguinte valor:

| g | f | e | d | c | b | a |
| 0 | 0 | 1 | 0 | 0 | 1 | 0 |

assim, "b" e "e" estariam apagados e o restante ligado.
Ou o número 9 (apenas "e" desligado): 

| g | f | e | d | c | b | a |
| 0 | | 1 | 0 | 0 | 0 | 0 |

Para facilitar e diminuir o nosso código criaremos uma função que irá converter os números em seus respectivos códigos para o display. Chamaremos essa função de "int2seg". int2seg deve ser criada em um arquivo contendo uma estrutura determinada, necessitaremos criar um PACKAGE. Com os PACKAGES você pode compartilhar soluções padrões para certos problemas e compartilhar entre diferentes estruturas. Para criar um PACKAGE precisamos:

- inserir as bibliotecas;
- declarar o package e o seu nome;
- criar o package body;
- criar suas funções e/ou procedures;
- inserir seu package em seu código principal.

a estrutura de um package

PACKAGE nome_do_package IS
             -- DECLARA AS FUNÇÕES, CONSTANTES, SINAIS, PROCEDURES
END nome_do_package;
                                 
PACKAGE BODY nome_do_package IS
            -- CRIA SUAS DECLARAÇÕES
END nome_do_package ;

Para criar a função criamos um package com nome PACOTE e dentro dele declaramos nossa função.

FUNCTION int2seg(A: INTEGERRETURN STD_LOGIC_VECTOR;

Essa função tem como argumento um número inteiro (armazenado como A) e retorna um STD_LOGIC_VECTOR (o código correspondendo para o display de 7 segmentos)

No package body a mesma declaração é utilizada, uma variável chamada result é criada, ela armazenará o valor correto do display e será retornada com o comando RETURN 

FUNCTION int2seg(A: INTEGERRETURN STD_LOGIC_VECTOR IS
VARIABLE result: STD_LOGIC_VECTOR(6 downto 0);
BEGIN 
CASE IS
WHEN 0 => result := "1000000";
                        .                            
                        .                            
                        .                            
END CASE;
RETURN result;
END int2seg;

O comando USE insere o package no nosso arquivo com a nossa função.

USE work.PACOTE.all; -- INSERE O PACKAGE 

Agora que já podemos escrever no display, precisamos fazer "o tempo correr". Isso é feito por meio do clock. A placa DE0 possui um clock interno de 50Mhz, isso significa temos um período de 20ns de duração. Ou seja, se pudermos contar de 0 até 49999999 contaremos 50000000, se cada contagem demora 20ns, contaremos um segundo. Teoricamente isso é correto, porém na prática, existe um pequeno erro (que não interfere no resultado).

A contagem será feita dentro do bloco PROCESS. Com process somos capazes de utilizar operações sequenciais como if-else, case... dentro do bloco process tudo é executado sequencialmente. Process possui uma "lista de sensibilidade" que irá disparar os eventos dentro dele. No nosso relógio, o process é executado observando o CLOCK_50, que é o clock do FPGA.

PROCESS(CLOCK_50) 

O código para o relógio é encontrado abaixo.
____________________________________________________

relogio_digital.vhdl

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
USE work.PACOTE.all;
ENTITY relogio_digital IS
GENERIC (SEG: INTEGER := 49999999); -- DEFINE O NUMERO DE CLOCKS PARA CONTAR UM SEGUNDO
PORT(
        -- AQUI DEFIMOS AS ENTRADAS E SAIDAS
HEX0_D :OUT STD_LOGIC_VECTOR (6 downto 0);
HEX1_D :OUT STD_LOGIC_VECTOR (6 downto 0);
HEX2_D :OUT STD_LOGIC_VECTOR (6 downto 0);
HEX3_D :OUT STD_LOGIC_VECTOR (6 downto 0);
CLOCK_50 :IN STD_LOGIC
);
END relogio_digital;

ARCHITECTURE behavioural OF relogio_digital IS 
SIGNAL TEMPORAL: INTEGER RANGE 0 TO SEG;

SIGNAL SS0: INTEGER RANGE 0 TO 59 := 0; -- SEGUNDOS
SIGNAL HH0: INTEGER RANGE 0 TO 9 := 0; --PRIMEIRO CARACTERE DA HORA
SIGNAL HH1: INTEGER RANGE 0 TO 2 := 0; --SEGUNDO CARACTERE DA HORA
SIGNAL MM0: INTEGER RANGE 0 TO 9 := 0; --PRIMEIRO CARACTERE DOS MINUTOS
SIGNAL MM1: INTEGER RANGE 0 TO 5 := 0; --SEGUNDO CARACTERE DOS MINUTOS

BEGIN
        -- ATRIBUI OS VALORES DOS SINAIS PARA O DISPLAY DE 7 SEGMENTOS
HEX0_D <= int2seg(MM0);
HEX1_D <= int2seg(MM1);
HEX2_D <= int2seg(HH0);
HEX3_D <= int2seg(HH1);

PROCESS(CLOCK_50) 
BEGIN
IF rising_edge(CLOCK_50) THEN -- NA BORDA DE SUBIDA
                        -- SE O CONTADOR NAO CHEGAR NO FIM DE 1 SEG
IF(TEMPORAL /= SEG) THEN
TEMPORAL <= TEMPORAL + 1; -- INCREMENTA 
                        -- SENAO, RESETA O CONTADOR E ATRIBUI UM SEGUNDO
ELSE 
TEMPORAL <= 0;
SS0 <= SS0 + 1;
IF(SS0 = 59) THEN -- 60 segundos eh 1 minuto
SS0 <= 0;
MM0 <= MM0 + 1;
IF(MM0 = 9) THEN -- CASO PASSE 10 minutos
MM0 <= 0; -- RESETA O DISPLAY 0
MM1 <= MM1 + 1; -- INCREMENTA O DISPLAY 1
                                      -- 60 MINUTOS EH 1 HORA E ASSIM POR DIANTE...
IF(MM1 = 5) THEN 
MM1 <= 0;
HH0 <= HH0 + 1;
IF(HH0 = 9) THEN
HH0 <= 0;
HH1 <= HH1 + 1;
ELSIF (HH0 = 3 AND HH1 = 2) THEN HH1 <= 0; HH0 <= 0; MM1 <= 0; MM0 <= 0;
END IF;
END IF;
END IF;
END IF;
END IF;
END IF;
END PROCESS;
END behavioural;

________________________________________________________

relogio_digital_pk.vhdl

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
PACKAGE PACOTE IS
FUNCTION int2seg(A: INTEGER) RETURN STD_LOGIC_VECTOR;
END PACOTE;
PACKAGE BODY PACOTE IS
FUNCTION int2seg(A: INTEGER) RETURN STD_LOGIC_VECTOR IS
VARIABLE result: STD_LOGIC_VECTOR(6 downto 0);
BEGIN 
CASE A IS
WHEN 0 => result := "1000000";
WHEN 1 => result := "1111001";
WHEN 2 => result := "0100100";
WHEN 3 => result := "0110000";
WHEN 4 => result := "0011001";
WHEN 5 => result := "0010010";
WHEN 6 => result := "0000010";
WHEN 7 => result := "1011000";
WHEN 8 => result := "0000000";
WHEN 9 => result := "0010000";
WHEN OTHERS => result := (OTHERS=>'0');
END CASE;
RETURN result;
END int2seg;

END PACOTE;

________________________________________________________

Salve os arquivos e compile Processing > Start Compilation.


Figura 3: Salvando os arquivos...

Se nenhum erro for reportado, é hora de declarar a pinagem. Isso pode ser feito da maneira antiga (ensinada no tutorial 1) ou importando a pinagem da placa DE0 utilizando os arquivos dentro do DVD fornecido pela terasic junto com sua placa. 
Para importar os pinos: Assignments > Import Assignments e escolha o arquivo DE0_Default.qsf (esse arquivo deve ser facilmente encontrado pela internet, caso você não possua o DVD).


Figura 4: Importando todos os pinos da placa DE0.

Compile novamente.

- Salvando seu programa na memória EEPROM.

Para que seu programa possa rodar mesmo depois da placa ser desligada precisamos primeiro declarar o tipo de EEPROM. A DE0 possui um chip Altera EPCS4. Em Assignments > Device clique em Device and Pin Options. Na nova tela marque o checkbox "use configuration device" e escolha o chip EPCS4.

Figura 5: Assignments > Device.

Figura 6: Selecionando a EEPROM.

Compile seu programa novamente.
Na sua placa selecione o modo PROG.

Figura 7: Modo PROG.

Logo após, em Tools > Programmer, selecione a opção Mode: Active Serial Programming e procure pelo arquivo com a extensão .pof

Figura 8: Programando a placa.

Clique em open e start. Ao completar o processo, troque o modo da placa para RUN e voilà.

Figura 9: Resultado final.

- Incrementos para o relógio...

É fato que o relógio começa no 00:00 e, a não ser que você ligue ele na meia-noite, ele vai funcionar mais como um "cronômetro". Então seria um bom desafio fazer com que o usuário pudesse corrigir o valor do relógio a partir dos botões e chaves da placa. Com esse mesmo código acima eu fiz um relógio capaz de editar a hora, confira no vídeo abaixo.



É isso, qualquer dúvida é só comentar.


terça-feira, 3 de junho de 2014

[FPGA] Tutorial 1 - Ligando LEDs com Switches em VHDL

Oi pessoal,
Nesse primeiro tutorial de VHDL para FPGA estaremos ligando os LEDs (light emitting diode) a partir das nossas chaves embutidas na placa. O conjunto de ferramentas utilizadas nesse projeto:

-          Altera DE0 Board;
-          Quartus II (versao 13.1);
-          Windows 8;

Acredito que você não enfretará nenhum problema caso esteja utilizando outra placa. O procedimento é basicamente o mesmo, embora as especificações do seu projeto (modelo do chip, modelo da EEPROM, localização dos pinos) serão diferentes.

Para iniciar, se este é o seu primeiro contato com a placa, você deve instalar os drivers USB-BLASTER para que a placa seja reconhecida pelo sistema operacional, para isso, no gerenciador de dispositivos do seu PC (pressione a tecla do windows + pause/break simultaneamente e clique em gerenciador de dispositivos) procure pelo USB-BLASTER e instale os drivers (são encontrados na pasta do Quartus II X:\Altera\13\quartus\drivers\usb-blaster). 


Figura 1: O driver foi instalado com sucesso.

Nota: Qualquer maior dificuldade, o manual do usuário deve ser consultado.

Ok, agora que sua placa já é reconhecida é hora de começar o projeto. Abra o Quartus II…


Figura 2: Tela inicial do Quartus II.


Clique em New Project Wizard
Nesse momento você irá passar por uma série de janelas as quais definirão as características da sua placa e do seu projeto. Leia a primeira tela e clique em next.


Figura 3: new project wizard.

Na segunda janela, escolha a pasta aonde você irá salvar seu projeto e nos outros dois campos escreva o nome do seu projeto, no caso “chaves”.

Dica: Não escolha nomes e diretórios com espaços vazio (“ “) nos seus projetos. Às vezes acontecem erros de compilação por causa disso... recomendo sempre utilizar caminhos curtos e sem espaços.


Figura 4: definindo a pasta e o nome do seu projeto.

Clique em next, nessa janela o programa pergunta se você já tem algum arquivo pronto e quer adicionar ao projeto, no nosso caso não temos nenhum, então pulamos essa janela, next.


Figura 5: adicionar arquivos ao projeto.

Nesse momento definimos a família do nosso FPGA. Para a DE0 Board escolhemos a família Cyclone III e em devices selecionamos EP3C16F484C6.

Nota: Caso esteja utilizando uma placa diferente as especificações nessa etapa serão diferentes. Observe qual a família e o chip da sua placa. Por exemplo, se estiver utilizando a placa DE2 você deve escolher a família Cyclone II e EP2C35F672C6.


Figura 6: Especificação da família.


Clicando em next, o programa pergunta se estaremos utilizando alguma ferramenta extra para utilização no projeto, como ferramentas CAD (EDA e CAD são basicamente sinônimos).


Figura 7: Escolha de ferramentas EDA.




A última tela exibe um relatório com todas as configurações que você escolher, revise e clique em finish.
Depois de instalar os drivers e configurar o seu projeto, eh hora de programar. Clique em File > New escolha a opção VHDL. Um editor de texto vai aparecer dentro da janela do Quartus II. Agora clique em Project > Add current file to project, com isso seu arquivo VHDL é adicionado ao seu projeto.

LIBRARY ieee;
USE ieee.std_logic_1164.all;
ENTITY chaves IS
        PORT (     
                LEDS :OUT STD_LOGIC_VECTOR (9 downto 0);
                SWITCHES :IN STD_LOGIC_VECTOR (9 downto 0)
        );
END chaves;

ARCHITECTURE arc_chaves OF chaves IS
BEGIN
        LEDS <= SWITCHES;

END arc_chaves;

Pronto, agora que o código ficou pronto, só nos resta entendê-lo.

LIBRARY ieee;
USE ieee.std_logic_1164.all;

Esse trecho do código chama o pacote ieee que é padrão para o VHDL. Dentro dele estao contidos vários outros arquivos que definem estruturas no nosso código, no exemplo de hoje chamamos apenas a std_logic_1164 que define sinais do tipo STD_ULOGIC, STD_ULOGIC_VECTOR e o resolved ulogic: STD_LOGIC e STD_LOGIC_VECTOR.





Figura 8: Código VHDL.

ENTITY chaves IS
        PORT (     
                LEDS :OUT STD_LOGIC_VECTOR (9 downto 0);
                SWITCHES :IN STD_LOGIC_VECTOR (9 downto 0)
        );
END chaves;


Nesse momento, criamos a entidade chaves que contem dois STD_LOGIC_VECTOR que chamamos de LEDS e SWITCHES, o primeiro sendo uma saida e o segundo uma entrada. E/S são definidas dentro de PORT, observe que a última linha antes de ‘);’ não contem ‘;’

ARCHITECTURE __chaves OF chaves IS
BEGIN
        LEDS <= SWITCHES;
END __chaves;

Finalizando, criamos um architecture. Ele é usado para descrever o comportamento, fluxo de dados e estrutura de uma entity. No exemplo, ele é utilizado para associar os valores de 'SWITCHES' nos 'LEDS'. Note que o sinal de atribuição não é o sinal de igualdade, e sim a seta '<='.

Para compilar nosso código clique no botão play roxo ou vá em Processing > Start compilation. Aguarde seu programa ser compilado, ao fim, o quartus irá exibir um relatório que pode ser acessado através do atalho ctrl + r.


Figura 9: Relatório de compilaçao.

Se seu programa não foi compilado com sucesso, revise as configurações do seu projeto (Assignment > Devices) e seu código. Agora, com tudo funcionando, vamos associar os pinos do seu FPGA com as entradas e saidas do nosso programa. Para isso, clique em Assignment > Pin Planner, uma nova tela abrirá. Nessa tela, com auxilio do seu manual de usuário você irá associar os pinos da sua placa com as variáveis de entrada e saida criadas. Por exemplo, para a variável SWITCHES[0], em location digite J6 e o pino PIN_J6 será marcado.

Nota: PIN_J6 é associado ao SWITCH_0 da placa DE0. Consulte o manual de usuário caso possua outra placa.




Figura 10: Tabela de pinos do switch da placa DE0.



Figura 11: Pin Planner.

Após associar todos os pinos, compile seu programa novamente.
Para testar seu programa na placa, ligue ela apertando no botao vermelho e com a chave no modo RUN. Clique em Programmer > Tools em Hardware Setup selecione USB-BLASTER e em mode selecione JTAG. Selecione o seu arquivo chaves.sof e clique em Start. Imediatamente o programa é transferido para sua placa e já pode ser testado.


Figura 12: gravacao da placa utilizando JTAG.

Agora é só usar as chaves para ligar os leds e voalà.


Figura 13: Resultado final.


Em breve estarei postando e ensinando a vocês como gravar o seu programa utilizando o active serial programming para que seu programa seja aberto mesmo quando a placa é desligada.

É isso ai, até a próxima.