Capítulo 4 Nível da microarquitetura Nível acima da lógica digital Função: Implementar a ISA (Instruction Set Architecture) O projeto da microarquitetura depende diretamente da ISA, além dos objetivos de custo e performance 4.1 Exemplo de Microarquitetura Cada microarquitetura é única => não existe fórmula Exemplo: Subconjunto da JVM => Apenas instruções com inteiros (IJVM) Microprograma (ROM): Busca, decodificação e execução das instruções IJVM Cada instrução IJVM é uma função a ser chamada pelo programa principal Programa principal: Loop simples, sem fim => determina a próxima função chamada, executa a função, determina a próxima função, executa a função,... Instruções IJVM: curtas, normalmente um ou dois campos. O primeiro campo é sempre o OPCODE. Microprograma: possui um conjunto de variáveis, acessadas por todas as funções => estado do computador MAR (Memory Address Register): Porta de endereçamento de memória (32 bits) MDR (Memory Data Register): Porta de dados de memória (32 bits), leitura ou escrita na memória. PC (Program Counter): indica a posição de memória(endereço) que contém a próxima instrução a ser executada. MBR (Memory Byte Register) : Porta de dados da memória (8 bits), apenas leitura da memória SP (Stack Pointer): Aponta para o topo da pilha LV (Local Variable): Aponta para a base das variáveis locais dentro da pilha CPP (Constant Pool Pointer): Aponta para a base da área de constantes. TOS OPC H (Holding Register) 79
4.1.1 Data Path Possui a ULA, suas entradas e saídas. Conjunto de registradores de 32 bits (arquivo de registradores) => acessíveis apenas no nível da microarquitetura (pelo microprograma) 6 linhas de seleção da função da ULA => apenas 16 funções úteis Número negativo: Complemento de dois Argumentos da ULA: 1º argumento: sempre do registrador H (Holding) 2º argumento: Qualquer um dos outros registradores, excluindo o H e MAR 80
Saída da ULA: Alimenta o deslocador Deslocador: SLL8 (Shift Left Logical) SRA1 (Shift Right Arithmetic) É possível ler e escrever em um mesmo registrador no mesmo ciclo de relógio. Temporização da via de dados Subciclos: liberação dos sinais de controle leitura dos registradores operação da ULA e deslocador propagação do resultado para os deslocadores Temporização rígida; Ciclo de relógio suficientemente grande Menor tempo possível de propagação na ULA (e deslocador) Carregamento rápido dos registradores 81
Operação de Memória Duas comunicações com a memória porta de acesso de memória de uma palavra, 32 bits => Controlada por MAR e MDR. porta de acesso de memória de um byte, 8 bits = > Controlada por MBR e PC. MAR contém endereços de palavras PC contém endereços de bytes MAR/MDR => Utilizados para ler palavras de dados no nível ISA => 32 bits PC/MBR => Utilizados para ler o programa executável no nível ISA => 8 bits Memória física orientada por Bytes (total de 4GB) => mapeamento de MAR no barramento de endereço. Leitura de MBR no barramento B: Com sinal Sem sinal 4.1.2 Microinstruções 29 sinais para controlar a via de dados: 9 sinais para escrita nos registradores 9 sinais para leitura dos registradores 8 sinais para controlar a ULA e deslocador 2 sinais para indicar leitura ou escrita via MAR/MDR 1 sinal para indicar busca de instrução via PC/MBR Os sinais especificam a operação da via de dados em um ciclo de relógio. Uma leitura da memória (em MDR ou MBR) iniciada ao fim do ciclo k só terá o dado disponibilizado a partir do ciclo k+2. É possível ler dados distintos da memória em dois ciclos consecutivos. Não é possível ler mais de um registrador ao mesmo tempo => decodificação da leitura => necessidade de apenas 24 bits para controlar a via de dados. 82
Os 24 bits controlam a via de dados em um ciclo da via de dados => necessidade de informar o que será feito no próximo ciclo => inclusão dos campos NEXT_ADDRESS e JAM. ADDR: Contém o endereço da possível próxima microinstrução JAM: Determina como a próxima microinstrução é selecionada ALU: Funções da ULA e Deslocador C: Seleção de qual registrador é carregado do barramento C MEM: Funções da memória B: Seleção de qual registrador é lido no barramento B 4.1.3 Controle das Microinstruções: MIC 1 Seqüenciador: Determina quais sinais de controle devem ser habilitados em cada ciclo, responsável por seguir uma seqüência de operações necessárias para a execução de uma única instrução ISA: 1. Determina o estado de cada sinal de controle do sistema 2. Explicita o endereço da próxima microinstrução a ser executada Control Store: memória que contém todo o microprograma (512 palavras de 36 bits). As microinstruções NÃO são armazenadas seqüencialmente na memória de controle => cada microinstrução especifica sua sucessora. MPC (MicroProgram Counter): Especifica o endereço da memória de controle MIR (MicroInstruction Register): Saída de dados da memória de controle 83
MIR é carregado na transição negativa do clock com o dado apontado por MPC. 84
JMPC = 0 Bit de mais alta ordem de MPC = (JAMZ AND Z) OR (JAMN AND N) OR NEXT_ADDRESS(8). Duas possíveis sucessoras para a microinstrução corrente: JMPC = 1 MPC = MBR OR NEXT_ADDRESS Normalmente os 8 bits de mais baixa ordem de NEXT_ADDRESS são zero. O bit de mais alta ordem pode ser "1" ou "0". 4.2 Exemplo de ISA: IJVM 4.2.1 Pilhas (Stacks) Local de memória utilizada para variáveis temporárias (locais). Acessada por dois registradores: LV Local Variables aponta para a base das variáveis locais SP Stack Pointer aponta para o topo da pilha Quadro local: estrutura de dados entre LV e SP 85
Pilha de operandos: Quando a pilha é utilizada para armazenar valores intermediários em uma expressão. Nem todas as máquinas utilizam este recurso. 4.2.2 Modelo de Memória da IJVM Memória vista de duas formas: Vetor de 4G bytes Vetor de 1G palavras, cada palavra 4 bytes Endereços implícitos que fornecem a base para um ponteiro => as instruções só podem acessar a memória indexando a partir destes ponteiros. Separação da memória em quatro partes, acessíveis a qualquer momento: 1. Área de constantes (Constant Pool): Não pode ser escrita por um programa. Carregada quando o programa é lido para a memória. Consiste de constantes, strings e ponteiros para outras áreas de memória. CPP: Aponta sempre para a base desta área (endereço da primeira palavra da área de constantes). 2. Quadro de variáveis Locais (Local Variable Frame): Não inclui a pilha de operandos Utilizada para a alocação de variáveis durante a execução de um procedimento. A pilha de operandos está localizada exatamente acima do quadro de variáveis locais. Argumentos da chamada do procedimento armazenados no começo do quadro de variáveis locais. LV: Possui o endereço da primeira posição do quadro de variáveis locais. 3. Pilha de Operandos: A pilha não excede um certo tamanho, garantido pelo compilador Java. A área da pilha de operandos é alocada acima da área de variáveis locais SP: aponta para o topo da pilha de operandos => valor alterado durante a execução do programa. 4. Área de Métodos: Região de memória que contém o programa. 86
Tratada como um vetor de bytes. PC: Aponta para a próxima instrução a ser buscada. CPP, LV e SP => Ponteiros para palavras (4 bytes), todos offsets utilizados para indexar estas áreas de memória são offsets de palavras => LV+1 não representa a posição de memória do byte acima de LV mas a posição de memória do quinto byte acima de LV. PC => Ponteiro para byte 87
4.2.3 Conjunto de instruções IJVM Cada instrução consiste de um OPCODE e, em alguns casos, de um operando (offset de memória ou constante) byte, const e varnum: um byte disp, index e offset: dois bytes Fontes das palavras a serem colocadas na pilha: Área de constantes: LDC_W Quadro de variáveis locais: ILOAD Área de instruções: BIPUSH Local de armazenamento das variáveis retiradas da pilha: Quadro de variáveis locais: ISTORE Própria pilha: IADD, ISUB, IAND, IOR Saltos: Incondicional: GOTO Condicional: IFEQ, IFLT, IF_ICMPEQ Chamada de função (método): INVOKEVIRTUAL Retorna de função : IRETURN 88
Execução de um método 1. A função que chama o método coloca um apontador (OBJREF) para o objeto a ser chamado na pilha; 2. A função que chama o método coloca os parâmetros do método na pilha (Parâmetro 1, Parâmetro 2, Parâmetro 3). 3. Finalmente executa se o INVOKEVIRTUAL 4. A instrução INVOKEVIRTUAL possui um deslocamento (disp), como argumento => indica a posição na área de constante que possui o endereço inicial na área de métodos. Primeiros 4 bytes na área de método: 2 bytes indicando o número de parâmetros (OBJREF é contado como o Parâmetro 0) => Juntamente com SP fornece a posição de OBJREF. LV apontará para OBJREF. 2 bytes indicando o tamanho da área de variáveis locais do método que será executado => necessário para determinar a nova posição de SP. Quinto byte é a primeira instrução a ser executada. 5. Os dois primeiros bytes da área de métodos são utilizados para posicionar LV => OBJREF é substituído por um ponteiro indicando a posição do antigo PC. 6. Acima do antigo valor de PC, o antigo valor de LV também é armazenado. 7. SP passa a apontar para o endereço da posição do antigo LV (pilha encontra se vazia). 8. PC passa a apontar para o quinto byte da área de métodos. 89
90
Retorno de um método A instrução IRETURN reverte as operação da instrução INVOKEVIRTUAL Desaloca o espaço utilizado pelo método que estava sendo executado. Retorna a pilha para o estado anterior: OBJREF e todos os parâmetros são retirados da pilha O valor de retorno é colocado no topo da pilha, na posição inicialmente ocupada por OBJREF. 91
4.2.4 Compilando Java para IJVM 92
4.3 Exemplo de Implementação 4.3.1 Microintruções e Notação Memória do microprograma: 36 bits por palavra Controle a cada ciclo: Possibilita executar diversas operações concorrentemente Necessário para compreender e verificar as operações Mais fácil para uma possível otimização (redução de ciclos por instrução) Notação: MAL (Micro Assembly Language) SP = SP+1; rd => incrementa SP e inicia uma operação de leitura da memória MDR = SP => copia o valor de SP para MDR MD = H + SP => adiciona o conteúdo do registrador H ao registrador SP escrevendo o resultado em MDR. MDR = SP + MDR => INVÁLIDO! (um dos argumentos tem que ser H) H = H MDR => INVÁLIDO! (o subtraendo deve vir de H) SP = SP+1;rw => Incrementa SP e inicia uma operação de escrita na memória (4 bytes) SP = SP+1; fetch => incremeta SP e inicia uma operação de busca de instrução (1 byte) goto label => salto incondicional para a próxima microinstrução marcada por "label" Z = TOS => Utilizada para verificar se o valor de um registrador é nulo. Simplesmente passa o registrador pela ULA, sem armazenar o resultado. N = TOS => Similar a anterior, porém testa se o valor do registrador é negativo. if (Z) goto L1; else goto L2 => Salto condicional Z=TOS; if (Z) goto L1; else goto L2 goto (MBR or value) => goto (MBR) => utiliza o bit JMPC 93
Nem todas as operações são permitidas O mesmo registrador não pode receber um valor da memória e da via de dados no mesmo ciclo: MAR = SP;rd MDR = H Cada microinstrução fornece o endereço para a próxima microinstrução 94
4.3.2 Implementação do IJVM usando o MIC 1 112 microinstruções As microinstruções não estão necessariamente em posições subsequentes TOS: contém o valor da memória apontada por SP Topo da pilha OPC: registrador temporário. Pode ser utilizado para armazenar o valor de PC em uma instrução de salto. A seqüência de microinstruções sempre começa no endereço igual ao OPCODE do IJVM. BIPUSH: Coloca um byte na pilha ILOAD: WIDE: Altera o índice das instruções ILOAD e ISTORE IINC: Soma uma constante a uma variável local 95
96
97
98