Skip to content

oruns/Projeto-OAC

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

62 Commits
 
 
 
 

Repository files navigation

Universidade Federal de Pernambuco Centro de Informática

Relatório do Projeto RISC-V Pipeline

Anderson Vitor Leoncio de Lima (avll), Iago Lopes da Silva (ils4), Pedro Henrique Santana Silva (phss2), Yago Kauan Mendes Silva (ykms)

Data de entrega: 11/12/25

Sumário:

1 - INTRODUÇÃO

  • 1.1: DA ORGANIZAÇÃO DAS IMPLEMENTAÇÕES

2- Implementação das Instruções:

  • 2.1: R-types
  • 2.2-1: I-types Aritméticas
  • 2.2-2: I-types Load
  • 2.3: S-types
  • 2.4: B-types
  • 2.5: J-types
  • 2.6: Halt

3 - CONCLUSÕES

  • 3.1: Resultados Obtidos
  • 3.2: Análise: Os objetivos foram alcançados?

1- Introdução:

O objetivo deste trabalho consiste na implementação de instruções que não foram incluídas no projeto original do processador RISC-V. Essas instruções são: JAL, JALR, BNE, BLT, BGE, LB, LH, LBU, SB, SH, SLTI, ADDI, SLLI, SRLI, SRAI, SUB, SLT, XOR, OR, HALT.

1.1 - Da Organização Das Implementações de Instrução:

Iremos dissertar, nos próximos tópicos deste relatório, acerca da implementação das instruções dadas. Para isso, agrupamos as instruções mencionadas acima quanto aos seus formatos de instrução (ou seja, R-types, I-types, S-types, B-types, U-types e J- types).

Descreveremos depois a instrução de HALT em um tópico separado.

2- Implementação das Instruções:

2.1- R-types:

As instruções do tipo R implementadas foram: SUB, SLT, XOR e OR, além da já existente ADD. Essas instruções utilizam dois operandos lidos do banco de registradores e executam operações puramente combinacionais na ALU.

Arquivos afetados:

  • ALUController.sv
  • alu.sv

Modificações realizadas:

No módulo ALUController, foram adicionadas condições para gerar os códigos de operação (Operation) corretos com base nos campos Funct3 e Funct7, dentro do caso onde ALUOp == 2'b10 (correspondente às instruções R-type).

Na ALU, cada operação foi mapeada dentro do bloco case(Operation) conforme a tabela abaixo:

