Aula 8: Estudo Guiado

Objetivos

  1. Projetar um contador microprocessado.

Contextualização

Até agora, fizemos o esboço geral do nosso projeto, um contador que deverá:

Primeiro Esboço do Computador
Primeiro Esboço do Computador

Além disso, a distribuição dos periféricos no espaço de endereçamento foi feita da seguinte forma:

E o endereçamento da memória RAM e dos LEDs está mostrado nos desenhos abaixo.

Conexão e Endereçamento da RAM
Conexão e Endereçamento da RAM

Conexão e Endereçamento dos LEDs
Conexão e Endereçamento dos LEDs

Falta fazer o endereçamentos dos displays de sete segmentos, das chaves e dos botões. Vamos começar pelo display, que está no mesmo bloco de E/S para escrita, juntamente com os LEDs. Portanto, teremos que ajustar o circuito para acomodar esses dois periféricos.

Os LEDs estão alocados na faixa de endereços para E/S de escrita (entre 256 e 319), nos seguintes endereços:

Vamos dividir essa faixa de 64 posições em duas metades:

A linha de endereço que faz essa divisão é o sinal A5. Portanto, quando A5 tiver o valor ‘0’, estaremos na primeira metade da faixa. De forma complementar, quando A5 tiver o valor ‘1’, estaremos na segunda metade da faixa.

Para manter o edereçamento definido anteriormente para os LEDs, devemos adicionar às portas AND a linha de endereço A5 invertida, conforme a listagem abaixo.

Para o ativar o conjunto de 8 LEDs, no endereço 256, teremos uma porta AND com quatro entradas:

Para o ativar o LEDR8, no endereço 257, teremos uma porta AND com quatro entradas:

Para o ativar o LEDR9, no endereço 258, teremos uma porta AND com quatro entradas:

O circuito para os displays será similar, porém com a utilização da linha de endereço A5 sem inversão. Isso fará com que os displays sejam endereçados como mostrado a seguir.

Endereço dos Displays de Sete Segmentos
Display Endereço em Decimal
HEX0 288
HEX1 289
HEX2 290
HEX3 291
HEX4 292
HEX5 293

O circuito, com todas as conexões desenhadas, ficaria como o mostrado abaixo.

Conexão e Endereçamento dos LEDs e Displays de Sete Segmentos
Conexão e Endereçamento dos LEDs e Displays de Sete Segmentos

Vamos implementá-lo em VHDL e testar o seu funcionamento utilizando um programa que escreva nesses endereços. Uma forma simples seria a utilização de uma sequência de incrementos e armazenamentos, como a mostrada abaixo.

Programa de teste para o display de sete segmentos
Linha Instrução Observação
0 LDI $1 Carrega o acumulador com o valor 1
1 STA @0 Armazena o valor do acumulador na posição zero da memória (MEM[0])
2 SOMA @0 Soma o valor atual do acumulador com o conteúdo de MEM[0]
3 STA @288 Armazena o valor do acumulador em HEX0
4 SOMA @0 Incrementa o valor do acumulador em uma unidade
5 STA @289 Armazena o valor do acumulador em HEX1
6 SOMA @0 Incrementa o valor do acumulador em uma unidade
7 STA @290 Armazena o valor do acumulador em HEX2
8 SOMA @0 Incrementa o valor do acumulador em uma unidade
9 STA @291 Armazena o valor do acumulador em HEX3
10 SOMA @0 Incrementa o valor do acumulador em uma unidade
11 STA @292 Armazena o valor do acumulador em HEX4
12 SOMA @0 Incrementa o valor do acumulador em uma unidade
13 STA @293 Armazena o valor do acumulador em HEX5
14 JMP @2 Desvia e continua incrementando e escrevendo nos displays

O caractere arroba (@) indica um endereço de memória (RAM ou ROM) enquanto que o caractere cifrão ($) indica um valor constante (imediato).

Programa, igual ao acima, para ser copiado e utilizado na ROM, com os devidos ajustes nos endereçamentos.

LDI $1
STA @0
SOMA @0
STA @288
SOMA @0
STA @289
SOMA @0
STA @290
SOMA @0
STA @291
SOMA @0
STA @292
SOMA @0
STA @293
JMP @2

Para as chaves e botões, vamos utilizar um circuito similar. Porém, endereçado no Bloco 5 (endereço 320 até 383), ou seja, conforme o descrito abaixo:

