Descrever o funcionamento do pipeline e os tipos de problemas de execução que podem ocorrer;
Projetar a unidade de encaminhamento (forwarding) para resolver esses problemas.
Até agora, temos o MIPS com pipeline conforme a figura abaixo. Ou seja, com as etapas divididas, conforme a lista abaixo, e os sinais de controle das instruções acompanhando cada instrução através do pipline.
Etapas funcionais do MIPS com pipeline:
Instruction Fetch (IF): busca da próxima instrução, na memória de programa, a ser executada;
Instruction Decode (ID): decodifica a instrução (UC) e faz a leitura dos registradores utilizados pela instrução;
Execute (EX): executa a operação definida pela instrução. É sempre feita na ULA;
Memory Acess (MEM): lê a memória ou escreve o resultado da execução na memória RAM;
Write Back (WB): escreve o resultado da execução no banco de registradores.
Como temos, potencialmente, 5 instruções executadas ao mesmo tempo, pode ocorrer que alguma dessas instruções não possa ser executada no mesmo clock que as outras.
Esse problema é chamado de pipeline hazard e existem três tipos:
Estruturais:
Dados:
Controle:
O conjunto de instruções do MIPS foi pensado para executar em um pipeline, como pode ser visto através de algumas de suas características:
Todas as instruções possuem o mesmo comprimento, ou seja, uma palavra de 32 bits:
Possui uma quantidade limitada de formatos (R, I, J) para as instruções e mantém o endereço dos registradores e opcodes na mesma posição, para todas instruções.
O acesso à memória RAM só é feito por duas instruções e após o cálculo do endereço.
Alinhamento dos dados na memória:
O cuidado no projeto do conjunto de instruções permitiu evitar problemas estruturais durante a execução do pipeline. Foram evitadas duas situações:
Se o MIPS tivesse uma só memória, para instruções e dados juntos, haveria conflito entre a busca de instruções e a escrita/leitura da memória. Por isso, o MIPS separa as memórias para os dados da memória das instruções.
O banco de registradores foi modificado para permitir a leitura e escrita de dados simultaneamente. Por isso ele pode ser acessado no segundo e quinto estágios do pipeline.
Envolve o retorno do endereço, calculado pela instrução beq e jump, para atualizar program counter. Trateremos dele na próxima aula.
As dependências entre os dados, em um fluxo de execução no pipeline, são divididas em quatro tipos:
No caso da dependência do tiplo RAW, a execução de uma instrução depende do resultado de outra instrução que ainda não finalizou sua execução.
Por exemplo, no código abaixo temos a dependência RAW no registrador $s0:
Que pode ser vista no diagrama de execução do pipeline:
O valor de $s0 ainda não terá seu valor calculado pelo add quando a instrução sub necessitar dele.
Para que o programa obtenha os valores corretos, teríamos que parar ou atrasar a instrução sub:
Chamamos esse sistema de parar a execução do pipeline, por alguns ciclos, de criar bolhas na execução.
Como o banco de registradores pode ser escrito e lido no mesmo ciclo de clock, podemos economizar um ciclo de clock.
Essa solução, chamada de stall, desperdiça tempo de processamento.
Outra opção, seria fazer uma análise durante a compilação do programa:
Procurando por possíveis problemas de dependência de dados;
E tentar trocar algumas instruções de lugar para resolver a dependência.
Caso essa solução não seja possível, o compilador deve adicionar instruções nop (não executam nenhuma operação) para criar as bolhas no pipeline:
No caso de uma soma acumulativa, como a mostrada abaixo, existe algum problema?
Temos um problema entre:
O resultado da instrução no estágio WB;
O resultado da instrução no estágio MEM;
O operando da instrução na no estágio EX.
O caso da instrução lw, que é a mais longa e ocupa todas as etapas do pipeline, se houver uma carga seguida de uma instrução do tipo R, como mostrado abaixo, em qual clock a instrução sub pode iniciar a sua execução? Por que?
Para resolver, é necessário a inserção de duas instruções nop, como mostrado abaixo:
Para todos os casos mostrados, será que não existe solução melhor do que inserir nops?
Essa técnica ajuda a mitigar o desperdício de tempo na execução de instruções dependentes.
Ela se baseia no fato de que, para obter dado desejado, não é necessário esperar a instrução atravessar todo o pipeline.
A partir do momento em que o dado foi computado, ele está na saída da ULA e a sua transferência pode ser feita através do próprio pipeline, como se existisse um atalho.
Em resumo, é a criação de um desvio interno ao pipeline.
Porém, como e em quais situações esse desvio deve ser ativado?
Para facilitar o entendimento, vamos padronizar a nomenclatura dos sinais. Iremos utilizar a seguinte notação:
Na figura abaixo, está o exemplo dos sinais Rs, Rt e Rd, logo após o registrador de borda entre a etapa ID e a etapa EX.

