Módulos genéricos parametrizáveis em VHDL
Posted on ter 12 março 2019 in vhdl • 6 min read
Uma das características interessantes de HDLs (incluindo VHDL), é a capacidade de reutilização de módulos. Podemos aumentar ainda mais a reutilização descrevendo módulos parametrizáveis. Este artigo trata de como descrever módulos genéricos parametrizáveis em VHDL.
Declarando um parâmetro genericamente
Em muitos casos, a descrição de um módulo em VHDL pode não ser reaproveitável porque precisamos de um módulo ligeiramente diferente. Um exemplo: se descrevermos um registrador de 8 bits, poderemos usar este registrador somente em projetos que utilizam registradores de exatamente 8 bits. Contudo, quando o hardware é regular, ou seja, seu funcionamento é idêntico independentemente da característica variável, é possível descrevê-lo de forma a definir a característica variável no momento da instância e não da descrição. Dessa forma, descreve-se o módulo genericamente e somente no momento de utilizá-lo parametrizamos as características variáveis. No exemplo do registrador, podemos descrevê-lo genericamente de forma que o seu tamanho seja um parâmetro.
A palavra reservada que possibilita isso é a generic
. Na descrição da entidade do módulo, podemos incluir esta palavra, como a seguir:
1 2 3 4 |
|
A lista_de_elementos_genericos
é uma lista de todas as características parametrizáveis, no formato: nome: tipo := valor_padrao
, separadas por ;
. O nome
pode ser o que você desejar, desde que seja um nome válido em VHDL. O tipo define qual tipo de dados será utilizado para aquele parâmetro e pode ser qualquer tipo suportado. O valor_padrao
é opcional e pode ser omitido. Quando omitido, termina-se a declaração após a declaração do tipo (exclui-se também o :=
). No caso da omissão do valor padrão, a declaração do valor no momento da instanciação é obrigatória. Caso o valor padrão esteja presente, ele será usado somente se a instância não especificar nenhum valor, caso contrário o valor da instância sobrepõe o valor padrão. Como boa prática, sempre defina o valor padrão. O valor_padrao
deve ser obrigatoriamente uma constante do mesmo tipo
que o parâmetro correspondente.
Depois de especificados, os parâmetros da lista_de_elementos_genericos
tornam-se constantes disponíveis em todo o restante do projeto, incluindo a declaração de portas da entidade e toda a arquitetura. Como estão disponíveis e podem ser usados no lugar de qualquer constante, é possível declarar portas, sinais e qualquer outra estrutura de VHDL usando o parâmetro no lugar de um valor fixo.
Instância
No momento da instância do componente, podemos definir os parâmetros que desejarmos. Lembre-se que, caso a descrição não estabeleça um valor padrão para um determinado parâmetro, a definição na instanciação é obrigatória. A sintaxe da instância é:
1 2 3 |
|
Se o componente não possui nenhum parâmetro (ausência de generic
) ou não deseja-se especificar nenhum (todos os valores padrões serão utilizados), toda a linha do generic map
pode ser omitida. A lista_de_associacao_de_elementos_genericos
segue o mesmo padrão de associação usado para as portas.
Síntese
O generic
não é uma estrutura sintetizável. No momento da síntese, todos os valores genéricos parametrizáveis devem ser resolvíveis, ou seja, o valor do parâmetro é fixo na instância. Não é possível mudar este valor dinamicamente durante ou após a síntese, e muito menos mudá-lo no hardware pronto. É possível herdar parâmetros, desde que a árvore de herança seja resolvível para um valor constante no momento da síntese.
É possível usar o generic
para parametrizar algo não sintetizável. Exemplos deste uso incluem variáveis condicionais de depuração e temporização.
Exemplo
Neste exemplo, mostrarei um registrador de deslocamento genérico com entrada paralela e saída serial, carga paralela síncrona, reset assíncrono e deslocamento para a direita ou para a esquerda. A modelagem contempla temporização (atraso e setup time) e depuração.
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
|
Um possível testbench para este registrador pode ser visto abaixo:
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
|
A execução deste testbench irá imprimir:
1 2 |
|
No código do testbench há três usos distintos do generic
. Note a instância do registrador usado no testbench:
1 2 3 |
|
A primeira coisa que podemos notar é que a atribuição dos parâmetros no generic map
foi feita por nome e não posicionalmente (como feita no port map
). Contudo, podemos usar atribuição posicional se desejado. O tamanho foi parametrizado como 4 bits, o tempo de propagação (tp
) como 40ns e foi habilitada a depuração. A primeira utilização do generic
foram estas atribuições. Já a segunda, foi uma atribuição implícita. O setup time (st
) não foi definido no genric map
, portanto a instância utilizará o valor padrão, que nesse caso é 15ns. Já a terceira diz respeito ao parâmetro debug
. Note que este parâmetro, se habilitado, permite a ativação de um process
(o último da descrição do registrador) que não é sintetizável e serve exclusivamente para emitir mensagens de violação do setup time durante uma simulação.