Aula 5: Instruções de Desvio de Execução

Questão: Como alterar a sequência de execução de um programa?

Objetivos

  1. Criar uma instrução de desvio de execução.
  2. Criar uma instrução de desvio condicional de execução.
  3. Criar uma instrução de chamada de sub rotina (desvio de execução com retorno).

Contextualização

Para atender aos desvios de sequenciamento do programa, é necessário que a instrução contenha a informação sobre o endereço de destino. A organização dos campos da instrução, que chamaremos de JMP (jump), pode ser a mostrada abaixo, onde M é o número de bits de endereço da memória de programa.

Organização dos Campos da Instrução JMP
opCode Endereço de Destino
X bits M bits

Não é necessário adicionar nenhum bit ao formato da instrução, uma vez que podemos aproveitar espaço do campo do endereço de memória das instruções de acesso à memória (LDA, STA, etc…). Só daremos mais uma interpretação para esse campo, como mostrado no desenho abaixo.

Para adicionar essa instrução ao nosso computador, devemos interligar o campo da instrução, referente ao Endereço de Destino, ao PC. Isso pode ser feito através de um MUX, como visto abaixo.

Fluxo de Dados com Jump
Fluxo de Dados com Jump

Responder o quiz de participação, no blackboard, em:

Conteúdos > Participação > Aula_5_Quiz-P1


Atividade: Adicionando a Instrução de Jump ao Computador

Precisamos adicionar a instrução de Jump e para tanto, precisaremos modificar o decodificador de instruções, adicionando um novo ponto de controle. A tabela abaixo mostra a adição dessa instrução.

Instruções e Pontos de Controle
Instrução Mnemônico Código Binário JMP Sel MUX Habilita A Operação habLeituraMEM habEscritaMEM
Sem Operação NOP 0000 X 0 XX 0 0
Carrega valor da memória para A LDA 0001 0 1 10 1 0
Soma A e B e armazena em A SOMA 0010 0 1 01 1 0
Subtrai B de A e armazena em A SUB 0011 0 1 00 1 0
Carrega valor imediato para A LDI 0100 1 1 10 0 0
Salva valor de A para a memória STA 0101 0 0 XX 0 1
Desvio de execução JMP 0110

As saídas que estão indicadas como X ou XX devem ser implementadas como 0 ou 00. Isso facilita o processo de debug do projeto.

Vamos implementar, e simular, esse circuito em VHDL, adicionando o MUX e alterando o decodificador. Para tanto, utilizaremos o programa abaixo. Note que os valores precisam ser convertidos para binário.

Programa de teste para o processador
Linha Instrução Observação
0 JMP @4 Deve desviar para a posição 4
1 JMP @5 Deve desviar para a posição 5
2 NOP
3 NOP
4 JMP @1 Deve desviar para a posição 1
5 NOP
6 JMP @6 Fim. Deve ficar neste laço

O caractere arroba (@) indica uma posição de memória, tanto para RAM quanto para ROM.

O resultado da compilação será o circuito, em RTL, mostrado no diagrama abaixo.

RTL do Computador
RTL do Computador

Para fazer a simulação não precisaremos de nenhum sinal de entrada, além do clock. Vale lembrar que o clock é aplicado através do sinal KEY(0).

A verificação do funcionamento do circuito pode ser feita através do monitoramento do Program Counter e sua comparação com a execução do programa mostrado no fim do ítem Procedimento desta atividade. O resultado esperado está mostrado abaixo.

Simulação do Programa de Teste
Simulação do Programa de Teste

Podemos ver que, de acordo com o esperado, o Program Counter assume a seguinte sequência de valores: 0, 4, 1, 5, 6, 6, 6, 6, …

Como a última instrução é o _JMP @6_, que está localizada na posição 6 da memória de instruções, ocorrerá um laço infinito que sempre executará essa instrução.


Desvio Condicional de Execução

O desvio condicional permite a criação de estruturas de programação do If … Then … Else e laços de execução (For … Next e Do … While).

Esse desvio só é executado se uma determinada condição for atendida. Por exemplo, podemos fazer o desvio sempre que uma comparação entre dois números resultar igual (verdadeira).

Nota-se que precisaremos de duas instruções: uma de comparação e outra para o desvio se a comparação for verdadeira. Um exemplo de código seria:

CEQ  M[enderecoVariavel]         # Compara se o valor do acumulador é igual ao valor contido no endereço de memória. Caso sim ativa o flag IGUAL.
...
...                              # Várias instruções que não executam comparações.
...
JEQ  EnderecoDestino             # Verifica o flag IGUAL e, caso verdadeiro, faz o desvio.

Onde: CEQ = compara se igual; IGUAL = flag (flip-flop) que recebe o resultado da comparação; JEQ = desvia se o flag de igual for verdadeiro (Jump Equal).

Pelo código, vemos que o resultado da comparação, armazenado no respectivo flag, deve ficar armazenado até a próxima instrução de comparação.

O destino do desvio condicional é retirado do mesmo campo imediato que armazena o destino do desvio incodicional (Jump). Portanto, o formato da instrução não precisa ser modificado.

Fluxo de Dados com Desvio Condicional
Fluxo de Dados com Desvio Condicional

O bloco “Lógica de Desvio” foi desenhado fora da UC para evidenciar a sua existência.

Detecção de Valor Zero na Saída da ULA

Utilizaremos uma porta NOR de 8 entradas, conectadas aos 8 bits da saída da ULA, para detectar que o resultado da operação da ULA é Zero, conforme mostrado abaixo.

Assim, quando a saída do detector tiver valor lógico ALTO, significa que o resultado presente na saída da ULA é zero.

Detector de Valor Zero
Detector de Valor Zero

Exemplo do código em VHDL:


Zero <= not (Saida(7) or Saida(6) or Saida(5) or Saida(4) or Saida(3) or Saida(2) or Saida(1) or Saida(0));


Atividade: Adicionando a Instrução de Desvio Condicional

Essa instrução necessitará de:

As duas tabelas abaixo mostram os sinais envolvidos nessa modificação. Elas devem ser completadas de acordo com o comportamento desejado para o JEQ.


Instruções e Pontos de Controle
Instrução Mnemônico Código Binário JMP JEQ Sel MUX Hab_A Operação habFlag= RD WR
Sem Operação NOP 0000 0 0 X 0 XX 0 0
Carrega valor da memória para A LDA 0001 0 0 0 1 10 1 0
Soma A e B e armazena em A SOMA 0010 0 0 0 1 01 1 0
Subtrai B de A e armazena em A SUB 0011 0 0 0 1 00 1 0
Carrega valor imediato para A LDI 0100 0 0 1 1 10 0 0
Salva valor de A para a memória STA 0101 0 0 0 0 XX 0 1
Desvio de execução JMP 0110 1 0 X 0 XX 0 0
Desvio condicional de execução JEQ 0111 0 1 X 0 XX 0 0
Comparação CEQ 1000 0 0

Observação: Hab_A : Habilita A; habFlag= : habFlagIgual; RD : habLeituraMEM; WR : habEscritaMEM;

As saídas que estão indicadas como X ou XX devem ser implementadas como 0 ou 00. Isso facilita o processo de debug do projeto.

Lógica de Desvio (Próxima Instrução)
JMP JEQ Flag de Igual Seleção do Mux Ação Resultante
0 0 X 0 Prox. Instrução
1 0 X 1 Desvio de JMP
0 1 0 0 Prox. Instr. JEQ
0 1 1 1 Desvio de JEQ

Vamos implementar, e simular, esse circuito em VHDL, fazendo as alterações necessárias na ULA e na Unidade de Controle. Para tanto, utilizaremos o programa abaixo. Note que os valores precisam ser convertidos para binário.