Instrução Operation Comportamento (Verilog)
SUB 0110 ALUResult = SrcA - SrcB;
OR 0001 `ALUResult = SrcA
XOR 0011 ALUResult = SrcA ^ SrcB;
SLT 1100 ALUResult = (signed(SrcA) < signed(SrcB)) ? 1 : 0;
  1. – Instruções do tipo I
  2. – Instruções Aritméticas Imediatas

As instruções implementadas nesta categoria foram: ADDI, SLTI, SLLI, SRLI e SRAI. Essas instruções utilizam um imediato de 12 bits gerado pelo módulo imm_Gen.sv e selecionam esse valor através do sinal de controle ALUSrc = 1.

Arquivos afetados:

  • Controller.sv
  • ALUController.sv
  • alu.sv
  • imm_Gen.sv

Implementação:

No módulo Controller, as instruções do tipo I (identificadas pelo opcode 0010011) foram configuradas com os seguintes sinais para permitir a decodificação correta via Funct3 e Funct7:

  • RegWrite = 1
  • ALUSrc = 1
  • ALUOp = 11

O módulo ALUController gera os códigos de operação (Operations) específicos conforme a tabela abaixo:

A ALU implementa as operações conforme o código Operation recebido. Para as instruções de deslocamento (shifts), a execução ocorre com o operando SrcB já configurado para receber o valor do imediato.

  1. – Instruções de Load

As instruções de load implementadas foram: LB, LH e LBU. Embora todas utilizem o mesmo opcode (0000011), a interpretação do dado lido depende do campo Funct3.

Arquivos afetados:

  • datamemory.sv
  • Memoria32Data.sv
  • Controller.sv
  • imm_Gen.sv

Implementação no hardware:

O módulo datamemory.sv decodifica o campo Funct3 para determinar como extrair o dado a partir dos 32 bits recebidos da Memoria32Data. A lógica segue o mapeamento abaixo:

Instrução Funct3 Comportamento na Extração
LB 000 Extrai 8 bits e realiza extensão de sinal (sign-extend).
LH 001 Extrai 16 bits e realiza extensão de sinal (sign-extend).
LBU 100 Extrai 8 bits e realiza extensão com zeros (zero-extend).

Além disso, o campo addr[1:0] é utilizado para determinar qual byte ou halfword específico será entregue dentro da palavra lida.

  1. – Instruções do tipo S

As instruções de store implementadas foram: SB (Store Byte) e SH (Store Halfword).

O armazenamento parcial (byte ou halfword) é implementado através de máscaras no módulo datamemory.sv, que são convertidas em sinais de escrita Wr[3:0] utilizados pela Memoria32Data.

Máscaras implementadas:

  • SB: Ativa apenas 1 bit do vetor Wr.
  • SH: Ativa 2 bits contíguos do vetor Wr, dependendo do alinhamento do endereço.

Além do controle de escrita, o dado a ser armazenado é deslocado para a posição correta dentro da palavra de 32 bits antes de ser enviado à memória.

  1. – Instruções do tipo B

As instruções de desvio condicional implementadas foram: BNE, BLT e BGE, somando-se à instrução já existente BEQ.

Arquivos afetados:

  • ALUController.sv
  • alu.sv
  • BranchUnit.sv

Implementação:

A decisão de desvio é processada pela ALU, que compara os operandos e retorna um resultado lógico (verdadeiro ou falso) com base no código de operação recebido:

Instrução Operation Resultado na ALU
BEQ 1000 SrcA == SrcB
BNE 1001 SrcA != SrcB
BGE 1010 SrcA >= SrcB
BLT 1011 SrcA < SrcB

O resultado dessa comparação é enviado para a BranchUnit, que determina a seleção do próximo PC (PcSel) através da seguinte lógica:

PcSel = (Branch && ALUResult[0]) || Jump;

  • Se PcSel = 1: O PC é atualizado para PC + Imm (desvio tomado).
  • Se PcSel = 0: O PC é atualizado para PC + 4 (fluxo normal).
  • A BranchUnit também é responsável pelo cálculo físico dos endereços de destino (PC + 4 e PC + Imm).
  1. – Instruções do tipo J

As instruções de salto incondicional implementadas foram: JAL e JALR.

Arquivos afetados:

  • Controller.sv
  • Datapath.sv
  • BranchUnit.sv
  • imm_Gen.sv

Implementação do JAL:

  • Utiliza um imediato do tipo UJ, montado corretamente no módulo imm_Gen.sv.
  • O endereço de retorno salvo no registrador de destino (rd) é PC + 4 (com RegWrite = 1).
  • O próximo PC é calculado como PC + Imm.

Implementação do JALR:

  • Utiliza um imediato do tipo I.
  • A BranchUnit seleciona o ALUResult como o próximo endereço do PC sempre que o sinal de controle Jalr = 1 estiver ativo.
  1. – HALT

Esse tópico será dedicado exclusivamente à implementação da instrução de HALT.

  1. Mudanças no módulo “Controller”: Foi adicionado um novo sinal de controle, o de “Halt_com”, ou “Halt Command”. Para implementá-lo, só criamos mais um sinal de output da unidade de Controller, e fizemos que esse sinal de controle recebesse nível lógico '1' exclusivamente caso o Opcode da instrução fosse igual ao da instrução de Halt.

  2. Alterações na branch unit: Passamos o sinal de Halt_com como um dos inputs para a Branch Unit, algo que é facilmente implementado na Branch Unit mas que tem implicações maiores para o Datapath e os registradores de Pipeline, conforme será mostrado em breve. Depois disso, fazemos com que, na seleção de Branch, caso Halt_com tenha nível lógico '1' (ou seja, caso tenhamos uma instrução de Halt), devemos sempre selecionar PC_four - 4. Ou seja, PC + 4 - 4 = PC + 0. Isso se deve ao fato que esse branch trava a execução em um loop infinito na qual somente a instrução de HALT (que não faz nada além desse branch) é executada, não permitindo que o resto do programa execute, conforme queríamos que fosse. A implementação dessa lógica na Branch Unit é mostrada pela imagem abaixo, que já foi mostrada anteriormente neste relatório.

  3. Alterações nos registradores de Pipeline: Aqui, basta modificar os parâmetros do registrador ‘B’ de modo que ele também possa armazenar o sinal de controle “Halt_Com”, algo facilmente implementado no módulo ‘RegPack’.

  4. Alterações no Datapath: Aqui, devemos passar como parâmetro no bloco ‘Always’ no qual modificamos o registrador de pipeline ‘B’ o sinal de controle de Halt_com. Devemos também passá-lo como parâmetro na Branch Unit, e como um dos inputs para o módulo de Datapath.

  5. Alterações no Módulo ‘Risc-V’: Precisamos adicionar Halt_com como uma variável, e colocar este entre os inputs do módulo de ‘Datapath’, além, é claro, de como output para o módulo de ‘Controller’.

    3 – Dos Resultados Obtidos

    A partir da implementação das instruções listadas na Seção 2, foram realizados testes de simulação no ModelSim para verificar o correto funcionamento do processador RISC-V pipelined. As simulações contemplaram:

  • Execução de instruções aritméticas e lógicas (R-types e I-types)
  • Acesso à memória por meio de loads e stores
  • Desvios condicionais (B-types)
  • Instruções de salto (JAL e JALR)
  • Funcionamento do pipeline com forwarding e hazard detection
  • Atualização do PC e propagação de sinais entre estágios

Os resultados observados confirmam que:

  • Instruções R-type (SUB, SLT, XOR, OR)

O ALUController gerou corretamente o código de operação para cada instrução. A ALU produziu os resultados esperados, sendo possível verificar no waveform que os valores lidos do banco de registradores foram combinados de acordo com a operação definida por Funct3 e Funct7, resultando em escrita correta no estágio WB.

  • Instruções I-type aritméticas (ADDI, SLTI, SLLI, SRLI, SRAI)

O imediato foi estendido corretamente pelo módulo imm_Gen.sv, e a ALU executou as operações com precisão. As instruções de deslocamento operaram conforme o valor de shamt, e o sinal RegWrite foi corretamente ativado ao final do pipeline.

  • Instruções de Load (LB, LH, LBU)

As simulações demonstraram que a memória converteu adequadamente os dados lidos, aplicando sign-extend ou zero-extend conforme o tipo da instrução. A indexação por addr[1:0] apresentou comportamento coerente com a extração de bytes e halfwords, validando o mecanismo de acesso parcial à memória.

  • Instruções de Store (SB, SH)

O módulo de memória aplicou corretamente as máscaras de escrita (write strobes), ativando somente os bytes correspondentes. Os dados foram armazenados corretamente nas posições de memória, sem sobrescrever outros bytes adjacentes.

  • Instruções de Desvio (BNE, BLT, BGE)

A ALU comparou corretamente os registradores de origem e produziu valores binários esperados para tomada ou não do desvio. O sinal PcSel foi ativado apenas quando as condições de desvio eram satisfeitas. O cálculo de endereço feito pela BranchUnit mostrou-se consistente com o imediato do tipo B.

  • Instruções de Salto (JAL e JALR)

As simulações de ambos os saltos mostraram atualização correta do PC para PC + Imm, além da escrita de PC + 4 no registrador de destino (rd), validando tanto o fluxo do salto quanto o mecanismo de link. No caso de JALR, foi observada a limpeza do bit menos significativo, conforme especificação do padrão RISC-V.

  • Pipeline: propagação correta pelos estágios IF/ID/EX/MEM/WB

Os registradores de pipeline (A, B, C, D) armazenaram e propagaram instruções e sinais de controle corretamente. Ao longo das simulações, foi possível observar instruções diferentes sendo processadas simultaneamente em estágios distintos, demonstrando a operação paralela típica de um pipeline funcional.

  • Forwarding

Em situações de dependência de dados entre instruções consecutivas, os sinais FAmuxSel e FBmuxSel foram corretamente acionados, permitindo que resultados mais recentes fossem encaminhados para a ALU sem necessidade de stalls. Os valores exibidos no waveform confirmaram que o bypass estava funcionando conforme previsto.

  • Hazard Detection (load-use stall)

Nos casos em que uma instrução dependia de um valor carregado imediatamente antes, o módulo de Hazard Detection aplicou corretamente o stall, mantendo IF/ID constante e inserindo o NOP em ID/EX. Esse comportamento foi confirmado pela ausência de inconsistências nos registros e na execução subsequente.

3.1 – Conclusão

Com base nos testes realizados, é possível concluir que o processador RISC-V desenvolvido cumpre com sucesso as funcionalidades propostas no projeto. Todas as instruções implementadas — R-types, I-types, loads, stores, branches e jumps — apresentaram comportamento coerente com a especificação do conjunto RV32I. A Unidade de Controle, a Unidade de Branch, o Geração de Imediatos e a ALU trabalharam de forma integrada e consistente.

Além disso:

  • O pipeline permitiu execução paralela correta.
  • O forwarding eliminou hazards de dados não relacionados a loads.
  • A detecção de hazards aplicou stalls apenas quando estritamente necessário.
  • A memória apresentou funcionamento adequado tanto para leituras como para escritas parciais.

Dessa forma, o processador produzido mostrou-se estável, funcional e capaz de executar corretamente o subconjunto de instruções esperado, validando o projeto desenvolvido pela equipe.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors