├── images ├── desenhofac.png └── desenhofac2.png ├── .gitignore ├── README.md ├── aula-11.md ├── aula-2.md ├── aula-1.md ├── aula-6.md ├── aula-22.md ├── aula-18.md ├── aula-7.md ├── aula-15.md ├── aula-10.md ├── aula-5.md ├── aula-17.md ├── aula-21.md ├── aula-9.md ├── aula-19.md ├── aula-23.md ├── aula-12.md ├── aula-3.md ├── aula-13.md ├── aula-14.md ├── aula-20.md ├── aula-4.md └── aula-16.md /images/desenhofac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeoSilvaGomes-zz/Fac/HEAD/images/desenhofac.png -------------------------------------------------------------------------------- /images/desenhofac2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeoSilvaGomes-zz/Fac/HEAD/images/desenhofac2.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Arquivo dos Trabalhos 2 | Trabalho/ 3 | 4 | # Arquivo dos Exercícios 5 | Exercícios/ 6 | 7 | # Perguntas 8 | Perguntas.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fac 2 | Conteúdo relacionado a matéria fundamentos de arquitetura de software (FAC) dado pela Universidade de Brasília (UnB) 3 | 4 | --- 5 | 6 | Esse repositório não contemplará as listas nem os trabalhos da disciplina, tendo em vista alguns argumentos feitos pelo professor no inicio do curso relacionado a CopyRight. 7 | 8 | Aproveitem :) 9 | -------------------------------------------------------------------------------- /aula-11.md: -------------------------------------------------------------------------------- 1 | # Aula 11 2 | 3 | # Continuação de Multiplicação (Algoritmo de Booth) 4 | 5 | Os algoritmos de multiplicação vistos lidam com números sem sinal. Para aplicá-los em números com sinal, deve-se: 6 | 7 | > Melhor que o da aula em visão de números de operações, menos tempo. 8 | 9 | - 1 - Guardar o sinal dos operandos, 10 | - 2 - Convertr os operandos em positivos (operação inversa do complemento a dois), 11 | - 3 - Aplicar o algoritmo, 12 | - 4 - Se os sinais dos operandos fossem diferentes, negar o resultado. 13 | 14 | Para evitar essas conversões, há um terceiro método: o algoritmo de Booth. O algoritmo de Booth adiciona um bit menos significativo ao registrados produto, e consiste nos seguintes passos: 15 | 16 | - Passo 1: Produto[63...32] = Produto[-1] = 0 17 | - Passo 2: Produto[31...0] = Q. 18 | - Passo 3: Se Produto[0...-1] = 01, faça Produto[63...32]=Produto[63..32]+m. Senão, se o Produto[0...-1]=10, faça Produto[63...32]-=m. 19 | - Passo 4: Faça o **deslocamento aritmético** do registrador Produto 1 bit à direita. 20 | - Passo 5: Se não for a 32º repetição, volte ao Passo3. 21 | 22 | Obs: Deslocamento aritmético significa, fazer um deslocamento lógico, porém preservando o valor do bit mais significativo. 23 | 24 | Exemplo: 7(decimal) * 3(decimal) = 0111(bin) * 0011(bin) 25 | 26 | |It.|Step| Product | Add 1 bit to right| 27 | |---|-----|----------|-| 28 | |0|Inicialização|0000 0011 0| 29 | |1|P[7..4]-=M|1001 0011 0| 1100 1001 1| 30 | |2| Passo 3 - Passo 4|1100 1001 1| 1110 0100 1| 31 | |3| Passo 3 - Passo 4|0101 0100 1| 0010 1010 0| 32 | |4| Passo 3 - Passo 4|0010 1010 0| **0001 0101 0** = 21| 33 | 34 | --- 35 | 36 | Exemplo da aula passada: 8(decimal) * 7(decimal) = 1000(bin) * 0111(bin) 37 | 38 | |It.|Step| Product | Add 1 bit to right| 39 | |---|-----|----------|-| 40 | |0|Inicialização|0000 0111| 41 | |1|P[7..4]-=M|1000 0111| 0100 0011| 42 | |2| Passo 3 - Passo 4|1100 0011 | 0110 0001 | 43 | |3| Passo 3 - Passo 4|1110 0001 | 0111 0000 | 44 | |4| Passo 3 - Passo 4|0111 0000 | **0011 1000** = 56| 45 | 46 | --- 47 | 48 | ## Instruções em assembly 49 | 50 | Para fazer a multiplicação, o MIPS contém a instrução 51 | 52 | mul reg1, reg2 53 | 54 | que inplementa algum dos algoritmos que vimos. Para implementá-las, a arquitetura oferece um par de registradores de 32 para simular o registrador Produto de 64 bits: são o **Hi** e o **Lo** 55 | 56 | |op1| * |op2| = |Hi|Lo| 57 | 58 | As instruções mult e multu fazem a multiplicação de números com o sem sinal, respectivamente. Para acessar os dados nos registradores Hi e Lo, há as instruções mfhi (move from Hi) e mflo (move from Lo). Para tirar os dados dos registradores Hi e Lo, há as instruções mthi (move to Hi) e mtlo (move to Lo). 59 | 60 | Exemplo: mult $s0, $s1 61 | mflo $s3 62 | mfhi $s2 -------------------------------------------------------------------------------- /aula-2.md: -------------------------------------------------------------------------------- 1 | # Aula 2 2 | 3 | ## Arquiteturas de RISC e CISC 4 | 5 | ### Intruções 6 | RISC | CISC 7 | - Consomem um único de | - podem consumir mais de um ciclo 8 | processamento | de processamento 9 | - Baixo número | - Alto número 10 | - Simples padronizadas | - mais complexos 11 | 12 | ### Projetos 13 | RISC | CISC 14 | Centrado no software: o | Centrado em hardware: o 15 | compilador é responsaável | conjunto de intruções e 16 | por compor instruções pa- | capaz de executar operações 17 | ra comandos deling de alto | de alto nível 18 | nivel | 19 | 20 | ### Memória RAM 21 | RISC | CISC 22 | uso menos eficiente | uso mais eficiente 23 | 24 | ### Execução 25 | RISC | CISC 26 | Uma camada de instruções | Suporta microprograma 27 | (direto no hardware) | 28 | 29 | ## Tipos de computadores 30 | 31 | - Computadores pessoais 32 | - Computadores para jogos (videogames) 33 | - GPU 34 | - Servidos 35 | - Mainframes 36 | - Microcontroladores 37 | - Computadores descartáveis 38 | - Computador portátil 39 | 40 | --- 41 | 42 | Linguagem 43 | ---------------------- 44 | Conjunto de Instruções 45 | ---------------------- 46 | Instrução 47 | 48 | ## A arquitetura MIPS 49 | 50 | MIPS, um acrônimo para microprocessador without pipelines stags, é um conjunto de instruções criado na década de 1980. O MIPS é uma arquitetura CISC baseada em registradores. 51 | 52 | Registrador é uma memoria que fica dentro da CPU e que é capaz de armazenar n bits. 53 | 54 | Nós usaremos a versão de 32 bits do MIPS. Isso significa que cada registrador possui capacidade de armazenar 32 bits. Por isso, um dado de 32 bits ocorre com frequência nessa arquitetura e recebe o nome de palavra. 55 | 56 | A CPU utiliza apenas registradores para realizar as operações. Não obstante, há instruções próprias de acesso à mémoria. 57 | 58 | O padrão básico de uma instrução do assembly MIPS trabalha com um mnemônico e três operandos: um de destino e dois de origem. 59 | 60 | Exemplo: 61 | 62 | add a, b,c 63 | | | | 64 | v v v 65 | mnemônico destino origem 66 | caracteriza | 67 | a operação v 68 | operandos 69 | 70 | ## Instruções elementares 71 | 72 | Temos 3 conjuntos de instruções na ISA do MIPS: 73 | 74 | - Lógicas e aritméticos 75 | - Desvio 76 | - Acesso a memória 77 | 78 | As 3 operações aritméticas são as mais elementos em MIPS: 79 | 80 | - add a,b,c #a=b+c 81 | - sub a,b,c #a=b-c 82 | - mut a,b,c #a=b*c 83 | -------------------------------------------------------------------------------- /aula-1.md: -------------------------------------------------------------------------------- 1 | # Aula 1 2 | 3 | ### Informações gerais 4 | 5 |

6 | Atendimento:
7 | segunda - 16h às 17h
8 | Site:
9 | johnlenongardenghi.com.br/courses/fac-19-2
10 | Email:
11 | john.gardenghi@unb.br 12 |

13 | 14 | ### Ementa 15 | 16 | 1 - Introdução à arquitetura de computadores 17 | 2 - Introdução à programação em linguagem de montagem 18 | 3 - Aritmética computacional 19 | 4 - Arquitetura interna de um processador 20 | 5 - Hierarquia de memória 21 | 6 - Barramentos de dados 22 | 23 | ## Conteúdo 24 | 25 | Arquitetura de computadores é o projeto estrutural de um computador, envolvendo os componentes físicos e lógicos essenciais ao seu funcionamento. 26 | 27 | Os componentes essenciais de um computador são divididos em 3 camadas: 28 | 29 | software de aplicação | software de sistema | hardware 30 | 31 | - Software de aplicação: são aplicações típicas que os usuários utilizam - Ex: Banco de dados, sistemas de gestão, IDEs para desenvolvimento. 32 | - Software de sistema: são programas que fornecem intermediários entre aplicação e hardware - Ex: Sistemas operacionais, compiladores e montadores. 33 | - Hardware: é a parte física, executa apenas operações binárias. O hardware entende apenas sinais elétricos, que se restringe a ligado e desligado. deste modo, o alfabeto de um computador é composto de apenas duas letras: os dígitos binários ou bits (0 e 1) 34 | 35 | Arquitetura de Von Neumann: 36 | 37 | 38 | memória <-> unidade lógica de aritmética 39 | ^ ^ | ^ 40 | | | v | 41 | v v out in 42 | unidade de controle 43 | 44 | Como a interação com o hardware usando código binário é ineficiente e improdutiva, foi-se criando linguagens que se assemelham mais à linguagem humana. 45 | 46 | Todavia, as primeiras linguagens eram muito arcaicas, e se pareciam muito com as operações que o hardware era capaz de executar. As linguagens de alto nivel foram aparecendo com anos de desenvolvimento e aparecem até hoje. 47 | 48 | Elas tem por objetivo: 49 | 50 |

51 | 1 - Fazer com que o programador escreva algo mais próximo de sua linguagem.
52 | 2 - Aumentar a produtividade.
53 | 3 - Fazer com que programas sejam independentes da plataforma. 54 |

55 | 56 | Isso gerou níveis de abstração: 57 | 58 |

59 | Aplicação
60 | ↓ código fonte
61 | Linguagem de alto nivel
62 | ↓ compilador
63 | Linguagem de montagem (assembly)
64 | ↓ montador (assembler)
65 | Sistema operacional
66 | ↓ linker
67 | Linguagem de máquina
68 | ↓
69 | Microprograma
70 | ↓
71 | Lógica Digital
72 |

73 | 74 | Toda plataforma de hardware possui um conjunto de operações que são capazes de executar. Essas operações são obstraídas em instruções. Assim sendo, toda plataforma possui sua ARQUITETURA DO CONJUNTO DE INSTRUÇÕES (ISA, Instruction set Architective), que também é chamado de assembly da arquitetura. 75 | 76 | A ISA de uma plataforma pode ser classificada em dois tipos, com relação à sua complexidade: 77 | 78 | 1 - RISC (Reducted Instruction set Computing) é um conjunto de instruções reduzido capaz de executar apenas algumas operações simples 79 | 80 | 2 - CISC (Complex Instruction set Architecture) é um conjunto composto por diversas instruções complexas. -------------------------------------------------------------------------------- /aula-6.md: -------------------------------------------------------------------------------- 1 | # Aula 6 2 | 3 | ### Continuação da aula passada 4 | 5 | 6 | maior = A[0]; 7 | for (i = 1; i < n; i++){ 8 | if(A[i] > maior){ 9 | maior = A[i]; 10 | } 11 | } 12 | 13 | $s0 - end base do A 14 | $t0 - i 15 | $s1 - maior 16 | $s2 - n 17 | 18 | --- 19 | 20 | add $t0, $s0, $zero # $t0 = end base de A 21 | lw $s1, 0($t0) # $s1 = A[0] 22 | sll $t1, $s2, 2 # $t1 = n*4 23 | loop: addi $t0, $t0, 4 24 | slt $t2, $t0, $t1 # $t0 < $t1 (i | | 66 | | | $s0 e $s1 | | 67 | | | | | 68 | 69 | int exemplo (int g, int h, int i, int j){ 70 | int f; 71 | f = (g + h) - (i + j); 72 | return f; 73 | } 74 | 75 | f - $v0 76 | g - $a0 77 | h - $a1 78 | i - $a2 79 | j - $a3 80 | 81 | --- 82 | 83 | Exemplo: 84 | 85 | addi $sp, $sp, -8 | 86 | sw $s1, 0($sp) | -> armazena na pilha 87 | sw $s0, 4($sp) | 88 | 89 | add $s0, $a0, $a1 90 | add $s1, $a2, $a3 91 | sub $v0, $s0, $s1 92 | 93 | lw $s0, 4($sp) | 94 | lw $s1, 0($sp) | -> restaura valores da pilha 95 | addi $sp, $sp, 8 | 96 | 97 | jr $ra #return 98 | -------------------------------------------------------------------------------- /aula-22.md: -------------------------------------------------------------------------------- 1 | # Aula 22 2 | 3 | ## Continuação da ultima aula 4 | 5 | Da memória principal para o cache: dado um endereço de memória x e uma cache com m = 2^n blocos, cada bloco com c = 2^b bytes, o endereço (i,j) de x na cache será: 6 | 7 | 8 | i = (x/c)%m 9 | j = (x%c) 10 | 11 | Ex: endereço 11 da memória principal: 12 | 13 | i = (11/4)%8 = 2%8 = 2 14 | j = 11%4 = 3 15 | 16 | 11 = 001011 17 | 18 | 001011 / 2² = srl 2 = 0010 19 | 001011 % 2² = 11 20 | 21 | Logo, no mapeamento direto por blocos: 22 | 23 | 1. Parte-se do endereço do dado na memória principal 24 | - O tamanho da memória principal é sempre potência de dois (Digamos 2^t) 25 | 2. Quanto à cache, devemos saber quantos blocos ela possui (quantidade de linha, 2^n) e qual o tamanho em bytes, de cada bloco (quantidade de colunda 2^b). 26 | 27 | Deste modo: 28 | 29 | Endereço da memória principal (t bits) 30 | 31 | | | 32 | tag | linha | coluna 33 | (t-(n+b) bits) | (n bits) | (b bits) 34 | | | 35 | 36 | ## Tamanho real da cache 37 | 38 | Para calcular o tamanho real da memória cache 39 | 40 | 1. Deve-se identificar quantos blocos ela possui e qual o tamanho (em bytes) de cada uma. 41 | 2. Deve-se calcular o tamanho da tag com isso, o tamanho real da cache será: 42 | 43 | ``` 44 | qtde. x | 1 + tamanho + tamanho | 45 | de blocos | da tag do bloco | 46 | ``` 47 | 48 | Cuidado! O tamanho da tag geralmente é dado em bits, portanto, para usar a fórmula acima, o tamanho do bloco também deve estar em bits. 49 | 50 | No nosso exemplo, 51 | 52 | Tamanho 53 | da cache = 8 x (1 + 1 + 32) = 8 x 34 bits 54 | (real) = 34 bytes 55 | 56 | Exemplo: 57 | 58 | Qual o tamanho real de uma memória cache diretamente mapeada com 16kb e blocos de 4 palavras, considerando-se que a memória principal possui 4gb? 59 | 60 | 1. Memória principal = 4gb = 2² * 2³⁰B 61 | = 2³²B 62 | 63 | Memória cache: 64 | -> Tamanho de um bloco = 4 palavras = 2⁴B 65 | -> Quantidade de blocos = 16KB/16Kb = 1K = 2¹⁰ 66 | 67 | 2. Tamanho da tag: 68 | 69 | Endereço da memória principal (32bits) 70 | 71 | ``` 72 | | | 73 | tag | linha | coluna 74 | 18 bits | 10 bits | 4 bits 75 | | | 76 | ``` 77 | 78 | Tamanho 79 | real da cache = 2¹⁰ * (1 + 18 + 16 * 8) 80 | = 2¹⁰ * 147 bits = 147 Kb 81 | = 18,375 * 2¹⁰ bytes = 18,375 Kb 82 | 83 | ### Exemplo 84 | 85 | Considere uma cache com 64 blocos e 16 bytes por bloco. Para qual endereço da cache o endereço 1200 da mamória principal é mapeado? 86 | 87 | K = 1200 | i = 1200 / 16 = 75 % 64 = 11 88 | | j = 1200 % 16 = 0 89 | 90 | ### Falahas de acesso 91 | 92 | Dado end_mem na forma (bloco = 2^b) 93 | (qtd.blocos = 2^n) 94 | 95 | --- 96 | 97 | Quando houver uma falha (seja eka de dados ou de instrução), o processador fica parado aguardando o dado ficar disponível (gerando bolhas no caso de pipeline). 98 | 99 | Quanto maior o tamanho do bloco, menor tende a ser a quantidade de falhas em decorrência da localidade de espacial. Todavia, há de se ter um trade-off entre o tamanho total da cache e o tamanho de cada bloco, pois à medida que o tamanho do bloco se aproxima do tamanho da cache, a localidade temporal seria desfavorecida. Além disso, quanto maior o tamanho do bloco, maior a penalidade de falha. -------------------------------------------------------------------------------- /aula-18.md: -------------------------------------------------------------------------------- 1 | # Aula 18 2 | 3 | ## Implementação Monociclo 4 | 5 | O objetivo dessa implementação é fazer com que todas as instruções do assembly sejam executadas em um único ciclo de clock do processador. Essa implementação consiste na construção de um controlador que determine o caminho que cada pedaço de uma instrução deve tomar no caminho de dados e que determine os calores dos bits de controle de alguns elementos do caminho de dados. Para este controlador, duas unidades de controle são necessárias: 6 | 7 | 1. O controlador da ULA e 8 | 2. O controlador princiapl 9 | 10 | ## O controlador da ULA 11 | 12 | A ULA é utilizado pelas instruções das 3 classes. 13 | 14 | 1. Para instruções lógicas e aritméticas, ela realiza soma (add), subtração (sub), *e* e *ou* lógicos (and e or) e comparação (slt). 15 | 16 | 2. Para instruções de acesso à memória, calcula o endereço de memória do dado pela soma do endereço base com o offset. 17 | 18 | 3. Para o beq, a ULA realiza uma subtração. 19 | 20 | O controlador da ULA determina o valor do sinal da ULA baseado no campo funct das instruções do tipo R e um sinal de 2 bits, que chamaremos de OpULA. O campo OpULA vale. 21 | 22 | - 00 para realizar add em acesso à memória 23 | - 01 para **sub** em **beq** 24 | - 10 para operações das instruções lógicas e aritméticas 25 | 26 | _____________ 27 | OpULA---------> | Controle | Identificador da Operação 28 | 2 bits | da | -----------------> 29 | | ULA | (4 bits) 30 | funct---------> | | 31 | 6 bits |___________| 32 | 33 | Os bits da OpULA são gerados pelo controlador principal com base na instrução a ser executada. 34 | 35 | ## O controlador principal 36 | 37 | A primeira função do controlador principal é segmentar a instrução e distribuir suas partes aos elementos do caminho de dados adequados, da seguinte forma: 38 | 39 | 1. Lógicos e aritméticos (Tipo R) 40 | 41 | _______ _______ _______ _______ _______ _______ 42 | Campo | op | rs | rt | rd | shamt | funct | 43 | |_______|_______|_______|_______|_______|_______| 44 | Posição 6 bits 5 bits 5 bits 5 bits 5 bits 6 bits 45 | 31:26 25:21 20:16 15:11 10:6 5:0 46 | 47 | 2. Acesso à memória e desvio condicional (Tipo I) 48 | 49 | _______ _______ _______ ______________________ 50 | Campo | op | rs | rt | endereço/offset | 51 | |_______|_______|_______|_____________________| 52 | Posição 6 bits 5 bits 5 bits 16 bits 53 | 31:26 25:21 20:16 15:0 54 | 55 | Deste modo, as instruções são dividas entre os canais no barramento do caminho de dados, e seguem os padrões: 56 | 57 | - O campo op está sempre nas posições 31:26. 58 | - Para instruções de acesso à memória: 59 | - **rs** é o registrador contendo o endereço base e ocupa sempre as posições 25:21. 60 | - **rt** é o registrador de leitura para sw e de escrita para lw e sempre ocupa as posições 20:16. 61 | - Os registradores de leitura estão sempre nas posições 25:21 e 20:16. 62 | - O offset (para beq, lw e sw) sempre ocupa as posições 15:0. 63 | 64 | Não obstante, o registrador de escrita pode: 65 | 66 | - Ocupar as posições 20:16 para lw 67 | - Ocupar as posições 15:11 para instruções do Tipo R. 68 | 69 | Portanto, é necessário incluir um multiplexador para resolver esse dois casos. Colocando tudo isso junto, chegamos ao caminho de dados do slide 7. Um caminho de dados completo, com a unidade de controle, é ilustrado no Slide 8. Os sinais de controle, no Slide 9, e a operação p/ as instruções, nos 10-12. -------------------------------------------------------------------------------- /aula-7.md: -------------------------------------------------------------------------------- 1 | # Aula 7 2 | 3 | ### Recursividade 4 | 5 | n! = n*(n-1)*(n-2)...1 6 | 7 | int fat(int n){ 8 | if(n<1) return 1; 9 | else return n*fat(n-1); 10 | } 11 | 12 | --- 13 | 14 | fat:addi $sp, $sp, -8 15 | sw $ra, 0($sp) 16 | sw $a0, 4($sp) 17 | slti $t0, $a0, 1 18 | beq $t0, $zero, L1 19 | addi $v0, $zero, 1 20 | addi $sp, $sp, 8 21 | jr $ra 22 | 23 | L1: addi $a0, $a0, -1 24 | jal fat 25 | 26 | lw $ra, 0($sp) 27 | lw $a0, 4($sp) 28 | addi $sp, $sp, 4 29 | mul $v0, $a0, $v0 30 | jr $ra 31 | 32 | O segmento da pilha que contém valores salvos e as variáveis de um escopo do procedimento é chamado **frame de procedimento** ou **registro de ativação**. Para indicar o frame de procedimento, existe o registrador $fp (frame pointer): 33 | 34 | |$ra (n=3)| 35 | |$a0 (n=3)| 36 | $fp-|$ra (n=2)| - registro de ativação da função fat(2) 37 | $sp-|$a0 (n=2)| 38 | 39 | A arquitetura MIPS possui a seguinte convenção de alocação de memória: 40 | 41 | | Pilha | 42 | $sp-| ^| | -> Heap 43 | | |v | 44 | | Dado dinâmico | 45 | $gp-| Dado estático | -> variáveis globais e estáticos 46 | PC- | Texto | -> instruções 47 | | Reservado | 48 | 49 | Memória Principal 50 | 51 | - **Texto** contém o **código de máquina** (compilado e montado). Program counter (PC) é um elemento de hardware que aponta para a instrução que está sendo executada. 52 | 53 | - **Dados estáticos** é a porção reservada à variaveis globais e estáticas. 54 | 55 | - **Dados dinâmicos** é a porção reservada para valores locais. Espaço usado pelo malloc do C, por exemplo, também conhecido por **heap**. 56 | 57 | ## Caracteres e Strings 58 | 59 | Os computadores usam 8 bits (1 byte) para representar caracteres. O padrão de representação está na tabela ASCII (American Standard Code for Information Interchange). Dos 128 caracteres na tabela ASCII: 60 | 61 | - 33 são sinais de controle (\n, \t, \0) e 62 | - 95 são sinais gráficos (letras, sinais de pontuação, símbolos matemáticos). 63 | 64 | As instruções de acesso à memória para caracteres são: 65 | 66 | - lb $t0, 0($sp) lê um byte 67 | - lbu $t0, 0($sp) lê um byte sem sinal 68 | - sb $t0, 0($sp) salva um byte 69 | 70 | **Strings** são cadeias de caracteres e podem ser representados de 3 formas: 71 | 72 | - 1- A primeira posição da string indica o tamanho 73 | - 2- Uma variavel aacompanhante possui o tamanho da string; 74 | - 3- A última posição da string é ocupada por um caracter que marca seu final. 75 | 76 | Exemplo: Vamos traduzir a função strcpy de C para assembly MIPS. 77 | 78 | void strcpy(char x[], char y[]){ 79 | for(int i=0; (x[i] = y[i]) != '\0'; i++); 80 | } 81 | 82 | $a0 - endereço base x 83 | $a1 - endereço base y 84 | 85 | --- 86 | 87 | strcpy: add $t0, $a0, $zero 88 | add $t1, $a1, $zero 89 | loop: lb $t2, 0($t1) 90 | sb $t2, 0($t0) 91 | addi $t0, $t0, 1 92 | addi $t1, $t1, 1 93 | bne $t2, $zero, loop 94 | 95 | ## Pseudo instruções de manipulação de dados 96 | 97 | - **li reg, cons** carrega no registrador **reg** a contante **cons**. 98 | 99 | - **la reg, label** carrega no registrador **reg** o endereço do **label**. 100 | 101 | - **move reg1, reg2** copia o conteúdo do registrador reg1 para o reg2. 102 | 103 | Há ainda as pseudo instruções condicionais: 104 | 105 | - bge reg1, reg2, label 106 | (branch if greater or equal) 107 | 108 | - bgt reg1, reg2, label 109 | (branch if greater than) 110 | 111 | - ble reg1, reg2, label 112 | (branch if less or equal) 113 | 114 | - blt reg1, reg2, label 115 | (branch if less or than) 116 | 117 | -------------------------------------------------------------------------------- /aula-15.md: -------------------------------------------------------------------------------- 1 | # Aula 15 2 | 3 | ## Pontos flutuantes 4 | 5 | As principais instruções do coprocessador 1 são: 6 | 7 | 1. Aritméticas: 8 | 9 | - adição; add.s res, reg1, reg2 10 | - subtração: sub.s res, reg1, reg2 11 | - multipliação: mul.s res, reg1, reg2 12 | - dicisão: div.s res, reg1, reg2 13 | 14 | Obs: Todas as instruções do processador 1 seguem a seguinte convenção: aquelas terminadas por **".s"** lidam exclusivamente com números de precisão **simples** e possuem uma **irmã** terminado em **".d"** que lida com números em precisão são dupla 15 | 16 | Ex: 17 | 18 | add.s $f0, $f1, $f2 19 | - faz $f0 = $f1 + $f2. Todos estão em precisão simples. 20 | add.d $f0, $f2, $f4 21 | - faz ($f0, $f1) = ($f2, $f3) + ($f4, $f5) 22 | 23 | Todos os operandos estão em precisão dupla 24 | 25 | 2. Instruções de acesso à memória 26 | 27 | - l.s reg, offset(base): carrega um ponto flutuante da memória 28 | - s.s reg, offset(base): salva um ponto flutuante na memória. 29 | 30 | Ex: 31 | 32 | |> reg. do proc. 1 33 | l.s $f0, 100($t2) -> base (reg. do coprec. 0) 34 | |> offset (número inteiro) 35 | 36 | 3. Movimentação de dados 37 | 38 | - mov.s reg1, reg2: faz reg1=reg2 39 | 40 | Ex: 41 | 42 | mov.s $f0, $f1. 43 | 44 | --- 45 | 46 | - mfc1 reg_c0, reg_c1 : reg_c0 = reg_c1 47 | - mtc1 reg_c0, reg_c1 : reg_c1 = reg_c0 48 | 49 | Essa instruções copiam os dados de um registrador do coprocessador 0 p/ o 1 e vice-vers. 50 | 51 | - mfc1: move **from** coprocessor 1 52 | - mtc1: move **to** coprocessor 1 53 | 54 | Obs: Não há conversão de dados nessa movimentação! 55 | 56 | Ex: 57 | 58 | ``` c 59 | mfc1 $t0, $f0 # $t0 = $f0 60 | ``` 61 | 62 | 4. Conversão de dados 63 | 64 | As instruções de conversão são feitas no coprocessador 1 e seguem a seguinte lógica: 65 | 66 | instrução reg-dest, reg-origem 67 | 68 | As instruções possuem o formato 69 | 70 | cvt. {d,s,w}.{d.s.w}, 71 | 72 | onde 73 | 74 | - w representa inteiro, 75 | - s representa precisão simples e 76 | - d representa precisão dupla 77 | 78 | logo, temos as instruções; 79 | 80 | - cvt.d.s - cvt.w.d - cvt.s.d 81 | - cvt.d.w - cvt.w.s - cvt.s.w 82 | 83 | Ex: 84 | 85 | |> double 86 | | |> inteiro 87 | cvt.d.w $f0, $f1 88 | | |> inteiro 89 | |> double 90 | 91 | Converte um inteiro em $f1 para um double, que será armazenado em $f0 92 | 93 | 5. Desvios condicionais 94 | 95 | São feitos em duas etapas 96 | 97 | - a. Comparação entre valores dos registrafores e 98 | - b. Desvio baseado no resultado da comparação 99 | 100 | As instruções de comparação tem a estrutura 101 | 102 | instrução reg1, reg2 #compara reg1 com reg2 (nessa ordem) 103 | 104 | As instruções são: 105 | 106 | - c.eq.s - c.lt.s - c.gt.s 107 | - c.ne.s - c.le.s - c.ge.s 108 | 109 | As instruções de comparação salvam o resultado num bit controlador estpecial chamdao **code**. Se o resultado d comparação for verdadeiro, o bit 1 é salvo em code, em caso contrário, ) é salvo. Os desvios são feitos com base no valor de **code**. Há duas instruções de desvio: 110 | 111 | - bc1t label # devia para **label** se code=1 (true) 112 | - bc1f label # devia para **label** se code=0 (false) 113 | 114 | Ex: Traduza o código a seguir; 115 | 116 | float f2c(float fahr){ 117 | return (5.0/9.0)*(fahr - 32.0) 118 | } 119 | 120 | --- 121 | 122 | .data 123 | c5: .float 5.0 124 | c9: .float 9.0 125 | c32: .float 32.0 126 | 127 | f2c: l.s $f10, c5 128 | l.s $f11, c9 129 | l.s $f12, c32 130 | 131 | div.s $f10, $f10, $f11 132 | sub.v $f0, $f0, $f12 133 | mul.s $f0, $f10, $f0 134 | 135 | jr $ra -------------------------------------------------------------------------------- /aula-10.md: -------------------------------------------------------------------------------- 1 | # Aula 10 2 | 3 | Para verificar overflow na adição de números sem sinal: 4 | 5 | addu $t0, $t1, $t2 6 | nor $t3, $t1, $zero #primeiro passo p/ comp. a dois 7 | 8 | Até aqui, $t3 9 | 10 | =2³² - $t1 - 1. Note que, se $t3 < $t2, então: 11 | 12 | $t3 = 2³²-$t1-1 < $t2 13 | => 2³²-1<$t1+$t2 14 | 15 | Logo, se $t3 < $t2, então, há 16 | 17 | overflow: 18 | sltu $t3, $t3, $t2 19 | bne $t3, $xero, overflow 20 | 21 | ## Multiplicação 22 | 23 | numa multiplicação MxQ, temos M o multiplicando e Q o multiplicador. 24 | 25 | M = 1000 bin 26 | x 27 | Q = 1001 bin 28 | ----------- 29 | 1000 30 | 0000 31 | 0000 32 | 1000 33 | ----------- 34 | 1001000 bin 35 | 36 | > Consideremos Q=qn*(qn+1)\*(qn+2)\*(qn+3)...\*q2\*q1*q0 37 | 38 | A cada etapa i da multiplicação, há i deslocamentos a esquerda de (q x M. Ao final, todos os deslocamentos são somados, derando o produto. Ou seja: 39 | 40 | P = M(q0*2⁰+q1*2¹+q2*2²+...+qn*2^n) 41 | 42 | Como o produto costuma ser muito maior que os operandos, a arquitetura implementa um registrador especial de 64 bits para armazenar o produto. Note qu esse espaço costuma ser suficiente pois a multiplicação de um número de n bits por um de m bits resulta num número de n+m bits. 43 | 44 | 45 | ### Algoritmos 46 | 47 | Consideramos que os operandos representam números ***sem sinal***. Para realizar a multiplicação, o algoritmo mais trivial (igual fazemos no papel) é o seguinte. 48 | 49 | Dados: M o multiplicando e Q o multiplicador. 50 | Saída: P o produto 51 | Passo1: Inicialize P=0 e contador=1. 52 | Passo2: Faça P= P+q0*M. 53 | Passo3: Faça o deslocamento lógico de um bit à esquerda, em M. 54 | Passo4: Faça um deslocamento lógico de um bit à direita em Q. 55 | Passo5: Se contador = 32, pare. Se não, contador++ e volte ao Passo2 56 | 57 | Para implementar esse algoritmo, a arquitetura precisa dos seguintes registradores: 58 | 59 | M | 64 bits | 60 | Q | 32 bits | 61 | P | 64 bits | 62 | 63 | Exemplo: 64 | 65 | M | 1000| 8 bits 66 | Q | 1000| 4 bits 67 | P | 1000| 8 bits 68 | 69 | M | 10000| 8 bits 70 | Q | 100| 4 bits 71 | P | 1000| 8 bits 72 | 73 | M | 100000| 8 bits 74 | Q | 10| 4 bits 75 | P | 1000| 8 bits 76 | 77 | M | 1000000| 8 bits 78 | Q | 1| 4 bits 79 | P | 1000| 8 bits 80 | 81 | M | 1000000| 8 bits 82 | Q | 1| 4 bits 83 | P | 1001000| 8 bits 84 | 85 | Não é o necessário um registrador de 64 bits para o multiplicando usando a estrutura: 86 | 87 | Multiplicando 88 | | 32 bits 89 | v 90 | --> ULA de 32 bits 91 | | | 92 | | v Produto 93 | | | desloca à direita | 64 bits 94 | | ^ 95 | |________| 96 | 97 | Chegamos ao seguinte algoritmo: 98 | 99 | Passo1: P[62...32] = 0. 100 | Passo2: P[31...0] = Q. 101 | Passo3: Se P[0]=1, faça 102 | P[62...32]=P[62...32]+M. 103 | Passo4: Desloque P um bit à direita. 104 | Passo5: Se contador = 32, pare. Se não, contador++ e volte ao Passo3 105 | 106 | 107 | Exemplo: 108 | 109 | 3dec*2dec = 0011bin\*0010bin 110 | 111 | It Passo Produto 112 | 0 Valores iniciais 0000 0010 113 | 1 3 0000 0010 114 | 4 0000 0010 115 | 2 3 0011 0001 116 | 4 0001 1000 117 | 3 3 0001 1000 118 | 4 0000 1100 119 | 4 3 0000 1100 120 | 4 0000 0110 -------------------------------------------------------------------------------- /aula-5.md: -------------------------------------------------------------------------------- 1 | # Aula 5 2 | 3 | ## Condicionais 4 | 5 | Para o uso dos condicionais, um conceito é importante 6 | 7 | Em assembly MIPS, rótulo é uma string simples usada para nomear um endereço da memória 8 | 9 | Exemplo: 10 | 11 | lw $t0, 1200($t1) 12 | L1: add $t0, $s2, $t0 13 | L2: sub $t0, $s2, $t0 14 | sw $t0, 1200($t1) 15 | 16 | Em linguagem C, a instrução mais comum para desvio condicional é o **if**. Em assembly MIPS, há duas instruções equivalentes. 17 | 18 | - 1 - **beq reg1, reg2, label** (branch if equal) significa desvio à instrução rotulada por **label** se o calor de reg1 for igual ao de reg2. 19 | 20 | - 2 - **bne reg1, reg2, label** (branch if not equal) significa desvio à instrução rotulada por **label** se o valor de reg1 for diferente do valor de reg2. 21 | 22 |