Programa de teste para o processador
Linha Instrução Observação
0 JMP @4 Deve desviar para a posição 4
1 JEQ @9 Deve desviar para a posição 9
2 NOP
3 NOP
4 LDI $5 Carrega acumulador com valor 5
5 STA @256 Armazena 5 na posição 256 da memória
6 CEQ @256 A comparação deve fazer o flagIgual ser 1
7 JMP @1 Vai testar o flagIgual depois do jump
8 NOP
9 LDI $4 Carrega acumulador com valor 4
10 CEQ @256 Compara com valor 5, deve fazer o flagIgual ser 0
11 JEQ @3 Não deve ocorrer o desvio
12 JMP @12 Fim. Deve ficar neste laço

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

O resultado da compilação será o circuito, em RTL, mostrado no diagrama abaixo.

RTL do Computador
RTL do Computador

Para fazer a simulação não precisaremos de nenhum sinal de entrada, além do clock. Vale lembrar que o clock é aplicado através do sinal KEY(0).

A verificação do funcionamento do circuito pode ser feita através do monitoramento do Program Counter e sua comparação com a execução do programa mostrado no fim do ítem Procedimento desta atividade. O resultado esperado está mostrado abaixo.

Simulação do Programa de Teste
Simulação do Programa de Teste

Podemos ver que, de acordo com o esperado, o Program Counter assume a seguinte sequência de valores: 0, 4, 5, 6, 7, 1, 9, 10, 11, 12, 12, 12 …

Como a última instrução é o _JMP @12_, que está localizada na posição 12 da memória de instruções, ocorrerá um laço infinito que sempre executará essa instrução.


Chamada de Sub Rotina

Uma outra instrução de desvio é o JSR (jump sub routine), que desvia a execução para um trecho de código que, após executado retorna para a posição seguinte à chamada da sub-rotina. Ela funciona juntamente com a instrução de retorno (RET), que indica o fim da rotina que foi chamada.

Isso pode ser implementado aumentando-se o MUX_JMP/PC+1 para quatro entradas e armazenando o valor de PC+1 do momento da chamada da sub-rotina. Esse valor será utilizado pela instrução RET, para retornar ao processamento no ponto deixado pela instrução JSR.

Essa implementação permite a chamada de somente uma sub-rotina, não permitindo sub-rotinas aninhadas. A implementação de sub-rotinas aninhadas necessita que seja utilizada uma estrutura de pilha controlada pelo hardware.

Fluxo de Dados com Desvio Condicional
Fluxo de Dados com Desvio Condicional

Para simplificar o diagrama, os sinais de clock, vindos do detector de borda, foram suprimidos no desenho. Mas, na implementação, o sinal de clock deve ser distribuito para todos os registradores.

O bloco “Lógica de Desvio” foi desenhado fora da UC para evidenciar a sua existência.


Responder o quiz de participação, no blackboard, em:

Conteúdos > Participação > Aula_5_Quiz-P2


Atividade: Adicionando a Chamada de Sub Rotina

Abaixo, temos os pontos de controle necessários para implementar essa nova instrução. Vamos fazer as alterações necessárias, implementar e simular o circuito.


Instruções e Pontos de Controle
Instrução Mnemônico Código Binário Hab Escrita Retorno JMP RET JSR JEQ Sel MUX Hab_A Operação habFlag= RD WR
Sem Operação NOP 0000 X 0 XX 0 0
Carrega valor da memória para A LDA 0001 0 1 10 1 0
Soma A e B e armazena em A SOMA 0010 0 1 01 1 0
Subtrai B de A e armazena em A SUB 0011 0 1 00 1 0
Carrega valor imediato para A LDI 0100 1 1 10 0 0
Salva valor de A para a memória STA 0101 0 0 XX 0 1
Desvio de execução JMP 0110 X 0 XX 0 0
Desvio condicional de execução JEQ 0111 X 0 XX 0 0
Comparação CEQ 1000
Chamada de Sub Rotina JSR 1001
Retorno de Sub Rotina RET 1010

