Você está na página 1de 27

4

Soluções
4.1
4.1.1 O valor dos sinais é o seguinte:

Matematicamente, o MemRead é um “don't care”: a instrução será


executada corretamente independente do valor escolhido. Praticamente,
no entanto, MemRead deve ser definido como false para evitar causar
uma falha de segmento ou falta de cache.
4.1.2 Registradores, ALUsrc mux, ALU e MemToReg mux.
4.1.3 Todos os blocos produzem alguma saída. As saídas de DataMemory e
Imm Gen não são usadas.

4.2 Reg2Loc para ld: Ao executar ld, não importa qual valor é passado para
“Read register 2”, porque o ALUSrc mux ignora a saída resultante “Read
data 2” e seleciona o valor imediato estendido de sinal.
MemToReg para sd e beq: Nem sd nem beq gravam um valor no
arquivo de registro. Não importa qual valor o mux MemToReg passa para
o arquivo de registro porque o arquivo de registro ignora esse valor.

4,3
4.3.1 25 + 10 = 35%. Somente Carregar e Armazenar usam a memória de dados.
4.3.2 100% Cada instrução deve ser buscada na memória de instruções antes de
poder ser executada.
4.3.3 28 + 25 + 10 + 11 + 2 = 76%. Apenas instruções do tipo R não usam o
extensor de sinal.
4.3.4 A extensão do sinal produz uma saída durante cada ciclo. Se sua saída não
for necessária, ela será simplesmente ignorada.

4.4
4.4.1 Apenas loads são quebradas. MemToReg é 1 ou “não me importo” para
todas as outras instruções.
4.4.2 I-type, loads, stores estão todas quebradas.

4.5 Para contexto: A instrução codificada é sd x12, 20(x13)


4.5.1
4.5.2 O novo PC é o antigo PC + 4. Este sinal vai do PC, através do somador
“PC + 4”, através do mux “branch”, e volta para o PC.
4.5.3 ALUsrc: Entradas: Reg[x12] e 0x0000000000000014; Saída:
0x0000000000000014
MemToReg: Entradas: Reg[x13] + 0x14 e <undefined>; saída:
<indefinido>
Ramificação: Entradas: PC+4 e 0x000000000000000A
4.5.4 Entradas ALU: Reg[x13] e 0x000000000000014
PC + 4 entradas somadoras: PC e 4
entradas somadoras: PC e 0x0000000000000028

4.6
4.6.1 Não são necessários blocos lógicos adicionais.
4.6.2 Ramificação: false
MemRead: false (Veja nota de rodapé da solução para o problema 4.1.1.)
MemToReg: 0
ALUop: 10 (ou simplesmente dizer “adicionar” é suficiente para este
problema)
MemWrite:
false ALUsrc:
1
RegWrite: 1
4.7
4.7.1 Tipo R: 30 + 250 + 150 + 25 + 200 + 25 + 20 = 700ps
4.7.2 ld: 30 + 250 + 150 + 25 + 200 + 250 + 25 + 20 = 950 ps
4.7.3 sd: 30 + 250 + 150 + 200 + 25 + 250 = 905
4,7,4 beq: 30 + 250 + 150 + 25 + 200 + 5 + 25 + 20 = 705
4,7,5 Tipo I: 30 + 250 + 150 + 25 + 200 + 25 + 20 = 700ps 4,7,6
950ps 4,8

Usando os resultados do Problema 4.7, vemos que o tempo médio por instrução
é
0,52*700 + 0,25*950 + 0,11*905 + 0,12 * 705 = 785,6 ps
Em contraste, uma CPU de ciclo único com um clock “normal” exigiria um
tempo de ciclo de clock de 950.
Assim, o aumento de velocidade seria 925/787,6 = 1,174

4,9
4.9.1 Sem melhoria: 950; Com melhorias: 1250
4.9.2 O tempo de execução de um programa na CPU original é 950*n. O
tempo de execução na CPU aprimorada é 1250*(0,95)*n = 1187,5.
Assim, o “speedup” é 0,8. (Assim, esta CPU “melhorada” é realmente
mais lenta que a original).
4.9.3 Como a adição de uma instrução de multiplicação removerá 5% das
instruções, o tempo de ciclo pode crescer até 950/(0,95) = 1000. Assim,
o tempo da ALU pode aumentar em até 50 (de 200 para 250).

4.10
4.10.1 Os registros adicionais nos permitirão remover 12% das loads e
armazenamentos, ou (0,12)*(0,25 + 0,1) = 4,2% de todas as instruções.
Assim, o tempo para executar n instruções diminuirá de 950*n para
960*.958*n = 919,68*n. Isso corresponde a uma aceleração de
950/895,73 = 1,03.
4.10.2 O custo da CPU original é 4507; o custo da CPU melhorada é 4707. PC:
5
I-Mem: 1000
Arquivo de registro: 200
ALU: 100
D-Mem: 2000
Sign Extend: 1002
Controles: 10002
somadores: 30*24
muxes: 4*102
portas simples: 2
*1
Assim, para um aumento de 3% no desempenho, o custo da CPU aumenta em
cerca de 4,4%.
4.10.3 Do ponto de vista estritamente matemático não faz sentido adicionar
mais registradores porque a nova CPU custa mais por unidade de
desempenho. No entanto, esse cálculo simples não leva em conta a
utilidade do desempenho. Por exemplo, em um sistema em tempo real,
um desempenho de 3% pode fazer a diferença entre cumprir ou perder
prazos. Nesse caso, a melhoria valeria o custo adicional de 4,4%.

4.11
4.11.1 Não são necessários novos blocos funcionais.
4.11.2 Apenas a unidade de controle precisa ser modificada.
4.11.3 Não são necessários novos caminhos de dados.
4.11.4 Não são necessários novos sinais.

4.12
4.12.1 Não são necessários novos blocos funcionais.
4.12.2 O arquivo de registradores precisa ser modificado para que possa
escrever em dois registradores no mesmo ciclo. A ALU também
precisaria ser modificada para permitir que os dados de leitura 1 ou 2
fossem passados ​para escrever os dados 1.
4.12.3 A resposta depende da resposta dada em 4.12.2: qualquer entrada que
não tenha permissão para passar pela ALU acima deve agora ter um
caminho de dados para gravar dados 2.
4.12.4 Seria necessário haver um segundo fio de controle RegWrite.
4.12.5 Muitas soluções possíveis.

4.13
4.13.1 Precisamos de alguns muxes adicionais para conduzir os caminhos de dados
discutidos em 4.13.3.
4.13.2 Nenhum bloco funcional precisa ser modificado.
4.13.3 É necessário haver um caminho da saída da ALU para a porta de dados
de gravação da memória de dados. Também precisa haver um caminho
dos dados lidos 2 diretamente para a entrada de endereço da memória de
dados.
4.13.4 Esses novos caminhos de dados precisarão ser controlados por muxes.
Esses muxes exigirão fios de controle para o seletor.
4.13.5 Muitas soluções possíveis.
4.14 Nenhum: todas as instruções que usam sign extend também usam o
arquivo de registro, que é mais lento.

4.15
4.15.1 O novo tempo do ciclo de clock seria 750. A ALU e a Memória de
Dados agora funcionarão em paralelo, então removemos efetivamente o
mais rápido dos dois (a ALU com tempo de 200) do caminho crítico.
4.15.2 Mais devagar. A CPU original leva 950*n picossegundos para executar
n instruções. O mesmo programa terá aproximadamente 1,35*n
instruções quando compilado para a nova máquina. Assim, o tempo na
nova máquina será 750*1,35n = 1012,5*n. Isso representa uma
“aceleração” de 0,93.
4.15.3 O número de loads e stores é o fator principal. A forma como as loads e
as stores são usadas também pode ter um efeito. Por exemplo, um
programa cujos carregamentos e armazenamentos tendem a ser apenas
alguns endereços diferentes também pode ser executado mais
rapidamente na nova máquina.
4.15.4 Esta resposta é uma questão de opinião.

4.16
4.16.1 Canalizado: 350; não canalizado: 1250 canalizado:
4.16.2 1250; sem pipeline: 1250
4.16.3 Divida o estágio de ID. Isso reduz o tempo do ciclo de clock para 300ps.
4.16.4 35%.
4.16.5 65%

4.17 n + k − 1. Vejamos quando cada instrução está no estágio WB. Em um


pipeline de estágio k, a 1ª instrução não entra no estágio WB até o ciclo
k. A partir desse ponto, no máximo uma das n − 1 instruções restantes
está no estágio WB durante cada ciclo.
Isso nos dá um mínimo de k + (n − 1) = n + k − 1 ciclos.
4,18 x13 = 33 e x14 = 36
4,19 x15 = 54 (O código será executado corretamente porque o resultado da
primeira instrução é escrito de volta no registrador no início do 5º ciclo,
enquanto a instrução final lê o valor atualizado de x1 durante
deste
ciclo metade
segunda
_
a

)
4.21.1 .O pipeline com encaminhamento requer 1,05*n*300ps. A aceleração é,
portanto, (1,4*250)/ (1,05*300) = 1,11.
4.21.2 Nosso objetivo é que o pipeline com encaminhamento seja mais rápido
do que o pipeline sem encaminhamento. Seja y o número de baias
restantes como porcentagem de instruções de “código”. Nossa meta é
300*(1+y)*n
< 250*1,4*n. Assim, y deve ser inferior a 16,7%.
4.21.3 Desta vez, nosso objetivo é 300(1 + y)*n < 250(1 + x)*n. Isso acontece
quando y < (250x − 50)/300.
4.21.4 Eu não posso. Na melhor das hipóteses, onde o encaminhamento elimina
a necessidade de cada NOP, o programa levará 300*n para ser executado
no pipeline com encaminhamento. Isso é mais lento do que os
250*1,075*n necessários no pipeline sem encaminhamento.
4.21.5 A aceleração não é possível quando a solução para 4.21.3 é menor que 0.
Resolvendo 0< (250x − 50)/300 para x dá que x deve ser pelo menos
0,2.
4.22
4.22.1 As barracas são marcadas com **:
sd x29, 12(x16) IF ID EX ME WB
ld x29, 8(x16) IF ID EX ME WB
sub x17, x15, x14 IF ID EX ME WB
bez x17, label ** * * IF ID EX ME WB
adicionar x15, x11, x14 IF ID EX ME WB
sub x15,x30,x14 IF ID EX ME WB
4.22.2 O código de reordenação não ajudará. Cada instrução deve ser buscada;
assim, todo acesso a dados causa um travamento. Reordenar o código
apenas mudará o par de instruções que estão em conflito.
4.22.3 Você não pode resolver esse risco estrutural com NOPs, porque mesmo
os NOPs devem ser buscados na memória de instruções.
4.22.4 35%. Todo acesso a dados causará uma parada.
4.23
4.23.1 O período do relógio não mudará porque não estamos fazendo nenhuma
alteração no estágio mais lento.
4.23.2 Mover o estágio MEM em paralelo com o estágio EX eliminará a
necessidade de um ciclo entre loads e operações que utilizam o resultado
das loads. Isso pode potencialmente reduzir o número de stalls em um
programa.
4.23.3 Remover o deslocamento de ld e sd pode aumentar o número total de
instruções porque algumas instruções ld e sd precisarão ser
substituídas por um par addi/ld ou addi/sd .
4.24 O segundo. Um exame cuidadoso da Figura 4.59 mostra que a necessidade
de um estol é detectada durante o estágio de ID. É este estágio que
impede a busca de uma nova instrução, efetivamente fazendo com que o
add repita seu estágio de ID.

