CPU uniciclo: datapath e controlo
Seguir R, lw, sw e beq, escolher sinais de controlo e diagnosticar falhas.
lw x5,12(x8) exige calcular um endereço, ler memória e escrever x5, enquanto sw x5,12(x8) calcula o mesmo endereço, mas escreve memória. O datapath contém os caminhos dos dados e o controlo escolhe as fontes e habilita a escrita adequada; o CPU didático de 32 bits desta lição suporta add, sub, and, or, lw, sw e beq, um subconjunto das instruções usadas nos programas RARS.
Caminho dos dados
O PC endereça a memória de instruções. Os campos rs1 e rs2 escolhem duas leituras do banco de registos, enquanto rd escolhe uma possível escrita; o gerador de imediatos reconstrói e estende o imediato segundo o formato da instrução.
A ALU recebe sempre o primeiro operando do banco. O segundo vem de um MUX: registo rs2 para operações R e comparação, ou imediato para calcular endereços de memória. O resultado da ALU pode ir diretamente para escrita num registo ou servir de endereço da memória de dados.
Uma soma separada calcula PC+4 e outra calcula PC+d para o branch. PCSrc=Branch AND Zero escolhe o destino de branch quando a instrução é beq e a comparação indica igualdade; o d já está em bytes, com o bit 0 implícito reconstruído, por isso não o multipliques novamente por 4.
Sinais de controlo
| Classe | RegWrite | ALUSrc | MemRead | MemWrite | MemtoReg | Branch | ALUOp |
|---|---|---|---|---|---|---|---|
| R | 1 | 0 | 0 | 0 | 0 | 0 | 10 |
| lw | 1 | 1 | 1 | 0 | 1 | 0 | 00 |
| sw | 0 | 1 | 0 | 1 | X | 0 | 00 |
| beq | 0 | 0 | 0 | 0 | X | 1 | 01 |
Um sinal de controlo é um bit, ou um pequeno código, produzido a partir da instrução. ALUSrc=0 escolhe rs2; ALUSrc=1 escolhe o imediato. MemRead habilita a leitura de dados. MemtoReg escolhe o dado da memória se 1 e o resultado da ALU se 0. RegWrite e MemWrite autorizam alterações de estado: têm de estar a 0 quando a instrução não escreve nesse destino.
Branch=1 identifica beq; PCSrc=Branch AND Zero decide se o salto é tomado.
ALUOp=00 pede soma; 01 pede subtração para a comparação; 10 usa f3 e f7 da instrução R. Neste modelo, ALUControl é 0000 para AND, 0001 para OR, 0010 para ADD e 0110 para SUB. ALUOp e ALUControl são sinais diferentes.
O campo que ocuparia rs2 numa instrução I contém bits do imediato. O banco pode lê-los fisicamente, mas lw não usa essa segunda leitura. Por isso uma falha nesse porto pode afetar sw, R e beq sem afetar lw.
Tomado e não tomado
Segue beq x5,x6,-8 em PC=0x00400010. A ALU subtrai x6 a x5 e produz Zero; se x5=x6, PCSrc=1 e o PC seguinte é 0x00400008, enquanto se diferirem, PCSrc=0 e segue 0x00400014. Um teste só com branches falsos nunca exercita o caminho de salto.
Seguir um load completo
Suponhamos lw x5,12(x8) em PC=0x00400020, x8=0x10010000, e memória em 0x1001000C contendo 42. O fetch lê a instrução. O banco entrega x8 e o gerador entrega 12. Com ALUSrc=1, a ALU soma e produz 0x1001000C.
Com MemRead=1, a memória devolve 42. Com MemtoReg=1, os 42 chegam à entrada de escrita; com RegWrite=1, o flanco guarda-os em x5. Com Branch=0, o PC seguinte é 0x00400024. O registo recebe 42, não o endereço calculado.
Num sw x5,12(x8), a mesma soma calcula o endereço, mas o dado de escrita vem da segunda leitura do banco, x5. RegWrite=0 evita alterar registos; MemWrite=1 altera memória.
No branch, a ALU usada para comparar não calcula o destino: esse caminho usa o somador do PC. Os dois resultados precisam de estar disponíveis no mesmo ciclo.
Falhas e extensões
Se MemtoReg ficar preso em 0, um load escreve o endereço em vez do conteúdo. Para o detetar, escolhe um teste em que endereço e conteúdo diferem: com o mesmo valor nos dois lados, a falha seria invisível. Se PCSrc ficar sempre em 0, só falham os branches que deveriam ser tomados. Um teste só com branches falsos nunca exercita o caminho de salto.
Se RegWrite ficar em 0, operações R e loads deixam de escrever. Stores e branches ainda podem funcionar. Se o bit alto de ALUOp ficar em 0, instruções R passam a pedir ADD: add ainda pode estar correto, mas sub, and e or precisam de testes que produzam resultados diferentes da soma.
addi pode reutilizar o datapath: ALUSrc=1, ALUOp=00, MemtoReg=0, RegWrite=1. jalr exige mais: escrever PC+4, escolher um destino calculado pela ALU e limpar o seu bit 0. Não chega mudar uma linha da tabela se o caminho físico ainda não existe.
Acrescentar jalr
jalr rd,d(rs1) faz duas escritas no mesmo flanco: coloca PC+4 em rd e coloca o destino calculado a partir de rs1 no PC. A ALU existente pode somar rs1 ao imediato I estendido com sinal. Falta encaminhar os dois resultados para os destinos corretos.
Uma opção conserva o MUX MemtoReg e acrescenta dois MUX de 32 bits:
- Na entrada de escrita do banco, um novo MUX escolhe a saída de MemtoReg quando
Link=0, ou PC+4 quandoLink=1. - Na entrada do PC, outro MUX escolhe a saída do MUX PCSrc habitual quando
JumpReg=0, ou o resultado da ALU com bit 0 limpo quandoJumpReg=1.
Limpar o bit 0 não precisa de uma segunda operação da ALU: liga o bit inferior dessa entrada do MUX a 0 e os outros 31 à saída da ALU. O somador PC+4 que já existe fornece o retorno enquanto a ALU calcula o alvo.
| Controlo em jalr | Valor | Efeito |
|---|---|---|
| ALUSrc, ALUOp | 1, 00 | Soma rs1 ao imediato I |
| RegWrite, Link | 1, 1 | Escreve PC+4 em rd |
| JumpReg | 1 | Encaminha o alvo da ALU para PC |
| MemRead, MemWrite, Branch | 0, 0, 0 | Não lê nem escreve dados, nem testa beq |
| MemtoReg | X | A sua saída é ignorada por Link=1 |
Nesta organização, Link=JumpReg=0 nas instruções anteriores conserva os seus caminhos. Em jalr, PCSrc também é irrelevante para o PC, porque JumpReg escolhe a outra entrada. O descodificador deve reconhecer opcode 1100111 e funct3 000, e ImmGen deve produzir o imediato I. Estes nomes de sinais são uma escolha deste circuito, não campos da ISA.
Para jalr ra,0(ra) em PC=0x00400020, com ra antigo=0x00400081, a ALU produz 0x00400081. O novo MUX do PC recebe 0x00400080; o novo MUX do banco recebe 0x00400024. As leituras usam o estado antigo, por isso escrever ra no mesmo flanco não altera a base usada para o salto.
Sem instruções comprimidas, limpar só o bit 0 não garante alinhamento a 4: um alvo terminado em 10 binário continua desalinhado. Neste modelo didático consideramos destinos válidos; implementar exceções exige também a lógica de deteção e tratamento.
Um ciclo e o seu custo
Todas as instruções terminam num período. O relógio tem de acomodar a instrução com maior atraso, normalmente o load neste modelo: leitura de instrução, banco, ALU, memória de dados e setup de escrita. Uma operação curta não termina num período mais curto num relógio fixo.
Com atrasos de 2, 1, 2 e 3 ns nestes módulos, mais 1 ns para o destino, o caminho do load demora 9 ns. Soma só os módulos percorridos em série e inclui os MUX relevantes se o enunciado lhes atribuir atraso. CPI=1 não implica tempo mínimo de execução, porque o período pode ser longo.
Exercícios
Controlo de um store
Para sw x7,-8(x8), dá RegWrite,ALUSrc,MemRead,MemWrite,Branch e a operação da ALU. Explica de onde vem o dado escrito.
Pista
O store calcula endereço e altera só memória.
Mais uma pista
rs2 fornece o dado; o imediato fornece o offset.
Solução
RegWrite=0, ALUSrc=1, MemRead=0, MemWrite=1, Branch=0, ALUOp=00 para soma. Endereço=x8−8 e dado=x7 pela segunda leitura do banco. MemtoReg é X porque não há escrita em registo. A ALU calcula o endereço, não o conteúdo a escrever.
Confere a tua resposta:
- RegWrite0, ALUSrc1, MemRead0, MemWrite1, Branch0 e ADD.
- O dado vem de x7, o endereço de x8−8.
Erros frequentes
Não ligues a entrada de dados da memória ao imediato.
Segunda leitura presa em zero
A segunda saída do banco de registos está sempre em zero. Que classes R,lw,sw,beq podem ser afetadas? Escolhe um teste para cada classe que deve falhar.
Pista
Verifica quem usa a segunda leitura.
Mais uma pista
O load usa base e imediato, sem esse valor.
Solução
lw continua a usar rs1+imediato. R pode falhar: add com rs2=3 perde esse 3. sw pode falhar: guardar x7=9 escreve 0. beq pode falhar: duas fontes iguais a 5 passam a comparar 5 com 0, deixando de saltar. Casos em que a segunda fonte já era zero podem esconder a falha.
Confere a tua resposta:
- lw não usa a segunda leitura.
- Dá valores que evidenciem erros em R, sw e beq.
Erros frequentes
Não concluas que toda execução de uma classe falha; depende dos valores do teste.
Dado ou endereço
MemtoReg fica preso em zero. Executamos lw em endereço 0x10010020 contendo 0x10010020. Esse teste deteta a falha? Propõe um que a detete.
Pista
O resultado correto e o errado podem coincidir, por isso escolhe conteúdos que distingam dado e endereço.
Solução
O teste não deteta: a ALU produz o endereço e a memória contém exatamente esse mesmo valor. Escolhe, por exemplo, conteúdo 42 no endereço 0x10010020. O correto é escrever 42; o defeituoso escreve 0x10010020. As habilitações e o acesso têm de ser válidos para isolar a falha.
Confere a tua resposta:
- O primeiro teste esconde a falha.
- Conteúdo 42 distingue dado e endereço.
Erros frequentes
Uma execução sem diferença observável não prova que o MUX funciona.
Uma instrução e o caminho necessário
O CPU uniciclo suporta R,lw,sw,beq. Para acrescentar addi, são necessários novos recursos de datapath? Indica o controlo.
Depois projeta a extensão para jalr. Conserva o MUX MemtoReg e acrescenta um MUX de writeback controlado por Link, com 0 para o caminho antigo e 1 para PC+4. Acrescenta outro MUX antes de PC, controlado por JumpReg, com 0 para o caminho antigo e 1 para o alvo da ALU. Indica as ligações, o tratamento do bit 0, todos os controlos necessários em jalr e como conservas as quatro classes anteriores.
Segue jalr ra,0(ra) em PC=0x00400020, com ra antigo=0x00400081. Supõe destinos alinhados a 4 depois de limpar o bit 0 e não acrescentes tratamento de exceções.
Pista
addi combina o imediato do load com o writeback das operações R. jalr precisa de dois valores diferentes no mesmo flanco.
Solução
addi usa RegWrite=1, ALUSrc=1, ALUOp=00, MemtoReg=0, MemRead=MemWrite=Branch=0 e Link=JumpReg=0. Precisa de nova descodificação, mas não de novo caminho de dados.
Em jalr, a ALU soma rs1 antigo ao imediato I estendido com sinal: ALUSrc=1 e ALUOp=00. Liga PC+4 à entrada 1 do novo MUX de writeback e escolhe Link=1, RegWrite=1. Liga a saída da ALU à entrada 1 do novo MUX de PC, com o bit 0 dessa entrada fixo a 0; escolhe JumpReg=1. MemRead=MemWrite=Branch=0. MemtoReg e o PCSrc antigo são X porque os novos MUX ignoram as suas saídas. ImmGen deve fornecer I e o controlo reconhecer opcode 1100111, funct3 000.
Nas quatro classes antigas, Link=JumpReg=0 deixa MemtoReg e PCSrc funcionar como antes. Não precisamos de outra ALU: o somador PC+4 produz o retorno em paralelo com o cálculo do alvo.
No exemplo, a ALU usa ra antigo e produz 0x00400081; PC recebe 0x00400080 e ra recebe 0x00400024. Os dois destinos capturam no mesmo flanco. Limpar bit 0 não garante por si só o alinhamento a 4; essa validade é uma hipótese do enunciado.
Confere a tua resposta:
- addi reutiliza o datapath com imediato e writeback ALU.
- jalr recebe caminhos PC+4→rd e ALU com bit 0 limpo→PC.
- Defini os novos MUX, os controlos e a compatibilidade com R, lw, sw e beq.
- O exemplo usa ra antigo e produz PC=00400080, ra=00400024.
Erros frequentes
Encaminhar a ALU para o PC sem acrescentar PC+4 ao writeback deixa o salto sem o retorno correto. Em rd=rs1, a fonte continua a ser o valor antigo.
Tomado e não tomado
No CPU uniciclo, beq x5,x6,-8 está em PC=0x00400030. Analisa duas execuções: x5=x6=7; x5=7 e x6=9. Em cada uma, dá Branch, Zero, PCSrc e o PC seguinte. Se PCSrc estivesse preso em 0, qual dos testes detetaria a falha?
Pista
Calcula PCSrc=Branch AND Zero nos dois casos.
Mais uma pista
Com Branch=1: Zero=1 dá PC+d; Zero=0 dá PC+4.
Solução
Branch=1 nos dois casos, porque a instrução é beq. Com as fontes iguais, a subtração dá zero, Zero=1 e PCSrc=1: PC seguinte=0x00400028. Com 7 e 9, a subtração dá -2, Zero=0 e PCSrc=0: PC seguinte=0x00400034. Se PCSrc ficar preso em 0, só o primeiro teste deteta a falha, pois o CPU avançaria para 0x00400034 em vez de saltar. O segundo já devia avançar para esse endereço.
Confere a tua resposta:
- Distinguistes Branch de Zero nos dois casos
- Obtiveste os PCs 0x00400028 e 0x00400034
- Identificaste o teste de igualdade como detetor da falha
Erros frequentes
Branch=1 em beq, não 0.