Este artigo descreve o funcionamento de uma memória ROM de 8 nibbles, que só permite a leitura dos dados armazenados. Este projeto é um exemplo VHDL no Quartus® II, retirado do livro "Elementos de Lógica Programável com VHDL e DSP" [1] e detalhado, utilizando as bibliotecas IEEE no VHDL, além de ser implementado no kit de CPLD. A memória ROM implementada aqui é um dispositivo combinacional bastante simples, basicamente uma estrutura de decodificação e multiplexação de dados. A simulação será feita por testbench no ModelSim®-Altera.
Neste projeto será utilizado:
A memória implementada aqui possui três entradas e uma saída: RDn, que é o pino de entrada para habilitação da leitura; ENABLEn, pino de entrada para habilitação de saída; ADDR: barramento de entrada para endereço de três bits; e DADO: barramento de saída de dados de quatro bits. Quando a entrada de leitura (RDn, ativa em nível lógico baixo) está habilitada juntamente da entrada de habilitação (ENABLEn, também ativa em nível lógico baixo), é possível realizar o processo de leitura do dado no endereço selecionado.
O endereço desejado é selecionado através da porta ADDR de três bits. No total, são oito endereços, e cada um aponta para um espaço da memória. Desta forma, se selecionarmos o endereço "001", a memória apresentará na saída o dado armazenado nesse endereço (contanto que os pinos ENABLEn e RDn estejam ativados). A entrada de habilitação, por sua vez, é ativa em nível lógico baixo, e se estiver desativada, a memória não funciona. Assim, para ler um dado, é necessário ativar a memória e também a entrada RDn, para habilitar a leitura. No entanto, estes processos devem ser feitos passo a passo para evitar (convergir transição de sinais); por exemplo, para ler o dado no endereço "000", primeiro deve-se selecionar o endereço, e em seguida ativar a leitura. Somente após conferir as entradas é possível realizar a leitura sem problemas ativando a memória.
A figura 1 ilustra o símbolo gerado pelo Quartus II para o projeto, com as suas entradas e saídas definidas no VHDL. Este bloco pode ser utilizado para a criação de projetos maiores e mais complexos.
Figura 1 - Símbolo resultante do VHDL
Um exemplo de sistema que se poderia desenvolver, incorporando um bloco de memória como o proposto aqui, poderia ser uma pequena CPU (unidade central de processamento) para um sistema computacional didático. Além da memória, também são utilizados registradores, ULAs e decodificadores de instruções, que podem ser encontrados descritos em outro artigos (veja "Exemplos Didáticos", no menu do portal).
O projeto foi modelado em VHDL, e pode ser dividido em quatro partes: declaração de bibliotecas, declaração da entidade, declaração dos tipos e sinais, e lógica de funcionamento.
A primeira parte do código apresenta as bibliotecas e pacotes que serão utilizados durante o programa. Os pacotes são arquivos que contém as declarações de funções e tipos que podem ser utilizados no desenvolvimento do projeto. Uma biblioteca é um conjunto de pacotes. No início de cada programa é preciso dizer quais as bibliotecas e quais os pacotes, dentro das bibliotecas, serão utilizados. Nesse projeto será utilizado a biblioteca padrão IEEE e os pacotes std_logic_1164, que define o tipo std_logic, e numeric_std, que possibilita a realização de algumas opereções matemáticas para o tipo std_logic.
A entidade é onde se declaram as entradas e saídas do projeto. Nesse projeto teremos como entradas: a habilitação do componente, a habilitação de leitura e o endereço de três bits. A saída será um dado de 4 bits, ou seja, um nibble.
Na sessão "architecture", é declarado um tipo, chamado arranjo, que será um "array" (vetor) de oito dados do tipo std_logic_vector (3 downto 0) que serão referenciados por um número inteiro (no caso, "integer range 0 to 7"). Depois de declarar o tipo, é declarado também uma constante do tipo array, que neste caso são os valores correspondente aos vetores (de x"1" a x"8", em que x indica que o valor atribuido está em hexadecimal). Para cada arranjo, há um endereço específico, e é possível acessar este endereço com o barramento de bits ADDR. Por exemplo; caso queira ler o endereço "010", seleciona-se os bits de entrada em ADDR e a memória efetua o processo de leitura do dado armazenado.
Após declarar os dados, é preciso descrever também a leitura do dado. Assim, na sessão "begin", a entrada DADO se relaciona com o respectivo endereço através de "DADO <= conteudo(to_integer(unsigned(ADDR)))", de forma que dado receberá o valor armazenado no endereço ADDR da constante conteúdo. Note que como ADDR é um "std_logic_vector" e o argumento aceito pela constante é do tipo inteiro, é necessário fazer uma conversão de "std_logic_vector" para inteiro sem sinal. A leitura só ocorre quando ENABLEn e RDn estão em nível lógico baixo. Para o restante das situações, a saída fica em alta impedância, como descreve a sessão "else (others => 'Z')".
Neste projeto, utilizamos o testbench para realizar a simulação. Um testbench é uma ferramenta do ModelSim que permite simular projetos através de estapas descritas e pré-definidas em um código, o qual é configurado no software e executado, exibindo as formas de onda como resultado. Observe abaixo o código do testbench para este projeto (você pode criar um com etapas diferentes).
O testbench funciona como um código em VHDL que, na simulação, realiza as estapas descritas no processo "SIMULACAO" do VHDL nesta sessão. Analisando o código, podemos verificar as etapas e pausas de tempo com a forma de onda na própria simulação do ModelSim-Altera, como mostra a figura 2. Observe que as etapas descritas na lógica de funcionamento foram seguidas, primeiro selecionando o endereço, e em seguida a leitura, para somente depois habilitarmos a memória.
Figura 2 - Simulação no ModelSim-Altera utilizando Testbench
O projeto é composto pelo módulo de CPLD, uma placa de LEDs do tipo catodo comum, uma barra de chaves e uma placa de botões, conectados respectivamente no CON1, CON2 e CON3 do kit, como mostra a figura 3. Lembre-se de que o CPLD usado é o EPM7064, da família MAX7000S.
No projeto, as entradas de endereçamento são pela placa de chaves, e as entradas de habilitação e leitura são implementadas pela placa de botões, tendo a necessidade de manter cada botão apertada para continuar a realizar o processo. A saída de dados é mostrada na placa de LEDs, utilizando quatro dos mesmos, e as funções lógicas pelo kit de CPLD.
Figura 3 - Estrutura física do projeto
Fique atento para que o Vcc e o GND dos periféricos fiquem alinhados com o do CPLD. Veja em detalhe a montagem do projeto na figura 4, feita no Fritzing.
Figura 4 - Montagem da periferia no Fritzing
A figura 5 ilustra a pinagem atribuída para o CPLD durante o processo "Pin Planner" no Quartus II, de acordo com a periferia escolhida.
Figura 5 - Pin Planner no Quartus II
Depois de implementar e atribuir os pinos do projeto, faça uma compilação do projeto para gerar o arquivo de gravação, além do programa procurar algum erro ou aviso no mesmo. Para gravar o projeto siga o tutorial do wink na etapa de "Gravação do CPLD" do artigo "Decodificador 3x8".
Lembre-se sempre de gravar o módulo de CPLD separadamente dos periféricos, evitando problemas na pinagem atribuida em projetos anteriores. É importante também sempre lembrar de definir os pinos não utilizados como entradas tri-state.
Para testar o funcionamento desse projeto, monte a periferia indicada na sessão "Montagem e Roteamento" deste artigo e vá alterando o barramento de endereço através das chaves, assim como os botões de leitura e habilitação.
Para fazer o download do projeto completo, clique aqui.
O arquivo está no formato ".zip", e inclui os arquivos de projeto do Quartus (".qpf"), de modo VHDL para simulação (".vhd"), de gravação do CPLD (".pof"), de símbolo para usar em projetos mais avançados (".bsf"), entre outros.
[1] COSTA, Cesar da; MESQUITA, Leonardo; PINHEIRO, Eduardo. Elementos de Lógica Programável com VHDL e DSP: Teoria e Prática. 1. ed. São Paulo: Érica, 2011.