Sei sulla pagina 1di 9

Introdução à VHDL

Gustavo G. Parma

Assunto: Introdução à VHDL.

Objetivos: O aluno deverá ser capaz de compreender os conceitos básicos de VHDL.

1 Introdução Teórica
VHDL, VHSIC (Very High Speed Integrated Circuits) Hardware Description Language, foi desenvol-
vido como um padrão de linguagem de programação que descreve a estrutura e o funcionamento de
circuitos integrados digitais. Sendo um padrão desenvolvido pelo IEEE, a cada 5 anos é feita uma
revisão do assunto podendo haver modificações no padrão inicialmente proposto.
A utilização do VHDL possibilita o desenvolvimento metodológico de sistemas complexos. Inicial-
mente ele permite a descrição do sistema em partes, ou seja, ele permite a decomposição de um grande
sistema em subsistemas indicando como estes subsistemas estão conectados. Em segundo lugar, ele
permite a utilização de formas padrões de programação no desenvolvimento de um sistema digital e,
como conseqüência, ele permite a simulação do sistema digital antes de sua implementação. Além
disto, por ser uma linguagem padrão de programação de circuitos digitais, o código VHDL é desen-
volvido independentemente do CI a ser utilizado permitindo, desta forma, uma grande flexibilidade
na hora da implementação do sistema.
Por se tratar de uma linguagem de programação, VHDL segue alguns preceitos básicos de um
linguagem estruturada.
Em VHDL um elemento que executa uma função digital qualquer é chamado de entity e os pinos
de entrada ou saı́da deste componente estão listados na seção de port. A primeira etapa no desenvol-
vimento de um sistema digital utilizando VHDL é a definição da entidade e seus pinos de entrada e
saı́da, a qual funciona como a interface entre que o componente realmente faz e o mundo exterior a
ele. A declaração utiliza o seguinte código:

entity nome-do-elemento is
port (NPI1,NP2I,NPI3 : in tipo-variável;
NPI4,NPI5 : in tipo-variável;
NPO1,NPO2 : out tipo-variável;
NPO3,NPO4 : out tipo-variável);
end nome-do-elemento;

onde:
NPI1,NPI2,NPI3,NPI4 e NPI5 são os nomes dos pinos de entrada;
NPO1, NPO2, NPO3 e NPO4 são os nomes dos pinos de saı́da;
As palavras em negrito são palavras reservadas;
o tipo-variável indica qual o tipo da varı́avel de entrada/saı́da do pino. Este tipo define quais
operações podem ser executadas com um determinado pino. Os seguintes tipos são suportados pelo
software da Xilinx:
1. BOOLEAN: o mais abstrato dos tipos de variável. Uma variável booleana pode ser verdadeira
ou falsa;
2. INTEGER range 0 to 31: A variável inteira permite operações matemáticas básicas com os
sinais, devendo ser especificado os limites desta variável. Os limites máximos para uma variável
do tipo integer são: −(231 ) até (231 − 1). Não se pode ter acesso direto aos bits de uma variável
do tipo inteiro.
3. BIT: pode assumir dois valores lógicos, 0 ou 1;
4. BIT VECTOR (3 downto 0): vetor de bits, no caso um vetor de quatro elementos;
5. STD LOGIC; Este tipo é pré-definido em uma biblioteca do software. Esta biblioteca permite,
dentre outras coisas, o acesso aos bits de uma variável do tipo inteiro.
6. STD LOGIC VECTOR (0 to 3): similar ao tipo anterior, porém definindo um vetor de 4 ele-
mentos.

Após a definição da entidade, é necessário a definição da arquitetura desta entidade. A arquite-


tura determina o comportamento da entidade, em função dos pinos de entrada/saı́da. A arquitetura
é definida utilizando a seguinte sintaxe:

architecture nome-da-arquitetura of nome-da-entidade is


begin
nesta parte entra o corpo da arquitetura
end nome-da-arquitetura;

O corpo da arquitetura é composto dos seguintes elementos:

1. Declaração de sinais, tipos, constantes, componentes ou sub-programas. Um sinal é uma variável


local à arquitetura ou ao bloco onde ela é definida, podendo, também, ser definido na arquitetura
da entidade, antes do begin;
2. Definição dos blocos concorrentes (executados paralelamente), logo após o begin, os quais podem
ser:

(a) associação de valores a sinais;


(b) chamadas de procedimentos: chamadas a algoritmos que executam uma determinada função
(c) Processos: Algoritmos executados ”sequencialmente”, podendo ler ou escrever sinais defi-
nidos no resto da arquitetura;

A associação de valores para as variáveis pode ser executada de duas formas distintas, em função
do tipo da variável e da forma como a variável foi definida (VARIABLE ou SIGNAL).

A := 1; – para VARIABLE
B <= 1; – para SIGNAL
C <=0 10 ; – para SIGNAL tipo BIT
D <= ”0101”; – para SIGNAL do tipo vetor de BIT (bit vector ou STD logic vector)

A diferença de uma variável SIGNAL - definida na arquitetura - de uma variável VARIABLE -


definida dentro do processo - é que a primeira tem o seu valor associado apenas quando o processo
termina, enquanto o valor da segunda é associado a cada passo de execução do processo. Além disto,

2
uma variável SIGNAL possui escopo global, ou seja, se a arquitetura possui mais de um processo,
todos os processos ”enxergam”a variável SIGNAL, enquanto que o escopo da variável VARIABLE é
local, somente o processo no qual ela foi definida consegue ”enxergá-la”.

variable A, B: BIT;
signal C: BIT VECTOR(1 to 4);
A := ’1’; – variável A possui o valor ’1’
B := ’0’; – variável B possui o valor ’0’
C <= ”1100”; – variável C possui o valor ”1100”

Em função do tipo da variável, pode-se executar operações matemáticas ou lógicas com a variável
criada. Os operadores em HDL podem ser lógicos, relacionais, unários, soma, multiplicadores ou
outros.
1. Operadores lógicos: AND, OR, NAND, NOR, XOR e NOT, podendo ser aplicados a variáveis
do tipo DIT, BOOLEAN ou vetores destes tipos. É importante ressaltar que a precedência de
execução do AND e igual à do OR, desta forma é importante separar os termos com parenteses
para que a implementação de uma dada função lógica não ocorra de forma erronea.

signal A, B, C: BIT VECTOR(3 downto 0);


signal D, E, F, G: BIT VECTOR(1 downto 0);
signal H, I, J, K: BIT;
signal L, M, N, O, P: BOOLEAN;
A <= B and C;
D <= E or F or G;
H <= (I nand J) nand K;
L <= (M xor N) and (O xor P);
2. Operadores relacionais: = (igual), / = (diferente), < (menor), <= (menor ou igual), > (maior),
e >= (maior ou igual). Os dois primeiros operadores (igual e diferente) podem ser aplicados a
qualquer tipo de variável. Os demais podem ser aplicados a variáveis com sinal ou inteiros. O
resultado de uma operação relacional é do tipo BOOLEAN. A comparação de vetores é feita bit
a bit, da esquerda para a direita, independentemente do número de elementos em cada um dos
vetores a serem comparados. Desta forma,o vetor 101011 é menor do que o vetor 1011, porque
o quarto bit (da esquerda para a direita) do primeiro vetor é igual à 0 e o quarto bit do segundo
vetor é igual à 1.

signal A, B: BIT VECTOR(3 downto 0);


signal C, D: BIT VECTOR(1 downto 0);
signal E, F, G, H, I, J: BOOLEAN;
G <= (A = B);
H <= (C < D);
I <= (C >= D);
J <= (E > F);
3. Operador unário: − (menos), utilizado para variáveis do tipo inteiro.

signal A, B: INTEGER range -8 to 7;


A <= -B;