4.25
4.25.1 … indica uma parada. ! indica um estágio que não faz trabalho útil.

ld x10, 0(x13) SE ID EX ME | WB
ld x11, 8(x13) SE ID EX | ME WB
adicionar x12, x10, x11 IF ID | .. EX EU! WB
adiciona x13, x13, -16 SE | .. ID EX EU! WB
bnez x12, LOOP | .. SE ID EX ME! WB!
ld x10, 0(x13) IF ID EX ME WB
ld x11, 8(x13) IF ID EX ME WB
adicionar x12, x10, x11 IF ID .. EX | MIM! WB
adiciona x13, x13, -16 SE .. ID | EX EU! WB
bnez x12, LOOP SE | ID EX EU! WB!
Completamente ocupado | NNN N N N NN |

4.25.2 Em um ciclo de clock específico, um estágio do pipeline não está


realizando um trabalho útil se estiver parado ou se a instrução que passa
por esse estágio não estiver realizando nenhum trabalho útil lá. Como
mostra o diagrama acima, não há ciclos durante os quais cada estágio do
pipeline está realizando um trabalho útil.
4.26
4.26.1 // EX apenas para
1º: adiciona x11,
x12, x13 adiciona
x14, x11, x15
adiciona x5, x6,
x7

// MEM apenas para


1º: ld x11, 0(x12)
adiciona x15,
x11, x13
adiciona x5, x6
, x7

// EX apenas para 2º:


adiciona x11,
x12, x13
adiciona x5, x6,
x7 adiciona x14,
x11, x12

// MEM apenas para


2º: ld x11, 0(x12)
adiciona x5, x6,
x7 adiciona x14,
x11, x13

// EX ao 1º e EX ao 2º:
adiciona x11, x12, x13
adiciona x5, x11,
x15 adiciona x16,
x11, x12
4.26.2 // EX apenas ao 1º: 2
nops adiciona x11, x12,
x13 nop nop
adi
cio
na
x14 , x11, x15
add x5, x6, x7

// MEM para 1º apenas: 2


stalls ld x11, 0(x12)
nop
nop
add x15, x11,
x13 add x5, x6,
x7

// EX para 2º apenas: 1
nop add x11, x12, x13
adiciona x5,
x6, x7 não
adiciona x14, x11, x12
// MEM apenas para 2º: 1
não ld x11, 0(x12)
adiciona x5,
x6, x7 não
adiciona x14, x11, x13

// EX to 1st e EX to 2nd: 2 nops