Agora, precisamos definir em quais estágios do pipeline podem existir dependências. Sabemos que, no caso do MIPS, a produção de dados é feita através do resultado de alguma operação ou do carregamento de um dado da memória.
Dessa forma, a fonte de novos dados está limitada a duas etapas do pipeline:
Na saída da ULA, na etapa EX.
Na leitura da memória de dados, na etapa MEM.
Por outro lado, o dado é consumido somente na entrada da ULA.
Como é necessário isolar o consumo da produção, a solução deverá ser parecida com o esquema abaixo:
Como o dado produzido sempre será armazenado em Rd e os dados consumidos sempre serão provenientes de Rs e Rt, podemos verificar a existência do hazard comparando-se os endereços de Rs e Rt:
Com o endereço de Rd, nas etapas seguintes:
Rd na etapa MEM;
Rd na etapa WB.
Portanto, no caso de igualdade nos endereços, podemos interligar (usando um MUX) os dados desses pontos:
Desde que tenhamos um registrador entre eles;
Impedindo que o circuito oscile.
Como a entrada da ULA pode vir do circuito de extensão de sinal, precisaremos de outro MUX. Ele será controlado pelo sinal ALUSrc.
Os sinais envolvidos no controle do forward são os seguintes:
RegWrite
EX/MEM.RegisterRd
ID/EX.RegisterRs
ID/EX.RegisterRt
MEM/WB.RegisterRd
Usaremos a seguinte padronização para a seleção dos MUXES do forward:
| Seleção do Mux | Fonte | Explicação |
|---|---|---|
| ForwardA=00 | ID/EX | Operando A (ULA) ← Banco Registradores. |
| ForwardA=10 | EX/MEM | Operando A (ULA) ← ALUresult ciclo anterior. |
| ForwardA=01 | MEM/WB | Operando A (ULA) ← Memória ou ALUresult (dois ciclos antes). |
| ForwardB=00 | ID/EX | Operando B (ULA) ← Banco Registradores. |
| ForwardB=10 | EX/MEM | Operando B (ULA) ← ALUresult ciclo anterior. |
| ForwardB=01 | MEM/WB | Operando B (ULA) ← Memória ou ALUresult (dois ciclos antes). |
Responder o quiz de participação, no blackboard, em:
Conteúdos > Participação > Aula_19_Quiz-P1
Quais as condições gerais que esses sinais devem ter para que o forward seja ativado?
Solução:
Condições gerais:
Ser escrita no registrador Rd:
Uma das igualdades de endereços ser verdadeira:
EX/MEM.RegisterRd = ID/EX.RegisterRs;
EX/MEM.RegisterRd = ID/EX.RegisterRt;
MEM/WB.RegisterRd = ID/EX.RegisterRs;
MEM/WB.RegisterRd = ID/EX.RegisterRt.
O endereço de Rd:
Utilizando os sinais de seleção definidos anteriormente, como ficariam as equações de ativação dos pontos do forwardA e forwardB no caso de hazard em EX/MEM?
Solução:
Equações para hazard em EX/MEM:
# A instrução em MEM vai escrever em RD e RD não é ZERO
if (EX/MEM.RegWrite and (EX/MEM.RegisterRd ≠ 0)
# E o Hazard acontece com RS
and (EX/MEM.RegisterRd = ID/EX.RegisterRs)) then ForwardA = 10 # A instrução em MEM vai escrever em RD e RD não é ZERO
if (EX/MEM.RegWrite and (EX/MEM.RegisterRd ≠ 0)
# E o Hazard acontece com RT
and (EX/MEM.RegisterRd = ID/EX.RegisterRt)) then ForwardB = 10Como ficariam as equações de ativação dos pontos do forwardA e forwardB no caso de hazard em MEM/WB?
No caso de uma soma acumulativa, como a abaixo, essas equações funcionam?
Solução:
Como temos um problema entre:
O resultado da instrução no estágio WB;
O resultado da instrução no estágio MEM;
O operando da instrução na no estágio EX.
Deve ser utilizado o valor da etapa MEM, por ser o mais atual.
Se o MEM/WB também estiver em conflito, optamos pelo EX/MEM.
Como ficam os sinais de controle, forwardA e forwardB, no caso da soma acumulativa?
Solução:
Esta comparação deverá substituir a comparação do caso de hazard em MEM/WB feita anteriormente.
Conflito com Rs:
# A instrução em WB vai escrever em RD e RD não é ZERO
if (MEM/WB.RegWrite and (MEM/WB.RegisterRd ≠ 0)
# A instrução em MEM não tem Hazard
and not ( EX/MEM.RegWrite and (EX/MEM.RegisterRd ≠ 0) and (EX/MEM.RegisterRd = ID/EX.RegisterRs) )
# A instrução em WB tem Hazard
and (MEM/WB.RegisterRd = ID/EX.RegisterRs))
then
# Escolhe o Forward de WB
ForwardA = 01
# Caso tenha Hazard nos dois, entra na comparação do EX/MEM, feita anteriormente.Conflito com Rt:
# A instrução em WB vai escrever em RD e RD não é ZERO
if (MEM/WB.RegWrite and (MEM/WB.RegisterRd ≠ 0)
# A instrução em MEM não tem Hazard
and not ( EX/MEM.RegWrite and (EX/MEM.RegisterRd ≠ 0) and (EX/MEM.RegisterRd = ID/EX.RegisterRt) )
# A instrução em WB tem Hazard
and (MEM/WB.RegisterRd = ID/EX.RegisterRt))
then
# Escolhe o Forward de WB
ForwardB = 01
# Caso tenha Hazard nos dois, entra na comparação do EX/MEM, feita anteriormente.O circuito para resolver os problemas de forwarding.
Solução:
Neste circuito é necessário adicionar o MUX referente à entrada do imediato com sinal estendido, como visto anteriormente.
Considere o trecho de código abaixo, onde todas instruções depois do lw dependem do valor de $2 (resultado do lw).
Nesse caso, a carga, da instrução lw $2, 20($1), só estará disponível durante o quinto ciclo de clock, na saída do registrador MEM/WB.
Porém, ela é necessária no terceiro estágio da instrução seguinte, o and $4, $2, $5, que ocorre no quarto ciclo de clock.
Como não é possível retornar o valor que só estará disponível no quinto clock para o quarto clock, já que ele ainda não foi calculado durante o quarto clock, teremos que adicionar uma parada no pipeline.
Como só temos essa situação no caso do lw seguido por uma instrução do tipo R, qual seria a condição para fazer a parada o pipeline?
Solução:
A condição seria:
Leitura da memória;
E o destino da leitura é o mesmo registrador do estágio anterior.
Como seria a equação para essa situação?
Solução:
A bolha deve ser criada na etapa ID, para evitar a leitura do registrador com Hazard. Portanto, a comparação será entre as etapas ID e EX.
A equação ficaria:
E como criar a bolha?
Solução:
Paramos o pipeline da seguinte forma:
Congelando o valor no PC:
Congelando o valor no Instruction Register (IF/ID):
E desativando os pontos de controle:
O circuito para resolver os problemas de forwarding e stall.
Solução:
Neste circuito é necessário adicionar o MUX referente à entrada do imediato com sinal estendido, como visto anteriormente.
De posse das equações, ou circuito, deve ser feita:
A implementação em VHDL;
O teste de funcionamento;
A integração com o Fluxo de Dados e seu teste completo: