# CPU multiciclo e máquina de controlo

Partilhar recursos, seguir estados e sinais e contar ciclos nas instruções executadas.

Página: https://resumos.rgo.pt/cadeiras/fsc/cpu-multiciclo/

O CPU multiciclo usa a ALU para calcular PC+4 no fetch e reutiliza-a mais tarde para calcular o endereço de um load. Divide a instrução em etapas e conserva os resultados intermédios em registos internos, e a memória também pode servir código e dados em ciclos diferentes.

## Recursos e registos internos

Usamos uma memória partilhada para código e dados, uma ALU e os registos IR, MDR, A, B e ALUOut. IR mantém a instrução; MDR mantém o dado lido; A e B mantêm os operandos lidos do banco; ALUOut mantém um resultado anterior da ALU.

Um resultado disponível na saída de uma unidade combinatória não é o mesmo que o valor já guardado num registo. Por exemplo, ao ler memória num ciclo, o writeback seguinte usa MDR, não uma nova leitura potencialmente diferente.

Para saltos relativos, é necessário conhecer o **PC da instrução**. A ISA define o destino como `PCinst+d`, com `PCinst` o endereço da própria instrução de salto. Neste modelo didático o fetch já fez `PC<-PC+4`; somar `d` ao PC avançado daria um destino 4 bytes à frente. Por isso, no decode de beq, o gerador estende `d` e calcula `d-4` para somar ao PC já avançado. Uma organização equivalente guarda `PCinst` num registo e soma-lhe `d`. A compensação `d-4` é uma adaptação deste modelo, não um campo da folha oficial do CPU.

## Seleções e habilitações

| Sinal | Valores |
| --- | --- |
| IorD | 0: endereço PC; 1: endereço ALUOut |
| ALUSrcA | 0: PC; 1: A |
| ALUSrcB | 00: B; 01: constante 4; 10: imediato |
| PCSource | 0: saída atual da ALU; 1: ALUOut |
| MemtoReg | 0: ALUOut; 1: MDR |

O gerador de imediatos fornece o deslocamento usual para lw e sw. No decode de beq, a compensação d−4 permite usar ALUSrcA=0 e ALUSrcB=10 com o PC avançado. Um circuito alternativo que conserve PCinst precisa de o disponibilizar ao somador do destino. Um desenho sem qualquer uma destas opções desloca incorretamente o branch em quatro bytes.

PC atualiza se $PCWrite+(PCWriteCond\cdot Zero)=1$. PCWrite permite escrita incondicional; PCWriteCond permite-a apenas na igualdade. IRWrite só se ativa no fetch, para um load não substituir a instrução pelos dados.

## Datapath partilhado

![Datapath multiciclo: memória partilhada endereçada por PC ou ALUOut; IR, MDR, A, B e ALUOut entre etapas; ALU com entradas PC/A e B/4/imediato; saída para ALUOut e MUX do PC; compensação d menos 4 no decode.](https://resumos.rgo.pt/cadeiras/fsc/cpu-multiciclo/figura-1.svg)

No fetch, IorD=0 endereça com o PC e a ALU soma PC+4 com ALUSrcA=0 e ALUSrcB=01. Na leitura de dados, IorD=1 endereça com ALUOut. A e B conservam os operandos lidos no decode; ALUOut conserva o destino do branch enquanto a ALU compara A com B no estado 8.

Exemplo numérico: beq em `PCinst=0x00400010` com `d=-8`. Após o fetch, PC=`0x00400014`. O decode calcula `(d-4)=-12` e faz `ALUOut<-0x00400014-12=0x00400008`, que é `PCinst+d`. Sem compensação, obteríamos `0x0040000C`, 4 bytes à frente.

## Estados e caminhos

![FSM multiciclo: todos passam por fetch 0 e decode 1. Load segue 2,3,4; store 2,5; R 6,7; beq 8. Todos os finais regressam a fetch.](https://resumos.rgo.pt/cadeiras/fsc/cpu-multiciclo/figura-2.svg)

| Estado | Ação principal | Controlo ativo ou seleção necessária |
| --- | --- | --- |
| 0, fetch | IR←Mem\[PC\], PC←PC+4 | MemRead, IRWrite, IorD=0, ALUSrcA=0, ALUSrcB=01, ALUOp=00, PCWrite, PCSource=0 |
| 1, decode | A←Reg\[rs1\], B←Reg\[rs2\]; preparar destino | ALUSrcA=0, ALUSrcB=10, ALUOp=00; para beq, ALUOut←PC+(d−4) |
| 2, endereço | ALUOut←A+imediato | ALUSrcA=1, ALUSrcB=10, ALUOp=00 |
| 3, ler dados | MDR←Mem\[ALUOut\] | MemRead, IorD=1 |
| 4, writeback lw | Reg\[rd\]←MDR | RegWrite, MemtoReg=1 |
| 5, store | Mem\[ALUOut\]←B | MemWrite, IorD=1 |
| 6, executar R | ALUOut←A op B | ALUSrcA=1, ALUSrcB=00, ALUOp=10 |
| 7, writeback R | Reg\[rd\]←ALUOut | RegWrite, MemtoReg=0 |
| 8, branch | Se A=B, PC←ALUOut | ALUSrcA=1, ALUSrcB=00, ALUOp=01, PCWriteCond, PCSource=1 |

As habilitações não indicadas ficam a 0. Os registos internos sem enable podem capturar em todos os ciclos; o controlo tem de garantir que um valor só é usado enquanto ainda está disponível. No estado 8, PCSource=1 usa o **ALUOut antigo**, que contém o destino, enquanto a ALU calcula a comparação atual.

O decode pode calcular um valor que depois não é usado. Em lw, o estado 2 substitui esse valor pelo endereço efetivo. Não interpretes cada atividade interna como uma alteração de estado arquitetural visível ao programa.

## Exemplo de load

Para `lw x5,12(x8)`, com x8=`0x10010000` e memória no destino contendo 42:

1.  Estado 0: IR recebe o código; PC avança 4.
2.  Estado 1: A recebe x8. A segunda leitura não será usada pelo load.
3.  Estado 2: ALUOut recebe `0x1001000C`.
4.  Estado 3: MDR recebe 42.
5.  Estado 4: x5 recebe 42. Segue-se o fetch da próxima instrução.

Logo lw demora 5 ciclos, sw 4, R 4 e beq 3. O branch demora 3 mesmo quando não é tomado; só a habilitação condicional do PC muda o resultado do último ciclo.

## Contar instruções e ciclos

Começa pelo corpo do ciclo que se repete. Um ciclo com três iterações que executa um lw, um add, um sub de contador e um beq por iteração custa $3(5+4+4+3)=48$ ciclos para essas instruções. Este total ainda não inclui o resto do programa.

Acrescenta depois a inicialização antes do ciclo, os testes finais e quaisquer saltos adicionais conforme o código concreto. Quando um branch salta sobre duas instruções, essas instruções não contam nessa execução, porque não foram executadas.

Uma pseudo-instrução que expanda em duas instruções máquina conta duas vezes, com os ciclos de cada expansão. Na dúvida, escreve a sequência de PCs ou uma tabela com o número de execuções de cada instrução real para não misturar linhas do ficheiro com instruções executadas.

O multiciclo pode ter período menor, pois cada etapa percorre menos módulos em série. O tempo é número de ciclos vezes período. Se o período passar a um terço e o CPI médio passar de 1 para 4, com o mesmo número de instruções, a razão de tempos é $4/3$: demora mais 33,3%. Partilhar recursos reduz o hardware necessário, mas a rapidez depende desta conta completa.

## Exercícios

Estado guardado entre ciclos

Segue um sw no CPU multiciclo e identifica onde ficam o dado e o endereço antes da escrita. Dá a sequência de estados e ciclos.

Pista

Há fetch,decode,endereço,escrita.

Mais uma pista

B e ALUOut conservam valores para o estado 5.

Solução

A sequência é 0,1,2,5, quatro ciclos. No decode B recebe Reg\[rs2\] e A recebe a base. No estado 2, ALUOut guarda A+imediato. No estado 5, IorD=1 seleciona esse endereço e MemWrite=1 escreve B. Não há writeback ao banco.

Confere a tua resposta:

*   Segue os estados 0,1,2,5, num total de quatro ciclos.
*   B guarda o dado; ALUOut guarda o endereço; MemWrite escreve B.

Erros frequentes

Não uses a saída atual da ALU no store se o caminho especifica ALUOut.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/fsc/cpu-multiciclo/#estados-e-caminhos)

O fetch já avançou

O branch estava em 0x00400040 com d=-16, mas PC já vale 0x00400044 no decode. Determina o alvo correto e duas maneiras de o calcular.

Pista

A ISA usa o PC da instrução.

Mais uma pista

Usar PC avançado exige compensar quatro bytes.

Solução

O alvo correto é 0x00400030. Pode somar d ao PCinst=0x00400040 conservado no fetch, ou somar d−4=-20 ao PC avançado 0x00400044. Somar -16 ao PC avançado daria 0x00400034 e saltaria para a instrução errada. O caminho e o gerador do imediato têm de implementar a opção escolhida.

Confere a tua resposta:

*   Alvo 00400030.
*   Conserva PCinst ou soma -20 ao PC avançado.

Erros frequentes

Uma tabela com PC+imm não é suficiente sem definir qual PC está a usar.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/fsc/cpu-multiciclo/#recursos-e-registos-internos)

Salto tomado e instruções não executadas

A execução passa por duas R, um lw, um beq tomado que salta sobre um sw, e outra R. Quantos ciclos gasta neste modelo?

Pista

Conta apenas a sequência efetivamente executada.

Mais uma pista

R=4,lw=5,beq=3; o sw saltado não conta.

Solução

São três R, um lw e um beq: $3\times4+5+3=20$ ciclos. O sw está no programa mas não nesta execução. Todos os fetch/decode já estão incluídos nos custos por instrução.

Erros frequentes

Não somes novamente fetch e decode, porque já estão incluídos nos custos por instrução.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/fsc/cpu-multiciclo/#contar-instru%C3%A7%C3%B5es-e-ciclos)

Instrução preservada durante load

Por que IRWrite não pode continuar ativo no estado de leitura dos dados de um lw?

Pista

A memória é partilhada por código e dados.

Solução

IR mantém o lw para conservar rd e outros campos até ao writeback. Se IRWrite estiver ativo no estado 3, o conteúdo lido como dado substitui o código da instrução e pode mudar o destino de escrita. O dado deve ir para MDR, enquanto IR conserva a instrução.

Confere a tua resposta:

*   IR conserva a instrução até ao writeback.
*   O dado lido vai a MDR, não a IR.

Erros frequentes

Não assumas que o mesmo valor lido pode substituir qualquer registo interno sem mudar o efeito.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/fsc/cpu-multiciclo/#sele%C3%A7%C3%B5es-e-habilita%C3%A7%C3%B5es)

A última comparação também custa

Num CPU multiciclo já alargado para suportar addi e jal, usa addi=4, lw=5, add=4, beq=3, jal=3 ciclos. t1 começa em 2. Conta só desde `teste` até chegar a `fim`, excluindo qualquer instrução em fim:

```
teste:
    beq t1, zero, fim
    lw t3, 0(t0)
    add t2, t2, t3
    addi t0, t0, 4
    addi t1, t1, -1
    jal zero, teste
fim:
```

Quantas vezes é executada cada instrução e quantos ciclos são necessários?

Pista

O corpo corre duas vezes, mas o beq corre uma terceira vez para sair do ciclo.

Mais uma pista

jal zero é aqui uma instrução real com custo explicitamente dado.

Solução

beq corre 3 vezes: duas vezes falso e uma vez verdadeiro. lw, add, as duas addi e jal correm 2 vezes cada. O total é $3\times3+2\times(5+4+4+4+3)=49$ ciclos. A sequência tem 13 instruções executadas, contando separadamente as duas addi. Os custos addi/jal são hipóteses do CPU alargado deste exercício; o CPU da lição só especifica R,lw,sw,beq.

Confere a tua resposta:

*   beq três vezes; cada outra instrução duas vezes.
*   Total 49 ciclos; custos de addi e jal são hipóteses deste exercício.

Erros frequentes

Não contes só dois beq nem atribuas automaticamente os custos deste CPU alargado a qualquer implementação RISC-V.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/fsc/cpu-multiciclo/#contar-instru%C3%A7%C3%B5es-e-ciclos)