add x11, x12, x13 nop nop
add
x5, x11, x15 add
x16, x11, x12
4.26.3 Considere este código:
ld x11, 0(x5) # MEM to 2nd --- one stall
add x12, x6, x7 # EX para 1st --- duas
barracas adicionam x13, x11, x12
adicionam x28, x29, x30
Se analisarmos cada instrução separadamente, calcularíamos que
precisamos adicionar 3 baias (uma para um “MEM to 2nd” e duas para
um “EX to 1st only”. No entanto, como podemos ver abaixo, precisamos
apenas de duas barracas:
ld x11, 0(x5)
add x12, x6, x7
nop
nop
add x13, x11, x12
add x28, x29, x30

4.26.4 Tomando uma média ponderada das respostas de 4,26 .2 dá 0,05*2 + 0,2*2
+ 0,05*1 + 0,1*1 + 0,1*2 = 0,85 stalls por instrução (em média) para
um CPI de 1,85. Isso significa que 0,85/1,85 ciclos, ou 46%, são
paralisações.
4.26.5 A única dependência que não pode ser tratada pelo encaminhamento é
do estágio MEM para a próxima instrução. Assim, 20% das instruções
irão gerar um stall para um CPI de 1,2. Isso significa que 0,2 de 1,2
ciclos, ou 17%, são paralisações.
4.26.6 Se encaminharmos apenas do registro EX/MEM, teremos as seguintes
barracas/NOPs
EX para 1º: 0
MEM para 1º: 2
EX para 2º: 1
MEM para 2º: 1
EX para 1º e 2º: 1
Isso representa uma média de 0,05*0 + 0,2*2 + 0,05*1 + 0,10*1 + 0,10*1
=
0,65 baias/instrução. Assim, o CPI é de 1,65
SE encaminharmos apenas de MEM/WB, temos as seguintes barracas/NOPs
EX para 1º: 1
MEM para 1º: 1
EX para 2º: 0
MEM para 2º: 0
EX para 1º e 2º: 1
Este representa uma média de 0,05*1 + 0,2*1 + 0,1*1 = 0,35
baias/instrução. Assim, o IPC é de 1,35.
4.26.7

4.26.8 CPI para encaminhamento completo é 1,2


CPI para encaminhamento de
“viagem no tempo” é 1,0 período de
relógio para encaminhamento
completo é 130
período de relógio para encaminhamento de “viagem no tempo” é 230
Aceleração = (1,2*130)/ (1*230) = 0,68 (Isso significa que o
encaminhamento de “viagem no tempo” realmente diminui a velocidade da
CPU.)
4.26.9 Ao considerar o encaminhamento “EX/MEM” em 4.26.6, o “EX para
1º” não gera travamentos, mas “EX para 1º e EX para 2º” gera um
travamento. No entanto, “MEM para 1º” e “MEM para 1º e MEM para
2º” sempre gerarão o mesmo número de baias. (Todas as dependências
“MEM para 1ª” causam um bloqueio, independentemente do tipo de
encaminhamento. Este bloqueio faz com que a fase ID da 2ª instrução se
sobreponha com a fase WB da instrução base, caso em que nenhum
encaminhamento é necessário.)

4.27
4.27.1 add x15, x12,
x11 nop
nop
ld x13, 4(x15)
ld x12,
0(x2) nop
ou x13, x15,
x13 nop
nop
sd x13, 0(x15)
4.27.2 Não é possível reduzir o número de NOPs.
4.27.3 O código é executado corretamente. Precisamos da detecção de perigo
apenas para inserir um estol quando a instrução que segue uma carga usa
o resultado da carga. Isso não acontece neste caso.
4.27.4

Como não há bloqueios neste código, PCWrite e IF/IDWrite são sempre


1 e o mux antes de ID/EX é sempre definido para passar os valores de
controle.
(1) EncaminharA = X; ForwardB = X (sem instrução no estágio EX ainda)
(2) ForwardA = X; ForwardB = X (sem instrução no estágio EX ainda)
(3) ForwardA = 0; ForwardB = 0 (sem encaminhamento; valores
retirados dos registradores)
(4) ForwardA = 2; ForwardB = 0 (registrador base obtido do resultado
da instrução anterior)
(5) ForwardA = 1; ForwardB = 1 (register base retirado do resultado
de duas instruções anteriores)
(6) ForwardA = 0; ForwardB = 2 (rs1 = x15 retirado do registrador;
rs2 = x13 retirado do resultado do 1º ld—duas instruções atrás)
(7) ForwardA = 0; ForwardB = 2 (registrador base retirado do arquivo
de registro. Dados a serem escritos obtidos da instrução anterior)
4.27.5 A unidade de detecção de risco também precisa dos valores de rd que
saem do registro MEM/WB. A instrução que está atualmente no estágio
ID precisa ser travada se depender de um valor produzido (ou
encaminhado) pela instrução no estágio EX ou pela instrução no estágio
MEM. Portanto, precisamos verificar o registrador de destino dessas
duas instruções. A unidade Hazard já possui o valor de rd do
registrador EX/MEM como entrada, então precisamos apenas somar o
valor do registrador MEM/WB.
Não são necessárias saídas adicionais. Podemos parar o pipeline usando
os três sinais de saída que já temos.
O valor de rd de EX/MEM é necessário para detectar o risco de dados
entre o add e o seguinte ld. O valor de rd form MEM/WB é
necessário para detectar o risco de dados entre a primeira ld e a or .
4.27.6

(1) PCWrite = 1; SE/IDWrite = 1; controle mux = 0


(2) PCWrite = 1; SE/IDWrite = 1; controle mux = 0
(3) PCWrite = 1; SE/IDWrite = 1; controle mux = 0
(4) PCWrite = 0; SE/IDWrite = 0; controle mux = 1
(5) PCWrite = 0; SE/IDWrite = 0; control mux = 1

4,28
4,28,1 O IPC aumenta de 1 para 1,4125.
Um desvio previsto incorretamente fará com que três instruções sejam
liberadas: as instruções atualmente nos estágios IF, ID e EX. (Neste
ponto, a instrução de desvio atinge o estágio MEM e atualiza o PC com
a próxima instrução correta.) Em outras palavras, 55% dos desvios
resultarão na liberação de três instruções, nos dando um CPI de 1 + (1 −
0,45)(0,25)3 = 1,4125. (Só para
ficar claro: o preditor sempre obtido está correto 45% das vezes, o que
significa,
é claro, que está incorreto 1 − 0,45 = 55% das vezes.)
4.28.2 O IPC aumenta de 1 para 1,3375 . (1 + (.25)(1 − .55) = 1,1125)
4.28.3 O IPC aumenta de 1 para 1,1125. (1 + (.25)(1 − .85) = 1,0375)
4.28.4 A aceleração é de aproximadamente 1,019.
Alterar metade das instruções de desvio para uma instrução ALU reduz
a porcentagem de instruções que são desvios de 25% para 12,5%. Como
as ramificações previstas e incorretas são substituídas igualmente, a taxa
de previsão incorreta permanece em 15%. Assim, a nova CPU é 1 +
(0,125)(1 − 0,85)
= 1,01875. Isso representa uma aceleração de 1,0375 / 1,01875 = 1,0184
4.28.5 A “aceleração” é 0,91.
Há duas maneiras de olhar para este problema. Uma maneira é olhar
para as duas instruções ADD como uma ramificação com um ciclo
“extra”. Assim, metade dos ramos tem 1 ciclo extra; 15% da outra
metade tem 1 ciclo extra (o enxágue do pipeline); e os ramos restantes
(aqueles corretamente previstos) não têm ciclos extras. Isso nos dá um
CPI de 1 + (.5)(.25)*1 +
(.5)(.25)(.15)*1 = 1.14375 e uma aceleração de 1.0375 / 1.14375 = .91.
Também podemos tratar as instruções ADD como instruções separadas.
O programa modificado agora tem 1.125n instruções (metade de 25%
produz
uma instrução extra). .125n dessas instruções 1.125n (ou 11,1%) são
ramificações. O CPI para este novo programa é 1 + (0,111)(0,15)*1 =
1,01665. Quando consideramos o aumento de 12,5% nas instruções,
obtemos uma aceleração de 1,0375 / (1,125 * 1,01665) = 0,91.
4.28.6 O preditor tem 25% de precisão nas ramificações restantes. Sabemos
que 80% das ramificações são sempre previstas corretamente e a
precisão geral é
de 0,85. Assim, 0,8*1 + 0,2*x = 0,85. Resolvendo para x mostra que x = 0,25.
4,29
4.29.1

4.29.2

4.29.3 As primeiras recorrências deste padrão não têm a mesma precisão que as
posteriores porque o preditor ainda está aquecendo. Para determinar a
precisão no “estado estacionário”, devemos trabalhar nas previsões de
ramificação até que os valores do preditor comecem a se repetir (ou seja,
até que o preditor tenha o mesmo valor no início da corrente e na
próxima recorrência do padrão).

4.29.4 O preditor deve ser um registrador de deslocamento de N bits, onde N é


o número de resultados de ramificação no padrão de destino. O
registrador de deslocamento deve ser inicializado com o próprio padrão
(0 para NT, 1 para T), e a previsão é sempre o valor no bit mais à
esquerda do registrador de deslocamento. O registro deve ser deslocado
após cada ramificação prevista.
4.29.5 Como a saída do preditor é sempre o oposto do resultado real da
instrução de desvio, a precisão é zero.
4.29.6 O preditor é o mesmo que na parte d, exceto que deve comparar sua
previsão com o resultado real e inverter (lógico NOT) todos os bits no
registrador de deslocamento se a previsão estiver incorreta. Este preditor
ainda sempre prevê perfeitamente o padrão dado. Para o padrão oposto,
a primeira previsão estará incorreta, então o estado do preditor é
invertido e depois
disso as previsões estão sempre corretas. No geral, não há período de
aquecimento para o padrão fornecido, e o período de aquecimento para o
padrão oposto é apenas uma ramificação.
4.30
4.30.1

4.30.2 O Mux que seleciona o próximo PC deve ter entradas adicionadas a ele.
Cada entrada é um endereço constante de um manipulador de exceção.
Os detectores de exceção devem ser adicionados ao estágio de pipeline
apropriado e as saídas desses detectores devem ser usadas para controlar
o Mux pré-PC e também para converter em instruções NOPs que já
estão no pipeline por trás da instrução de disparo de exceção.

4.30.3 As instruções são buscadas normalmente até que a exceção seja


detectada. Quando a exceção é detectada, todas as instruções que estão
no pipeline após a primeira instrução devem ser convertidas em NOPs.
Como resultado, a segunda instrução nunca é concluída e não afeta o
estado do pipeline. No ciclo que segue imediatamente ao ciclo em que a
exceção é detectada, o processador buscará a primeira instrução do
manipulador de exceção.

4.30.4 Essa abordagem exige que busquemos o endereço do manipulador da


memória. Devemos adicionar o código da exceção ao endereço da tabela
de vetores de exceção, ler o endereço do manipulador da memória e
pular para esse endereço. Uma maneira de fazer isso é lidar com isso
como uma instrução especial que coloca o endereço em EX, carrega o
endereço do manipulador em MEM e configura o PC em WB.

4.30.5 Precisamos de uma instrução especial que nos permita mover um valor
do registrador Cause (exceção) para um registrador de uso geral.
Devemos primeiro salvar o registrador de propósito geral (para que
possamos restaurá-lo mais tarde), carregar o registrador Cause nele,
adicionar o endereço da tabela de vetores a ele, usar o resultado como
um endereço para um carregamento que obtém o endereço do
manipulador de exceção direito da memória e, finalmente, pule para esse
manipulador.
4.31.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
li x12, 0 IF ID EX ME WB
jal ENT IF ID .. EX ME WB

bne x12, x13, TOP IF .. ID EX ME WB


slli x5, x12, 3 IF .. ID .. EX ME
WB

add x6, x10, x5 IF .. ID EX ME WB


ld x7, 0(x6) IF .. ID .. EX ME WB

ld x29, 8(x6) SE .. ID EX ME WB
sub x30, x7, x29 SE .. ID .. .. EX ME WB

adicionar x31, x11, x5 SE .. .. ID EX ME WB


sd x30, 0(x31) SE .. .. ID .. EX ME WB

addi x12, x12, 2 SE .. ID EX ME WB


bne x12, x13, TOP IF .. ID .. EX ME WB

slli x5 , x12, 3 IF .. ID EX ME WB
adicionar x6, x10, x5 IF .. ID .. EX ME WB

ld x7, 0(x6) IF .. ID EX ME WB
ld x29, 8(x6) IF .. ID .. EX ME WB

sub x30, x7, x29 IF .. ID .. EX ME WB


adicionar x31, x11, x5 IF .. ID .. .. EX ME WB

sd x30, 0(x31) IF .. .. ID EX ME WB
addi x12, x12, 2 IF .. .. ID .. EX ME WB

bne x12, x13, TOP IF .. ID EX ME WB


slli x5, x12, 3 IF .. ID .. EX ME WB
4.31.2 O o código original requer 10 ciclos/loop em uma máquina de 1 edição
(paradas mostradas abaixo) e 10 ciclos/loop na máquina de 2 edições.
Isso não dá speedup líquido. (Esse é um resultado terrível, considerando
que quase dobramos a quantidade de hardware.) Sabemos que o código
leva 10 ciclos/iteração na máquina de 2 edições porque a primeira
instrução no loop 1 (O slli) começa a ser executada no ciclo 6 e a
primeira instrução na iteração 3 começa
a execução no ciclo 26, então (26–6)/2 = 10.
li x12,0
jal ENT
TOP:
slli x5, x12,
3 add x6, x10,
x5 ld x7,
0(x6)
ld x29 , 8(x6)
<stall>
sub x30, x7,
x29 add x31,
x11, x5 sd x30,
0(x31) addi
x12, x12, 2
ENT:
bne x12, x13, TOP
4.31.3 Aqui está uma possibilidade:
beqz x13,
DONE li x12,
0
jal ENT
TOP:
slli x5, x12,
3 add x6, x10,
x5 ld x7,
0(x6)
ld x29, 8(x6)
addi x12, x12,
2 sub x30, x7,
x29 add x31,
x11 , x5 sd
x30, 0(x31)
ENT:
bne x12, x13,
TOP DONE:
Se mudarmos para uma abordagem “baseada em ponteiro”, podemos
salvar um ciclo/loop.
O código abaixo faz isso:
for (i = 0; i ! = j; i+ = 2) {
*b = *a -
*(a+1); b+=2;
a+=2;
}
bez x13,
DONE li x12,
0
jal ENT
TOP:
ld x7, 0(x10)
ld x29,
8(x10)
addi x12, x12, 2
sub x30, x7, x29
sd x30, 0(x11)
addi x10, x10 ,
16 addi x11,
x11, 16
ENT:
bne
x12,x13,TOP DONE:
4.31.4 Aqui está uma possibilidade:
beqz x13,
DONE li x12,
0
TOP:
slli x5, x12, 3
add x6, x10,
x5 ld x7, 0(
x6)
add x31, x11, x5
ld x29, 8(x6)
addi x12, x12, 2
sub x30, x7, x29
sd x30, 0(x31)
bne x12, x13,
TOP
DONE:
Se mudarmos para um “pointer- baseado”, podemos salvar um ciclo/loop.
O código abaixo faz isso:
for (i = 0; i ! = j; i+ = 2) {
*b = *a -
*(a+1); b+=2;
a+=2;
}
beqz x13, DONE
li x12, 0
TOP:
ld x7, 0(x6)
add x12, x12, 2
ld x29, 8(x6)
addi x6, x6, 16
sub x30, x7,
x29 sd x30,
0(x31)
bne x12, x13,
TOP DONE:

4.31.5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 beqz
x13, DONE IF ID EX ME WB
li x12, 0 IF ID .. EX ME WB

slli x5, x12, 3 IF .. ID EX ME WB


adiciona x6, x10, x5 IF .. ID .. EX ME
WB

ld x7, 0(x6) IF .. ID EX ME WB
adiciona x31, x11, x5 IF .. ID EX ME WB

ld x29, 8(x6) IF ID EX ME WB
addi x12, x12, 2 IF ID EX ME WB

sub x30, x7, x29 IF ID .. EX ME WB


sd x30, 0(x31) IF ID .. .. EX ME WB

bne x12, x13, TOP IF .. .. ID EX ME WB


slli x5, x12, 3 IF .. .. ID .. EX ME WB

add x6, x10, x5 IF .. ID EX ME WB


ld x7, 0(x6) SE .. ID .. EX ME WB

adiciona x31, x11, x5 SE .. ID EX ME WB


ld x29, 8(x6) SE .. ID EX ME WB

addi x12, x12 , 2 IF ID EX ME WB


sub x30, x7, x29 IF ID .. EX ME WB

sd x30, 0(x31) IF .. ID EX ME WB
bne x12, x13, TOP IF .. ID EX ME WB

slli x5, x12, 3 IF ID EX ME WB


adicionar x6, x10, x5 IF ID .. EX ME WB

4.31.6 O código de 4.31.3 requer 9 ciclos por iteração em. O código de 4.31.5
requer 7,5 ciclos por iteração. Assim, a aceleração é 1,2.
4.31.7 Aqui está uma
possibilidade: beqz
x13, DONE li
x12, 0
TOP:
slli x5, x12, 3
add x6, x10, x5
add x31, x11,
x5 ld x7, 0(x6)
ld x29, 8(x6)
ld x5, 16(x6)
ld x15,
24(x6)
addi x12, x12,
4 sub x30, x7,
x29 sub x14,
x5, x15 sd x30,
0(x31) sd x14,
16(x31)
bne x12, x13,
TOP DONE :
4.31.8 Aqui está uma possibilidade:
beqz x13,
DONE li x12,
0
addi x6, x10,
0 TOP:
ld x7, 0(x6)
add x31, x11, x5
ld x29, 8(x6)
addi x12, x12, 4
ld x16 , 16(x6)
slli x5, x12, 3
ld x15, 24(x6)
sub x30, x7, x29
sd x30, 0(x31)
sub x14, x16,
x15 sd x14,
16(x31)
adicionar x6,
x10, x5 bne
x12,x13,TOP
DONE:
4.31.9 O código de 4.31.7 requer 13 ciclos por iteração desenrolada. Isso é equivalente a 6,5 ​ciclos por
iteração original. O código de 4.30.4 requer 7,5 ciclos por iteração desenrolada. Isso é
equivalente a 3,75 ciclos por iteração original. Assim, a aceleração é de 1,73.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
beqz x13, DONE IF ID EX ME WB
li x12, 0 IF ID .. EX ME WB
addi x6, x10, 0 SE .. ID EX ME WB
ld x7, 0(x6) SE .. ID .. EX ME WB
adicionar x31, x11, SE .. ID EX ME WB
x5
ld x29, 8(x6) SE .. ID EX ME WB
addi x12, x12, 4 IF ID EX ME WB
ld x16, 16(x6) IF ID EX ME WB
slli x5, x12, 3 IF ID EX ME WB
ld x15, 24(x6) IF ID EX ME WB
sub x30, x7 , x29 IF ID EX ME WB
sd x30, 0(x31) IF ID .. EX ME WB
sub x14, x16, x15 IF .. ID EX ME WB
sd x14, 16(x31) IF .. ID EX ME WB
add x6 , x10, x5 IF ID EX ME WB
bne x12,x13,TOP IF ID EX ME WB
ld x7, 0(x6) IF ID EX ME WB
adicionar x31, x11, x5 IF ID EX ME WB
ld x29, 8(x6) IF ID EX ME WB
addi x12, x12, 4 IF ID EX ME WB
ld x16, 16(x6) IF ID EX ME WB
slli x5, x12, 3 IF ID EX ME WB
ld x15, 24(x6) IF ID EX ME WB
sub x30, x7, x29 IF ID EX ME WB
sd x30, 0(x31) IF ID EX ME WB
sub x14, x16, x15 IF ID .. EX ME WB
sd x14, 16(x31) IF .. ID EX ME WB
add x6, x10, x5 IF .. ID EX ME WB
bne x12,x13,TOP IF ID EX ME WB
ld x7, 0(x6) IF ID EX ME WB
4.31.10 Usando o mesmo código da versão 4.31.8, o novo caminho de dados
não oferece nenhuma melhoria de rede, pois não há travamentos
devido a riscos estruturais.

4.32
4.32.1 A energia para os dois projetos é a mesma: I-Mem é ​lido, dois
registradores são lidos e um registrador é escrito. Temos: 140pJ +
2*70pJ + 60j = 340pJ.
4.32.2 A memória de instruções é lida para todas as instruções. Cada instrução
também resulta em duas leituras de registradores (mesmo que apenas um
desses valores seja realmente usado). Uma instrução de carregamento
resulta em uma leitura de memória e uma escrita de registrador; uma
instrução de armazenamento resulta em uma gravação na memória;
todas as outras instruções resultam em, no máximo, uma única gravação
de registro. Como a soma da energia de leitura de memória e de
gravação de registrador é maior que a energia de gravação de memória,
a instrução de pior caso é uma instrução de carregamento. Para a energia
gasta por uma carga, temos:
140pJ + 2*70pJ + 60pJ + 140pJ = 480pJ.
4.32.3 A memória de instruções deve ser lida para cada instrução. No entanto,
podemos evitar a leitura de registradores cujos valores não serão
utilizados. Para fazer isso, devemos adicionar as entradas de controle
RegRead1 e RegRead2 à unidade de Registros para habilitar ou
desabilitar cada leitura de registro. Devemos gerar esses sinais de
controle rapidamente para evitar prolongar o tempo do ciclo de clock.
Com esses novos sinais de controle, uma instrução de carregamento
resulta em apenas um registrador lido (ainda precisamos ler o registrador
usado para gerar o endereço), então nossa alteração economiza
70pJ (um registrador lido) por carga. Esta é uma economia de 70/480 = 14,6%.
4.32.4 jal se beneficiará, porque não precisa ler nenhum registro. As
instruções do tipo I também serão beneficiadas porque precisam ler
apenas um registrador. Se adicionarmos lógica para detectar x0 como
um registrador de origem, então instruções como beqz (ou seja beq
x0, …) e li (addi xn, x0, …) também podem se beneficiar.
4.32.5 Antes da mudança, a unidade de controle decodifica a instrução
enquanto as leituras de registro estão acontecendo. Após a alteração, as
latências de Controle e Leitura de Registro não podem ser sobrepostas.
Isso aumenta a latência do estágio de ID e pode afetar o tempo do ciclo
de clock do processador se o estágio de ID se tornar o estágio de maior
latência. No entanto, a soma das latências do registrador lido (90ps) e da
unidade de controle (150ps) é menor que o tempo de ciclo atual de
250ps.
4.32.6 Se a memória for lida em cada ciclo, o valor é necessário (para uma
instrução de carga) ou não passa pelo WB Mux (para uma instrução sem
carga que grava em um registrador) ou não é gravado em qualquer
registro (todas as outras instruções, incluindo filiais e bancas). Essa
alteração não afeta o tempo de ciclo do relógio porque o tempo de ciclo
do relógio já deve permitir tempo suficiente para que a memória seja
lida no estágio MEM. Isso pode afetar o desempenho geral se as leituras
de memória não utilizadas causarem faltas de cache.
S-24 Capítulo 4 Soluções

A mudança também afeta a energia: Uma leitura de memória ocorre em


cada ciclo em vez de apenas em ciclos quando uma instrução de carga
está no estágio MEM. Isso aumenta o consumo de energia em 140pJ
durante 75% dos ciclos de clock de 250ps. Isso corresponde a um
consumo de aproximadamente 0,46 Watts (sem contar a energia
consumida como resultado de faltas de cache).
4.33
4.33.1 Para testar uma falha preso em 0 em um fio, precisamos de uma
instrução que coloque esse fio no valor 1 e tenha um resultado diferente
se o valor no fio estiver preso em zero.
Se o bit menos significativo da linha do registrador de gravação estiver
preso em zero, uma instrução que grava em um registrador de número
ímpar acabará gravando no registrador de número par. Para testar isso
(1) coloque um valor de 10 em x1, 35 em x2e 45 em x3, então (2)
execute add x3, x1, x1. O valor de x3 deve ser 20. Se o bit 0 da
entrada Write Register para a unidade de registradores estiver travado
em zero, o valor será escrito em x2 que significa que x2 será 40 e x3
permanecerá em 45.

4.33.2 oo teste para travado em zero requer uma instrução que define o sinal
como 1; e o teste para preso em 1 requer uma instrução que define o
sinal como 0. Como o sinal não pode ser 0 e 1 no mesmo ciclo, não
podemos testar o mesmo sinal simultaneamente para preso em 0 e preso
em -1 usando apenas uma instrução.
O teste para preso em 1 é análogo ao teste preso em 0: (1) Coloque um
valor de 10 em x1, 35 em x2e 45 em x3, então (2) execute add x2,
x1, x1. O valor de x2 deve ser 20. Se o bit 0 da entrada Write
Register para a unidade de registradores estiver preso em 1, o valor será
escrito em x3 que significa que x3 será 40 e x2 permanecerá em 35.

4.33.3 oCPU ainda é utilizável. A solução “mais simples” é reescrever o


compilador para que ele use apenas registradores ímpares (não que
escrever compiladores seja especialmente simples). Também
poderíamos escrever um “tradutor” que convertesse código de máquina;
no entanto, isso seria mais complicado porque o tradutor precisaria
detectar quando dois registros em “colisão” são usados ​simultaneamente
e (1) colocar um dos valores em um registro não utilizado ou (2) colocar
esse valor na pilha.

4.33.4 Para testar essa falha, precisamos de uma instrução para a qual
MemRead seja definido como 1, portanto, deve ser ld. A instrução
também precisa ter branch definido como 0, que é o caso de ld.
Finalmente, a instrução precisa ter um resultado diferente MemRead
está incorretamente definido como 0. Para uma carga, definir MemRead
como 0 resulta em não ler a memória. Quando isso acontece, o valor
colocado no registrador é “aleatório” (o que quer que esteja na
saída da unidade de memória). Infelizmente, esse valor “aleatório” pode
ser o mesmo que já está no registro, portanto este teste não é conclusivo.

4.33.5 Only R-type instructions set RegRd to 1. Most R-type instructions would
fail to detect this error because reads are non-destructive—the erroneous
read would simply be ignored. However, suppose we issued this
instruction: add x1, x0, x0. In this case, if MemRead were
incorrectly set to 1, the data memory would attempt to read the value in
memory location 0. In many operating systems, address 0 can only be
accessed by a process running in protected/kernel mode. If this is the
case, then this instruction would cause a segmentation fault in the
presence of this error.

Você também pode gostar