Componentes em VHDL
Posted on qui 14 março 2019 in vhdl • 6 min read
A linguagem VHDL é inerentemente hierárquica, sendo muito fácil usar um componente de um projeto em outro. Esta característica permite algumas vantagens como: reutilização de descrições, divisão do projeto e partes menores (permite que projetistas trabalhem em paralelo e aumenta a legibilidade) e o teste separado (de um módulo e da integração).
Usar componentes é muito simples em VHDL pois toda a entidade (entity
) define um componente. No entanto, precisamos declarar e instanciar o componente, o que veremos neste post.
Considere a figura ao lado, que representa uma ULA (Unidade Lógica e Aritmética) modelo 74181 (fonte: datasheet da Texas). Os números nos pinos representam o número do pino físico (e.g. a entrada M
está no pino 8). Como convencionado, entradas de dados estão acima, saídas abaixo, entradas de controle a esquerda e saída de controle a direita. As entradas de dados são A
e B
de 4 bits e a saída de dados F
também é de 4 bits. O carry_in ($\overline{C_n}$) e o carry_out ($\overline{C_{n+4}}$) são ambos ambos ativos baixo. A entrada M
define se a operação é lógica ou aritmética e a entrada S
(4 bits) define qual operação a ULA realizará. Há ainda a saída $A=B$ e as saídas para cascateamento generate ($\overline{G}$) e propagate ($\overline{P}$), ambas ativas baixo.
A entidade para este componente é algo assim:
1 2 3 4 5 6 7 8 |
|
Para usar essa ULA como componente, há duas fases distintas: a declaração e a instância.
Declarando um componente
A declaração tem o seguinte formato:
1 2 3 4 |
|
A cláusula is
é opcional, assim como o nome_da_entidade
no final da declaração. O generic
serve para declarar componentes parametrizáveis, que está coberto em outro post. Já o port
, apesar de opcional, é o que declara as portas do componente disponíveis para a entidade que o utilizará como módulo. De modo geral, é esperado que as cláusulas generic
e port
sejam idênticas à declaração da entidade, então a ULA 181 do nosso exemplo deve ser declarada como componente assim:
1 2 3 4 5 6 7 8 |
|
Instanciando um componente
Depois de declarado, ainda precisamos instanciar o componente.
1 2 3 4 5 6 |
|
Note que há três modos distintos de instanciar um componente, que são mutuamente exclusivos. O primeiro usamos o nome do componente (opcionalmente antecedido por component
), no segundo usamos o nome da entidade e no terceiro usamos o nome da configuração (a configuração é um elemento primário do VHDL).
Quando usamos a instanciação pelo component
, instanciaremos exatamente o componente que declaramos. A escolha de qual arquitetura depende do sintetizador (normalmente é a última arquitetura descrita no arquivo).
Na instanciação pela entidade, podemos escolher qual arquitetura usamos. A sintaxe comum é work.nome_da_entidade(nome_da_arquitetura)
, mas pode-se omitir o nome_da_arquitetura
(deixando a cargo do sintetizador escolher). A palavra reservada work
refere-se à biblioteca padrão, o local onde ficam todos os componentes do seu projeto que não pertencem a uma biblioteca (não estão em um package
). Neste caso de instanciação podemos omitir a declaração do componente se o nome da entidade for único. Caso seja necessário, você pode incluir a biblioteca work
no preâmbulo o arquivo VHDL usando use work.all;
ou selecionando explicitamente o componente que quer usar: use work.nome_da_entidade;
A terceira maneira é declarar explicitamente uma configuração (configuration
), mas não cobrirei neste post.
Na prática, acabamos quase sempre por usar o primeiro método por ser mais simples e compatível com todas as versões de VHDL:
1 2 3 |
|
Agora observe as cláusulas map
. Elas mapeiam os sinais do seu componente para a instância e ambos seguem o mesmo padrão usado para declaração de portas. O generic map
está coberto em outro post, então vamos focar na instância simples da nossa ULA.
1 |
|
No trecho acima, declaramos uma instância do componente alu181
chamada minha_alu
. Note que a ordem dos sinais é a mesma usada na declaração do componente. Isto significa que o sinal A
, que deve existir na arquitetura que está usando este componente, será ligado ao sinal a
desta instância de ULA. Chamamos este tipo de associação de associação posicional pois a ordem do sinal importa. Repare também que não vamos usar o _carry_in__ (cn
), portanto fixamos esta entrada em 0
. Também não usaremos as saídas gn
e pn
, então não conectaremos a lugar algum, o que pode ser feito em VHDL usando a palavra reservada open
.
Uma outra forma de fazer a associação é usando associação nomeada. Neste tipo de associação, dizemos explicitamente qual sinal é ligado em qual porta da instância do componente, então a ordem em que fazemos o port map
não importa:
1 2 3 4 5 6 |
|
A sintaxe é sempre sinal_do_componente => sinal_da_arquitetura_pai
.
Exemplo
Neste exemplo, usamos duas ULAs 181 para formar uma ULA de 8 bits, sem carry_in, carry_out e sem saídas de cascateamento, mas mantendo a saída de igualdade. Usamos os dois tipos de associação para exemplificar. Lembre-se que as portas de uma entidade são sinais válidos dentro da sua arquitetura.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
Qual associação utilizar?
A associação nomeada torna o código mais robusto pois se algo mudar de lugar na entidade, os sinais continuam a mapear a instância corretamente. Também facilita a compreensão da ligação pois, se usar nomes significativos para seus sinais, o projetista não precisará consultar a declaração do componente a todo momento para entender a ligação na instância.
Já a associação posicional é mais enxuta e aumenta a legibilidade do código. No entanto para entender a ligação do seu componente você precisa consultar a declaração.
Na prática, sugiro usar declaração posicional sempre que possível, sempre acompanhada de comentários explicando sua intenção. No entanto, se o seu componente tem portas suficientes para que o port map
ultrapasse uma ou duas linhas, considere fortemente usar a associação nomeada para melhorar o entendimento.
Uso nas disciplinas de graduação
Você está livre para usar qualquer um dos dois formatos que desejar. No entanto, o juiz usado nas disciplinas de graduação usa a associação posicional, o que significa que se você montar uma entidade diferente do enunciado, sua descrição não será avaliada. Siga estritamente o enunciado, especialmente em relação à entidade.