Como vamos ler esses periféricos, devemos interligá-los ao barramento de leitura de dados e utilizar o sinal de Leitura (RD) na lógica das portas AND.

Para facilitar a leitura do diagrama, vamos eliminar as linhas e indicar as conexões através do nome do sinal sendo conectado. Isso faz com que o diagrama fique mais limpo e continue informando sobre as conexões feitas. Não podemos esquecer de que todos os pontos com o nome de um dado sinal devem ser interligados na hora de fazer o projeto em VHDL.

Endereçamento das Chaves e Botões
Endereçamento das Chaves e Botões

Vamos implementá-lo em VHDL e testar o seu funcionamento utilizando um programa que leia as chaves e botões e escreva os valores nos displays ou nos LEDs. Como o processador estará rodando a 50MHz, a sensação será de que as chaves e botões estão ligados diretamente aos displays e LEDs.

Como usamos KEY0 como botão de entrada, o clock deve vir de outro botão ou do Clock_50.

Programa de teste para as chaves e botões
Linha Instrução Observação
0 LDA @320 Carrega o acumulador com a leitura das chaves SW0 até SW7
1 STA @288 Armazena o valor do acumulador no display HEX0
2 LDA @321 Carrega o acumulador com a leitura da chave SW8
3 STA @289 Armazena o valor do acumulador no display HEX1
4 LDA @322 Carrega o acumulador com a leitura da chave SW9
5 STA @290 Armazena o valor do acumulador no display HEX2
6 LDA @352 Carrega o acumulador com a leitura do botão KEY0
7 STA @291 Armazena o valor do acumulador no display HEX3
8 LDA @353 Carrega o acumulador com a leitura do botão KEY1
9 STA @292 Armazena o valor do acumulador no display HEX4
10 LDA @354 Carrega o acumulador com a leitura do botão KEY2
11 STA @293 Armazena o valor do acumulador no display HEX5
12 LDA @355 Carrega o acumulador com a leitura do botão KEY3
13 STA @257 Armazena o valor do bit0 do acumulador no LDR8
14 LDA @356 Carrega o acumulador com a leitura do botão FPGA_RESET
15 STA @258 Armazena o valor do bit0 do acumulador no LDR9
16 JMP @0 Desvia e continua atualizando os valores das entradas nas saídas

O caractere arroba (@) indica um endereço de memória (RAM ou ROM) enquanto que o caractere cifrão ($) indica um valor constante (imediato).

Programa, igual ao acima, para ser copiado e utilizado na ROM, com os devidos ajustes nos endereçamentos.

LDA @320
STA @288
LDA @321
STA @289
LDA @322
STA @290
LDA @352
STA @291
LDA @353
STA @292
LDA @354
STA @293
LDA @355
STA @257
LDA @356
STA @258
JMP @0

O botão KEY0 será usado para incrementar o contador. Dessa forma, cada vez que ele for pressionado devemos incrementar uma única vez o valor da contagem.

Para evitar que ocorram múltiplos incrementos, devemos garantir que os ruídos de chaveamento de KEY0 sejam eliminados. Isso é feito através do circuito de debounce implementado no edge detector, como mostrado no circuito abaixo.

Circuito do Discriminador de Borda
Circuito do Discriminador de Borda
Simulação do Funcionamento do Discriminador de Borda
Simulação do Funcionamento do Discriminador de Borda

Como o edge detector transforma o sinal do botão pressionado em apenas um pulso de clock, devemos utilizar um circuito de memorização da ativação desse botão (flag de botão apertado). Esse circuito de memorização deve ser limpo após qualquer leitura do botão que retorne a informação de botão pressionado.

Esse tipo de implementação está mostrado no circuito abaixo, onde qualquer acesso ao endereço de limpaLeitura (endereço 511 ou 0x1FF) fará com que o valor armazenado no flip-flop seja limpo (a saída Q assumirá o valor 0).

Debounce e Memorização de KEY0
Debounce e Memorização de KEY0

Precisaremos fazer a decodificação do endereço 511 para implementar a limpeza do flag. Como temos que decodificar um único endereço e não pode ter espelhamento em outro endereço (como ocorre com as outras decodificações que fizemos), devemos utilizar todos os oito bits de endereço nessa decodificação.

Endereçamento do Limpa Leitura
Endereçamento do Limpa Leitura

Como utilizamos o sinal escrita (WR) na decodificação do endereço 511/510, o acesso de escrita executará a limpeza do valor armazenado no flip-flop, ou seja, uma instrução STA 511.