Observação: Hab_A : Habilita A; habFlag= : habFlagIgual; RD : habLeituraMEM; WR : habEscritaMEM;

As saídas que estão indicadas como X ou XX devem ser implementadas como 0 ou 00. Isso facilita o processo de debug do projeto.

Lógica de Desvio (Próxima Instrução)
JMP RET JSR JEQ Flag de Igual Seleção do Mux Ação Resultante
0 0 0 0 X 00 Prox. Instrução
1 0 0 0 X 01 Desvio de JMP
0 0 0 1 0 00 Prox. Instr. JEQ
0 0 0 1 1 01 Desvio de JEQ
0 0 1 0 X 01 Desvio de JSR
0 1 0 0 X 10 Desvio de RET

Para padronizar os testes do decodificador, iremos assumir a seguinte distribuição de bits na palavra de controle:

Bit 11 Bit 10 Bit 9 Bit 8 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
habEscritaRetorno JMP RET JSR JEQ SelMUX Habilita A Operação Bit 1 Operação Bit 0 HabFlagIgual HabLeituraMEM HabEscritaMEM


Dado o aumento do número de pontos de controle, fica mais simples e menos propenso a erros fazer o seguinte:

Abaixo, temos um exemplo de como utilizar essa técnica.

-- Antes do BEGIN, depois da definição dos respectivos SIGNALs

  alias SelMUX:       std_logic is controlWord(6);
  alias operacaoULA:  std_logic_vector(1 downto 0) is controlWord(4 downto 3);

-- Depois do BEGIN, para fazer a atribuição individual.

SelMUX <= '1'  when (opcode = ???) or (opcode = ???) else '0';

operacaoULA <= "00" when  (opcode = ???) else
               "01" when  (opcode = ???) else
               "10" when  (opcode = ???) or (opcode = ???) else
               "11";


TESTE

Para testar, utilizaremos o programa abaixo. Note que os valores precisam ser convertidos para binário.

Programa de teste para o processador
Linha Instrução Observação
0 JSR @14 Deve desviar para a posição 14
1 JMP @5 Deve desviar para a posição 5
2 JEQ @9 Deve desviar para a posição 9
3 NOP
4 NOP
5 LDI $5 Carrega acumulador com valor 5
6 STA @256 Armazena 5 na posição 256 da memória
7 CEQ @256 A comparação deve fazer o flagIgual ser 1
8 JMP @2 Vai testar o flagIgual depois do jump
9 NOP
10 LDI $4 Carrega acumulador com valor 4
11 CEQ @256 Compara com valor 5, deve fazer o flagIgual ser 0
12 JEQ @3 Não deve ocorrer o desvio
13 JMP @13 Fim. Deve ficar neste laço
14 NOP
15 RET Retorna para a posição 1

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

Esta atividade deverá ser entregue no Black Board, na semana seguinte a esta aula.

O resultado da compilação será o circuito, em RTL, mostrado no diagrama abaixo.

RTL do Computador
RTL do Computador

Para fazer a simulação não precisaremos de nenhum sinal de entrada, além do clock. Vale lembrar que o clock é aplicado através do sinal KEY(0).

A verificação do funcionamento do circuito pode ser feita através do monitoramento do Program Counter e sua comparação com a execução do programa mostrado no fim do ítem Procedimento desta atividade. O resultado esperado está mostrado abaixo.

Simulação do Programa de Teste
Simulação do Programa de Teste

Podemos ver que, de acordo com o esperado, o Program Counter assume a seguinte sequência de valores: 0, 14, 15, 1, 5, 6, 7, 8, 2, 9, 10, 11, 12, 13, 13 …

Como a última instrução é o _JMP @13_, que está localizada na posição 13 da memória de instruções, ocorrerá um laço infinito que sempre executará essa instrução.


Somente a última Atividade deverá ser entregue através do Blackboard!




Ferramentas