2. Teoridel
2.3. Tilknytningsteori
2.3.2. Traumereaksjoner
O CABARE (Computador Acadêmico BAsico com REconfiguração), desenvolvido por [Silva, 2011], foi projetado para ser um processador RISC de propósito geral de baixa complexidade de hardware, composto por um pequeno conjunto de instruções sem padrão específico e uma lógica reconfigurável capaz de receber e executar instruções que não estão presentes no conjunto de instruções original, sendo tais instruções previamente armazenadas em posições definidas na memória.
A arquitetura do CABARE foi baseada em [Altera, 2013] como um exercício didático da fabricante de FPGA’s Altera® para demonstrar o funcionamento do Modelo de Von Neumann [Stallings, 2010] em um processador single-tasking1. Tal estrutura foi modificada para incluir novas instruções, operações, funcionalidades e a lógica de reconfiguração.
A organização física de 32 bits do CABARE é formada por um conjunto de 12 registradores de propósito geral (R0 a R11) e um registrador (R12) utilizado como contador de programa (PC), um multiplexador (MUX) de 16 canais de entrada, um registrador de instrução (IR), uma ULA com a primeira entrada ligada exclusivamente a um registrador acumulador (A) e a segunda entrada ligada ao barramento de dados, com
41 a saída conectada a um registrador de resultado (G), além de registradores para interface com a memória (DOUT e ADDR). Uma visão geral do CABARE está ilustrada na Figura 4.1.
Figura 5.1 - Arquitetura do processador CABARE
Fonte: autoria própria
A entrada de dados no processador é realizada por meio de um barramento de entrada de 32 bits (DIN), que recebe os dados provenientes da memória externa e os envia para uma das entradas do multiplexador. A saída do multiplexador é chamada de barramento de dados (BUS), e está conectado a quase todos os demais registradores do processador (R0 a R12, A, DOUT, ADDR e a ULA).
Todas as instruções lógicas e aritméticas necessitam de, no mínimo, três ciclos de relógio para sua execução, visto que existe apenas um barramento de dados que conecta os registradores à ULA, e um dos operandos de entrada deve ser obrigatoriamente armazenado no registrador acumulador A. Esta configuração demonstra a simplicidade do CABARE. A ordem dos canais de entrada do multiplexador para envio ao barramento de dados está disposta na Tabela 4.1.
42
Tabela 5.1 – Registradores de uso geral no CABARE Entrada do MUX Endereço (bin)
R0 0000 R1 0001 R2 0010 R3 0011 R4 0100 R5 0101 R6 0110 R7 0111 R8 1000 R9 1001 R10 1010 R11 1011 R12 (PC) 1100 Mask 1101 G 1110 DIN 1111
A ULA é responsável pelas operações lógicas e/ou aritméticas de soma, subtração, E lógico, OU lógico, NÃO lógico, transparência (throughput) e deslocamento de 1 bit à esquerda ou à direita. Também possui alguns flags que indicam se a operação resultou em zero (Z), um número negativo (S) ou se houve uma propagação de carry (Cy) a partir do seu MSB (Most Significative Bit). A relação de operações da ULA está descrita na Tabela 4.2.
Tabela 5.2 – Operações da ULA do CABARE Código Operação 000 Soma 001 Subtração 010 And 011 Or 100 not A 101 Transparência
110 deslocamento para direita 111 deslocamento para esquerda
O CABARE possui um conjunto composto por 20 instruções de 32 bits de palavra, de modo que cada instrução possua um único formato válido, na forma: IIIIIIIIXXXXYYYYZZZZZZZZZZZZZZZZ, onde IIIIIIII (8 bits) representa o código da operação (CodOp), XXXX (4 bits) o registrador RX, YYYY (4 bits) o registrador RY e ZZZZZZZZZZZZZZZZ (16 bits) um valor constante de 16 bits. Caso a instrução
43 utilize apenas dois registradores como operandos, então os 16 bits restantes não são considerados, podendo receber qualquer valor (Don’t Care). Quando a instrução utiliza apenas um registrador, os bits YYYY não são considerados, mas não podem fazer parte do valor constante, ou seja, a constante #D ocupa apenas os 16 bits reservados na palavra da instrução.
A cada busca na memória externa, a instrução atual é enviada para o registrador de instrução (IR), que o encaminha à unidade de controle para decodificação. A constante de 16 bits presente na instrução é enviada da unidade de controle para o barramento por meio da entrada Mask, cujo valor de 32 bits contém o valor 0x0000DDDD, obtido após uma operação AND cuja finalidade é eliminar os primeiros 16 bits da instrução contendo o código da operação e o endereço dos registradores, cujos valores não devem fazer parte da constante #D.
Este sistema executa as instruções apresentadas na Tabela 4.3. As 2 primeiras colunas mostram o código da operação (CodOp), em binário e em hexadecimal respectivamente, a terceira coluna mostra o nome da instrução e seus operandos e a última coluna o significado. A sintaxe Rx ← [Ry] significa que o conteúdo do registrador Ry é carregado no registrador Rx, enquanto que a expressão Rx ← #D indica que um valor constante #D de 32 bits é carregado no registrador Rx.
Tabela 5.3 – Operações realizadas pelo processador CABARE Código da
Operação (Bin)
Código da Operação (Hexa)
Operação Função Executada
00000000 00 mv Rx, Ry Rx ← [Ry]
00000001 01 mvi Rx, #D Rx ← #D
00000010 02 mvnz Rx, Ry IF G != 0, Rx ← Ry
00000011 03 add Rx, Ry Rx ← [Rx] + [Ry]
00000100 04 sub Rx, Ry Rx ← [Rx] – [Ry]
00000101 05 and Rx, Ry Rx ← [Rx] and [Ry]
00000110 06 or Rx, Ry Rx ← [Rx] or [Ry] 00000111 07 not Rx Rx ← not[Rx] 00001000 08 ld Rx, [Ry] Rx ← [[Ry]] 00001001 09 st Rx, [Ry] [Ry] ← [Rx] 00001010 0A ldi Rx G ← Rx 00001011 0B jmp #D PC ← #D 00001100 0C jnz #D IF G != 0, PC ← #D 00001101 0D shl Rx, Ry Rx ← [Ry] << 1 00001110 0E shr Rx, Ry Rx ← [Ry] >> 1 00001111 0F jnc #D IF Cy != 0, PC ← #D
44 00010000 10 jns #D IF S != 0, PC ← #D 00010001 11 jz #D IF Z = 0, PC ← #D 00010010 12 jc #D IF Cy = 0, PC ← #D 00010011 13 js #D IF S = 0, PC ← #D 00010100 a 00011110 14 a 1E Reservado --- 00011111 1F Halt PC ← PC (fim) 1111xxxx --- Configuráveis ---
A arquitetura pode executar diferentes operações a cada ciclo, controladas pela unidade de controle (control unit). Essa unidade determina quando um dado em particular é enviado ao barramento e qual registrador deve carregá-lo. Por exemplo, se a unidade de controle determinar MUX em 0x0000 e ativar o bit AIn, então o multiplexador transmitirá o conteúdo de R0 para o barramento e esse dado será carregado no acumulador A na próxima subida de relógio. A Tabela 4.4 indica os sinais de controle que devem ser ativados em cada pulso de relógio para cada instrução.
Tabela 5.4 – Sinais do controle do CABARE em cada instrução/tempo
T0 T1 T2 T3
(mv): IRIn MUX=Ry, RxIn, Done
(mvi): IRIn MUX=Mask, RxIn,
Done
(mvnz): IRIn Se !Z=> MUX=Ry, Gin Senão => Done
MUX=G, RxIn, Done
(add): IRIn MUX=Rx, AIn MUX=Ry, ULA=add, GIn
MUX=G, RxIn, Done
(sub): IRIn MUX=Rx, AIn MUX=Ry, ULA=sub, GIn
MUX=G, RxIn, Done
(and): IRIn MUX=Rx, AIn MUX=Ry, ULA=and, GIn
MUX=G, RxIn, Done
(or): IRIn MUX=Rx, AIn MUX=Ry, ULA=or, GIn
MUX=G, RxIn, Done
(not): IRIn MUX=Rx, ULA=not, GIn
MUX=G, RxIn, Done
(ld): IRIn MUX=Ry, ADDRIn, R_D
MUX=DIN, RxIn, Done
(st): IRIn MUX=Rx, DOUTIn MUX=Ry, ADDRIn, W_D, Done
(ldi): IRIn MUX=Rx,
ULA=transp, GIn, Done
(jmp): IRIn MUX=Mask, R12In, Done
45 (jnz): IRIn Se !Z=> MUX=Mask, R12In, Done Senão => Done (shl): IRIn MUX=Ry, ULA=shl, GIn
MUX=G, RxIn, Done
(shr): IRIn MUX=Ry, ULA=shr, GIn
MUX=G, RxIn, Done
(jnc): IRIn Se !Cy=> MUX=Mask, R12In, Done Senão => Done (jns): IRIn Se !S=> MUX=Mask, R12In, Done Senão => Done (jz): IRIn Se Z=> MUX=Mask, R12In, Done Senão => Done (jc): IRIn Se Cy=> MUX=Mask, R12In, Done Senão => Done (js): IRIn Se S=> MUX=Mask, R12In, Done Senão => Done (halt): IRIn -
4.1.1. Reconfiguração de instruções
O CABARE usa uma arquitetura RISC com 20 instruções “fixas” (já presentes no processador durante sua concepção), 10 códigos de operações não utilizados, reservados para uso futuro, e 16 códigos de operações livres para novas instruções. Este tipo especial de código de operação não é definido previamente na unidade de controle, sendo necessária a criação de um conjunto de microinstruções pelo usuário, os quais devem ser armazenados na memória externa juntamente com o restante do programa armazenado (dados e instruções).
A fim de simplificar a definição da quantidade de instruções reconfiguradas e sua faixa de representação, todos os códigos de operação (CodOp’s) com os 4 bits iniciais iguais a ‘1’ foram reservados para instruções reconfiguradas (códigos 0xFF00 a 0xFFFF). Ao decodificar o CodOp de uma instrução reconfigurada, a unidade de controle deverá buscá-la na memória em uma região de memória previamente delimitada para armazenar os dados de cada instrução.
A memória externa deve ser logicamente dividida em quatro áreas bem definidas: área de instruções, área de dados, armazenamento de microinstruções e área
46 de ponteiros, sendo esta última contendo o endereço de cada primeira microinstrução reconfigurada inicial, conforme mostra a Figura 4.2. A área de ponteiros reserva as últimas 16 células da memória para cada uma das 16 instruções reconfiguradas, independente do tamanho da memória utilizada. Dessa forma, o CodOp 0xF0 deverá ser associado ao endereço de memória 0xFF...F0, o CodOp 0xF1 associado ao endereço 0xFF...F1, e assim sucessivamente. Para inicializar os dados e instruções na memória, é utilizada uma MIF (Memory Initialization File) contendo todas estas áreas em intervalos definidos pelo programador.
Figura 5.2 – Distribuição de memória no CABARE
Fonte: autoria própria
Uma instrução reconfigurável é um conjunto de n palavras de 32 bits, de modo que cada palavra refere-se a uma microinstrução. Os campos desta palavra são os bits de controle de todas as unidades funcionais do caminho de dados do processador, como os bits de escrita dos registradores, os bits de operação da ULA, seleção do multiplexador, dentre outros. Para poder modificar o comportamento do caminho de dados, é preciso que todos os bits de controle do caminho de dados possam ser alterados por meio de uma microinstrução, exigindo total compreensão sobre o funcionamento da organização e arquitetura do processador por parte do programador, incluindo detalhes sobre quantidade e função de cada registrador, a ordem das entradas do multiplexador e o índice de operações da ULA. Como explicado no Cap. 2, o caminho de dados dos processadores RISP não é reconfigurado fisicamente; suas unidades funcionais se comportam conforme especificado em cada nova microinstrução, definindo a cada etapa
47 qual registrador será enviado como operando da ULA, que operação aritmética ou lógica será realizada e o registrador que receberá o resultado da operação.
Para criar uma microinstrução, é necessário definir os bits de controle do processador e selecionar um dos formatos de microinstrução, presentes na Tabela 4.5. O formato (a) envia todos os bits de controle para as unidades funcionais correspondentes na unidade operativa, de acordo com a ordem estabelecida. O formato (b) é utilizado para operações de desvio condicional e incondicional dentre as microinstruções reconfiguradas de um mesmo CodOp, por meio do envio do endereço de memória para um registrador de índice de estados reconfiguráveis. O formato (c) envia apenas os sinais de controle de escrita nos registradores, utilizando os 16 bits menos significativos para endereçamento ou operações com valores imediatos.
Tabela 5.5 - Configuração de uma microinstrução no CABARE
Formato R0In...12In ULA MUX IR A G DOUT ADDR WD RD END
4 13 3 4 1 1 1 1 1 1 1 1
(a)
Formato JMP Z Z’ S S’ CY CY’ - END 4 1 1 1 1 1 1 1 6 15
(b)
Formato R0In...R12In END
4 13 15
(c)
Os primeiros 4 bits de cada microinstrução alteram o formato da palavra. Com apenas uma configuração, não seria possível realizar uma integração entre os bits que compõem o código da instrução e suas respectivas microinstruções. Como exemplo, caso fosse utilizada uma instrução 0xF067000A, os bits referentes aos campos de registradores X = R6, Y = R7 e endereço ADDR = 0x000A só poderiam ser utilizados em uma única ordem na execução da instrução, já que não haveria como alterar estas informações em diferentes microinstruções. Variando a quantidade de formatos, é possível prover esta alteração de ordem dos dados em diferentes microinstruções, aumentando a gama de possibilidades de reconfiguração. A Tabela 4.6 apresenta os formatos de instrução definidos para este processador.
48
Tabela 5.6 – Formatos de microinstruções do CABARE Formato Utilização da microinstrução
0000 microinstrução tipo (a)
0001 microinstrução tipo (c) 0010 R_In = Rx 0011 R_In = Ry 0100 MUX = Rx 0101 MUX = Ry 0110 R_In = Rx se Z = 0 0111 R_In = Ry se Z = 1 1000 R_In = Rx se S = 0 1001 R_In = Ry se S = 1 1010 R_In = Rx se Cy = 0 1011 R_In = Ry se Cy = 1 1100 MUX = Rx se Z = 1 1101 MUX = Rx se S = 1 1110 MUX = Rx se Cy = 1 1111 MUX = ‘-‘
Não foi definido um limite mínimo ou máximo de palavras para cada instrução reconfigurável, sendo este trabalho realizado pelo programador. A única obrigatoriedade está no fato de que a última microinstrução de cada instrução reconfigurada deve ter o valor do seu bit_end setado (‘1’), o que indica para a unidade de controle voltar ao modo de execução normal. A Figura 4.3 apresenta um quadro detalhando as etapas para a execução de uma instrução reconfigurada.
49 É possível que uma instrução reconfigurada seja exatamente igual a uma instrução já existente no conjunto de instruções do processador, com exceção das instruções de desvio. Isto permite a uma instrução reconfigurada criar um bloco de instruções ou uma combinação de duas ou mais instruções a partir de um único CodOp, com funcionamento análogo a uma sub-rotina. Embora uma instrução reconfigurada possa substituir uma instrução fixa, tal procedimento não é recomendado para reconfigurar apenas uma única instrução, visto que a quantidade de ciclos de relógio para executar uma instrução reconfigurada é maior do que a quantidade de ciclos para uma instrução existente no conjunto de instruções.
A execução de instruções reconfiguradas ocorre de maneira semelhante às instruções fixas, com a inclusão de alguns ciclos adicionais para o endereçamento indireto da primeira microinstrução customizada. O valor de PC somente será atualizado após o término do microprograma da instrução reconfigurada.
4.1.2. Exemplo de funcionamento
No momento da compilação do código VHDL, um arquivo no formato .mif inicializa a memória do processador com o programa que será executado. Desse modo, a cada novo programa o projeto deve ser recompilado junto com o novo programa. O exemplo do quadro a seguir carrega um valor do endereço #9 da memória e faz um laço de repetição até atingir o valor que foi carregado.
Seguindo o formato de instrução já apresentado, a primeira instrução (mvi r5,#9) possui CodOp (IIIIIIII) como 0x01 e o registrador R5 (XXXX) possui o código 0x5. Como essa instrução não utiliza o segundo registrador, então o valor YYYY foi codificado como 0x0. O valor da constante de 16 bits (ZZZZZZZZZZZZZZZZ) recebe o valor 0x0009. Esta e as demais instruções do exemplo são detalhadas na Tabela 4.7.
(1) mvi r5,#9 //armazena #9 em R5 (será endereço de memória) (2) mvi r4,#1 //armazena #1 em R4
(3) ld r6,r5 //carrega da memória o valor do endereço em R5 e armazena em R6 (4) sub r6,r4 //subtrai valor de R6 por R4 e armazena em R6
(5) jnz #4 //se o resultado da subtração não for zero pula para endereço 4 (6) halt //fim de programa
50
Tabela 5.7 – Código objeto para o exemplo 1 Instrução mnemônica CodOp (IIIIIIII) Rx (XXXX) Ry (YYYY) #D Código objeto mvi r5,#9 01 5 0 0009 01500009 mvi r4,#1 01 4 0 0001 01400001 ld r6,r5 08 6 5 0000 08650000 sub r6,r4 04 6 4 0000 04640000 jnz #4 0C - - 000004 0C000004 Halt 1F 0 0 0000 1F000000
O próximo exemplo mostra como inserir uma instrução reconfigurada ADDI X,
Y, D (X Y + D), que não consta no conjunto de instruções original do CABARE. A
instrução 0xF0670005 tem como objetivo fazer com que o registrador R6 (registrador na posição X da instrução) receba o resultado da adição do registrador R7 (registrador na posição Y da instrução) com o valor imediato D = 0x0005. Estas microinstruções devem ser descritas pelo usuário, definindo os sinais de controle de acordo com o comportamento desejado em cada etapa da instrução, e inseridas nas posições correspondentes na memória seguindo configuração de áreas da MIF. Ao decodificar o código de operação desta instrução, os bits 0xF0 informam que a instrução é do tipo reconfigurada, ocasionando uma busca no endereço de memória 0xF...F0, que aponta para o início da sequência das seguintes microinstruções:
(1) ACC R7 (Y)
Formato R0In ... R12In ULA MUX IR A G DOUT ADDR WD RD END 0101 0000000000000 101 POS2 0 1 0 0 0 0 0 0
(2) MUX END (D), ULA = ADD (G = A + 0x0D)
Formato R0In ... R12In ULA MUX IR A G DOUT ADDR WD RD END 0001 0000000000000 000 MASK 0 0 1 0 0 0 0 0
(3) R6 (X) G, fim de procedimento
Formato R0In ... R12In ULA MUX IR A G DOUT ADR WD RD END 0010 POS1 101 MUX_G 0 0 0 0 0 0 0 1
Após o final da sequência de microinstruções, o valor do PC é atualizado, continuando a sequência de execução do processador. As instruções reconfiguradas podem ser utilizadas dentro de um programa da mesma forma que uma instrução fixa.
51
4.2. Processador CRASIS
O processador CRASIS (Computer with Reconfiguration And Selection of Instruction Set) foi desenvolvido por [Casillo, 2005]. A organização do processador foi inspirada na arquitetura MIC-1, descrito por [Tanembaum, 2007] e sua versão final apresentada em [Casillo, 2005a]. Trata-se de uma arquitetura RISC de 32 bits de palavra desenvolvida como uma prova de conceito de uma microarquitetura genérica que possa conter vários conjuntos de instruções, alternados a qualquer momento em tempo de execução, ajustando o conjunto de instruções que melhor se adapte ou que contenha as melhores instruções para executar um determinado programa, além de prover a criação de novas instruções por meio de uma unidade de reconfiguração dedicada.
Sua unidade operativa é composta por um banco de 32 registradores, sendo quatro destinados a funcionalidades específicas, como acumulador e apontador de pilha e o restante para uso geral, um deslocador de 32 bits para a esquerda ou direita, e uma ULA com capacidade para 8 operações distintas: transparência do primeiro operando, AND e OR lógicos, negação do primeiro operando, negação do segundo operando e operações aritméticas de adição, subtração e multiplicação de dois operandos. Para a comunicação com a memória, são utilizados dois registradores de interface, sendo um para endereços (MAR) e um para dados (MBR). A Figura 4.4 ilustra o caminho de dados.
Figura 5.4 – Diagrama de blocos do processador CRASIS
52 Todas as instruções possuem 32 bits de palavra. Existem 5 formatos diferentes de instruções, que utilizam de 0 a 4 operandos. Em todos os formatos de instrução, o primeiro byte é destinado ao CodOp, sendo os demais utilizados para endereçamento ou para determinação do(s) registrador(es) selecionado(s) para a operação. Cada novo formato exclui 5 bits do campo de operando para a seleção de um registrador. Dessa forma, o campo de operando possui 24 bits no primeiro formato, 19 bits no segundo formato, e assim sucessivamente. A Figura 4.5 mostra a disposição de bits para cada formato de instrução.
Figura 5.5 – Formatos de instrução do CRASIS
Fonte: autoria própria
A característica de reconfiguração do CRASIS não está na modificação de sua parte operativa, e sim na possibilidade de criar e reconfigurar instruções em sua unidade de controle, utilizando uma RFU (Reconfiguration Functional Unit) externa. Assim como no CABARE, para efetuar qualquer reconfiguração é necessário o conhecimento de todas as unidades funcionais do processador e seu funcionamento, visto que a RFU terá como funcionalidade armazenar os códigos de operação das novas instruções e os seus respectivos microprogramas, formados por uma sequência de sinais de controle que definem o comportamento de cada uma das unidades funcionais de sua unidade operativa. A Figura 4.6 apresenta uma visão geral do CRASIS contendo todas as suas unidades funcionais.
53
Figura 5.6 – Arquitetura do processador CRASIS
54
4.2.1. Modos de funcionamento
O CRASIS pode atuar sob dois modos de funcionamento: modo de reconfiguração e modo de execução, sendo que o modo de execução pode ser subdividido em execução de instruções fixas (instruções contidas nos conjuntos de instruções) e execução de instruções reconfiguradas (instruções criadas ou combinadas a partir da unidade de reconfiguração). A Figura 4.7 mostra um diagrama simplificado contendo a máquina de estados (FSM – Finite State Machine) destes modos de funcionamento.
Figura 5.7 – Diagrama de estados simplificado do CRASIS
Fonte: autoria própria
A determinação de qual modo de funcionamento será realizado é feita através do estado denominado Verifica_Reconfiguração. Este estado é responsável por verificar se houve um pedido de reconfiguração de uma instrução através do sinal de requisição externo. Caso este sinal contenha um pedido externo de reconfiguração, será iniciada a sequência de reconfiguração de instruções; caso contrário será iniciado o processo de execução normal.
Ao iniciar a execução de uma instrução, é verificado se a instrução atual é fixa ou customizada. Caso já seja existente no conjunto de instruções, a unidade de controle envia os comandos correspondentes aos estados que compõe a instrução, tal qual se encontra no conjunto de instruções selecionado. Caso contrário, a unidade de controle
55 busca os estados correspondentes à instrução customizada armazenados na unidade de reconfiguração.
Com o intuito de validar a proposta do processador de conter diversos conjuntos de instruções e poder alterná-los durante o seu funcionamento, foram implementados três conjuntos de instruções simples compostos por 32 instruções cada, sendo 30 de propósito geral específicas de cada conjunto, e 2 instruções privilegiadas comuns a todos os conjuntos, para possibilitar a alternância entre os conjuntos.
O conjunto de instrução 1 é similar ao conjunto apresentado em [Tanembaum, 2007] para o MIC-1, com o acréscimo de instruções de tratamento de interrupção e Entrada/Saída. Neste conjunto apenas o acumulador e o apontador de pilha (Stack Pointer - SP) são acessíveis ao usuário. O segundo conjunto possui instruções em que