This project implements a RISC-V pipeline simulator with two variants: a No-Forwarding Processor (NoForwardingProcessor) and a Forwarding Processor (ForwardingProcessor). The simulator models a 5-stage pipeline (IF, ID, EX, MEM, WB) and supports a subset of RISC-V instructions, including arithmetic (e.g., ADD, SUB, AND, OR), immediate operations (e.g., ADDI, ANDI, ORI), load/store (LB, SW), branches (BEQ, BNE, BLT, BGE), and jumps (JAL, JALR, AUIPC, LUI). The simulator reads instructions from an input file, simulates the pipeline for a specified number of cycles, and outputs the pipeline execution diagram to a file.
- Register File: It creates an array of 32 registers and initialises all of them to be zero. It defines to funcitons read and write for reading and writing operations.
- ALU Unit: It does appropriate operations based on ALUCtrl signals .
- It initialises all pipeline registers, stall bools, etc. and copies all instructions in a memory vector.
- 5-Stage Pipeline: The simulator implements a classic 5-stage RISC-V pipeline: Instruction Fetch (IF), Instruction Decode (ID), Execute (EX), Memory Access (MEM), and Write-Back (WB). This structure is standard for RISC-V and allows for clear separation of concerns in each stage.
- Pipeline Registers: Each stage is separated by pipeline registers (
IF_ID,ID_EX,EX_MEM,MEM_WB) to hold intermediate data (e.g., PC, instruction, operands, control signals). This ensures proper data propagation between stages. - Control Signals: A
ControlSignalsstruct is used to manage control signals (e.g.,RegWrite,ALUSrc,MemRead,MemWrite,MemToReg,Branch) for each instruction, set during the ID stage and propagated through the pipeline.
- Instruction Types: The simulator supports RISC-V instruction types: R-type (e.g.,
ADD), I-type (e.g.,ADDI,LB,JALR), S-type (e.g.,SW), B-type (e.g.,BEQ), J-type (e.g.,JAL), and U-type (e.g.,AUIPC,LUI). - Decoding Functions: Separate decoding functions (
decodeRType,decodeIType, etc.) are implemented for each instruction type to extract fields likeopcode,rd,rs1,rs2,funct3,funct7,imm, andshamt. This modular approach simplifies handling different instruction formats. - Immediate Sign-Extension: Immediate values are sign-extended appropriately to 32-bit for each instruction type (e.g., 12-bit for I-type, 13-bit for B-type, 21-bit for J-type) to ensure correct arithmetic operations.
- Stage Functions: Each pipeline stage is implemented as a separate method (
fetchStage,decodeStage,executeStage,memoryStage,writeBackStage) in theProcessorbase class for both in different files, withexecuteStageoverridden inNoForwardingProcessorandForwardingProcessorto handle their specific behaviors. - Pipeline Diagram: A 2D vector (
std::vector<std::vector<std::string>> vec) tracks the pipeline stage of each instruction across cycles. TheprintPipelineDiagramfunction outputs the diagram in the formatinstruction;stage0;stage1;stage2;..., where stages areIF,ID,EX,MEM,WB, " " (not yet reached), or"-"(passed or stalled). - Cycle Simulation: The
runSimulationmethod iterates over the specified number of cycles, executing each stage in reverse order (WB → MEM → EX → ID → IF) to ensure proper data propagation.
- No-Forwarding Processor:
- Stalls for Data Hazards: Detects data hazards in the
decodeStageby checking if the destination register (rd) of instructions in EX/MEM or MEM/WB matches the source registers (rs1,rs2) of the current instruction. If a hazard is detected, the pipeline stalls for the required number of cycles (2 for EX/MEM, 1 for MEM/WB). - Stall Implementation: Uses
stall,stallCycles,stallIF, andstallIDflags to manage stalls. During a stall, the IF and ID stages are paused, and NOPs are inserted into the pipeline.
- Stalls for Data Hazards: Detects data hazards in the
- Forwarding Processor:
- Data Forwarding: Implements forwarding to resolve data hazards without stalling in most cases. In the
executeStage, it forwards values from EX/MEM (ex_mem.aluResult) or MEM/WB (mem_wb.memData) to the operands (op1,op2) if the destination register matches the source registers. - Stalls for Load-Use Hazards: Stalls only for load-use hazards (e.g.,
lbfollowed by an instruction using the loaded value). For example, if anlbin EX/MEM writes tord, and the next instruction in ID/EX usesrdasrs1orrs2, a 1-cycle stall is inserted. - Forwarding Logic: Checks for forwarding from EX/MEM and MEM/WB stages, prioritizing the most recent value (EX/MEM over MEM/WB). Special handling is added for load instructions (
opcode == 0x03) and store instructions (opcode == 0x23).
- Data Forwarding: Implements forwarding to resolve data hazards without stalling in most cases. In the
- Branches and Jumps: The
executeStagehandles branches (BEQ,BNE,BLT,BGE) and jumps (JAL,JALR). Branch conditions are evaluated using the ALU result (ex_mem.aluResult), and the branch target (ex_mem.branchTarget) or jump target (ex_mem.jump) is set if the condition is met. - Pipeline Flush: After a branch or jump is taken, the
fetchStageupdates the PC to the target address (ex_mem.branchTargetorex_mem.jump) and clears thejumpandbranchTargetfields. A 1-cycle NOP is inserted into the pipeline (viaid_ex.nop) to account for the branch delay slot. - Branch Prediction: The simulator assumes a simple "not-taken" prediction, meaning it continues fetching the next instruction until the branch is resolved in the EX stage. If the branch is taken, the incorrect instructions are flushed by updating the PC.
- Makefile: A
Makefileis provided to build two executables:noforward(forNoForwardingProcessor) andforward(forForwardingProcessor). It usesg++with C++11 standard and includes debugging flags (-g) and warnings (-Wall).
- RISC-V Specification
- Computer Organization and Design: The Hardware/Software Interface (RISC-V Edition)
- ChatGPT (Grok AI) for debugging assistance