23 | Essas duas instruções são denominadas de desvio condicional.
24 | Há também uma instrução de desvio incondicional;
25 |

26 | 27 | - **j label** (jump) desvia o fluxo de execução para a instrução rotulada por label. 28 | 29 | Exemplo: Suponha que f a j correspondam aos cinco registrdores $s0 a $s4. Qual será o código MIPS compilado destas instruções? 30 | 31 | if (i == j){ 32 | f = g + h; 33 | } else { 34 | f = g - h; 35 | } 36 | 37 | f - $s0 38 | g - $s1 39 | h - $s2 40 | i - $s3 41 | j - $s4 42 | 43 | Em MIPS: 44 | 45 | Primeira alternativa: 46 | 47 | bne $s3, $s4, else 48 | add $s0, $1, $s2 49 | j exit 50 | 51 | else: sub $s0, $s1, $s2 52 | exit: 53 | 54 | Segunda alternativa; 55 | 56 | beq $s3, $s4, if 57 | sub $s0, $s1, $s2 58 | j exit 59 | 60 | if: add $s0, $s1, $s2 61 | exit: 62 | 63 | Observações: A execução em assembly é sequencial, e apenas instruções de desvio alteram esse fluxo de execução. 64 | 65 | Para ajudar com os desvios, há ainda uma instrução para testes lógicos de comparação 66 | 67 | - **slt res, reg1, reg2** (set on less than) atribui 1 ao registrador **res** se o valor **reg1** for menor que o valor em **reg2**, e 0 caso contrário. 68 | 69 | - **slti res, reg, cons** é a versão imediata de slt. 70 | 71 | Observações: 72 | 73 | - Chama-se de **bloco básico** um conjunto de instruções que não possui desvios. 74 | - Todas as operações de comparação podem ser implementadas em MIPS. 75 | 76 | ### Menor ou Igual 77 | 78 | registradores: 79 | $t0 <= $t1? 80 | 81 | slt $t2, $t1, $t0 82 | beq $t2, $zero, mi 83 | j exit 84 | 85 | mi: # $t0 <= $t1 86 | exit: # $t0 > $t1 87 | 88 | ### Maior 89 | 90 | registradores: 91 | $t0 > $t1? 92 | 93 | slt $t2, $t1, $t0 94 | 95 | ### Maior ou Igual 96 | 97 | registradores: 98 | $t0 >= $t1? 99 | 100 | Duas possibilidades: 101 | 102 | $t0 >= $t1 ou $t0 < $t1 103 | slt $t2, $t0, $t1 104 | 105 | - Se $t2 = 0, então $t0 >= $t1. 106 | 107 | --- 108 | 109 | **Laços:** com os desvios, é possível construir laços 110 | 111 | Exemplo: 112 | 113 | while(A[i] == k){ 114 | i = i + 1; 115 | } 116 | 117 | $s0 - base de A 118 | $s1 - i 119 | $s2 - k 120 | 121 | Em MIPS: 122 | 123 | com beq: 124 | 125 | add $s1, $s0, $zero 126 | loop: lw $t0, 0($s1) 127 | beq $t0, $s2, if 128 | j exit 129 | 130 | if: add $1, $s1, 4 131 | j loop 132 | exit: 133 | 134 | com bne: 135 | 136 | add $s1, $s0, $zero 137 | loop: lw $t0, 0($s1) 138 | bne $t0, $s2, exit 139 | add $1, $s1, 4 140 | j loop 141 | 142 | exit: 143 | 144 | Exemplo; 145 | 146 | maior = A[0]; 147 | for (i = 1; i < n; i++){ 148 | if(A[i] > maior){ 149 | maior = A[i]; 150 | } 151 | } 152 | 153 | $s0 - end base do A 154 | $t0 - i 155 | $s1 - maior 156 | $s2 - n 157 | 158 | Em MIPS; 159 | 160 | add $s1, $s0, $zero 161 | 162 | 163 | ## Simuladores 164 | 165 | - MARS (+pseudoinstruções) 166 | - SPIM -------------------------------------------------------------------------------- /aula-17.md: -------------------------------------------------------------------------------- 1 | # Aula 17 2 | 3 | ## Instruções de acesso à memória 4 | 5 | São do tipo I: lw $t0, 0($s0) 6 | 7 | |op |rs |rb endereço 8 | 9 | | op | | rs | | rb | | endereço | | 10 | | --- |---| --- |---| --- | ---|---|--- 11 | |6 bits | | 5 bits | | 5 bits | | 16 bits | | 12 | 13 | Operam da seguinte forma: 14 | 15 | a. calculam um endereço de memória como sendo 16 | 17 | reg_base + offset 18 | 19 | b. lê um dado do registrador e armazena na memória **ou** lê um dado da memória e armazena num registrador. 20 | 21 | A etapa **a** faz uso de um **somador** e a **b**, de um **banco de registradores**, ambos os mesmo utilizados pelas instruções lõgicas e aritméticas. 22 | 23 | Além desses elementos, é necessário uma **unidade de extensão de sinal**, que estende o sinal do endereço de 16 bits calculado na etapa **a** para um de 32 bits a ser usado peo somador. 24 | 25 | 26 | 16 | Extensão | 32 27 | ---> | de | ---> 28 | bits | sinal | bits 29 | 30 | Por último, é necessário o uso de uma **unidade de memória** de dados que escreve ou leia dados em um dado endereço. 31 | 32 | Por ter operações de leitura e escrita, a memória de dados possui um sinal de controle de leitura e escrita, separados, embora apenas um seja ativado por ciclo de clock. 33 | 34 | 35 | | memwrite 36 | __________|_________ 37 | Endereço | Dado | 38 | ----------> | lido |-----> 39 | | | 40 | | | 41 | | | 42 | Dado | Memória de | 43 | ----------> | dados | 44 | de Escrita | | 45 | -----------|---------- 46 | | memread 47 | 48 | 49 | ## Instruções de desvio 50 | 51 | Também seguem o formato do tipo I. Estamos interessados na instrução 52 | 53 | beq reg1, reg2, label 54 | 55 | Essa instrução possui dois operandos registradores e um endereço que indica qual instrução será alvo no desvio. Esse endereço na verdade é um **offset**, e o **endereço de destino do desvio** é calculado usando-se o PC e offset neste sentido: 56 | 57 | - o endereço base para cálculo do endereço de destino do desvio é pc +4 58 | - offset representa o número de palavras, e não de bytes. 59 | 60 | Deste modo, 61 | > Endereço de = (Pc+4) + offset * 4. destino do desvio 62 | 63 | Para decidir qual seá a próxima instrução, a comparação reg1=reg2 é feita. Se for verdadeira, a próxima instrução será a do endereço de destino do desvio; nesse caso, dizemos que o desvio é **tomado** segue a sequência e dizemos que o desvio é **não tomado**. 64 | 65 | Logo, o caminho de dados deve ser capaz de 66 | 67 | 1. Calcular o endereço de destino do desvio e 68 | 2. comparar o conteúdo dos registradores. 69 | 70 | Para **2**, utiliza-se a ULA para fazer reg1-reg2 71 | 72 | Se a flag **zero** da ULA for 1, então reg1=reg2. Caso contrário, reg1 != reg2. Os elementos do caminho de dados para desvios é ilustrado no Slide 1. 73 | 74 | ## Um caminho de dados para as 3 classes 75 | 76 | O objetivo é executar qualquer instrução em um único ciclo de clock, portanto qualquer elemento do caminho de dados que é utilizado por mais de uma classe de instruções não precisa ser duplicado. 77 | 78 | As operações das instruções que vimos diferentes. 79 | 80 | - As intruções lógicas e aritméticas utilizam a ULA com entradas vindas de registradores, enquanto de acesso à memória e desvio condicional recebem um dos operandos do extensor de sinal. 81 | 82 | - O valor armazenado num registrador pode vir da ULA (tipo R), ou da memória (lw). 83 | 84 | Para resolver essas diferenças, um multiplexador é colocado na entrada da ULA e outro na porta do banco de registradores. 85 | 86 | A primeira versão de um caminho de dados pode ser visto no Slide 2. 87 | 88 | Para incluir a etapa comum(busca de instruções), faz-se necessário o uso de um multiplexador para selecionar se o endereço da próxima instruções é PC+4 ou o endereço de destino do desvio. Um caminho de dados assim pode ser visto no Slide 3. -------------------------------------------------------------------------------- /aula-21.md: -------------------------------------------------------------------------------- 1 | # Aula 21 2 | 3 | ## Aula à memória cache 4 | 5 | Dados da memória principal são carregados para a memória cache sempre que processador fez uma requisição e o dado não está presente na cache. 6 | 7 | A primeira questão é como saber se um dado, requisitado por seu endereço na memória principal, está ou não presente na cache. Para tanto, é necessário fazer um mapeamento dos endereços. O mapeamento mais comum é o mapeamento direto. Nesse esquema, cada endereço da memória princiapl é mapeado para um único local da cache. Uma cache que usa essa estratégia de mapeamento é dita diretamente mapeada. O mapeamento de um enderço. E numa cache que possui B blocos é dado por: E % B (% é o operador módulo). 8 | 9 | Mapeamento: 10 | 11 | ____ ____ 12 | 0|____| |____| 0 - 0 13 | 1|____| |____| 1 - 1 14 | 2|____| |____| 2 - 2 15 | 3|____| |____| 3 - 3 16 | |____| 4 - 0 17 | |____| 5 - 1 18 | |____| 6 - 2 19 | |____| 7 - 3 20 | |____| 8 - 0 21 | |____| 9 - 1 22 | |____|10 - 2 23 | |____|11 - 3 24 | 25 | 26 | 27 | Para o mapeamento direto, é necessário que as unidades de memória possuam tamaho potência de dois, digamos que: 28 | - a memória principal possui tamanho 2^t 29 | - a memória cache possui tamanho 2^n 30 | 31 | O mapeamento direto conta com 3 elementos. 32 | 33 | - O **índice** são os n bits menos significativos da memória principal e representa o endereço mapeado na cache. 34 | - A **tag** são os bits mais significativos do endereço da memória principal e 35 | - O **bit de validade** marca a validade do dado presente na cache. 36 | 37 | ``` 38 | ____________________ 39 | | | | | 40 | | | | | 41 | | | | | 42 | | | | | 43 | |_|_________|________| 44 | | | | 45 | v v v 46 | bit tag dados 47 | de 48 | validade 49 | 50 | ``` 51 | 52 | No exemplo acima, a amemória principal possui 16=2⁴ bytes e a cache, 2²=4 bytes. Logo, a tag possui 2 bits e o tamanho real da cache é: 53 | 54 | 4 * (1 + 2 + 8) 55 | quantidade bit tag tamanho 56 | de blocos * de + + dos dados 57 | (linhas) da val. 58 | cache 59 | 60 | = 44 bits = 5,5 bytes 61 | 62 | Em função do princípio da localidade espacial, dados costumam ser carregados para a memória cache por **blocos**. Por exemplo, na arquitetura MIP, uma palavra possui 4 bits, portanto faria mais sentido carregar blocos de 4 bytes da memória principal para a cache. 63 | 64 | Por isso, existe o mapeamento direto por blocos. 65 | 66 | __________________________________________ 67 | |______|______|______|______|______|______| 0 68 | |______|______|______|______|______|______| 1 69 | |______|______|______|______|______|______| 2 70 | |______|______|______|______|______|______| 3 71 | | | \_____________ ____________/ 72 | v v v 73 | bit tag bloco de dados de 4 bytes 74 | de 75 | validade 76 | 77 | --- 78 | __________ 79 | 000000 |__________| 0 80 | 000001 |__________| 1 81 | 000010 |__________| 2 82 | 000011 |__________| 3 Memória 83 | 000100 |__________| 4 principal 84 | 000101 |__________| 5 de 64 bytes 85 | 000110 |__________| 6 86 | 000111 |__________| 7 87 | 001000 |__________| 8 88 | 001001 |__________| 9 89 | 90 | 91 | | | | | | | | | | 92 | | --- | - | - | --- | --- | --- | - | --- | 93 | | 0,0 | 0 | | 0,0 | 0,1 | 0,2 | | Matriz | 94 | | 0,1 | 1 | mapeamento -> | 1,0 | 1,1 | 1,2 | -> | m linhas | 95 | | 0,2 | 2 | | 2,1 | 2,1 | 2,2 | | n coluna | 96 | | 1,0 | 3 | 97 | | 1,1 | 4 | 98 | | 1,2 | 5 | 99 | | 2,0 | 6 | 100 | | 2,1 | 7 | 101 | | 2,2 | 8 | 102 | -------------------------------------------------------------------------------- /aula-9.md: -------------------------------------------------------------------------------- 1 | # Aula 9 2 | 3 | # Aritmética Computacional 4 | Vamos trabalhar com 3 bases numéricas: 2 (binário), 10 (decimal) &, menos frequentemente, 16 (hexadecimal). 5 | 6 | Os binários, em MIPS 32, possui 32 bits de largura. Chamaremos de **bit mais significativo** o bit mais à esquerda e **bit menos significativo** o bit mais à direita. 7 | 8 | 31 32 31 30 29 28 27 ... 6 5 4 3 2 1 0 9 | ------------------------------------------------- 10 | | | | | | | | | ... | | | | | | | | 11 | ------------------------------------------------- 12 | 13 | > Dentro da arquitetura MIPS 32, o **bit mais significativo** é o bit 31 & o **bit menos significativo** é o bit 0. 14 | 15 | Com esse modelo, podemos **representar** 2^32 números distintos. Essa limitação é a capacidade de representação de um computador, e neste limite ele é capaz de efetuar operações aritméticas. 16 | 17 | Quando, numa dessas operações, acontece de o resultado não caber nesse espaço de 32 bits, diz-se que houve um **overflow**. Cabe ao programa e, em última instância, ao sistema operacional, decidir o que fazer quando isso ocorre. 18 | 19 | ___ 20 | ## Observações 21 | - 1 O nome "complemento a dois" deve-se ao fato de que um número somado ao seu negativonuma representação de *n* bits dá 2^n 22 | - 23 | - 2 A instrução **lb** (load byte), carrega um byte (8 bits) na representação de 32 bits e replica o bit mais significativo, pois considera que o byte possui sinal. 24 | - 25 | - 3 Por outro lado, o **lbu** (load byte unsigned) sempre preenche os bits mais significativos com zeros, já que considera que o byte não possui sinal 26 | - 27 | - 4 Cuidado também com as instruções **slt** e **sltu**. 28 | - 29 | > Exemplo: Suponha qye $s0 contenha 0 em todos os bits e $s1 contenha 1 em todos os bits 30 | > 31 | > 31 32 31 ... 2 1 0 32 | > 33 | > | 1 | 1 | 1 | ... | 1 | 1 | 1 | 34 | > | 35 | > |- 36 | > | Representações do número 37 | > | Sem sinal: 2^32 - 1 38 | > | Com sinal: -1 39 | > 40 | > Logo e fizermos: 41 | > 42 | > slt $t0, $s1, $s0 então $t0=1 43 | > 44 | > sltu $t0, $s1, $s0 então $t0=0 45 | 46 | - 6 slt reg, reg1, reg2 47 | 48 | reg = 0, se reg1 >= reg2 49 | reg = 1, se reg1 < reg2 50 | 51 | - 5 Como negar um número emassembly MIPS? Queremos negar o $s0: 52 | 53 | ### Maneira correta: 54 | nor $s1, $s0, $zero 55 | addi $s1, s1, 1 56 | ### Maneira errada 57 | multi $s1, $s1, -1 58 | 59 | ___ 60 | ## Tabela de detecção overflow na adição 61 | 62 | Overflow na soma acontece nas seguintes operações: 63 | 64 | | Operação | A | B | Resultado 65 | | - | - | - | - 66 | | **A + B** | >=0 | >=0 | <0 67 | | **A + B** | <0 | <0 | >=0 68 | | **A - B** | >=0 | <0 | <0 69 | | **A - B** | <0 | >=0 | >=0 70 | 71 | >Soma de números com sinais opostos e subtração de números com mesmo sinal **nunca** acarretam em overflow 72 | 73 | 74 | Na arquitetura MIPS 75 | - **add**, **addi** e **sub** causam exceções no overflow. 76 | - **addu**, **addiu** e **subu** não causam exceções no overflow. 77 | 78 | ___ 79 | ## Exceções 80 | Também chamadas de interrupções, as exceções são eventos não planejados que interrompem a execução do programa. 81 | 82 | Deste modo, exceções lançadas por instruções são indesejadas. Por isso, em caso desuspeita de exceções, deve-se explorar as instruções que não causam interrupções para detecção de overflow. 83 | 84 | ___ 85 | ## Detectando overflow na adição 86 | 1. Considere $t1 e $t2 números **com sinal**. 87 | 88 | addu $t0, $t1, $t2 // t0 = t1 + t2 89 | xor $t3, $t1, $t2 // MSB 90 | // 1 caso $t1 e $t2 tiverem sinais opostos 91 | // 0 caso contrário 92 | 93 | slt $t4, $t3, $zero // t3 é negativo? 94 | bne $t4 $zero, sem_overflow // se t4!=0 não há overflow 95 | xor $t3 $t0,$t1 96 | slt $t4, $t3, $zero 97 | bne $t4, $zero, overflow 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /aula-19.md: -------------------------------------------------------------------------------- 1 | # Aula 19 2 | 3 | ## Implementação com pipeline 4 | 5 | **Pipeline** é uma técninca de implementação em que instuções são divididas em etapas e várias etapas são executadas ao mesmo tempo por ciclo de clock. 6 | As instruções do assembly MIPS podem ser divididas em 5 etapas. 7 | - 1. Buscar a instrução na memória de instruções 8 | - 2. Ler dados de registradores 9 | - 3. Executar uma operação aritmética 10 | - 4. Acessar a memória de dados 11 | - 5. Escrever i resyktadi byn registrador 12 | 13 | Usando tais etapas, é possível aplicar a técnica de pipeline. 14 | 15 | ### Exemplo 16 | Consideremos as instruções do subconjunto que estamos tratando, e os os seguintes tempos de operação para as principais unidades funcionais: 17 | - ***200ps*** para acesso à memória 18 | - ***100ps*** para operações com a **ULA** 19 | - ***50ps*** para leitura e escrita de registradores 20 | 21 | O tempo de execução de cada instrução, com base nas etapas descritas acima, é dado abaixo: 22 | 23 | Instrução | Etapa 1 | Etapa 2 | Etapa 3 | Etapa 4 | Etapa 5 | **Total** | 24 | --------- |-------- | ------- | ------- | ------- | ------- | ----- | 25 | lw | 200 | 50 | 100 | 200 | 50 | 600 | 26 | sw | 200 | 50 | 100 | 200 | ------- | 550 | 27 | tipo R | 200 | 50 | 100 | ------- | 50 | 400 | 28 | beq | 200 | 50 | 100 | ------- | ------- | 350 | 29 | 30 | Numa implementação de monociclo, o tempo de clock deveria ser ***600ps***, que é o tempo da instrução mais lenta. No pipeline, seria 200ps, que é o tempo da etapa mais lenta. 31 | 32 | > 3 execuções de ***lw*** em monociclo: **1800ps**. 33 | > 3 execuções de ***lw*** em pipeline: 1000 + 200 + 200 = **1400ps**. 34 | 35 | Portanto, para 3 execuções de lw, o pipeline é 1,3 vezes mais rápido que o monociclo. 36 | 37 | 1000.003 execuções 38 | |-> **Monociclo**: ***600.001.800ps*** 39 | |-> **Pipeline**: 1000 + 200*1.000.002 = ***200.001.400ps*** 40 | 41 | 42 | Nesse cenário, pipeline é 2,99 vezes mais rápido. 43 | 44 | Desse modo, podemos concluir que: 45 | - O pipeline melhora o desempenho aumentando a **vazão** ao invés de otimizar o tempo individiaul de uma instrução. Isso é interessante, pois um processaddor executa milhões de milhões de instruções por segundo. 46 | 47 | ___ 48 | ## Hazards de pipeline 49 | Hazards, em pipeline, acontecem quando uma etapa de uma instrução não pode ser executada por algum impedimento. 50 | Há 3 tipos de hazards de pipeline: 51 | - 1. Estruturais 52 | - 2. Hazards de dados 53 | - 3. Hazards de controle 54 | 55 | ### Hazards estruturais 56 | Hazards estruturais acontecem quando, por algum motivo estrutural da arquitetura, alguma etapa da instrução não pudesse ser executada. 57 | 58 | A nossa implementação do caminho de dados já resolve qualquer hazard estrutural. Mas teríamos um hazard estrutural na seguinte circunstância: 59 | Suponha que tivéssemos apenas uma unidade de memória do nosso caminho de dados (para acessar tanto instruções quanto dados). 60 | Suponha que no exemplo do **Slide14** tivéssemos uma quarta chamada a ***lw***. 61 | Essa última não poderia ser executada no quarto ciclo de clock, pois seria necessário buscá-la na memória, que já estaria sendo ocupada pela quarta etapa da primeira ***lw***. 62 | 63 | 64 | ### Hazards de dados 65 | Acontecem quando os dados para executar uma instrução numa etapa não estão disponíveis. Por exemplo: 66 | 67 | add $s0, $t0, $t1 68 | add $s1, $s0, $t2 69 | 70 | A segunda adição utiliza o resultado da primeira, que estará disponível apenas depois da quinta etapa da *primeira add*. 71 | para um caso desse, uma possível solução seria passar o resultado da adição diretamente para a *segunda add*, antes mesmo de terminar a primeira. 72 | Para tanto, cria-se um dispositivo que encaminhe o resultado da ULA diretamente como entrada da próxima instrução. 73 | Essa técnica é chamada de **forwading** ou **bypassing**. 74 | 75 | Neste caso, teríamos 76 | 77 | 78 | 79 | O forwading só funciona se a etapa de destino estiver a frente no tempo que o estágio de origem. 80 | Por isso, nem sempre é possível resolver todos os hazards de dados com forwading. 81 | Por exemplo: 82 | 83 | add $s0, 20($t1) 84 | add $t2, $s0, $s1 85 | 86 | O dado na lw estará disponível apenas na quarta etapa: 87 | 88 | -------------------------------------------------------------------------------- /aula-23.md: -------------------------------------------------------------------------------- 1 | # Aula 23 2 | 3 | ## Tratamento de escritas 4 | 5 | Quando o processador executa uma instrução de escrita na mémoria, o processador faz a escrita na memória cache, sem alterar diretamente a memória principal, respeitando a hierarquia. Quando isso acontece, a memória cache fica com um valor diferente da memória principal, e dizemos que os dados estão inconsistentes, Há duas estratégias para resolver isso. 6 | 7 | 1. Write-through: os dados escritos na cache são imediatamente progamados para a memória princiapl. Enquanto a propagação é feita, o processador fica ocioso. Como acesso à memória principal é lento, isso afeta o desempenho do processador para instruções de escrita. Para solucionar esse problema de tempo, usa-se um buffer de escrita: um buffer que armazena os dados que devem ser propagados à memória principal. Assim que os dados são escritos no buffer, o processador entende que o processo de escrita terminou e prossegue com o processamento. 8 | 9 | 2. Write-back: os dados escritos na cache permanecem apenas na cache até que ocorra uma falha. Com isso, o sistema de memória aproveita a penalidade de falha para resolver a inconsistência. 10 | 11 | ## Medindo o desempenho da cache 12 | 13 | Quando se fala em desempenho, geralmente estamos preocupados com o **tempo de execução.** 14 | 15 | O tempo absoluto de CPU que um programa consome é: 16 | 17 | Tempo de = ciclos de * Período 18 | CPU clock da cpu de clock 19 | 20 | Nesse tempo, há de se levar em conta o acesso à memória. Nesse contexto, quando há um **acerto**, o tempo de acesso à memória pode ser incluído no próprio tempo de clock da CPU. Todavia, falahas interrompem a execução do CPU a afetam o tempo de um programa: 21 | 22 | Tempo de = ciclos de + ciclos de * Período 23 | CPU clock cpu clock de de clock 24 | stall de memória 25 | 26 | Os stalls (bolhas) de memória podem ser de leitura e escrita. Os de leitura são: 27 | 28 | Ciclos de Leituras Taxa de Penalidade 29 | stall de = Perguntas * falhas de * de falha 30 | leitura leitura 31 | 32 | O tempo de stalls de escrita dependem da estratégia adotada. Para write-through, uma falha de dados faz com que o bloco que contém a palavra a ser sobrescrita seja antes buscada na memória principal. Além disso, uma escrita pode gerar stalls no procesador caso o buffer de escrita esteja cheio. Deste modo, 33 | 34 | Ciclos de leitura Taxa Penalidade 35 | stall de = ________ * de falhas * de falhas 36 | leitura Programa 37 | 38 | Stalls do 39 | + buffer de escrita 40 | 41 | Os stalls do buffer de escrita podem ser egnorados, pois os buffers são projetados para atender ao menos o dobro da escrita média em programas. Logo, os ciclos de stall de escrita coincidem com de leitura. O mesmo aontece com o esquea write-back, por definição. 42 | 43 | Logo, 44 | 45 | Ciclos de Acesso à memória Taxa de Penalidade 46 | clock de = ________________ * falha * de falha 47 | stall de programa 48 | memória 49 | 50 | Intruções Falhas Penalidade 51 | = _________ * ______ * de falha 52 | Programa Instrução 53 | 54 | Obs: Calcular os ciclos de clock consumido por um programa é algo muito dependente de programas e exigiria testes particulares para cada um. Por isso, processadores possuem uma medida médio chamada CPI-ciclos de clock por instrução. Com essa medida, 55 | 56 | ciclos de Quantidade 57 | clock de = CPI * de instruções 58 | um programa 59 | 60 | **Exemplo** 61 | 62 | Seja um cache com falhas de 63 | - 2% para instruções 64 | - 4% para dados 65 | 66 | Se um processador possui um CPI de 2, sem considerar stalls de memória, e a penalidade de falha é 100 ciclos, determine o quão mais rápida seria o processador se a cache nunca falhasse. COnsidere que em médio, 36% das instruções são de acesso à memória. 67 | 68 | ## Cache multinível 69 | 70 | O objetivo é reduzir a penalidade de falha e o tempo de acerto. Num sistema de cache multinível: 71 | 72 | - a priméria possui blocos de tamanho menor, objetivando-se diminuir o **tempo de acerto**. 73 | - a(s) secundário(s) possuem blocos de tamanho maior, objetivando-se minimizar a taxa de falhas em detrimento do tempo de acerto. 74 | -------------------------------------------------------------------------------- /aula-12.md: -------------------------------------------------------------------------------- 1 | # Algoritmos de Divisão 2 | Data: 11/10 3 | ___ 4 | ### Exemplo: 5 | > 1001010(bin) / 1000(bin) 6 | > 7 | > Dividendo divisor 8 | 9 | ---------------------------- 10 | | | Divisor 11 | ---------------------------- 12 | 64 bits 13 | 14 | ---------------------------- 15 | | | Resto 16 | ---------------------------- 17 | 64 bits 18 | 19 | 20 | ## Algoritmo: 21 | Para efetuar o algoritmo de divisão, basta seguir os seguintes passos: 22 | 23 | - 1. Salvar o Dividendo no e defina contador = 1. 24 | - 2. Subtraia o Divisor do Resto e Salve o resultado no Resto 25 | - 3. Faça um deslocamento à esquerda de 1 bit no quociente. 26 | - 1. Se resto >= 0: 27 | - defina o bit menos significativo do Quociente como 1. 28 | - 2. Se resto < 0: 29 | - restaure o valor original do Resto 30 | - 4. Faça um deslocamento à direita de 1 bit no Divisor 31 | - 5. Se contador <= 32: 32 | - Contador = Contador + 1 33 | - Voltar ao passo 2 34 | 35 | ### Exemplo: 36 | 7(dec) / 2(dec) = 0111(bin) / 0010(bin) 37 | 38 | IT | Passo | Quociente | Divisor | Resto 39 | ---| --- | --- | --- | --- 40 | 0 | Valor Inicial | 0000 | 0010 0000 | 0000 0111 41 | ---|-----------------|----------------|----------------|---------------- 42 | 1 | 2 | 0000 | 0010 0000 | 1110 0111 43 | 1 | 3 | 0000 | 0010 0000 | 0000 0111 44 | 1 | 4 | 0000 | 0001 0000 | 0000 0111 45 | ---|-----------------|----------------|----------------|---------------- 46 | 2 | 2 | 0000 | 0001 0000 | 1111 0111 47 | 2 | 3 | 0000 | 0001 0000 | 0000 0111 48 | 2 | 4 | 0000 | 0000 1000 | 0000 0111 49 | ---|-----------------|----------------|----------------|---------------- 50 | 3 | 2 | 0000 | 0000 1000 | 1111 1111 51 | 3 | 3 | 0000 | 0000 1000 | 0000 0111 52 | 3 | 4 | 0000 | 0000 0100 | 0000 0111 53 | ---|-----------------|----------------|----------------|---------------- 54 | 4 | 2 | 0000 | 0000 0100 | 0000 0011 55 | 4 | 3 | 0001 | 0000 0100 | 0000 0011 56 | 4 | 4 | 0001 | 0000 0010 | 0000 0011 57 | ---|-----------------|----------------|----------------|---------------- 58 | 5 | 2 | 0001 | 0000 0010 | 0000 0001 59 | 5 | 3 | 0011 | 0000 0010 | 0000 0001 60 | 5 | 4 | 0011 | 0000 0001 | 0000 0001 61 | 62 | Note que metde dos bits do divisor valem zero durante toda a execução do algoritmo. Note ainda que o primeiro passo nunca produzirá 1 no quociente, portanto, é desnecessário, o que pode nos poupar 1 iteração no algoritmo. 63 | 64 | Com isto, podemos **refinar o hardware** para utilizar 65 | 66 | -------------- 67 | | | Divisor 68 | -------------- 69 | 32 bits 70 | 71 | -------------- 72 | | | Quociente 73 | -------------- 74 | 32 bits 75 | 76 | ---------------------------- 77 | | | Resto 78 | ---------------------------- 79 | 64 bits 80 | 81 | 82 | ## Algoritmo Refinado 83 | Para realizar o algoritmo de maneira refinada, deve-se seguir os passos: 84 | - 1. Salve o Dividendo no Resto. Zere o Quociente. Defina Contador = 1. 85 | - 2. Faça um deslocamento à esquerda de 1 bit no Resto. 86 | - 3. Subtraia o divisor dos 32 bits mais significativos do Resto e salve o resultado nos bits mais significativos do Resto. 87 | - 4. Desloque o Quociente 1 bit à esquerda 88 | - 1. Se Resto >= 0 89 | - Quociente[0] =1 90 | - 2. Se Resto < 0 91 | - Restaure o Resto 92 | - 5. Se Contador < 32 93 | - Contador++ 94 | - Volte ao passo 2 95 | ### Exemplo: 96 | 7(dec) / 2(dec) = 0111(bin) / 0010(bin) 97 | 98 | IT | Passo | Quociente | Divisor | Resto 99 | ---| --- | --- | --- | --- 100 | 0 | Valor Inicial | 0000 | 0010 | 0000 0111 101 | ---|-----------------|----------------|----------------|---------------- 102 | 1 | 2 | 0000 | 0010 | 0000 1110 103 | 1 | 3 | 0000 | 0010 | 1110 1110 104 | 1 | 4 | 0000 | 0010 | 0000 0000 105 | ---|-----------------|----------------|----------------|---------------- 106 | 2 | 2 | 0000 | 0010 | 0001 1100 107 | 2 | 3 | 0000 | 0010 | 1111 1100 108 | 2 | 4 | 0000 | 0010 | 0001 1100 109 | ---|-----------------|----------------|----------------|---------------- 110 | 3 | 2 | 0000 | 0010 | 0011 1000 111 | 3 | 3 | 0000 | 0010 | 0001 1000 112 | 3 | 4 | 0001 | 0010 | 0001 1000 113 | ---|-----------------|----------------|----------------|---------------- 114 | 4 | 2 | 0001 | 0010 | 0011 0000 115 | 4 | 3 | 0001 | 0000 | 0001 0000 116 | 4 | 4 | 0011 | 0010 | 0001 0000 117 | -------------------------------------------------------------------------------- /aula-3.md: -------------------------------------------------------------------------------- 1 | # Aula 3 2 | 3 | ## As instruções elementares 4 | 5 | Vimos as instruções 6 | 7 | - add 8 | - sub 9 | - mul 10 | 11 | Exemplo: 12 | 13 | O que um compilador de C para arquitetura MIPS poderia produzir para a expressão: 14 | 15 | f = (g + h) - (i + j); 16 | 17 | add t0, g, h 18 | add t1, i, j 19 | sub f, t0, t1 20 | 21 | Como o MIPS trabalha com registradores, as "variáveis" usadas nos comandos devem ser os próprios registradores. A arquitetura MIPS possui, no processador principal, 32 registradores. 22 | 23 | Registradores | Numéricos | Descrição 24 | -------------- | --------- | ----- 25 | $zero | 0 | Constante zero 26 | $at | 1 | Resevado ao assembler 27 | $v0-$v1 | 2-3 | Retorno de funções 28 | $a0-$a3 | 4-7 | Argumentos de funções 29 | $t0-$t7 | 8-15 | Temporários 30 | $s0-$s7 | 16-23 | Salvos 31 | $t8-$t9 | 24-25 | Temporários 32 | $k0-$k1 | $gp | 28 | Ponteiro Global 33 | $sp | 29 | Ponteiro da pilha 34 | $fp 26-27 | Reservado ao S.O. 35 | $gp | 28 | Ponteiro Global 36 | $sp | 29 | Ponteiro da pilha 37 | $fp | 30 | Ponteiro de frame 38 | $ra | 31 | Endereço de Retorno 39 | 40 | Exemplo: 41 | 42 | $s0 - f add $t0, $s1, $s2 43 | $s1 - g add $t1, $s3, $s4 44 | $s2 - h sub $s0, $t0, $t1 45 | $s3 - i 46 | $s4 - j 47 | 48 | Numa arquitetura, a quantidade de memória (de registradores) é limitado, e isso não é suficiente para lidar com programas complexos que contêm milhares de variáveis. Por isso, um computador possui unidades de memória de maior capacidade de armazenamento (como a memória RAM, o HD, etc). À memória de maior capacidade chamaremos de memória principal. A memória princiapl é vista como um grande vetor unidimensional. 49 | 50 | Assim sendo, o MIPS possui instruções de transferência de dados que recuperam ou armazenam dados da/para a memória principal. São elas: 51 | 52 | - \b (bad byte) e lw (load word) carregam um byte de dado e uma palavra inteira da memória principal para o resgitrador, respectivamente. 53 | - sb (store byte) e sw (store word) salvam um byte e uma palavra do registrador para a memória, respectivamente. 54 | 55 | Exemplo: 56 | 57 | lw $t0, 20($a0) carrega uma palavra inteira do endereço de memória [$a0+20] no r$gp | 28 | Ponteiro Global 58 | $sp | 29 | Ponteiro da pilha 59 | $fp egistrador $t0 60 | 61 | Essas instruções utilizam um endereçamento indexado, que é feito da seguinte forma: offset(reg-base), onde 62 | 63 | - reg-base é o registrador que contém o endereço base 64 | - offset é o deslocamento necessário a partir do endereço base 65 | 66 | Importante: como cada palavra possui 4 bytes (= 32bits), o endereçamento da memória é feito de 4 em 4 unidades 67 | 68 | ### Memória princial: 69 | Endereço | Dados | Posição 70 | --- | ---- | -- 71 | 16 | | 4 72 | 12 | | 3 73 | 8 | | 2 74 | 4 | | 1 75 | 0 | | 0 76 | 77 | Endereço = 4 * Posição 78 | 79 | A sintaxe das instruções de acesso à memória é: 80 | 81 | op reg_operando, offset(reg_base) 82 | | 83 | V 84 | endereço 85 | 86 | Exemplo: 87 | 88 | Suponhamos que A seja um vetor de 100 palavras e as variáveis g e h estejam associadas aos registradores $s1 e $s2. Suponha ainda que o endereço base de A esteja em $s3. Como compilar a instrução: 89 | 90 | g = h + A[8] 91 | 92 | lw $t0, 32($s3) 93 | add $s1, $s2, $t0 94 | 95 | e quanto à instruções: 96 | 97 | A[12] = g+ A[8]; 98 | 99 | lw $t0, 32($s3) != $t0=A[8] 100 | add $t0, $s2, $t0 != $t0=g + A[8] 101 | sw $t0, 48($s3) != A[12]=$t0 102 | 103 | register int i; 104 | 105 | Obs: No MIPS, endereço bases devem ser multiplos de 4. Isso é chamado restrição de alinhamento 106 | 107 | Exercício: 108 | 109 | Consideremos o código a seguir. Suponha que o endereço base de A está em $s3 e result, em $s4 110 | 111 | int A[4] = {1, 2, 3, 4}; 112 | int result=A[0]+A[1]+A[2]+A[3] 113 | 114 | lw $t0, 0($s3) 115 | add $s4, $t0, $zero 116 | lw $t0, 4($s3) 117 | add $s4, $s4, $t0 118 | lw $t0, 8($s3) 119 | add $s4, $s4, $t0 120 | lw $t0, 12($s3) 121 | add $s4, $s4, $t0 -------------------------------------------------------------------------------- /aula-13.md: -------------------------------------------------------------------------------- 1 | # Aula 13 2 | 3 | ### Continuação da aula passada 4 | 5 | Obs: Note que ambos o quociente e o resto recebem deslocamentos à esquerda. Além isso, os bits menos significativos do resto permanecem nulos, Isso sugere um segundo refinamento no algoritmo de divisão. 6 | 7 | 8 | Passo 1: Salve o dividendo na porção menos significativa do Resto e defina cnt=1. 9 | Passo 2: Faça um deslocamento à esquerda no Resto. 10 | Passo 3: Resto [63..32]-= Dividor. 11 | Passo 4: Se Resto >= 0, faça um deslocamento de 1 bit à esquerda no Resto e defina Resto[0]=1. Se Resto<0 restaure o valor no resto e desloque-o 1 bit à esquerda. 12 | Passo 5: Se cnt < 32, cnt++ e volte ao Passo 3. 13 | Passo 6: Desloque Resto[63..32] 1 bit à direita. 14 | 15 | Exemplo: 16 | 17 | 4dec / 5dec = 0100 bin / 0101 bin 18 | 19 | |It.|Step| Resto | 20 | |---|-----|----------| 21 | |0|Inicialização|0000 0100| 22 | |||0000 1000| 23 | |1|3|1010 1000| 24 | ||4|0001 0000| 25 | |2|3|1010 0000| 26 | ||4|0010 0000| 27 | |3|3|1010 0000| 28 | ||4|0100 0000| 29 | |4|3|1111 0000 | 30 | ||4|1000 0000| 31 | |- |srl Resto[63..32]|0100 0000| 32 | 33 | --- 34 | 35 | ### Agora com número negativo 36 | 37 | -7dec / 2dec = -3dec, -1dec 38 | -4dec, +1dec 39 | 40 | Para a divisão de inteiros com sinal: 41 | 42 | - 1 - Salve o sinal do dividendo e do divisor, e torne-os positivos. 43 | - 2 - Faça a divisão sando o algoritmo acima. 44 | - 3 - Faça com que o resto tenha o mesmo sinal que o dividendo tinha ao início. 45 | - 4 - Tome o quociente negativo se o sinal do dividendo era diferente que o do divisor. 46 | 47 | Exemplo: 48 | 49 | ### Sinais na divisão de números (resultado e resto) 50 | 51 | 7 / 2 = 3, resto 1. 52 | -7 / 2 = -3, resto -1. 53 | 7 / -2 = -3, resto 1. 54 | -2 / -2 = 3, resto -1. 55 | 56 | 57 | ## Instruções MIPS 58 | 59 | Assembly MIPS, temos a instrução 60 | 61 | div dividendo, divisor 62 | | 63 | v 64 | registradores 65 | 66 | O resultado é salvo no par de registradores Hi elo: 67 | 68 | |Resto|Quociente| 69 | Hi Lo 70 | 71 | Deste modo: 72 | 73 | div $s0, $s1 # faz $s0 / $s1 74 | mflo $s2 # $s2 = quociente 75 | mfhi $s3 # $s3 = resto 76 | 77 | ## Ponto flutuante 78 | 79 | A representação de números reais no computador baseia-se na **notação científica**. Nessa notação, o número é escrito com 80 | 81 | +- F * 10^E, 82 | 83 | onde F é um real tal que 1 <= F < 10, chamado de **fração** (ou significando, ou mantissa) e E é um inteiro chamado **expoente**. Tal notação serve para representar qualquer número, por exemplo: 84 | 85 | 3.155.760.000 = 3,155760 * 10⁹ 86 | 87 | Em binário, o formato geral da notação científica é: 88 | 89 | 1,xxxxx...x * 2^yyyyyy... (**) 90 | 91 | Essa notação recebe o nome de ponto flutuante porque para preservar o formato padrão (**), é necessário deslocar o ponto binário constantemente. 92 | 93 | ***Representação em ponto flutuante*** 94 | 95 | Um número real em notação científica é representado em uma palavra da seguinte forma: 96 | 97 | |31|30|29|...|23|22|21|...|3|2|1|0| 98 | |--|--|--|--|--|--|--|--|--|--|--|--| 99 | |Bit de sinal|\|||Expoente (8 bits)||\||| Fração (23 bits) |||| 100 | 101 | Logo, o número em ponto flutuante possui o seguinte formato: 102 | 103 | (1)⁵ * F * 2^E 104 | 105 | Essa é a representação e precisão simples (o float, em C). 106 | 107 | Nessa representação: 108 | 109 | - 1 - Se o número for muito grande de forma que o expoente não caiba nos 8 bits, temos um **overflow** 110 | - 2 - Se o número for muito pequeno de forma e expoente (muito próximo de zero), ocorre o que achamamos de underflow. 111 | 112 | Casos assim ocorrem com frequência na representação de precisão simples. Pro isso, usamos com frequência a representação com **precisão dupla:** 113 | 114 | |31|30|29|...|20|19|18|...|3|2|1|0| 115 | |--|--|--|--|--|--|--|--|--|--|--|--| 116 | |S|\|||Expoente (10 bits)||\||| Fração (20 bits) |||| 117 | 118 | |31|30|29|...|20|19|18|...|3|2|1|0| 119 | |--|--|--|--|--|--|--|--|--|--|--|--| 120 | ||||||Fração (32 bits)||||||| 121 | 122 | Esses formatos seguem o padrão de ponto flutuante IEEE 754, usado amplamente nas arquiteturas desde a década de 1980. 123 | 124 | Nesse padrão, para ampliar a capacidade de representação da fração, o 1 fica implícito e apenas a porção à direita da vírgula é representada. 125 | 126 | A única exceção a essa representação é o zero. Ademais, temos: 127 | 128 | (-1)⁵ * (1+Fração) * 2^E 129 | 130 | O padrão IEE 754 especifica a representação da seguinte forma: 131 | 132 | 1,xxxx * 2^yyyy (2048) 133 | 134 | Divisão por 0: **NaN** 135 | Infinito: **Inf** -------------------------------------------------------------------------------- /aula-14.md: -------------------------------------------------------------------------------- 1 | # Aula 14 2 | 3 | ## O padrão IEEE754 4 | O padrão IEEE754 especifica as seguintes representações: 5 | 6 | | Precisão simples | | Precisão Dupla | | Representação | 7 | | --- |---| --- |---| --- | 8 | Expoente | Fração | Expoente | Fração | | 9 | 0 | 0 | 0 | 0 | **Zero** | 10 | 0 | !=0 | 0 | !=0 | +/- **Número desnormalizado** 11 | 1-254 | * | 1-2046 | * | +/- **Ponto flutuante** 12 | 255 | 0 | 2047 | 0 +/- | **Infinito** 13 | 255 | !=0 | 2047 | !=0| **NaN** (Not a Number) 14 | 15 | ***Obs***: 16 | > Um número em notação científica está normalizado se estiver na forma 17 | > 18 | > *(-1)⁵ * 1,xxx..x * 2^yyy...* 19 | > 20 | 21 | Na representação, o expoente aparece antes da fração para facilitar a ordenação: **números com expoentes maiores são maiores do que aqueles com expoentes menores**. 22 | 23 | Por isso, representar expoentes em complemento a dois compromete essa estratégia. Por exemplo: 24 | 25 | ---------------------- 26 | 1,0 * 2^(-1) = |0|11111111|000...000| (I) 27 | ---------------------- 28 | |S| exp. | fração 29 | 30 | Enquanto que: 31 | 32 | ---------------------- 33 | 1,0 * 2^1 = |0|00000001|000...000| (II) 34 | ---------------------- 35 | |S| exp. | fração 36 | 37 | 38 | Note que ***1,0 * 2^1*** **>** ***1,0 * 2^(-1)*** , porém, **(I) > (II)**. 39 | 40 | Por isso, o que se faz é representar o expoente mais negativo por 000...(bin) e o maior por 111...111(bin). Essa representação é chamada de **representação deslocada** e é feita somando-se 127 ao expoente na precisão simples e 1023 na dupla. 41 | 42 | Por exemplo,*-1* é representado por 43 | > -1 + 127 = 126 44 | 45 | Esse deslocamento é chamado de **Bias**. Com isso, o número em ponto flutuante é representado da seguinte forma: 46 | 47 | (-1)⁵ * (1+Fração) + 2^(E-Bias) 48 | 49 | 50 | Com essa representação, o menor número positivo que podemos representar é 51 | >| 0 | 00000001 | 000...000 | 52 | >---------------------- 53 | >S(1bit) 54 | >Expoente(8bits) 55 | >Fração(23bits) 56 | > 57 | > Que corresponde a: 58 | > 59 | > *N*(min) = 1,000...0(bin) * 2^(1-127) 60 | > = 1,000...0 * 2^(-126) 61 | > ~= 1,2 * 10^-38 62 | 63 | Enquanto que o maior é 64 | >| 0 | 11111110 | 111...111 | 65 | >---------------------- 66 | >S(1bit) 67 | >Expoente(8bits) 68 | >Fração(23bits) 69 | > 70 | > *N*(max) = 1,111...1(bin) * 2^(127) = (2-2^23) * 2^127 71 | > ~= 2^128 72 | > ~= 3,4*10^38 73 | 74 | Analogamente, podemos calcular os extremos para **precisão dupla**, levando-nos à seguinte capacidade de representação. 75 | 76 | | Precisão | *E*min | *E*max | *N*min | *N*max | 77 | |--- | --- | --- | --- | --- | 78 | | Simples | -126 | 127 | 2^(-126) ~= 1,2*10^(-38) | 2^128 ~= 3,4*10^38 79 | | Dupla | -1022 | 1023 | 2^(-1022) ~= 2,2*10^(-308) | 2^1024 ~= 1,8*10^308 80 | 81 | ___ 82 | # Operações aritméticas em ponto flutuante 83 | ## Adição 84 | Para fazer adição, seguimos o seguinte algoritmo: 85 | - 1. Compare os expoentes dos números. Desloque a fração do menor expoente para a direita e some 1 ao expoente que os expoentes coincidam; 86 | - 2. Some as frações; 87 | - 3. Normalize a soma; 88 | - Desloque a fração à direita e incremente o expoente; 89 | - ou 90 | - Desloque a fração à esquerda e decremente o expoente; 91 | - 4. Se houve **undeflow** ou **overflow** 92 | - lance uma exceção 93 | - 4. Senão 94 | - Arredonde a fração para o número de bits apropriado 95 | 96 | ### Exemplo 97 | 0,5(dec) - 0,4375(dec) = 1,0(bin)*2^(-1) - 1,110*2^(-2) 98 | 99 | 1. 1,110 * 2^(-2) -> 0,1110 * 2^(-1) 100 | 2. 1,1110 101 | 3. Nada a fazer 102 | 4. Nada a Fazer 103 | **Resultado** = 1,1110 * 2^(-1) 104 | 105 | ### Exemplo 106 | 1,1011 * 2^-3 + 1,1100 * 2^(-4) 107 | 108 | 1. 0,11100 * 2^(-3) 109 | 2. 10,1001 * 2^(-3) 110 | 3. 1,01001 * 2^(-2) 111 | 4. Nada a fazer 112 | 113 | ___ 114 | ## Subtração 115 | O algoritmo da subtração segue os mesmos passos do algoritmo da **adição**, com a diferença de que será realizada a operação de subtração ao invés da soma. 116 | 117 | ___ 118 | ## Multiplicação 119 | O algoritmo para multiplicação é o seguinte: 120 | 121 | - 1. Some os expoentes e, em seguida, some um **Bias** ao resultado; 122 | > (*E*1 - Bias) + (*E*2 - Bias) + Bias 123 | > = (*E*1 - *E*2) - Bias 124 | - 2. Multiplique as frações; 125 | - 3. Normalize o produto; 126 | - 4. Se houve underflow ou overflow 127 | - lance uma exceção; 128 | - 4. senão 129 | - faça o **arredondamento**; 130 | - 5. Ajuste o sinal do produto com base no sinal dos operandos; 131 | 132 | ___ 133 | ## Arredondamento 134 | 135 | O **IEEE754** admite 4 padrões de arredondamento: 136 | 137 | 1. **Sempre para cima**: 138 | 2,11 = 2,2 139 | 2,15 = 2,2 140 | 2,19 = 2,2 141 | 142 | 2. **Sempre para baixo**: 143 | 2,11 = 2,1 144 | 2,15 = 2,1 145 | 2,19 = 2,1 146 | 147 | 3. **Truncamento**: 148 | Simplesmente despreza os bits menos significativos 149 | 150 | 4. **Ao mais próximo**: 151 | 2,11 = 2,1 152 | 2,19 = 2,2 153 | 154 | Neste caso, e *2,15*? A solução é escolher a solução mais coerente do ponto de vista estatístico: ao dígito **par** mais próximo 155 | 156 | 2,15 = 2,2 157 | 2,25 = 2,2 -------------------------------------------------------------------------------- /aula-20.md: -------------------------------------------------------------------------------- 1 | # Hierarquia da memória do computador 2 | 3 | --------------- 4 | | Processador | 5 | --------------- 6 | ---------- | 7 | |Nível 1 | | Distância 8 | | | | 9 | |Nível 2 | | 10 | | | | Tempo de acesso 11 | |Nível 3 | | 12 | | | | 13 | | ... | | Tamanho da memória 14 | | | | 15 | |Nível n | | 16 | ---------- | 17 | \/ 18 | 19 | 20 | 21 | 22 | Os dados são mantidos no nível inferior, e cópias são feitas apenas entre níveis adjacentes. 23 | 24 | O processador acessa os dados diretamente do nível mais superior. Se o dado requisitado pelo processador estiver presente, dizemos que houve um **acerto**. 25 | Caso contrário, houve uma **falha**. 26 | 27 | A **taxa de acerto** é a fração dos acessos do processador à memória que resultaram em acertos. 28 | A **taxa de falha** é a fração de acessos cujos dados não foram encontrador no nível superior. 29 | 30 | O **tempo de acerto** é o tempo necessário para a hierarquia de memória determinar se um dado requisitado está no nível superior. 31 | A **penalidade de falha** é o tempo de carregamento do dado procurado de um dos níveis inferiores para o nível superior. 32 | 33 | # Tipos de memória 34 | Podemos destacar 4 tecnologias de memória: ***SRAM***, ***DRAM***, ***memória Flash*** e ***discos magnéticos*** 35 | 36 | ## Memória *SRAM* 37 | **SRAM** (Static Random Access Memory) é a memória de mais rápido acesso e é a tecnologia usualmente utilizada nos níveis superiores da hierarquia. 38 | 0Memórias deste tipo podem armazenar dados enquanto houver energia passando por ela (mesmo em baixa quantidade). 39 | 40 | ## Memória *DRAM* 41 | **DRAM** (Dinamic Random Access Memory) é uma memória de acesso mais lento que a *SRAM*. 42 | 43 | As memórias DRAM usam um único transistor por bit de armazenamento, por isso, são muito mais densas que as *SRAM*'s e, consequentemente, bem mais baratas. 44 | 45 | Os dados numa ***DRAM*** são armazenados como uma carga elétrica em um capacitor, por isso, não permanecem indefinidamente e necessitam de atualizações periódicas. Por isso é chamada "dinâmica". 46 | 47 | O acesso aos dados na memória ***DRAM*** é feito por linhas, e há um buffer que armazena linhas e funciona como os *SRAM*. 48 | 49 | Com isso, o acesso a bits aleatórios pode ser feito eficientemente. 50 | 51 | Além disso, as ***DRAM***'s contam com um clock interno. A vantagem disso é que o tempo de sincronização com o processador é desnecessário. 52 | 53 | Essa tecnologia chama-se Synchronous DRAM, ou ***SDRAM***. 54 | 55 | A transferência de linhas entre a memória DRAM e seu buffer é feita dentro de um ciclo de clock da memória. 56 | 57 | Versões mais atuais da ***SDRAM*** transferem dados tanto no início quanto no final do clock. Essa tecnologia chama-se Double Data Rate (**DDRSDRAM**). 58 | Por exemplo, uma memória DDR$-3200 SDRAM usa a versão 4 da tecnologia DDR e é capaz de fazer 3200 milhões de transferências por segundo, ou seja, tem um clock de 1600MHz. 59 | 60 | 61 | ## Memória FLash 62 | É um tipo de memória EEPROM (Electrically Erasable Programmable Read Only Memory)). 63 | 64 | Semelhantes à tecnologia DRAM, a Memória Flash permite que múltiplos bits sejam lidos e escritos numa púnica operação. 65 | 66 | A diferença é que escritas desgastam os bits em uma memória flash. Por isso, há um controlador que "esplaha" as operações de escrita, remapando endereços 67 | para blocos menos utilizados. 68 | 69 | É a tecnologia utilizada em cartões microSD e HD's SSD. 70 | 71 | 72 | ## Discos magnéticos 73 | São unidades de armazenamento que consistem num conkjunto de pratos magnéticos que efetuam de 5400 a 15000 rotações em torno de um eixo. Para fazer leitura e escrita, sobre cada prato há um braço que contém uma bobina eletromagnética chamada cabeça de leitura e escrita 74 | 75 | Cada disco é dividido em circulos circuncêntricos chamados **trilhas**. Costuma-se ter algo na casa de 10000 trilhas por disco. Cada trilha é dividida em milhares de **setores**, que possuem tamanho entre 512 e 4096 bytes. 76 | 77 | Os braços movem-se de forma síncrona entre os discos. O conjunto de trilhas que encontram-se sob a cabeça de leitura num instante de tempo é chamado **cilindro**. 78 | 79 | A busca de dados num disco magnético consome 3 etapas: 80 | - 1. **Tempo de busca**: mover a cabeça de leitura sobre a trilha apropriada. 81 | - 2. **Latência de rotação**: dada a trilha, busca o setor. 82 | - 3. **Tempo de transferência**: tempo para transferir um bloco de bits. 83 | 84 | O tempo médio de busca costuma ser algo entre 3ms e 13ms, a latência rotaciona média num disco de 5400 RPM algo em torno de 6ms e a taxa de transferência 100 e 200MB's. 85 | 86 | Essa tecnologia pode sofrer **fragmentação de dados**. 87 | 88 | A memória flash costuma ser 1000 vezes mais rápida que discos magnéticos e a DRAM; 10000 vezes naus rápida. Todavia, os discos magnéticos são entre 10 e 100 vezes mais baratos que outras tecnologias. 89 | 90 | 91 | # Memória CACHE 92 | É o nível de mempirua mais superior, que fornece dados direto ao processador. 93 | 94 | Quando o processador requisita um dado, o sistema de memória primeira verifica se o dado está ou não presente na CACHE. 95 | O processador requisita um dado usando seu denreço, logo a cache deve, de alguma forma, armazenar dados e mapeá-los para o endereço do nível inferior. 96 | Portanto, é necessário fazer um mapeamento de endereços do npivel inferior para a cache. -------------------------------------------------------------------------------- /aula-4.md: -------------------------------------------------------------------------------- 1 | # Aula 4 2 | 3 | **Observação**: É comum que programas possuam mais variáveis que a quantidade de registradores do processador. Por isso, é necessário escolher quais variáveis ficam na memória e quais nos registradores. Antigamente, o programador tomava essa escolha (veja o modificador register da linguagem C, por exemplo). Atualmente, o próprio compilador procura gerar um assembly que otimize essa escolha. Esse processo de escolha é chamado spilling registers. 4 | 5 | ## Assembly para Linguagem de Máquina 6 | 7 | A representação do assembly no nível lógico consiste em traduzir cada instrução para uma representação numérica binária. Essa representação binária é o que chamamos de linguagem de máquina. 8 | 9 | Para representar uma instrução em linguagem de máquina, é feita uma conversão da seguinte forma: 10 | 11 | operação op1, op2, op3 | 12 | | | -> Formato geral das instruções 13 | v | 14 | registradores | 15 | 16 | 17 | - Os registradores são mapeados de acordo com o número de 0 a 31 a que são associados (veja a tabela dos registradores). 18 | - A cada operação é associado um ou dois identificadores numéricos 19 | 20 | Exemplo: 21 | 22 | A instrução 23 | add $t0, $s1, $s2 24 | 25 | é mapeado, em decimal, da seguinte forma. 26 | 27 | | 0 | 17 | 18 | 8 | 0 | 32 | 28 | 29 | Cada um dos segmentos dessa representação é chamado campo. 30 | 31 | - O primeiro e o último caracterizam a operação add. 32 | - O segundo e o terceiro campus representam os operandos. 33 | - O quarto campo é o resgistrador que recebe o resultado. 34 | - O quinto campo é o deslocamento (Como é irrelevante no caso, recebeu zero). 35 | 36 | A forma geral da representação desse tipo de instruções é: 37 | 38 | | op | rs | rt | rd | shamt | funct | 39 | | 6 bits | 5 bits | 5 bits | 5 bits | 5 bits | 6 bits | 40 | 41 | - **op** é a operação (opcode) 42 | - **rs** e **rt** são os operadores 43 | - **rd** é o destino 44 | - **shamt** é a quantidade de deslocamento (shift a mount) 45 | - **funct** é o código da função (complementar opcode) 46 | 47 | Este formato chama-se formato R, ou tipo-R. No exemplo acima, teríamos, para a máquina: 48 | 49 | 50 | | 000000 | 10001 | 10010 | 01000 | 00000 | 100000 | 51 | | 6 bits | 5 bits | 5 bits | 5 bits | 5 bits | 6 bits | 52 | 53 | Considere agora a instrução: 54 | 55 | lw reg, offset(reg_base) 56 | 57 | Ela precisa de dois registradores e uma constante numérica. Se utilizarmos o formato R, a constante deveria ser armazenada no campo **rd**, que possui 5 bits. Com isso, a constante limitaria-se a 2⁵ possibilidade, ou seja, variaria entre 0 e 31. Por essa razão, há ainda o formmato I ou do tipo-I. 58 | 59 | | op | rs | rt | const/endereço | 60 | | 6 bits | 5 bits | 5 bits | 16 bits | 61 | 62 | Observação: O R vem de resgistrador e I vem de imediato. Há uma classe de instruções chamadas imediatas que lidam com constantes 9numéricas nas instruções ao invés de registradores 63 | 64 | Exemplo: 65 | 66 | add $t0, $s0, $s1 67 | addi $t0, $s0, 5 68 | 69 | A imediata usa o segundo operando como número 70 | Apenas numeros inteiros 71 | 72 | Exemplo: 73 | 74 | Seja **A** um vetor com endereço base em $s0 e uma variável **h** em $s1. O código: 75 | 76 | ### Assembly 77 | 78 | A[300] = h + A[300]; 79 | lw $t0, 1200($s0) 80 | add $t0, %s1, $t0 81 | sw $t0, 1200($s0) 82 | 83 | --- 84 | 85 | ### Linguagem de máquina 86 | 87 | | op | rs | rt | (rd | end/shamt | funct) | 88 | |--------|--------|--------|--------|------------|--------| 89 | | 35 | 16 | 8 | 1200 | 90 | | 0 | 17 | 8 | 8 | 0 | 32 | 91 | | 43 | 16 | 8 | 1200 | 92 | 93 | ### Importante 94 | 95 | - 1 Uma instrução também é uma palavra. 96 | - 2 Por serem palavras os dados (arquitetura de Van Nemann). Isso é chamado de programa armazenado. 97 | 98 | Observação: Uma palavra pode representar inteiros de -2³¹ a 2³¹ - 1, ou -2.147.483.648 a 2.147.483.647. 99 | O campo contante, de -2¹⁵ a 2¹⁵ - 1, ou -32.768 a 32.767 100 | 101 | Para representarmos o 0 tiramos 1 na parte positiva, por isso do "-1". 102 | 103 | ## Operações 104 | 105 | Há 5 operações lógicas 106 | - 1 **Shift lógica à esquerda** desloca todos os bits de uma palavra para a esquerda. O bit mais significativo é descartada. 107 | 108 | Exemplo: 109 | 110 | 0010 1000 <²- 1010 0000 111 | 112 | Em Assembly MIPS, a instrução é 113 | 114 | sll $t2, $t0, 2. 115 | (shift left logical) 116 | 117 | - 2 **Shift lógico à direita** é análo ao sll. Em assembly: 118 | 119 | ``` 120 | srl $t2, $t0, 2. 121 | (shift right logical) 122 | ``` 123 | 124 | - 3 **AND** é o **e** lógico bit a bit 125 | 126 | Exemplo: 127 | 128 | 0010 1010 |-> e 129 | 1011 0110 | 130 | __________ 131 | 0010 0010 132 | 133 | Em assembly MIPS: 134 | 135 | and $t0, $t1, $t2. 136 | 137 | - 4 **OR** é o **ou** lógico bit a bit. 138 | 139 | Exemplo: 140 | 141 | 0010 1010 |-> ou 142 | 1011 0110 | 143 | __________ 144 | 1011 1110 145 | 146 | Em assembly MIPS: 147 | 148 | or $t0, $t1, $t2. 149 | 150 | - 5 **NOT** é o **não** lógico bit a bit. Para manter o padrão de 3 operandos, o assembly MIPS possui a instrução: 151 | 152 | nor $t0, $t1, $t2 153 | 154 | que faz o **not** do **or** entre $t1 e $t2: 155 | 156 | Observações: 157 | 158 | - 1 Como fazer o NOT apenas? 159 | - nor $t2, $t1, $t1 160 | - nor $t2, $t1, $zero 161 | 162 | - 2 sll e srl são do formato R. Deste modo, usa-se o campo shamt. É desnecessário suas representações em tipo-I, já que deslocamentos maiores que 31 bits não fazem sentido. 163 | 164 | - 3 Também há andi e ori. 165 | 166 | Exemplo: 167 | 168 | andi $t1, 4t0, 5. 169 | 170 | - 4 Fazer shift a esquerda de deslocamento i equivale a multiplicar por 2^i. -------------------------------------------------------------------------------- /aula-16.md: -------------------------------------------------------------------------------- 1 | # Arquitetura do Processador 2 | ## Elementos do caminho de dados 3 | O **processador**, também chamado de CPU(*Central Processing Unit*), é o dispositivo de um computador que executa as instruções definidas em uma arquitetura, de forma a realizar rigorosamente as operações por elas definidas. 4 | 5 | O processador possui dois componentes principais: 6 | * caminho de dados 7 | * controle 8 | 9 | O **caminho de dados** é o responsável por realizar as operações sovre os dados e conduzi-los da Entrada à Saída. 10 | Enquanto o **controle** decide quais elementos do caminho de dados, da memória e de periféricos são necessários para cada instrução. 11 | 12 | Nosso objetivo agora é entender como projetar um caminho de dados para as três classes de instruções da arquitetura MIPS: 13 | - 1. Instruções lógicas e aritméticas (em particular *add*, *sub*, *and*, *or* & *slt*) 14 | - 2. Intruções de acesso à memória (em particular *lw* & *sw*) 15 | - 3. Instruções de desvio condicional (em particular *beq*) 16 | 17 | Esse subconjunto de instruções ilustra bem os princípios de projeto de um caminho de dados. Cada classe de instruções precisa de elementos básicos para ser executada. 18 | 19 | ___ 20 | ## Etapas comum 21 | Todas as instruções passam primeiro por uma etapa comum, que consiste em **buscar na memória** uma instrução, dados ou endereço. 22 | 23 | Para executar essa primeira etapa, são necessários três elementos básicos: 24 | - 1. Uma **memória de instruções** que, dado o endereço, retorna uma instrução 25 | - 2. O **contador de programa** (*PC*), que contém o endereço da instrução que deve ser executada pelo processador e 26 | - 3. O **somador**, que é utilizado para incrementar o *PC* e fazê-lo apontar para a próxima instrução a sex executada. 27 | 28 | Na execução sequencial (sem desvios), esta etapa consiste em: 29 | - 1. Buscar, na memória, a instrução cujo endereço encontra-se em *PC* e 30 | - 2. incrementar o *PC* em 4 bytes para apontar para a próxima instrução. 31 | 32 | ### Abstração do processo sequencial de instruções: 33 | 34 | |-------------------------------------------------------------------| 35 | | ____ ______ | 36 | | | | |--------------------------------> | \ | 37 | |--> | | | |_ \ | 38 | | PC | | ___________________ \ SOMA \ -----| 39 | ---> |____|-----*------>| Endereço | 4 _/ / 40 | | Inst. |----> | / 41 | | | |______/ 42 | | Memória de | 43 | | Instruções | 44 | |___________________| 45 | 46 | ___ 47 | ## Instruções lógicas e aritméticas 48 | Relembrando, essas instruçes seguem o formato do *tipo **R*** 49 | 50 | _______ _______ _______ _______ _______ _______ 51 | | op | rs | rt | rd | shamt | funct | 52 | |_______|_______|_______|_______|_______|_______| 53 | 6 bits 5 bits 5 bits 5 bits 5 bits 6 bits 54 | 55 | Para executar uma instrução deste tipo, é necessário: 56 | - 1. Ler o conteúdo de dois registradores 57 | - 2. Realizar uma operação aritmética com o conteúdo lido em **'1.'** 58 | - 3. Escrever o resultado num registrador. 59 | 60 | As etapas **'1.'** & **'3.'** usam-se de uma estrutura chamada **banco de registradores**. 61 | Um banco de registradores é uma estrutura que armazena os registradores da arquitetura e realiza operações de leitura e escrita dado o identificador de um registrador. 62 | 63 | Para instruções do *tipo **R***, duas palavras devem ser lidas, e uma, escrita. Deste modo, o banco de registradores contém: 64 | - Duas entradas que especificam os identificadores dos registradores a serem lidos 65 | - Uma entrada que especifica o identificador do registrador de escrita 66 | - Uma entrada contendo a palavra a ser escrita. 67 | 68 | O banco possui duas saídas, que são as palavras contidas nos registradores de leitura. 69 | 70 | O banco de registradores conta ainda com sinal de controle de escrita, que será **1** se houver dados para serem escritos no registrador de escrita e **0** caso contrário. 71 | 72 | ### Abstração de um banco de registradores 73 | _______________________________ 74 | 5 | | 75 | --->| Reg. leitura | 76 | 5 | Dado lido 1 |-----> 77 | --->| Reg. leitura | 78 | 5 | | 79 | --->| Reg. escrita | 80 | 32 | Dado lido 2 |-----> 81 | --->| Dado | 82 | |_______________________________| 83 | | 84 | RegWrite 85 | 86 | **obs**: A escrita no banco de registradores é feita na transição do clock. Isso significa que a estrutura lê e escreve ao mesmo tempo: a leitura obtém o valor recém escrito na transição do clock anterior. 87 | Isso que viabiliza operações do tipo: *add $t0, $t0, $t1* 88 | 89 | Outro elemento utilizado é a unidade lógica e aritmética, a *ULA*. A *ULA* é capaz de executar as operações lógicas e aritméticas próprias de cada instrução. Cada operação é identificada na *ULA* por um controle de 4 bits. A saída é gerada numa palavra de 32 bits, e um sinal que será 1 se a saída for zero. 90 | 91 | _ opULA (4 bits) 92 | | 93 | ___|___ 94 | 32 | \ 95 | --->|_ \ 96 | bits \ ULA \ 97 | _/ / 98 | 32 | / 99 | --->|_______/ 100 | bits 101 | 102 | 103 | ___ 104 | ## Instruções de acesso à memória 105 | Seguem o formato do **tipo *I***: 106 | 107 | _______ _______ _______ _________________ 108 | | op | rs | rt | endereço | 109 | |_______|_______|_______|_________________| 110 | 6 bits 5 bits 5 bits 16 bits 111 | 112 | E operam da seguinte forma: 113 | - 1. Calculam o endereço como sendo 114 | > reg-base + offset 115 | - 2. Lê um dado do registrador e armazena na memória ou vice-versa 116 | 117 | A etapa 1 faz uso de uma *ULA* e a etapa 2 faz uso do banco de regustradires, ambos já utilizados pelas instruções lógicas e aritméticas --------------------------------------------------------------------------------