Mudanças entre as edições de "DI2022802 2024 1 AULA12"

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
 
(52 revisões intermediárias pelo mesmo usuário não estão sendo mostradas)
Linha 266: Linha 266:
 
*O primeiro carácter deve ser uma letra.
 
*O primeiro carácter deve ser uma letra.
 
*O último não pode ser ''underscore''.
 
*O último não pode ser ''underscore''.
*Não são permitidos 2 ''underscores'' em sequência.
+
*Não são permitidos dois ''underscores'' em sequência.
 
*Maiúscula / Minúscula são equivalentes.
 
*Maiúscula / Minúscula são equivalentes.
  
 
==Objetos de dados==
 
==Objetos de dados==
 +
 +
Existem 3 tipos de objetos: sinais, constantes e variáveis. Observadas as seguintes regras:
 +
# Não pode ser uma palavra-chave de VHDL.
 +
# Tem que iniciar com uma letra.
 +
# Não pode terminar com ''underscore'' "_".
 +
# Não pode ter dois caracteres ''underscore'' juntos.
  
 
;CONSTANTES: - Assumem apenas um valor em todo o código.
 
;CONSTANTES: - Assumem apenas um valor em todo o código.
Linha 300: Linha 306:
 
  variable resultado: std-logic := ‘0’
 
  variable resultado: std-logic := ‘0’
  
==Tipos de Dados==
+
==Tipos de Dados Básicos em VHDL==
  
 +
Um tipo de dado é uma classificação do conjunto possível de valores que determinado item pode assumir. VHDL é uma linguagem fortemente tipada, o que significa que a escolha do tipo de dado para um sinal, variável ou constante é de suma importância pois, para converter de um tipo ao outro, devemos utilizar funções de conversão. Uma vantagem de se utilizar uma linguagem fortemente tipada é que o sintetizador pode perceber a maioria dos erros cometidos pelos projetistas. Por exemplo, atribuir um grupo de sinais de 4 bits para um grupo de 8 bits ou atribuir um grupo de bits sem representação numérica para um grupo de bits representando um inteiro.
  
VHDL é ''strongly typed'': objetos de dados de tipos diferentes não podem ser atribuídos um ao outro, sem conversão explícita.
+
É importante salientar que, apesar de usar um estilo de programação, a linguagem VHDL é uma linguagem de descrição de hardware, portanto no final da síntese todos os tipos assumem valores altos ou baixos. Os conceitos de tipos das linguagens de programação não existem em nenhuma HDL e esse é um dos erros mais comuns dos projetistas de hardware.  
  
Tipos escalares
+
;Lembre-se: Você não está descrevendo um programa e sim um hardware.
  
*Enumeration Types
+
O tipo de dado implicitamente influencia na síntese do seu circuito.
Lista de valores que o objeto pode assumir.
 
  
Exemplo:
+
===Tipos pré-definidos===
  
type estado is (espera, erro, cálculo, transmitido);
+
Os tipos de VHDL são definidos pelos padrões IEEE 1076 e IEEE 1164. São divididos em escalares, vetores, enumerados e compostos. Todos os tipos pré-definidos estão na biblioteca <code style="color: red">std.standard</code>, que é incluída implicitamente em todos os projetos de VHDL (não é necessário incluí-la).
  
Pode-se então declarar um sinal do tipo:
 
  
signal estado_atual: estado;
+
{| class="wikitable" style="text-align:center;"
 +
|- style="font-weight:bold;"
 +
! Tipo
 +
! Categoria
 +
! style="text-align:left;" | Sintetizável?
 +
! Valores
 +
|-
 +
| bit
 +
| enumerado
 +
| Sim
 +
| 0 ou 1
 +
|-
 +
| boolean
 +
| enumerado
 +
| Sim
 +
| FALSE ou TRUE
 +
|-
 +
| real
 +
| escalar
 +
| Não
 +
| -1.0E38 a +1.0E38
 +
|-
 +
| character
 +
| enumerado
 +
| Não
 +
| ASCII
 +
|}
  
Os dados desse tipo são ordenados, ou seja
 
  
erro > = espera
+
O tipo <code style="color: red">bit</code> é o mais utilizado. O <code style="color: red">boolean</code> é útil para tomadas de decisão, como por exemplo em condições para um <code style="color: red">if-else</code>. É importante notar que há um mapeamento direto entre <code style="color: red">FALSE</code> e <code style="color: red">0</code>, e entre <code style="color: red">TRUE</code> e <code style="color: red">1</code>, portanto <code style="color: red">FALSE</code><<code style="color: red">TRUE</code>. O <code style="color: red">real</code> normalmente é tratado como um número de ponto flutuante de precisão dupla. O <code style="color: red">character</code> representa um grupo de 8 bits correspondentes aos 256 caracteres da tabela ASCII. Note que estes dois últimos '''não são sintetizáveis''', portanto não devem ser utilizados como entradas ou saídas dos módulos. Apesar de não serem sintetizáveis, estes tipos são úteis durante as simulações.
  
Já definidos pela norma:
+
O tipo <code style="color: red">integer</code> também é bastante utilizado e possui dois subtipos padrões:
  
<syntaxhighlight lang=vhdl>
 
type boolean is (FALSE, TRUE);
 
type bit is (‘O’, ‘1’);
 
type std_ulogic is ( ‘U’, -- não inicializada
 
                    ‘X’, -- desconhecida forte
 
                    ‘0’, -- 0 forte
 
                    ‘1’, -- 1 forte
 
                    ‘Z’, -- alta impedância
 
                    ‘W’, -- desconhecida fraca
 
                    ‘L’, -- O fraco
 
                    ‘H’, -- 1 fraco
 
                    ‘-’, -- tanto faz
 
);
 
subtype std_logic is resolved std_ulogic
 
</syntaxhighlight>
 
  
resolved -> Existe uma função de resolução para os casos em que existe + de 1 driver para o sinal.
+
{| class="wikitable" style="text-align:center;"
 +
|- style="font-weight:bold;"
 +
! Tipo
 +
! Categoria
 +
! Sintetizável?
 +
! Valores
 +
|-
 +
| integer
 +
| escalar
 +
| Sim
 +
| -2147483647 a 2147483647
 +
|-
 +
| natural
 +
| escalar
 +
| Sim
 +
| 0 a 2147483647
 +
|-
 +
| positive
 +
| escalar
 +
| Sim
 +
| 1 a 2147483647
 +
|}
  
'''NOTA''': para usar std_logic e std_ulogic devem-se acrescentar as seguintes linhas antes da entidade:
+
A especificação da linguagem demanda que no mínimo os números da tabela sejam aceitos, mas não limita o número de bits do inteiro. Grande parte das ferramentas implementam o inteiro como um mapeamento direto para o inteiro de 32 bits, presente na maioria das plataformas. A forma de interpretação também não é definida, mas a maioria das ferramentas interpreta como uma representação em complemento de dois. Os tipos <code style="color: red">natural</code> e <code style="color: red">positive</code> são apenas limitações nos valores que um objeto deste tipo poderá assumir. É possível declarar inteiros com uma limitação personalizada:
  
 
<syntaxhighlight lang=vhdl>
 
<syntaxhighlight lang=vhdl>
library ieee;
+
signal meusinal : integer range -8 to 7;
use ieee.std_logic_1164.all;
 
[...]
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
O trecho acima declara o <code style="color: red">meusinal</code> como um inteiro de 4 bits. Contudo, valores maiores que a implementação da ferramenta de síntese não são possíveis, portanto se você precisar de um inteiro maior que 32 bits, verifique se sua ferramenta suporta inteiros grandes ou utilize vetores. A utilização da limitação do inteiro (com range ou usando os subtipos <code style="color: red">natural</code> e <code style="color: red">positive</code>) ajuda na detecção de erros pois, se em algum momento da simulação for feita a tentativa de atribuir um valor fora da faixa permitida, o simulador irá emitir uma mensagem de erro. Além disso, usar a limitação explícita diminui o número de bits utilizados para a representação, o que economizará portas lógicas no seu circuito. Pense assim: por que você precisa de um somador de 32 bits se seus inteiros só vão assumir valores de -16 a 15?
  
*Integer Types
+
Os tipos <code style="color: red">bit</code> e <code style="color: red">character</code> também possuem suas versões em vetores:
  
Inteiros na faixa de valores possíveis: (<math>-2^{31} - 1</math>) a (<math>2^{31} - 1</math>)
+
{| class="wikitable" style="text-align:center;"
 +
|- style="font-weight:bold;"
 +
! Tipo
 +
! Categoria
 +
! Sintetizável?
 +
! Valores
 +
|-
 +
| bit_vector
 +
| vetor
 +
| Sim
 +
| bits
 +
|-
 +
| string
 +
| vetor
 +
| Não
 +
| caracteres
 +
|}
  
Exemplo:
+
O <code style="color: red">bit_vector</code> é muito utilizado para representar um grupo de bits. Já o tipo <code style="color: red">string</code> é usado somente para mensagens durante a simulação (note que ele não é sintetizável).
  
variable altura: integer range 0 to 255;
+
Há ainda dois tipos que não são sintetizáveis mas são importantes em VHDL:
  
 +
{| class="wikitable" style="text-align:center;"
 +
|- style="font-weight:bold;"
 +
! Tipo
 +
! Categoria
 +
! Sintetizável?
 +
! Valores
 +
|-
 +
| severity_level
 +
| enumerado
 +
| Não
 +
| note, warning, error ou failure
 +
|-
 +
| time
 +
| enumerado
 +
| Não
 +
| depende
 +
|}
  
*Flooting Types
+
O tipo <code style="color: red">severity_level</code> é usado em ''testbenchs'' para informar a gravidade do problema encontrado. O tipo <code style="color: red">time</code> é usado para descrever a temporização do circuito, tanto em descrições temporizadas quanto em ''testbenchs''. Os valores de tempo são acompanhados dos multiplicadores que indicam a escala de tempo: <code style="color: red">fs</code> (fentosegundos), <code style="color: red">ps</code> (picosegungos), <code style="color: red">ns</code> (nanosegundos), <code style="color: red">us</code> (microsegundos), <code style="color: red">ms</code> (milisegundos), <code style="color: red">sec</code> (segundos), <code style="color: red">min</code> (minutos) e <code style="color: red">hr</code> (horas).
  
Esse tipo vai de -1.0E38 a 1.0E38, porém são pouco utilizados, por envolverem grande quantidade de recursos.
+
No exemplo abaixo, a mensagem "Teste" será impressa na tela sem parar a simulação e o <code style="color: red">sinal</code> assumirá o valor <code style="color: red">entrada</code>, mas somente após 10ns.
  
 +
<syntaxhighlight lang=vhdl>
 +
report "Teste" severity note;
 +
sinal <= entrada after 10 ns;
 +
</syntaxhighlight>
  
*Physical Types
+
Neste outro exemplo a definição dos valores de tempo:
 
 
Exemplo:
 
  
 
<syntaxhighlight lang=vhdl>
 
<syntaxhighlight lang=vhdl>
Linha 383: Linha 453:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
===Pacote IEEE 1164===
  
Este tipo relaciona-se diretamente a grandezas físicas
+
Um dos pacotes mais utilizados em VHDL é o <code style="color: red">std_logic_1164</code> da biblioteca <code style="color: red">ieee</code>, que define um MVL (lógica multivariada, ou o nome completo ''Multivalue Logic System for VHDL Model Interoperability''). Pra usar este pacote, é necessário incluir a declaração de uso no preambulo do seu projeto:
  
===Tipos compostos===
+
 +
<syntaxhighlight lang=vhdl>
 +
library ieee;
 +
use ieee.std_logic_1164.all;
 +
</syntaxhighlight>
  
*Array
 
  
Exemplo:
+
O tipo de dado primário definido nesta biblioteca é o <code style="color: red">std_ulogic</code> (''standard unresolved logic''), que pode assumir outros valores usados em projeto digital além dos valores ideais <code style="color: red">0</code> e <code style="color: red">1</code>. Esta modelagem de valores é mais próxima do mundo real, mas deve ser utilizada com cuidado.
 +
 
 +
{| class="wikitable" style="text-align:center;"
 +
|- style="font-weight:bold;"
 +
! Valor
 +
! Significado
 +
|-
 +
| U
 +
| Não inicializado (''uninitialized'')
 +
|-
 +
| X
 +
| Desconhecido (forte)
 +
|-
 +
| 0
 +
| Zero (forte)
 +
|-
 +
| 1
 +
| Um (forte)
 +
|-
 +
| Z
 +
| Alta impedância (''tri-state'')
 +
|-
 +
| W
 +
| Desconhecido (fraco)
 +
|-
 +
| L
 +
| Zero (fraco)
 +
|-
 +
| H
 +
| Um (fraco)
 +
|-
 +
| -
 +
| Qualquer um (''don't care'')
 +
|}
 +
 
 +
Exemplo de código <code style="color: red">std_ulogic</code> definidos pela norma:
  
 
<syntaxhighlight lang=vhdl>
 
<syntaxhighlight lang=vhdl>
type ward is array (15 downto 0) of bit;
+
type boolean is (FALSE, TRUE);
signal b: ward;
+
type bit is (‘O’, ‘1’);
 +
type std_ulogic is ( ‘U’, -- não inicializada
 +
                    ‘X’, -- desconhecida forte
 +
                    ‘0’, -- 0 forte
 +
                    ‘1’, -- 1 forte
 +
                    ‘Z’, -- alta impedância
 +
                    ‘W’, -- desconhecida fraca
 +
                    ‘L’, -- O fraco
 +
                    ‘H’, -- 1 fraco
 +
                    ‘-’, -- tanto faz
 +
);
 +
subtype std_logic is resolved std_ulogic
 
</syntaxhighlight>
 
</syntaxhighlight>
  
*Record
 
  
Exemplo:
+
O valor <code style="color: red">U</code> não foi pensado para ser utilizado pelo projetista mas sim pelas ferramentas de simulação. Quando seu circuito é simulado, um sinal em <code style="color: red">U</code> significa que até aquele momento não houve nenhuma atribuição para aquele sinal. Isso é útil para depuração pois permite diferenciar um sinal que nunca foi atribuído de um que foi atribuído com zero, por exemplo. É especialmente útil para detectar o esquecimento do acionamento do reset de um circuito, pois é comum os projetistas negligenciarem o reset antes de começar a simular.
 +
 
 +
Já o <code style="color: red">X</code> e o <code style="color: red">W</code> indicam valores que estão fora do escopo naquele ponto do projeto. Atribuir o valor <code style="color: red">X</code> para um sinal não é uma boa prática, mas ele também é útil em uma simulação. Quando aparecer um sinal com valor <code style="color: red">X</code> ou <code style="color: red">W</code> na sua simulação, muito provavelmente houve mais de uma atribuição para o mesmo sinal e elas são divergentes (e.g. uma atribuição <code style="color: red">0</code> e uma <code style="color: red">1</code> em pontos diferentes da sua descrição). Se sua simulação tem um destes valores, corrija o seu circuito antes de sintetizá-lo pois este valor não existe no mundo real: o circuito vai efetivamente assumir <code style="color: red">1</code> ou <code style="color: red">0</code>, fechando um curto-circuito caso haja uma atribuição divergente.
 +
 
 +
Note também que o <code style="color: red">X</code> '''não representa''' o ''don't care'' mas sim um valor desconhecido. Como a letra X é utilizada para o ''don't care'' nos métodos manuais (e.g. mapa de Karnaugh), é comum a confusão entre os dois valores.
 +
 
 +
A diferença entre um valor '''forte''' e '''fraco''' é apenas que os fracos indicam a utilização de resistores de ''pull-up'' ou ''pull-down'', portanto se uma saída <code style="color: red">H</code> for ligada a uma saída <code style="color: red">0</code>, o sinal será <code style="color: red">0</code> e não há problemas além do consumo de energia extra ocasionado pelo resistor. Contudo, se uma saída forte <code style="color: red">1</code> for ligada a uma saída <code style="color: red">0</code>, o resultado é um curto-circuito e possível dano ao circuito. Você pode livremente atribuir <code style="color: red">H</code>, <code style="color: red">1</code>, <code style="color: red">L</code> ou <code style="color: red">0</code> para um sinal, e ambos serão sintetizados similarmente, mas as versões <code style="color: red">H</code> e <code style="color: red">L</code> instruem o sintetizador a usar portas com tecnologia de dreno aberto (''open-drain'') ou similar, permitindo a utilização de resistores de ''pull-up'' ou ''pull-down''.
 +
 
 +
Se a plataforma alvo não suportar buffers ''tri-state'' o valor <code style="color: red">Z</code> não será sintetizado, mas as ferramentas normalmente conseguem inferir um decisor baseado em multiplexador para substituir a escolha de qual saída será colocada no sinal. Note que a plataforma alvo pode não ter ''tri-state'', então tome cuidado ao interligá-la com circuitos externos que esperam que ela tenha.
  
<syntaxhighlight lang=vhdl>
+
Outro tipo desta biblioteca é o <code style="color: red">std_logic</code>. Ele é idêntico ao <code style="color: red">std_ulogic</code> e pode assumir qualquer valor dos citados acima, mas tem uma diferença aos olhos do sintetizador: ele pode ser resolvido. O <code style="color: red">std_ulogic</code> não especifica o que acontece quando você faz duas atribuições para o mesmo sinal (não importa se diferente ou iguais). Se o sinal for <code style="color: red">std_logic</code>, o sintetizador entende que você sabe o que está fazendo e não te indicará nada ou no máximo mostrará uma mensagem de alerta, enquanto se o sinal for do tipo <code style="color: red">std_ulogic</code>, o sintetizador irá se recusar a continuar a síntese, alertando-o que há mais de uma atribuição para aquele sinal.
type iocell is record
+
 
    buffer_inp: bitvector (7 downto 0);
+
Ambos os tipos desta biblioteca suportam a versão em vetor:
    enable: bit;
+
 
    buffer_out: bitvector (7 downto 0);
+
 
end record;
+
{| class="wikitable" style="text-align:center;"
</syntaxhighlight>
+
|- style="font-weight:bold;"
 +
! Tipo
 +
! Categoria
 +
! Sintetizável?
 +
! Valores
 +
|-
 +
| std_ulogic
 +
| enumerado
 +
| Sim
 +
| multivariado
 +
|-
 +
| std_logic
 +
| enumerado
 +
| Sim
 +
| multivariado
 +
|-
 +
| std_ulogic_vector
 +
| vetor
 +
| Sim
 +
| std_ulogic s
 +
|-
 +
| std_logic_vector
 +
| vetor
 +
| Sim
 +
| std_logic s
 +
|}
 +
 
 +
Os valores multivariados são qualquer um da tabela no início desta seção. A síntese é possível para estes tipos, mas esteja atento para as observações nesta seção quando eles forem diferentes de <code style="color: red">0</code> ou <code style="color: red">1</code>.
 +
 
 +
===Qual tipo utilizar?===
 +
 
 +
 
 +
Não existe uma regra de que tipo utilizar, mas há várias dicas de como utilizar melhor a infraestrutura de tipos em VHDL.
 +
 
 +
A primeira dica é usar o bom senso. Se você está projetando uma unidade aritmética (e.g. multiplicador), faz pleno sentido que as entradas e saídas sejam <code style="color: red">unsigned</code> ou <code style="color: red">signed</code> de acordo com a maneira como a unidade aritmética as interpreta (e.g. o multiplicador é de inteiros sem ou com sinal). Contudo, se você está projetando um multiplexador, não faz sentido usar um tipo de dados com interpretação embutida pois o multiplexador não opera sobre os dados. Nesse caso, utilize o tipo <code style="color: red">bit</code> ou <code style="color: red">std_logic</code>.
 +
 
 +
 
 +
<span style="font-size:x-large;"><code style="color: red">std_logic</code> ou<code style="color: red">bit</code></span>
 +
 
 +
 
 +
Este é um tema de debate entre os projetistas há anos. A maioria dos projetistas opta por utilizar o <code style="color: red">std_logic</code> e evitar aborrecimentos, mas esta não é uma boa prática. Para escolher corretamente, você precisa pensar no circuito que está desenvolvendo e na arquitetura alvo.
 +
 
 +
O FPGA, por exemplo, não suporta internamente nenhum dos tipos do<code style="color: red">std_logic</code>, portanto não faz sentido utilizá-lo pois internamente só haverá bits. Se sua arquitetura alvo é um FPGA, como por exemplo nas disciplinas de laboratório, use sempre o tipo <code style="color: red">bit</code>. As exceções onde a utilização do <code style="color: red">std_logic</code> é correta são: (i) quando você estiver projetando um barramento, (ii) quando estiver lidando com a saída, e (iii) em simulações.
 +
 
 +
No caso (i) a utilização do ''tri-state'' pode ser útil pois você poderá interligar saídas sem problemas, desde que somente uma delas esteja ativa e as demais estejam em ''tri-state''. Contudo, se o seu barramento for interno ao FPGA, ele será sintetizado usando multiplexadores e não ''tri-state'' real pois o FPGA não tem esta funcionalidade internamente. Lembre-se que o ''tri-state'' do <code style="color: red">std_logic</code> é representado por <code style="color: red">Z</code>.
 +
 
 +
Já no caso (ii) você pode utilizar o <code style="color: red">std_logic</code> livremente pois a maioria dos FPGAs implementa ''open-drain'' e ''tri-state'' nos buffers de saída. Você pode facilmente usar ''tri-state'' e valores de ''weak'' (que implementam ''pull-up'' e ''pull-down''), mas lembre-se que nem todos os valores do ''std_logic'' são sintetizáveis.
 +
 
 +
No último caso (iii), o valor <code style="color: red">U</code> (''uninitialized'') pode ser útil para saber se um determinado valor foi ou não escrito alguma vez durante a simulação pois é o valor padrão do <code style="color: red">std_logic</code>. Na simulação todos os valores do <code style="color: red">std_logic</code> são expressos corretamente, mas lembre-se que na síntese os valores sempre vão assumir <code style="color: red">0</code> ou <code style="color: red">1</code> mesmo que nunca tenham tido um valor atribuído.
 +
 
 +
Uma das falácias do tipo <code style="color: red">std_logic</code> é o ''don't care''. Ele é representado pelo <code style="color: red">-</code> e não pelo <code style="color: red">X</code> (''unknown'') normalmente usado nos métodos manuais (e.g mapa de Karnaugh). Se usado corretamente, a maioria das ferramentas interpreta o <code style="color: red">-</code> como o ''don't care'' esperado, inclusive na atribuição condicional. Algumas ferramentas tratam ambos os <code style="color: red">X</code> e o <code style="color: red">-</code> como ''don't care'' para evitar a confusão, mas ela ainda acontece especialmente entre projetistas iniciantes, portanto evite-o.
 +
 
 +
De fato, a maioria dos projetos não necessita do <code style="color: red">std_logic</code> e acabam por utilizá-lo apenas com os valores <code style="color: red">0</code> ou <code style="color: red">1</code>, como substituição ao tipo <code style="color: red">bit</code>. Como regra geral, utilize o tipo de dado certo para o trabalho que está fazendo e, enquanto você estiver aprendendo, utilize somente o tipo bit para evitar problemas. Quando estiver confortável, transicione para o tipo <code style="color: red">std_ulogic</code> quando precisar de sinais multivariados e só quando realmente precisar de um sinal de multivariado com múltiplas atribuições (e.g. barramento) use o <code style="color: red">std_logic</code>.[5]
  
 
=Exercícios=
 
=Exercícios=
Linha 428: Linha 607:
  
 
;Parte 2
 
;Parte 2
#Projetar e simular:
+
#Projetar:
 
##Biestável tipo JK com ''clock'' e borda de subida, com descrição comportamental.
 
##Biestável tipo JK com ''clock'' e borda de subida, com descrição comportamental.
 
##Idem, com ''Preset'' e ''Clear'' assíncronos
 
##Idem, com ''Preset'' e ''Clear'' assíncronos
Linha 434: Linha 613:
 
#Comentar os códigos abaixo:
 
#Comentar os códigos abaixo:
  
*Código 1
+
{{collapse top|bg=#E6E6FA|Código 1}}
 
<syntaxhighlight lang=vhdl>  
 
<syntaxhighlight lang=vhdl>  
 
library ieee;
 
library ieee;
Linha 459: Linha 638:
 
end Ex1;
 
end Ex1;
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
{{collapse bottom}}
  
*Código 2
+
{{collapse top|bg=#E6E6FA|Código 2}}
 
 
 
<syntaxhighlight lang=vhdl>  
 
<syntaxhighlight lang=vhdl>  
 
library ieee;
 
library ieee;
Linha 492: Linha 671:
 
END teste;
 
END teste;
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
{{collapse bottom}}
  
*Código 3
+
{{collapse top|bg=#E6E6FA|Código 3}}
 
 
 
<syntaxhighlight lang=vhdl>  
 
<syntaxhighlight lang=vhdl>  
 
library ieee;
 
library ieee;
Linha 529: Linha 708:
  
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
{{collapse bottom}}
  
{{collapse top|Outros Exemplos}}
+
{{collapse top|bg=#E6E6FA|Outros Exemplos}}
  
 
*Código 4: VIGIA
 
*Código 4: VIGIA
Linha 696: Linha 876:
 
[4] [https://drive.google.com/file/d/1zVjHMsvCz-HFm3S2Vp0vWk1MSqTRHmqK/view?usp=sharing Videoaula VHDL]
 
[4] [https://drive.google.com/file/d/1zVjHMsvCz-HFm3S2Vp0vWk1MSqTRHmqK/view?usp=sharing Videoaula VHDL]
  
 +
[5] https://balbertini.github.io/vhdl_datatypes-pt_BR.html
  
  

Edição atual tal como às 15h20min de 25 de julho de 2024

VHDL - Linguagem de Descrição de Hardware

OBJETIVOS
O aluno será capaz de:
  • Saber sobre as definições da linguagem VHDL;
  • Conhecer o ciclo de projeto;
  • Diferenciar Entidades e Arquiteturas; e
  • Conhecer Identificadores e Tipos de Dados.


METODOLOGIA
A aula será expositiva (gravada), utilizando apresentação de texto base na Internet, onde será apresentado uma introdução ao VHDL que uma linguagem utilizada para descrição de hardware amplamente utilizada.


INTRODUÇÃO
VHDL ou (Very High Speed Integrated Circuits) é uma linguagem utilizada para descrever hardware. Ela foi concebida pelo departamento de defesa dos Estados Unidos (DARPA) na década de 80 para a documentação dos circuitos integrados vendidos às forças áreas americanas. Em 1987 ela foi padronizada pela IEEE como uma linguagem de descrição de hardware, o fato de ser padronizada e de domínio público ampliou e muito a sua utilização. A sua sintaxe é altamente tipada e lembra ADA e Pascal mas as semelhanças param por ai. A descrição de um circuito difere completamente de um software e muitas pessoas quando saem de uma linguagem de software e vem para linguagens de descrição de hardware encontram muitas dificuldades. Sempre mantenha em mente que você está descrevendo hardware e não escrevendo um algoritmo que um processador vai executar sequencialmente. VHDL é utilizando tanto para concepção de circuitos ASIC (por exemplo um i7 da Intel), quanto para desenvolvimento em FPGA. O resultado de uma “compilação” de um VHDL não é um executável mas sim uma netlist com todos os fios, conexões, componentes combinacionais e sequências de um hardware. [1]
NOTA
Este texto foi adaptado da Apostila Básica de VHDL do Prof. Dr. Renato Giacomini e da Apostila de Dispositivos Lógicos Programáveis dos professores: Caio Augusto de Oliveira, Jéssica Azevedo de Aguiar e Mateus Galvão Said Fontanini (UNESP).


Definições e Abreviaturas

ASIC (Application Specific Integrated Circuits) Circuito integrado de aplicação específica.
CPLD (Complex Programmable Logic Devices) Dispositivo lógico programável complexo.
FPGA (Field Programmable Gate Array) Arranjo de portas programável em campo.
VHDL (VHSIC Hardware Description Language). A sigla é derivada de outra abreviatura - VHSIC (Very High Speed Integrated Circuits), já que seu objetivo inicial era voltado ao projeto de circuitos integrados de altíssima velocidade.
VHSIC (Very High Speed Integrated Circuits) Circuito integrado de altíssima velocidade.


Histórico

Nas décadas de 70 e 80 foi posto em prática um programa do Departamento de Defesa (DoD) americano, para desenvolvimento de circuitos integrados de alta velocidade, denominado VHSIC e originado da preocupação de domínio das tecnologias envolvidas. Desde o princípio, anteviu-se o problema de representação dos projetos segundo uma linguagem que fosse comum às várias empresas envolvidas e que permitisse uma documentação fechada e clara para projetos de complexidade crescente. Nesta mesma época, já havia no mercado de computação a chamada crise do software, caracterizada pela dificuldade de gestão, documentação e sistematização do ciclo de vida do software (que envolve, em termos gerais, todas as atividades de relativas à sua criação e uso)[1]. O problema surgiu inicialmente no software porque era no software que se desenvolviam as aplicações, que tornavam o produto particular para um uso específico. Isso fazia com que um grande volume de projetos fossem conduzidos, quase sempre com muita complexidade e pouco método. No hardware a questão era mais bem controlada, pois os projetos eram, em geral, mais genéricos, especialmente para o hardware digital. Os projetos de microprocessadores, que eram os mais complexos, visavam na maior parte dos casos o uso geral. Um mesmo microprocessador seria utilizado em várias aplicações, seria reproduzido e testado aos milhões. Isso significava que o investimento em um único projeto poderia ser muito maior, inclusive em termos de especificação e testes. A evolução da metodologia de projeto de software foi então natural, em função dos argumentos sustentados pela crise. Hoje ainda é grande o desenvolvimento de novos métodos e a busca por uma linguagem descritiva universal, como o UML, para especificação e projeto de software orientado a objetos.

A questão preocupante quanto ao hardware, para o programa VHSIC americano, era que alguns fatos indicavam para uma evolução do mercado que faria surgirem problemas semelhantes aos apresentados pela crise de software. Entre estes fatos:

  • A fabricação de circuitos integrados deixava de ser exclusiva de alguns poucos fabricantes. Surgiam empresas especializadas na fabricação de projetos de terceiros, o que permitiria, em conjunto com o desenvolvimento de tecnologias de fabricação menos custosas, a criação de CIs de uso específico para certas aplicações.
  • As novas tecnologias de fabricação (abaixo de 2mm, na época) permitiam um aumento de densidade dos CIs, com conseqüente aumento de complexidade.
  • Surgiam componentes de lógica programável (PLDs e PALs), inicialmente de pequena densidade, mas que poderiam evoluir para densidades muito maiores rapidamente. Tais componentes poderiam criar, como de fato ocorreu posteriormente, um grande mercado de projetos de pequena escala e aplicações específicas, muito parecido com o mercado de projetos de software.

A criação de uma linguagem de descrição de hardware patrocinada pelo programa e, em paralelo algumas outras linguagens criadas pela indústria, foi então uma decorrência natural. Em 1987, o VHDL foi normalizado pelo IEEE, tornando-se um padrão mundial, ao lado do Verilog, uma alternativa também expressiva no mercado de projetos de hardware. Hoje, praticamente todas as ferramentas de desenvolvimento de hardware computadorizadas aceitam essas linguagens como entrada, de forma que um projeto baseado em VHDL ou Verilog pode ser implementado com qualquer tecnologia.


Vantagens do Uso

A alternativa para uso de uma linguagem formal de descrição de hardware como o VHDL é a descrição por diagramas esquemáticos. O VHDL apresenta as seguintes vantagens:

  • Em sistemas sequenciais, o detalhamento da lógica de controle é realizado pelas ferramentas de automação do projeto, o que evita a trabalhosa e limitada aplicação das técnicas manuais tradicionais;
  • O objetivo do projeto fica mais claro que na representação por esquemáticos, nos quais a implementação se sobrepõe à intenção do projeto;
  • O volume de documentação diminui, já que um código bem comentado em VHDL substitui com vantagens o esquemático e a descrição funcional do sistema;
  • O projeto ganha portabilidade, já que pode ser compilado em qualquer ferramenta e para qualquer tecnologia. É comum, na indústria, o uso de FPGAs e CPLDs para produções iniciais ou de menores escalas em projetos que posteriormente possam ser implementados em ASICs. Todas as implementações podem usar o mesmo código VHDL.


Ciclo de Projeto

O projeto de um sistema digital auxiliado por ferramentas computadorizadas segue normalmente as etapas descritas a seguir.


1. Especificação

Esta etapa visa determinar os requisitos e funcionalidade de projeto, incluindo timing dos sinais e definido completamente as interfaces. É uma etapa comum e necessária a qualquer projeto, independentemente do uso do VHDL.

2. Codificação

Nesta etapa, o objetivo é descrever, de forma estruturada e bem documentada, em VHDL, todo o projeto, segundo seus padrões de sintaxe. Assim, formaliza-se a especificação da etapa anterior, numa implementação de alto nível, em que são descritos apenas os aspectos relevantes à solução do problema.

3. Simulação de Código Fonte

Nesta etapa, procura-se simular o código em uma ferramenta confiável, a fim de verificar, preliminarmente, o cumprimento da especificação. Esta simulação não considera ainda detalhes tecnológicos de implementação.

4. Síntese Otimização e Fitting

Síntese - É o processo de “tradução” ou compilação de um código VHDL para uma descrição abstrata, em linguagem mais próxima da implementação. Naturalmente, a síntese é ainda um processo independente da tecnologia. Basicamente, o resultado obtido é o que se chama de RTL (register- transfer level), em que se definem registros, suas entradas e saídas e a lógica combinacional entre elas. Otimização - É o processo de seleção da melhor solução de implementação para uma dada tecnologia. Logicamente, as ferramentas EDA (Engineering Design Automation) são preparadas para aceitar diretivas de otimização, dependendo do resultado que se quer (minimização de área, consumo, tempo de resposta, etc). Fitting - É o processo em que a lógica sintezada e otimizada é mapeada nos recursos oferecidos pela tecnologia.

5. Simulação do modelo pós-layout

A simulação realizada com o resultado do fitting permite resultados mais apurados de comportamento e timing, porque considera as características da implementação definitiva na tecnologia, como, por exemplo, os tempos de propagação dos sinais.

6. Geração

É a fase de configuração das lógicas programáveis ou de fabricação de ASICs.

Entidades e Arquiteturas

O código abaixo representa um comparador binário para palavras de quatro bits:


-- comparador de 4 bits
entity comp4 is
    port ( a, b: in bit-vector (3 downto 0);
           equals: out bit);
end comp4;

architecture arq1 of comp4 is
begin
    equals <= 1 when (a=b) else O;
end arq1;


Uma entidade (entity) é uma abstração que descreve um sistema, uma placa, um chip, uma função ou, até mesmo, uma porta lógica. Na declaração de uma entidade, descreve-se o conjunto de entradas e saídas. No exemplo dado, a entidade comp4 possui duas entradas, a e b, e uma saída, equals, que definem o port da entidade.

Os ports correspondem a pinos e são tratados como objetos de dados. Pode-se atribuir valores ou obtê-los de ports. Cada entrada ou saída possui um modo (mode) de operação. Os modos possíveis são:

in – entrada;

out – saída: os pinos definidos como saída não podem ser utilizados como entradas, nem seus valores utilizados na lógica interna;

buffer - saída com possibilidade de realimentação;

inout - substitui qualquer um dos outros, mas seu uso deve ser limitado aos casos em que o pino deva ser utilizado como entrada e saída, para clareza na descrição.


Corpo de Arquitetura

A arquitetura de uma entidade pode ser descrita de três formas distintas, mas que, em geral, conduzem a uma mesma implementação.


Descrição comportamental

A arquitetura (architecture) descreve as funções realizadas pela entidade. No caso do exemplo é atribuído o valor ‘1 ‘ à saída equals, sempre que as entradas forem iguais e ‘0’, caso contrário.

Esta é a forma mais flexível e poderosa de descrição. São definidos processos concorrentes (process). A cada processo é associada uma lista de sensibilidade, que indica quais são as variáveis cuja alteração deve levar à reavaliação da saída. No simulador funcional, quando uma variável da lista é modificada, o processo é simulado novamente. O código abaixo ilustra a aplicação deste tipo de descrição ao comparador do exemplo.


-- comparador de 4 bits
entity comp4 is
    port ( a, b: in bit_vector (3 downto Æ);
           equals: out bit);
end comp4;

architecture comport of comp4 is
begin
    comp: process (a,b) -- lista de sensibilidade
    begin
       if a = b then
          equals < = 1 ;
       else
          equals < = O ;
       end if;
    end process comp;
end comport;


Uma arquitetura pode ter mais de um processo e eles serão executados concorrentemente entre si.


Descrição por fluxo de dados

Neste tipo de descrição, os valores de saída são atribuídos diretamente, através de expressões lógicas. Todas as expressões são concorrentes no tempo.

-- comparador de 4 bits
entity comp4 is
    port ( a, b: in bit_vector (3 downto 0);
           equals: out bit);
end comp4;
architecture fluxo of comp4 is
begin
    equals < = 1 when (a=b) else O;
end fluxo;

Descrição estrutural

A descrição estrutural apresenta netlists e instanciação de componentes básicos, ou seja, é como se fosse uma lista de ligações entre componentes básicos pré-definidos.

-- comparador de 4 bits
entity comp4 is
    port ( a, b: in bit_vector (3 downto 0);
           equals: out bit);
end comp4;

use work.gateskg.all;
architecture estrut of comp4 is
    signal x bit_vector (0 to 3);
begin
    U0: xnor2 port map (a(0), b(0), x(0));
    U1: xnor2 port map (a(1), b(1), x(1));
    U2: xnor2 port map (a(2), b(2), x(2));
    U3: xnor2 port map (a(3), b(3), x(3));
    U4: and4 port map (x(0), x(1), x(2), x(3), equals);
end estrut;

Identificadores

Os identificadores são usados como referência a todos os objetos declarados no código. As regras para formação de nomes são:

  • O primeiro carácter deve ser uma letra.
  • O último não pode ser underscore.
  • Não são permitidos dois underscores em sequência.
  • Maiúscula / Minúscula são equivalentes.

Objetos de dados

Existem 3 tipos de objetos: sinais, constantes e variáveis. Observadas as seguintes regras:

  1. Não pode ser uma palavra-chave de VHDL.
  2. Tem que iniciar com uma letra.
  3. Não pode terminar com underscore "_".
  4. Não pode ter dois caracteres underscore juntos.
CONSTANTES
- Assumem apenas um valor em todo o código.

Exemplo:

constant largura: integer: = 8 ;

Podem ser declaradas no níveis de

package
entity
architecture
process

e valem apenas no contexto em que são declaradas.


SINAIS
- Representam ligações (fios) que interligam componentes. Ports são exemplos de sinais.

Exemplo:

signal valor-de-contagem: bit-vector (3 downto 0):

Podem ser declarados na entidade ou na arquitetura.


VARIÁVEIS
- São utilizadas em processos e subprogramas e devem ser declaradas neles. São atualizadas imediatamente e não correspondem à implementação física, como no caso dos sinais.

Exemplo:

variable resultado: std-logic := ‘0’

Tipos de Dados Básicos em VHDL

Um tipo de dado é uma classificação do conjunto possível de valores que determinado item pode assumir. VHDL é uma linguagem fortemente tipada, o que significa que a escolha do tipo de dado para um sinal, variável ou constante é de suma importância pois, para converter de um tipo ao outro, devemos utilizar funções de conversão. Uma vantagem de se utilizar uma linguagem fortemente tipada é que o sintetizador pode perceber a maioria dos erros cometidos pelos projetistas. Por exemplo, atribuir um grupo de sinais de 4 bits para um grupo de 8 bits ou atribuir um grupo de bits sem representação numérica para um grupo de bits representando um inteiro.

É importante salientar que, apesar de usar um estilo de programação, a linguagem VHDL é uma linguagem de descrição de hardware, portanto no final da síntese todos os tipos assumem valores altos ou baixos. Os conceitos de tipos das linguagens de programação não existem em nenhuma HDL e esse é um dos erros mais comuns dos projetistas de hardware.

Lembre-se
Você não está descrevendo um programa e sim um hardware.

O tipo de dado implicitamente influencia na síntese do seu circuito.

Tipos pré-definidos

Os tipos de VHDL são definidos pelos padrões IEEE 1076 e IEEE 1164. São divididos em escalares, vetores, enumerados e compostos. Todos os tipos pré-definidos estão na biblioteca std.standard, que é incluída implicitamente em todos os projetos de VHDL (não é necessário incluí-la).


Tipo Categoria Sintetizável? Valores
bit enumerado Sim 0 ou 1
boolean enumerado Sim FALSE ou TRUE
real escalar Não -1.0E38 a +1.0E38
character enumerado Não ASCII


O tipo bit é o mais utilizado. O boolean é útil para tomadas de decisão, como por exemplo em condições para um if-else. É importante notar que há um mapeamento direto entre FALSE e 0, e entre TRUE e 1, portanto FALSE<TRUE. O real normalmente é tratado como um número de ponto flutuante de precisão dupla. O character representa um grupo de 8 bits correspondentes aos 256 caracteres da tabela ASCII. Note que estes dois últimos não são sintetizáveis, portanto não devem ser utilizados como entradas ou saídas dos módulos. Apesar de não serem sintetizáveis, estes tipos são úteis durante as simulações.

O tipo integer também é bastante utilizado e possui dois subtipos padrões:


Tipo Categoria Sintetizável? Valores
integer escalar Sim -2147483647 a 2147483647
natural escalar Sim 0 a 2147483647
positive escalar Sim 1 a 2147483647

A especificação da linguagem demanda que no mínimo os números da tabela sejam aceitos, mas não limita o número de bits do inteiro. Grande parte das ferramentas implementam o inteiro como um mapeamento direto para o inteiro de 32 bits, presente na maioria das plataformas. A forma de interpretação também não é definida, mas a maioria das ferramentas interpreta como uma representação em complemento de dois. Os tipos natural e positive são apenas limitações nos valores que um objeto deste tipo poderá assumir. É possível declarar inteiros com uma limitação personalizada:

signal meusinal : integer range -8 to 7;

O trecho acima declara o meusinal como um inteiro de 4 bits. Contudo, valores maiores que a implementação da ferramenta de síntese não são possíveis, portanto se você precisar de um inteiro maior que 32 bits, verifique se sua ferramenta suporta inteiros grandes ou utilize vetores. A utilização da limitação do inteiro (com range ou usando os subtipos natural e positive) ajuda na detecção de erros pois, se em algum momento da simulação for feita a tentativa de atribuir um valor fora da faixa permitida, o simulador irá emitir uma mensagem de erro. Além disso, usar a limitação explícita diminui o número de bits utilizados para a representação, o que economizará portas lógicas no seu circuito. Pense assim: por que você precisa de um somador de 32 bits se seus inteiros só vão assumir valores de -16 a 15?

Os tipos bit e character também possuem suas versões em vetores:

Tipo Categoria Sintetizável? Valores
bit_vector vetor Sim bits
string vetor Não caracteres

O bit_vector é muito utilizado para representar um grupo de bits. Já o tipo string é usado somente para mensagens durante a simulação (note que ele não é sintetizável).

Há ainda dois tipos que não são sintetizáveis mas são importantes em VHDL:

Tipo Categoria Sintetizável? Valores
severity_level enumerado Não note, warning, error ou failure
time enumerado Não depende

O tipo severity_level é usado em testbenchs para informar a gravidade do problema encontrado. O tipo time é usado para descrever a temporização do circuito, tanto em descrições temporizadas quanto em testbenchs. Os valores de tempo são acompanhados dos multiplicadores que indicam a escala de tempo: fs (fentosegundos), ps (picosegungos), ns (nanosegundos), us (microsegundos), ms (milisegundos), sec (segundos), min (minutos) e hr (horas).

No exemplo abaixo, a mensagem "Teste" será impressa na tela sem parar a simulação e o sinal assumirá o valor entrada, mas somente após 10ns.

report "Teste" severity note;
sinal <= entrada after 10 ns;

Neste outro exemplo a definição dos valores de tempo:

type time in range - 2147483647 to 2147483647
units
    fs;
    ps = 1000 fs;
    ns = 1000 ps;
    us = 1000 ns;
    ms = 1000 us;
    sec = 1000 ms;
    min = 60 sec;
    hr = 60 min;
end units;

Pacote IEEE 1164

Um dos pacotes mais utilizados em VHDL é o std_logic_1164 da biblioteca ieee, que define um MVL (lógica multivariada, ou o nome completo Multivalue Logic System for VHDL Model Interoperability). Pra usar este pacote, é necessário incluir a declaração de uso no preambulo do seu projeto:


library ieee;
use ieee.std_logic_1164.all;


O tipo de dado primário definido nesta biblioteca é o std_ulogic (standard unresolved logic), que pode assumir outros valores usados em projeto digital além dos valores ideais 0 e 1. Esta modelagem de valores é mais próxima do mundo real, mas deve ser utilizada com cuidado.

Valor Significado
U Não inicializado (uninitialized)
X Desconhecido (forte)
0 Zero (forte)
1 Um (forte)
Z Alta impedância (tri-state)
W Desconhecido (fraco)
L Zero (fraco)
H Um (fraco)
- Qualquer um (don't care)

Exemplo de código std_ulogic definidos pela norma:

type boolean is (FALSE, TRUE);
type bit is (O, 1);
type std_ulogic is ( U, -- não inicializada
                     X, -- desconhecida forte
                     0, -- 0 forte
                     1, -- 1 forte
                     Z, -- alta impedância
                     W, -- desconhecida fraca
                     L, -- O fraco
                     H, -- 1 fraco
                     -, -- tanto faz
);
subtype std_logic is resolved std_ulogic


O valor U não foi pensado para ser utilizado pelo projetista mas sim pelas ferramentas de simulação. Quando seu circuito é simulado, um sinal em U significa que até aquele momento não houve nenhuma atribuição para aquele sinal. Isso é útil para depuração pois permite diferenciar um sinal que nunca foi atribuído de um que foi atribuído com zero, por exemplo. É especialmente útil para detectar o esquecimento do acionamento do reset de um circuito, pois é comum os projetistas negligenciarem o reset antes de começar a simular.

Já o X e o W indicam valores que estão fora do escopo naquele ponto do projeto. Atribuir o valor X para um sinal não é uma boa prática, mas ele também é útil em uma simulação. Quando aparecer um sinal com valor X ou W na sua simulação, muito provavelmente houve mais de uma atribuição para o mesmo sinal e elas são divergentes (e.g. uma atribuição 0 e uma 1 em pontos diferentes da sua descrição). Se sua simulação tem um destes valores, corrija o seu circuito antes de sintetizá-lo pois este valor não existe no mundo real: o circuito vai efetivamente assumir 1 ou 0, fechando um curto-circuito caso haja uma atribuição divergente.

Note também que o X não representa o don't care mas sim um valor desconhecido. Como a letra X é utilizada para o don't care nos métodos manuais (e.g. mapa de Karnaugh), é comum a confusão entre os dois valores.

A diferença entre um valor forte e fraco é apenas que os fracos indicam a utilização de resistores de pull-up ou pull-down, portanto se uma saída H for ligada a uma saída 0, o sinal será 0 e não há problemas além do consumo de energia extra ocasionado pelo resistor. Contudo, se uma saída forte 1 for ligada a uma saída 0, o resultado é um curto-circuito e possível dano ao circuito. Você pode livremente atribuir H, 1, L ou 0 para um sinal, e ambos serão sintetizados similarmente, mas as versões H e L instruem o sintetizador a usar portas com tecnologia de dreno aberto (open-drain) ou similar, permitindo a utilização de resistores de pull-up ou pull-down.

Se a plataforma alvo não suportar buffers tri-state o valor Z não será sintetizado, mas as ferramentas normalmente conseguem inferir um decisor baseado em multiplexador para substituir a escolha de qual saída será colocada no sinal. Note que a plataforma alvo pode não ter tri-state, então tome cuidado ao interligá-la com circuitos externos que esperam que ela tenha.

Outro tipo desta biblioteca é o std_logic. Ele é idêntico ao std_ulogic e pode assumir qualquer valor dos citados acima, mas tem uma diferença aos olhos do sintetizador: ele pode ser resolvido. O std_ulogic não especifica o que acontece quando você faz duas atribuições para o mesmo sinal (não importa se diferente ou iguais). Se o sinal for std_logic, o sintetizador entende que você sabe o que está fazendo e não te indicará nada ou no máximo mostrará uma mensagem de alerta, enquanto se o sinal for do tipo std_ulogic, o sintetizador irá se recusar a continuar a síntese, alertando-o que há mais de uma atribuição para aquele sinal.

Ambos os tipos desta biblioteca suportam a versão em vetor:


Tipo Categoria Sintetizável? Valores
std_ulogic enumerado Sim multivariado
std_logic enumerado Sim multivariado
std_ulogic_vector vetor Sim std_ulogic s
std_logic_vector vetor Sim std_logic s

Os valores multivariados são qualquer um da tabela no início desta seção. A síntese é possível para estes tipos, mas esteja atento para as observações nesta seção quando eles forem diferentes de 0 ou 1.

Qual tipo utilizar?

Não existe uma regra de que tipo utilizar, mas há várias dicas de como utilizar melhor a infraestrutura de tipos em VHDL.

A primeira dica é usar o bom senso. Se você está projetando uma unidade aritmética (e.g. multiplicador), faz pleno sentido que as entradas e saídas sejam unsigned ou signed de acordo com a maneira como a unidade aritmética as interpreta (e.g. o multiplicador é de inteiros sem ou com sinal). Contudo, se você está projetando um multiplexador, não faz sentido usar um tipo de dados com interpretação embutida pois o multiplexador não opera sobre os dados. Nesse caso, utilize o tipo bit ou std_logic.


std_logic oubit


Este é um tema de debate entre os projetistas há anos. A maioria dos projetistas opta por utilizar o std_logic e evitar aborrecimentos, mas esta não é uma boa prática. Para escolher corretamente, você precisa pensar no circuito que está desenvolvendo e na arquitetura alvo.

O FPGA, por exemplo, não suporta internamente nenhum dos tipos dostd_logic, portanto não faz sentido utilizá-lo pois internamente só haverá bits. Se sua arquitetura alvo é um FPGA, como por exemplo nas disciplinas de laboratório, use sempre o tipo bit. As exceções onde a utilização do std_logic é correta são: (i) quando você estiver projetando um barramento, (ii) quando estiver lidando com a saída, e (iii) em simulações.

No caso (i) a utilização do tri-state pode ser útil pois você poderá interligar saídas sem problemas, desde que somente uma delas esteja ativa e as demais estejam em tri-state. Contudo, se o seu barramento for interno ao FPGA, ele será sintetizado usando multiplexadores e não tri-state real pois o FPGA não tem esta funcionalidade internamente. Lembre-se que o tri-state do std_logic é representado por Z.

Já no caso (ii) você pode utilizar o std_logic livremente pois a maioria dos FPGAs implementa open-drain e tri-state nos buffers de saída. Você pode facilmente usar tri-state e valores de weak (que implementam pull-up e pull-down), mas lembre-se que nem todos os valores do std_logic são sintetizáveis.

No último caso (iii), o valor U (uninitialized) pode ser útil para saber se um determinado valor foi ou não escrito alguma vez durante a simulação pois é o valor padrão do std_logic. Na simulação todos os valores do std_logic são expressos corretamente, mas lembre-se que na síntese os valores sempre vão assumir 0 ou 1 mesmo que nunca tenham tido um valor atribuído.

Uma das falácias do tipo std_logic é o don't care. Ele é representado pelo - e não pelo X (unknown) normalmente usado nos métodos manuais (e.g mapa de Karnaugh). Se usado corretamente, a maioria das ferramentas interpreta o - como o don't care esperado, inclusive na atribuição condicional. Algumas ferramentas tratam ambos os X e o - como don't care para evitar a confusão, mas ela ainda acontece especialmente entre projetistas iniciantes, portanto evite-o.

De fato, a maioria dos projetos não necessita do std_logic e acabam por utilizá-lo apenas com os valores 0 ou 1, como substituição ao tipo bit. Como regra geral, utilize o tipo de dado certo para o trabalho que está fazendo e, enquanto você estiver aprendendo, utilize somente o tipo bit para evitar problemas. Quando estiver confortável, transicione para o tipo std_ulogic quando precisar de sinais multivariados e só quando realmente precisar de um sinal de multivariado com múltiplas atribuições (e.g. barramento) use o std_logic.[5]

Exercícios

Parte 1
  1. O que significa CPLD, FPGA e VHDL?
  2. Quais as principais vantagens no uso do VHDL?
  3. Quais as etapas de um ciclo de projeto de sistemas em VHDL?
  4. Qual a diferença entre uma Entidade (entity) e uma Arquitetura (architecture)?
  5. O que são os Ports?
  6. Que tipo de pinos (ports) existem e onde são aplicados?
  7. O que é uma Descrição comportamental?
  8. O que é uma Descrição por fluxo de dados?
  9. O que é uma Descrição estrutural?
  10. Quais são as regras para a formação dos Identificadores?
  11. O que é uma constante?
  12. Qual a diferença de Sinais e de Variáveis?
  13. Quais os tipos de dados disponíveis em VHDL?
Parte 2
  1. Projetar:
    1. Biestável tipo JK com clock e borda de subida, com descrição comportamental.
    2. Idem, com Preset e Clear assíncronos
  2. Realizar o projeto de um contador síncrono, com uma sequência pré-definida em VHDL e simular.
  3. Comentar os códigos abaixo:
Código 1
 
library ieee;
use ieee.std_logic_1164.all;

entity FF_D is
	port
	(
		D	: in  std_logic;
		clk	: in  std_logic;
		Q	: out std_logic
	);
end FF_D;

architecture Ex1 of FF_D is
begin
-- Update the register output on the clock's rising edge
process (clk)
begin
	if (rising_edge(clk)) then
		Q <= D;
	end if;
end process;
end Ex1;
Código 2
 
library ieee;
use ieee.std_logic_1164.all;
 
ENTITY ff_jk is
    port(j,k,clk: in bit;
         q: out bit);
END ff_jk;
 
ARCHITECTURE teste of ff_jk is
BEGIN

process(clk)
variable temp : bit :='0';
begin
	if( falling_edge(clk) ) then
		if (j='1' and k='0') then
			temp:='1';
		elsif (j='0' and k='1') then
			temp:='0';
		elsif (j='1' and k='1') then
			temp:= not temp;
		else
			temp:=temp;
		end if;
		q<=temp;
	end if;
end process;
END teste;
Código 3
 
library ieee;
use ieee.std_logic_1164.all; 
use ieee.std_logic_arith.all;

entity CONTADOR is
port(
	CLK:	in  std_logic;
    	RESET:	in  std_logic;
	ENABLE:	in  std_logic;
	Q:	out std_logic_vector (3 downto 0)
);
end CONTADOR;

architecture CONTADOR_arq of CONTADOR is 
begin
	process(CLK,RESET)
		variable X: integer range 0 to 15;
	begin
		if (RESET = '1') then
			X := 0;

		elsif (CLK'event and CLK='1') then
			if (ENABLE = '1') then
				X := X + 1;
			end if;
		end if;

		Q <= conv_std_logic_vector(X, 4);

	end process;
end CONTADOR_arq;
Outros Exemplos
  • Código 4: VIGIA
--1 - VIGIA
LIBRARY ieee;
USE ieee.std_logic_1164.all;
ENTITY Vigia IS
PORT( clk : IN STD_LOGIC;
      SensorA, SensorB : IN STD_LOGIC;
      Multa_Vel, Multa_Comp : OUT STD_LOGIC);
END Vigia;

ARCHITECTURE Intuitiva OF Vigia IS
    TYPE STATE_TYPE IS (Espera,
        Verificando_Velocidade,
        Verificando_Tamanho,
        Multa_Velocidade,
        Multa_Tamanho, 
    Erro);
    SIGNAL Estado: STATE_TYPE;
    SIGNAL Cronometro: INTEGER RANGE 0 to 31;

BEGIN
    PROCESS (clk)
    BEGIN
        IF clk'EVENT AND clk = '1' THEN
            Multa_Vel <= '0';
            Multa_Comp <= '0';
            CASE Estado IS
                WHEN Espera =>
                    IF SensorA = '1' THEN
                        IF SensorB = '0' THEN
                            Estado <= Verificando_Velocidade;
                        ELSE
                            Estado <= Erro;
                        END IF;
                    END IF;
                WHEN Verificando_Velocidade =>
                    IF SensorA = '1' THEN
                        IF SensorB = '0' THEN
                            Cronometro <= Cronometro + 1;
                        ELSE
                            IF Cronometro < 8 OR Cronometro > 24 THEN
                                Estado <= Multa_Velocidade;
                            ELSE
                                Estado <= Verificando_Tamanho;
                            END IF;
                        END IF;
                    ELSE
                        IF SensorB = '0' THEN
                            Estado <= Espera;
                            Cronometro <= 0;
                        ELSE
                            Estado <= Erro;
                        END IF;
                    END IF;
                WHEN Verificando_Tamanho =>
                    IF SensorA = '1' THEN
                        IF SensorB = '1' THEN
                            Cronometro <= Cronometro -1;
                            IF Cronometro = 0 THEN
                                Estado <= Multa_Tamanho;
                            END IF;
                        ELSE
                            Estado <= Erro;
                        END IF;
                    ELSE
                        Estado <= Espera;
                        Cronometro <= 0;
                    END IF;
                WHEN Multa_Velocidade =>
                    Multa_Vel <= '1';
                WHEN Multa_Tamanho =>
                    Multa_Comp <= '1';
                WHEN Erro =>
                    Multa_Vel <= '1';
                Multa_Comp <= '1';
            END CASE;
        END IF;
END PROCESS;
END Intuitiva;
Exemplo 5: RECEPTOR
--2 – RECEPTOR
-- Receptor serial
entity Receptor is
    port( data_in, clock, limpa: in bit;
          pronto, ocupado: out bit;
          data_out: buffer bit_vector(7 downto 0));
end Receptor

architecture Receptor of Receptor is
    TYPE STATE_TYPE IS ( Espera,
                         Start_bit,
                         Recebendo,
                         Pronto);
    SIGNAL Estado: STATE_TYPE;
    SIGNAL Cronometro: INTEGER RANGE 0 to 7;
    SIGNAL Conta_bits: INTEGER RANGE 0 to 7;
process
begin
    if clock'event and clock = '1' then
    CASE Estado IS
        WHEN Espera =>
            IF Data_in = '0'then
                Cronometro <= 0;
                Ocupado <= '1';
                Estado <= Start_bit;
            ELSE
                Ocupado <= '0';
            END IF;
        WHEN Start_bit =>
            IF Cronometro < 4 then
                Cronometro = Cronometro+1;
            ELSE
                Cronômetro = 0;
                Conta_bits =0;
                Estado <= Recebendo;
            END IF;
        WHEN Recebendo =>
            IF Conta_bits < 6 then
                IF Cronometro < 3 then
                    Cronometro = Cronometro+1;
                ELSE
                    Cronômetro = 0;
                    Conta_bits = Conta_bits+1;
                    Data_out(0)<= Data_out(1);
                    Data_out(1)<= Data_out(2);
                    Data_out(2)<= Data_out(3);
                    Data_out(3)<= Data_out(4);
                    Data_out(4)<= Data_out(5);
                    Data_out(5)<= Data_out(6);
                    Data_out(6)<= Data_out(7);
                    Data_out(7)<= Data_in;
                END IF;
            ELSE
                Estado <= Pronto;
                Pronto <= '1';
            END IF;
        WHEN Pronto =>
            IF Limpa = 0 Then
                Ocupado <= '0';
                Pronto <= '0';
                Estado <= Espera;
            END IF;
    END CASE;
    END IF;
end process;
end Receptor;

Referências

[1] https://www.embarcados.com.br/vhdl-basico-parte-1-entidade/

[2] http://www.embarcados.com.br/tutorial-de-modelsim-vhdl-fpga/

[3] https://www.altera.com/products/design-software/model---simulation/modelsim-altera-software.html

[4] Videoaula VHDL

[5] https://balbertini.github.io/vhdl_datatypes-pt_BR.html



Icone voltar.png Icone menu.png Icone prox.png