Para testar o novo circuito, vamos fazer um programa parecido com o listado abaixo.

Como usamos KEY0 como botão de entrada, o clock deve vir de outro botão ou do Clock_50.

Programa de teste para KEY0 com detector de borda
Linha Instrução Observação
0 LDI $0 Carrega o acumulador com o valor 0
1 STA @0 Armazena o valor do acumulador em MEM[0] (constante 0)
2 STA @2 Armazena o valor do acumulador em MEM[2] (contador)
3 LDI $1 Carrega o acumulador com o valor 1
4 STA @1 Armazena o valor do acumulador em MEM[1] (constante 1)
5 NOP
6 LDA @352 Carrega o acumulador com a leitura do botão KEY0
7 STA @288 Armazena o valor lido em HEX0 (para verificar erros de leitura)
8 CEQ @0 Compara com o valor de MEM[0] (constante 0)
9 JEQ @11 Desvia se igual a 0 (botão não foi pressionado)
10 JSR @32 O botão foi pressionado, chama a sub-rotina de incremento
11 NOP Retorno da sub-rotina de incremento
12 JMP @5 Fecha o laço principal, faz uma nova leitura de KEY0
32 STA @511 Limpa a leitura do botão
33 LDA @2 Carrega o valor de MEM[2] (contador)
34 SOMA @1 Soma com a constante em MEM[1]
35 STA @2 Salva o incremento em MEM[2] (contador)
36 STA @258 Armazena o valor do bit0 do acumulador no LDR9
37 STA @293 Armazena o valor do acumulador no HEX5
38 RET Retorna da sub-rotina

O caractere arroba (@) indica um endereço de memória (RAM ou ROM) enquanto que o caractere cifrão ($) indica um valor constante (imediato).

Neste programa, as instruções NOP são para facilitar a localização dos destinos de desvios. Em um programa normal elas não deveriam ser utilizadas para essa finalidade.

Programa, igual ao acima, para ser copiado e utilizado na ROM, com os devidos ajustes nos endereçamentos.

LDI $0
STA @0
STA @2
LDI $1
STA @1
NOP
LDA @352
STA @288
CEQ @0
JEQ @11
JSR @32
NOP
JMP @5


STA @511
LDA @2
SOMA @1
STA @2
STA @258
STA @293
RET

Problemas de Leitura de um Único Bit no Barramento

No caso de KEY0 e KEY1, a sua leitura não possui os 7 bits mais significativos. Isso pode gerar erros na leitura devido a esses bits não estarem conectados e poderem ser lidos tanto como valor alto como valor baixo.

Para resolver esse inconveniente, podemos utilizar uma porta tristate de oito bits com os sete bits mais significativos conectados ao nível lógico baixo.

No caso da leitura de KEY0, o circuito ficaria como o mostrado abaixo.

Leitura de KEY0 com 8 Bits
Leitura de KEY0 com 8 Bits

Outra solução, mais elegante, é a manutenção do circuito com um único bit e a implementação da instrução AND no processador. Assim, após cada leitura de KEY0 (ou KEY1) devemos fazer uma operação AND com a máscara adequada (b0000_0001) para limpar os bits 7 até 1 e deixar somente o valor do bit zero.

Leitura de KEY0 com mascaramento do Bit 0
Linha Instrução Observação
0 STA @511 Limpa a leitura do botão
1 LDI $1 Carrega o acumulador com o valor 1
2 STA @1 Armazena o valor do acumulador em MEM[1] (constante 1)
3 NOP
4 LDA @352 Carrega o acumulador com a leitura do botão KEY0
5 STA @288 Armazena o valor lido em HEX0 (para verificar erros de leitura)
6 AND @1 Utiliza a máscara b0000_0001 para limpar todos os bits menos o bit 0
7 STA @289 Armazena o valor mascarado em HEX1
8 NOP
9 JMP @3 Fecha o laço principal, faz uma nova leitura de KEY0

Como usamos KEY0 como botão de entrada, o clock deve vir de outro botão ou do Clock_50.

Programa, igual ao acima, para ser copiado e utilizado na ROM, com os devidos ajustes nos endereçamentos.

STA @511
LDI $1
STA @1
NOP
LDA @352
STA @288
AND @1
STA @289
NOP
JMP @3

Esta Atividade deverá ser entregue através do Blackboard!



Ferramentas