4 DESENVOLVIMENTO
4.1 DESCRIÇÃO DO BIP II
4.1.7 decoder
Para a execução de uma instrução, é preciso que o processador identifique o que está sendo solicitado. Tal identificação é feita a partir da decodificação de um código (identificado como código de operação) presente na palavra de instrução. Logo, torna-se necessário a existência de um componente capaz de compreender tais códigos e coordenar os demais dispositivos.
O decodificador é responsável por decodificar o código de operação existente na palavra de instrução, e gerar os sinais de controle para os demais componentes do processador a fim de realizar a operação solicitada.
O módulo decoder foi descrito para trabalhar como o decodificador do BIP II. O mesmo recebe o código de operação da instrução a ser executada, decodifica-o e envia os sinais necessários para os outros módulos. Uma abstração desse módulo com suas conexões é apresentada pela Figura 56.
Figura 56 – Conexões do módulo decoder.
decoder reset_in
ir_wr_out opcode_in
status_Z_in status_N_in clock_in
ir_reset_out pc_wr_out pc_reset_out
acc_wr_out acc_reset_out status_wr_out status_reset_out data_memory_wr_out alu_op_out
branch_out sel_A_out sel_B_out
Fonte: O Autor.
Como pode ser observado a partir da Figura 56, o módulo decoder dispõe das seguintes conexões:
• opcode_in: Entrada para o código de operação a ser decodificado;
• status_Z_in: Entrada para o sinal que informa se a operação realizada por alu resultou em zero;
• status_N_in: Entrada para o sinal que informa se a operação realizada por alu resultou em um número negativo;
• clock_in: Entrada para o sinal de clock;
• reset_in: Entrada para o sinal de reinicialização do módulo decoder;
• data_memory_wr_out: Saída do sinal que habilita a escrita na memória de dados;
• alu_op_out: Saída do sinal para selecionar a operação a ser realizada na ULA;
• branch_out: Saída do sinal que indica a realização de um desvio;
• sel_A_out: Saída do sinal para seleção da entrada do mux_A;
• sel_B_out: Saída do sinal para seleção da entrada do mux_B.
Além dessas conexões, existem outras para controle da escrita e da reinicialização dos registradores. As conexões que são dedicadas para habilitação da escrita possuem o mesmo nome do registrador de destino, acrescido da terminação _wr_out. E as dedicadas à reinicialização possuem o mesmo nome do registrador de destino, acrescido da terminação _reset_out.
Para o registrador de instrução, por exemplo, o ir_wr_out é o pino dedicado ao controle da escrita e o ir_reset_out é pino o dedicado à reinicialização do mesmo. Um par semelhante de conexões existe para os registradores PC, ACC e STATUS.
A execução de uma instrução é dividida em dois estados. O estado de busca (fetch) onde a instrução é armazenada no registrador de instruções. E o estado de execução (exec) onde a instrução é decodificada e executada.
É possível realizar a decodificação e a execução em apenas um estado porque todas as instruções gastam o mesmo número de pulsos de clock, e não necessitam de mais operandos para serem executadas. Trata-se de uma organização monociclo (apresentada na subseção 2.1.5).
Além desses dois estados, o decoder possui o estado de reinicialização (reset), que reconfigura todos os registradores. O diagrama de estados presente na Figura 57 exibe esses quatro estados e as condições para que as transições entre os mesmos seja realizada.
Figura 57 – Diagrama de estados do módulo decoder.
EXEC FETCH
RESET posedge clock_in
&&
reset_in == 0
posedge clock_in
&&
reset_in == 1
negedge reset_in
posedge clock_in
posedge clock_in
negedge reset_in
Fonte: O Autor.
De acordo com a Figura 57, quando entra em operação, o primeiro estado que decoder assume é o RESET Nesse estado, ir_reset_out, pc_reset_out, acc_reset_out e status_reset_out assumem nível lógico alto (1). Logo, os registradores do BIP II são reinicializados. As demais saídas de decoder apresentam nível lógico baixo (0).
Ainda no estado RESET, caso reset_in seja igual a 0 quando for detectada uma borda de subida em clock_in, decoder permanece em RESET. Caso contrário (reset_in igual a 1), esse módulo avança para o próximo estado.
No estado FETCH, a instrução a ser executada deve ser guardada no registrador de instrução. Para isso, a conexão ir_wr_out tem seu valor configurado para 1, habilitando a escrita em IR. Já as outras conexões de saída enviam o valor 0.
Nesse mesmo estado, caso seja detectada uma borda de descida em reset_in, decoder muda para o estado RESET. Se esse fenômeno não ocorrer, decoder avança para o estado EXEC quando identificar uma borda de subida em clock_in.
No estado EXEC, a saída ir_wr_out retorna para 0, desabilitando a escrita no registrador de instrução. Além disso, o código de operação da instrução armazenada em IR é encaminhado para a entrada opcode_in. A partir desse código, decoder identifica qual é a operação a ser realizada. Com isso, os sinais de controle são enviados por suas saídas, a fim de executar o que foi solicitado.
Ainda no estado EXEC, se for identificada uma borda de descida em reset_in, decoder altera seu estado para RESET. Caso tal sinal não seja detectado, esse módulo retorna para o estado FETCH, iniciando um novo ciclo de execução.
Para descrever esses estados e suas transições, uma máquina de estados foi elaborada em SystemVerilog dentro do módulo decoder. Tal dispositivo é responsável pela realização do ciclo de execução apresentado pelo diagrama da Figura 57.
Antes de iniciar o desenvolvimento dessa máquina de estados, utilizou-se um bloco de parâmetros locais (localparam) visando facilitar a identificação dos estados durante o desenvolvimento, já que esses estados são basicamente representados como códigos binários na descrição de hardware. A estrutura dessa associação é descrita no Código 18.
Código 18 – Declaração de parâmetros locais em SystemVerilog.
1 l o c a l p a r a m
2 _ F E T C H = 2 ’ b00 , 3 _ E X E C = 2 ’ b01 , 4 _ R E S E T = 2 ’ b10 ;
Fonte: O Autor.
Por exemplo, o código binário 102 foi vinculado ao parâmetro _RESET. Logo, toda vez que _RESET for invocado ao longo da descrição do módulo decoder, o mesmo será substituído pelo número 102 no momento da síntese.
Esses parâmetros possuem um funcionamento semelhante ao das constantes dis-poníveis em linguagens de programação. Um bloco localparam também foi descrito para referenciar os códigos de operação presentes no BIP II.
A transição de estados de decoder é feita através do uso do bloco de procedimentos always_ff, que utiliza como referência para mudança de estados os sinais de clock_in e reset_in. Esse bloco é descrito conforme apresentado pelo Código 19.
Código 19 – Definição da transição de estados do módulo decoder.
1 a l w a y s _ f f @ (p o s e d g e c l o c k _ i n or n e g e d g e r e s e t _ i n ) 2 b e g i n
3 if( r e s e t _ i n )
4 c u r r e n t _ s t a g e = n e x t _ s t a g e ;
5 e l s e
6 c u r r e n t _ s t a g e = _ R E S E T ; 7 end
Fonte: O Autor.
NoCódigo 19, foram definidas duas variáveis: next_stage para indicar o próximo estado, e current_stage para indicar o estado atual. A cada borda positiva de clock_in ou borda negativa de reset_in é feita uma verificação do sinal presente em reset_in.
Se reset_in estiver em nível lógico alto, current_stage recebe o valor presente em next_stage. Ou seja, decoder avança para o próximo estado. Caso reset_in esteja em nível lógico baixo, decoder assume o estado RESET.
Ao manter-se reset_in em 1, decoder realiza o ciclo de execução normalmente, transitando entre os estados FETCH e EXEC a cada borda de subida em clock_in. Caso ocorra uma borda de descida em reset_in, o valor presente em tal sinal será 0, e com isso decoder entrará no estado RESET independente do seu estado atual.
Antes da máquina de estados entrar em operação, é preciso definir seu estado inicial.
Essa ação é realizada com o uso do bloco initial. Esse bloco é descrito antes do always_ff e é executado apenas no início da operação. Tal bloco pode ser visto no Código 20.
Código 20 – Definição do estado inicial do módulo decoder.
1 i n i t i a l 2 b e g i n
3 n e x t _ s t a g e = _ R E S E T ; 4 end
Fonte: O Autor.
Ao determinar que next_stage recebe o código binário referente ao estado RESET, define-se que RESET será o primeiro estado que decoder assumirá quando entrar em operação.
É possível afirmar tal funcionamento devido ao fato de current_stage assumir o valor de next_stage dentro de always_ff (caso reset_in esteja em 1), e next_stage estar armazenando o valor referente ao estado RESET. Com isso, decoder entrará no estado RESET quando começar a trabalhar.
Para que o módulo assuma os estados na sequência desejada, é necessário definir qual valor next_stage deve assumir (para qual estado next_stage apontará) de acordo com o estado atual. A fim de determinar isso, ao entrar em um novo estado, a variável next_stage é atualizada com o valor referente ao próximo estado.
Com isso, quando decoder entra em RESET (current_stage igual a _RESET), next_stage recebe _FETCH. Para current_stage igual a _FETCH, next_stage recebe _EXEC, e para current_stage igual a _EXEC, next_stage recebe _FETCH. Essas associações permitem que decoder transite corretamente pelos estados do ciclo de execução apresentado pela Figura 57.
Como abordado no início dessa subseção, no estado EXEC, as saídas de decoder dependem do código de operação decodificado. Logo, cada código resulta em valores específicos nos sinais de controle emitidos por decoder.
O funcionamento do módulo decoder para cada código de operação e seus respectivos testes são apresentados nos tópicos a seguir. Tal funcionamento segue as descrições das operações do BIP II contidas na Tabela 2.
4.1.7.1 HLT
A operação de parada (halt - HLT), de código 000002, é um procedimento que impede o avanço do contador de programa. Nessa operação, o endereço armazenado no registrador PC não é incrementado, permanecendo inalterado.
Logo, quando o decodificador se depara com o código referente à HLT, nenhuma outra operação será executada após HLT porque o PC não avançará. Na arquitetura BIP II, somente a operação HLT impede acréscimos ao valor presente em PC.
Para que o valor contido no registrador PC seja mantido inalterado, é necessário que as saídas pc_wr_out e pc_reset_out estejam em nível lógico baixo. As outras saídas de decoder também são configuradas para 0. Com isso, os demais componentes não sofrerão modificações em seus dados durante a execução de HLT.
Com o intuito de averiguar a resposta de decoder ao receber o código referente à operação HLT, um teste foi descrito. Nesse teste, são inseridos em decoder um sinal de clock, o código da operação HLT, os sinais de reset e de status.
Os valores dos sinais de status são relevantes somente para as operações de desvio, não influenciando nas demais operações. Os resultados obtidos no teste são apresentados pela Figura 58.
Figura 58 – Teste da operação HLT.
Fonte: O Autor.
A partir daFigura 58, nota-se o intervalo de inicialização de decoder no começo do teste (0-2 ns). Nesse trecho, decoder entra no estado de RESET mesmo com reset_in em
nível lógico baixo porque esse componente começou a trabalhar.
Com isso, as saídas de reinicialização dos registradores (acc_reset_out, pc_reset_out, status_reset_out e ir_reset_out) apresentaram o valor 1, e as outras saídas de decoder exibiram o valor 0.
No intervalo seguinte (2-4 ns), decoder emitiu os sinais para realização da busca da instrução a ser executada (estado FETCH). Dessa forma, a saída ir_wr_out foi configurada para 1, habilitando a escrita em IR, e as demais saídas foram configuradas para 0.
No estado EXEC (4-6 ns), todas as saídas de decoder apresentaram nível lógico baixo. Assim, nenhuma alteração é realizada nos registradores e o processador não buscará a próxima instrução presente na memória, pois o registrador PC não foi incrementado.
Dessa maneira, foi possível verificar que decoder gerou as saídas necessárias para a execução correta da operação HLT.
4.1.7.2 STO
A operação de armazenar (storage - STO), de código 000012, foi projetada para guardar dados provenientes do processador na memória de dados. Nessa operação, o valor presente em ACC é armazenado na memória de dados. Além disso, o operando da palavra de instrução representa o endereço de memória onde tal valor será salvo.
A fim de executar a operação STO, faz-se necessário habilitar a escrita na memória de dados. Logo, a saída data_memory_wr_out deve apresentar nível lógico alto. Ademais, o registrador PC é incrementado para que seja buscada a próxima instrução no ciclo de execução seguinte.
Com isso, pc_wr_out também apresentará nível lógico alto. Como somente a memória de dados e o registrador PC sofrerão alterações nessa operação, as saídas restantes enviarão 0.
O trecho descrito para verificar o comportamento de decoder diante do código da operação STO engloba um ciclo de execução (estados FETCH e EXEC). Nesse parte do teste, reset_in foi mantido em 1 e os sinais de status foram mantidos em 0. O retorno do módulo decoder pode ser visualizado na Figura 59.
Figura 59 – Teste da operação STO.
Fonte: O Autor.
NaFigura 59, é possível observar o período de busca da instrução (6-8 ns). Nessa etapa, apenas o sinal ir_wr_out apresentou nível lógico alto. Com isso, o registrador de instrução deve armazenar a palavra de instrução a ser executada.
Na etapa seguinte (8-10 ns), decoder identificou a operação STO e configurou as saídas data_memory_wr_out e pc_wr_out para 1. Dessa forma, a memória de dados armazena o dado que lhe foi enviado, e o endereço guardado em PC é incrementado. Logo, o módulo decoder dispôs em suas saídas os valores esperados para o código da operação STO.
4.1.7.3 LD e LDI
A operação de carga (load - LD), de código 000102, permite armazenar uma palavra de dados no acumulador, onde essa palavra é proveniente da memória de dados. Desse modo, o operando da instrução representa o endereço da memória de dados no qual o valor a ser guardado está localizado.
Além dessa operação de carga de dados, a arquitetura do BIP II dispõe de uma outra operação para carga imediata (load immediate - LDI), de código 000112. Nessa operação, o dado a ser carregado em ACC é o operando presente na palavra de instrução.
Ambas as operações (LD e LDI) provocam a alteração do conteúdo de ACC.
Logo, para sua execução, é preciso habilitar a escrita no acumulador. Portanto, a saída acc_wr_out deve apresentar nível lógico alto.
De acordo com a organização do BIP II, a entrada de dados do registrador ACC está conectada à saída de um multiplexador de três entradas. É a partir desse multiplexador que é possível selecionar qual será a origem dos dados que serão guardados em ACC.
Desse modo, para que a palavra a ser armazenada seja proveniente da memória de dados (operação LD), a conexão sel_A_out deve enviar o valor 002, pois a saída dessa memória estará conectada à primeira entrada do multiplexador.
Já para a operação LDI, na qual a palavra a ser armazenada é o operando da instrução, a conexão sel_A_out apresentará o valor 012. Isso deve-se ao fato de que o caminho para o operando da palavra de instrução estará ligado à segunda entrada do multiplexador.
No teste de LD, decoder recebeu o código referente à tal operação. Com reset_in mantido em 1, o ciclo de execução é feito normalmente. A Figura 60 mostra os sinais gerados por decoder para realizar essa operação.
Figura 60 – Teste da operação LD.
Fonte: O Autor.
No intervalo entre 10 e 12 ns (Figura 60), nota-se o início do ciclo de execução, onde decoder enviou o sinal para habilitar a escrita em IR (ir_wr_out = 1). Após o envio dos sinais para realização da busca da instrução (12-14 ns), decoder reconheceu o código da operação LD e a saída acc_wr_out foi configurada para 1, ativando a escrita em ACC.
Além disso, a saída sel_A_out foi mantida com o valor 002, informando ao multiplexador que a entrada de dados a ser utilizada é a primeira, pois tal entrada recebe
a palavra proveniente da memória de dados. Nessa operação, pc_wr_out também foi configurado para 1, o que permite a incrementação de PC.
Para o teste da operação LDI, uma abordagem semelhante foi utilizada. Inseriu-se o código referente a essa operação na entrada opcode_in de decoder e reset_in foi mantido em 1. Com isso, decoder transitou pelos estados FETCH e EXEC, e a operação solicitada foi realizada. o teste da execução de LDI pode ser visualizado na Figura 61.
Figura 61 – Teste da operação LDI.
Fonte: O Autor.
A partir da Figura 61, é possível observar a passagem de decoder pelo estado FETCH (14-16 ns), onde apenas a saída ir_wr_out apresentou nível lógico alto. No intervalo seguinte (16-18 ns), a operação LDI é identificada e executada.
Semelhante à execução de LD, as saídas acc_wr_out e pc_wr_out foram configu-radas para 1. Contudo, dessa vez o valor apresentado em sel_A_out foi 012, indicando para o multiplexador que a entrada a ser utilizada é a segunda. É por essa entrada que o operando da instrução em execução virá para ser armazenado em ACC.
Com isso, foi possível avaliar o comportamento de decoder ao receber os códigos referentes às operações LD e LDI, e concluir que nessas ocasiões tal módulo funcionou de acordo com o esperado.
4.1.7.4 ADD e ADDI
A operação de adicionar (add - ADD) requisita à ALU a soma de dois números.
Na arquitetura do BIP II, essa operação utiliza como operando A o número existente no acumulador.
O operando B pode ser um número proveniente da memória de dados (operação ADD, de código 001002), ou pode ser o próprio operando da palavra de instrução, o que caracteriza uma adição imediata (add immediate - ADDI), de código 001012.
Para que o resultado dessa soma não seja perdido, o mesmo é armazenado no acumulador. Dessa forma, nota-se que ambas operações requerem a escrita em ACC. Além disso, toda operação aritmética (soma ou subtração) do BIP II exige o armazenamento das características do resultado (se tal número é igual a zero, ou se é negativo). Logo, o registrador STATUS também precisa estar habilitado para escrita.
Ao executar as operações ADD e ADDI, o valor de alu_op_out precisa estar em 0, pois tal valor indica à ALU que uma operação de soma é requisitada. Além disso, decoder deve configurar acc_wr_out e status_wr_out para nível lógico alto, permitido a escrita em ambos.
Já para possibilitar a seleção da origem do operando B, a organização do BIP II dispõe de um multiplexador de duas entradas conectado à entrada B da ALU. A primeira entrada desse multiplexador recebe o número proveniente da memória de dados, e a segunda entrada recebe o número proveniente da palavra de instrução.
Então, para utilizar dados da memória no endereço indicado pelo operando da instrução, o valor da saída sel_B_out precisa ser 0 (operação ADD). Já para utilizar o operando da palavra de instrução imediatamente na operação, o valor de sel_B_out precisa ser 1 (operação ADDI).
Por fim, para que ACC receba o resultado gerado pela ALU, é necessário que o multiplexador de três entradas ligado à entrada de ACC esteja com sua terceira entrada selecionada. Isso deve-se ao fato de que essa entrada recebe a saída da unidade lógica e aritmética. Logo, decoder precisa definir o valor de sel_A_out para 102.
Em resumo, as saídas de decoder devem ser configuradas para os seguintes valores:
alu_op_out = 0, acc_wr_out = 1, status_wr_out = 1, pc_wr_out = 1, sel_A_out
= 10, e sel_B_out = 0 se a operação for ADD, e 1 se A operação for ADDI. As demais saídas serão mantidas em 0.
Para testar a operação ADD no módulo decoder, inseriu-se em opcode_in o valor referente a ADD e reset_in continuou a receber 1. O resultado do ciclo de execução dessa operação obtido pelo teste é apresentado pela Figura 62.
Figura 62 – Teste da operação ADD.
Fonte: O Autor.
NaFigura 62, nota-se o intervalo de busca da instrução (18-20 ns), onde apenas a saída ir_wr_out apresentou nível lógico alto. Em seguida, no estado de execução (20-22 ns), é possível observar que acc_wr_out, pc_wr_out e status_wr_out assumiram o valor 1, habilitando a escrita de tais registradores.
Além disso, sel_A_out enviou o valor 102 para selecionar a saída da ALU como fonte de dados para ACC, sel_B_out enviou o valor 0, definindo o número proveniente da memória de dados como operando B, e as saídas restantes foram mantidas em 0. Dessa maneira, verificou-se que decoder emitiu corretamente por suas saídas os sinais necessários para a execução da operação ADD.
O teste da execução de ADDI é parecido com o teste de ADD. O módulo decoder recebeu em sua entrada opcode_in o código referente à operação ADDI, e em reset_in recebeu nível lógico alto.
Os valores de status_Z_in e status_N_in não influenciam na execução dessa operação. Assim como nos testes das operações anteriores, ambos foram mantidos em 0.
A resposta de decoder quando foi submetido a essas condições é mostrada na Figura 63.
Figura 63 – Teste da operação ADDI.
Fonte: O Autor.
Como pode ser visto na Figura 63, após o estado de busca da instrução (22-24 ns), as saídas acc_wr_out, pc_wr_out e status_wr_out enviaram o valor 1, e sel_A_out o valor 102 no trecho de execução (24-26 ns) . Esse resultado é esperado pois esses sinais são configurados de forma idêntica tanto em ADD quanto em ADDI.
Ao observar sel_B_out, nota-se que, diferente da resposta apresentada para o código da operação ADD na Figura 62, essa saída emitiu o valor 1. Isso informa que o operando da instrução deve ser utilizado de forma imediata na operação, pois o número presente na segunda entrada do multiplexador para seleção do operando B provém da palavra de instrução.
Com o resultado obtido a partir desse teste, foi possível concluir que o módulo decoder também funcionou de maneira apropriada ao receber o código referente à operação ADDI.
4.1.7.5 SUB e SUBI
A operação de subtrair (subtract - SUB) presente no BIP II solicita à ALU a subtração de dois números e, assim como a operação de soma, armazena o resultado no acumulador. Como trata-se de uma operação aritmética, a gravação no registrador STATUS também é requisitada.
Essa operação utiliza como operando A o número armazenado em ACC, e como operando B o número presente na memória de dados no endereço indicado pela palavra
de instrução (operação SUB, de código 001102). Já a operação de subtrair de imediato (subtract immediate - SUBI), de código 001112, utiliza o próprio operando da instrução
como operando B.
Para que a unidade lógica e aritmética efetue uma subtração, é necessário indicar por meio da saída alu_op_out tal requisição. Dessa forma, quando decoder deparar-se com o código da operação SUB ou SUBI, o mesmo deve configurar a saída alu_op_out para enviar nível lógico alto.
Além disso, as saídas acc_wr_out, pc_wr_out e status_wr_out precisam possuir o valor 1 para habilitar a escrita nos registradores ACC, PC e STATUS, respectivamente, e sel_A_out deve apresentar o valor 102, o que liga a saída da ALU com a entrada do registrador ACC por meio de um multiplexador.
Por fim, para a operação SUB, a saída sel_B_out exibirá nível lógico baixo, selecionando a memória de dados como origem para o operando B, e para a operação SUBI, sel_B_out exibirá nível lógico alto, selecionando o operando da palavra de instrução como operando B.
Com isso, a configuração das saídas ficou de seguinte forma: alu_op_out = 1, acc_wr_out = 1, status_wr_out = 1, pc_wr_out = 1, sel_A_out = 10, e sel_B_out = 0 se a operação for SUB, e 1 se A operação for SUBI. As outras saídas devem manter o valor 0.
O teste para operação SUB foi realizado com a inserção de seu código na entrada opcode_in. A entrada reset_in foi mantida em 1 para possibilitar a realização do ciclo de execução baseado no sinal em clock_in, e os sinais de status continuaram em 0. O comportamento de decoder é exibido pela Figura 64.