3
4. Operador de soma: podendo ser soma aritmética ou concatenação. Os operadores + e − são
utilizados para operação aritmética entre variáveis do tipo inteiro e o operador & é utilizado
para a concatenação de todo os tipos variáveis unidimensionais e é utilizado para a construção
de vetores.

signal A, D: BIT VECTOR(3 downto 0);


signal B, C, G: BIT VECTOR(1 downto 0);
signal E: BIT VECTOR(2 downto 0);
signal F, H, I: BIT;
signal J, K, L: INTEGER range 0 to 3;
A <= not B & not C; – Array & array
D <= not E & not F; – Array & element
G <= not H & not I; – Element & element
J <= K + L; – Simple addition
5. Operadores de multiplicação: ∗ (multiplicação), / (divisão), mod (quociente da divisão), ou rem
(resto da divisão), utilizados para variáveis do tipo inteiro. Para a utilização dos operadores /,
mod ou rem o operador do lado direito deve ser uma potência de dois.

signal A, B, C, D, E, F, G, H: INTEGER range 0 to 15;


A <= B * 4;
C <= D / 4;
E <= F mod 4;
G <= H rem 4;
6. Outros: existem mais duas operação disponı́veis, exponenciação (∗∗) ou módulo (ABS). Para
a utilização da exponenciação é necessário que o operador do lado esquerdo seja uma potência
de dois. Estas duas operações são definidas para variáveis do tipo inteiro.

signal A, B: INTEGER range -8 to 7;


signal C: INTEGER range 0 to 15;
signal D: INTEGER range 0 to 3;
A <= abs(B);
C <= 2 ** D;

Como mencionado, um processo é um algoritmo executado sequencialmente pelo dispositivo. Desta


forma, todo comando que tiver uma origem sequencial (desvio de fluxo de programa) , tais como IF,
CASE, LOOPS precisam, necessariamente, ser inseridos dentro de um processo. A declaração de um
processo segue as seguintes diretrizes:

[ label: ] process [ ( sensitivity list ) ]


{ process declarative item }
begin
{ sequential statement }
end process [ label ] ;

Onde:

4
sensitivity list é a lista das variáveis (SIGNALs e PORTs) que serão acessı́veis ao processo, sepa-
rados por vı́rgula;
process declarative item é o espaço no qual são feitas as declarações de sub-programas, Tipos,
constantes e variáveis locais ao processo;
sequential statement é o espaço no qual é feita a programação sequencial, propriamente dita.
Pode-se utilizar os seguintes códigos de programa nos processos:

1. Comando IF THEN
if condition then
{ sequential statement }
{ elseif condition then
{ sequential statement } }
[ else
{ sequential statement } ]
end if;

Exemplo:

signal A, B, C, P1, P2, Z: BIT;


if (P1 = ’1’) then
Z <= A;
elseif (P2 = ’0’) then
Z <= B;
else
Z <= C;
end if;
2. Comando CASE

case expression is
when choices =>
{ sequential statement }
{ when choices =>
{ sequential statement } }
end case;

exemplo:

signal VALUE is INTEGER range 0 to 15;


signal Z1, Z2, Z3, Z4: BIT;
Z1 <= ’0’;
Z2 <= ’0’;

5
Z3 <= ’0’;
Z4 <= ’0’;
case VALUE is
when 0 => – verifica o 0
Z1 <= ’1’;
when 1 | 3 => – verifica o 1 ou 3
Z2 <= ’1’;
when 4 to 7 | 2 => – verifica o 2, 4, 5, 6 ou 7
Z3 <= ’1’;
when others => – verifica os valores restantes, 8 até 15
Z4 <= ’1’;
end case;
3. Comandos de LOOP
Existem ao menos três tipos de iteração para loops

(a) Loop: Esquema básico sem critério de parada pré-definido. O processo é executado ao
encontrar um comando NEXT ou um comando EXIT. Este esquema deve conter ao menos
uma linha de comando de espera.

[label :] loop
{ sequential statement }
end loop [label];
(b) while...loop: Este equema de loop possui um sistema de iteração booleana. Se a condição
da iteração for avaliada como TRUE, a sequencia é executada uma vez. A condição é,
então, reavaliada. O loop só irá terminar quando a condição for avaliada como FALSE.
Este esquema deve conter ao menos uma linha de comando de espera.

[label :] while condition loop


{ sequential statement }
end loop [label];
(c) for...loop: Este esquema possui um sistema de iteração do tipo inteiro, onde o número de
repetições é determinado pelo limite do inteiro.Este esquema não deve conter um comando
de espera.

[label :] for identifier


in range loop
{ sequential statement }
end loop [label];

O seguinte exemplo mostra dois códigos equivalentes:


variable A, B: BIT VECTOR(1 to 3);
– primeiro código, utilizando loop
for I in 1 to 3 loop
A(I) <= B(I);
end loop;

6
– segundo código, utilizando associação direta
A(1) <= B(1);
A(2) <= B(2);
A(3) <= B(3);

4. Comando WAIT
Uma linha de espera suspende a execução de um processo até que seja detectado uma transição
positiva ou negativa em um determinado sinal, pode-se utilizar as seguintes sintaxes:

(a) wait until signal = value ;


(b) wait until signal’event and signal = value ;
(c) wait until not signal’stable
(d) and signal = value ;

5. Comando NEXT
Este comando termina a iteração corrente de um loop e muda para o primeiro comando dentro
do loop.

next [ label ] [ when condition ] ;

onde label pode ser o nome do loop.


exemplo:

signal A, B, COPY ENABLE: BIT VECTOR (1 to 8);


...
A <= ”00000000”;
...
– Assumindo que B possua o valor ”01011011”
– Assumindo que COPY ENABLE possua o valor ”11010011”
. . .
for I in 1 to 8 loop
next when COPY ENABLE(I) = ’0’;
A(I) <= B(I);
end loop;
6. Comando EXIT
Este comando termina a iteração corrente de um loop e continua a execução do processo no
primeiro comando depois do loop.

exit [ label ] [ when condition ] ;

onde label pode ser o nome do loop que se deseja terminar.


exemplo:

7
signal A, B: BIT VECTOR(1 downto 0);
signal A LESS THAN B: Boolean;
. . .
A LESS THAN B <= FALSE;
for I in 1 downto 0 loop
if (A(I) = ’1’ and B(I) = ’0’) then
A LESS THAN B <= FALSE;
exit;
elsif (A(I) = ’0’ and B(I) = ’1’) then
A LESS THAN B <= TRUE;
exit;
else
null; – continua a comparação
end if;
end loop;

A seguir é mostrado o exemplo simples de um contador módulo 10, utilizando um processo.

library IEEE; use IEEE.std logic 1164.all;


use IEEE.std logic arith.all;
use IEEE.std logic unsigned.all;
entity COUNTER is
port (CLEAR: in BIT;
IN COUNT: in INTEGER range 0 to 9;
OUT COUNT: out INTEGER range 0 to 9);
end COUNTER;
architecture EXAMPLE of COUNTER is
begin
process(IN COUNT, CLEAR)
begin
if (CLEAR = ’1’ or IN COUNT = 9) then
OUT COUNT <= 0;
else
OUT COUNT <= IN COUNT + 1;
end if;
end process;
end EXAMPLE;

É importante ressaltar que um processo não é chamado ou executado, ele é, simplesmente, im-
plementado. Uma arquitetura pode conter vários processos, sendo que os processos são concorrentes
entre si. Desta maneira, as atribuições a variáveis ou pinos dentro de um processo só acontece real-
mente ao fim do processo, se pensarmos de forma sequencial. Por isto deve-se tomar cuidado extra ao
se trabalhar com processos, nos quais podemos pensar de forma ”sequencial”(entre aspas) mas que é
implementado concorrentemente aos demais comandos da arquitetura.

8
2 Parte Experimental :
Desenvolva o código VHDL para a implementação do semáforo da aula anterior, o qual havia sido
implementado usando a ferramenta gráfica do Quartus II.

Potrebbero piacerti anche