├── 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 |
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 |
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 |
23 | Essas duas instruções são denominadas de desvio condicional.
24 | Há também uma instrução de desvio incondicional;
25 |
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
--------------------------------------------------------------------------------