A progressive series of MIPS processor simulators demonstrating the evolution from a simple instruction interpreter to a fully pipelined processor with hazard detection.
This repository contains three versions of MIPS simulators, each building upon the previous one to demonstrate increasingly sophisticated processor architectures:
- HW1: Basic instruction-level simulator with text-based assembly input
- HW2: Single-cycle processor simulator with binary instruction support
- HW3: Pipelined processor with hazard detection and pipeline stalls
This project demonstrates:
- Instruction Set Architecture (ISA): Understanding how instructions are encoded and decoded
- Processor Datapath: How data flows through different stages of execution
- Control Logic: Generation and use of control signals
- Pipelining: Parallel execution of instructions for performance improvement
- Hazard Detection: Identifying and resolving data and control hazards
- Performance Trade-offs: Comparing sequential vs. pipelined execution
Each version builds upon the previous, showing the evolution from a simple interpreter to a modern pipelined processor architecture.
MIPS-simulator/
├── HW1_[Min]/ # Version 1: Basic Simulator
│ ├── CA HW1/
│ │ ├── HW1_[3223763]_[seok_min_Lim].c # Simple instruction simulator
│ │ └── example.txt # Assembly input file
│ ├── HW1_[32213763]_[임석민]_document.pdf
│ └── MIPS_Green_Sheet.pdf
│
├── HW2_[Min]/ # Version 2: Single-Cycle
│ ├── CA HW2/
│ │ ├── 소스.cpp # Single-cycle processor
│ │ ├── input1.bin # Binary test programs
│ │ └── input2.bin
│ └── HW2_32213763_임석민_document.pdf
│
└── HW3_[Min]/ # Version 3: Pipelined
├── 소스.cpp # Pipelined processor with hazard detection
├── input1.bin # Binary test programs
└── input2.bin
Architecture: Sequential instruction execution
Key Features:
- Text-based assembly language input
- 10 general-purpose registers (r0-r9)
- Simple instruction-by-instruction execution
- Custom instruction format with line numbers
- Direct register manipulation
Supported Instructions:
- Arithmetic: ADD, SUB, MUL, DIV
- Data Movement: MOV, LW, SW
- Comparison: SLT
- Control Flow: BEQ, BNE, JMP
- Utility: RST (reset registers)
Input Format: Human-readable assembly (e.g., 1: ADD r0 r1 r2)
Use Case: Learning basic instruction set architecture concepts
Architecture: Single-cycle datapath with five functional stages
Key Features:
- Binary machine code execution (.bin files)
- 32 MIPS registers (full MIPS register file)
- Complete control unit with control signal generation
- Five execution stages executed sequentially per instruction:
- F1_If: Instruction Fetch
- F2_Id: Instruction Decode & Register Read
- F3_Ex: Execute/ALU Operation
- F4_Mem: Memory Access
- F5_Wb: Write Back
- Big-endian memory organization
- Proper ALU operations with multiplexers
- Support for R-type, I-type, and J-type instructions
Additional Instructions:
- Shift operations: SLL, SRL
- Logical operations: AND, OR, NOR
- Jump operations: J, JAL, JR
- Signed/unsigned variants: ADDU, SUBU, SLTU
Input Format: MIPS binary machine code
Performance: CPI = 5 (5 stages per instruction, sequential execution)
Use Case: Understanding single-cycle processor design and control signals
Architecture: 5-stage pipeline with hazard detection and stalling
Key Features:
- True pipelined execution (multiple instructions in flight)
- Pipeline registers between stages:
- IF/ID: Instruction Fetch to Decode
- ID/EX: Decode to Execute
- EX/MEM: Execute to Memory
- MEM/WB: Memory to Write Back
- Data hazard detection and resolution:
- Detects RAW (Read-After-Write) hazards
- Automatic pipeline stalling when hazards detected
- Bubble insertion for control hazards
- Control hazard handling for branches
- Pipeline forwarding logic
- Separate pipeline stage execution per clock cycle
Pipeline Stages:
- F1_If (IF): Instruction fetch from memory
- F2_Id (ID): Decode instruction, read registers, detect hazards
- F3_Ex (EX): Execute ALU operations
- F4_Mem (MEM): Access data memory, handle branches
- F5_Wb (WB): Write results back to registers
Hazard Detection:
- Load-use hazards: Automatic stall insertion
- Branch hazards: 3-cycle stall for proper resolution
- Data dependencies: Detection across pipeline stages
- Register dependency tracking
Input Format: MIPS binary machine code (same as HW2)
Performance:
- Ideal CPI ≈ 1 (with no hazards)
- Actual CPI > 1 (due to pipeline stalls)
- Much faster than HW2 for long instruction sequences
Use Case: Understanding modern pipelined processor design, hazard detection, and performance optimization
| Feature | HW1 | HW2 | HW3 |
|---|---|---|---|
| Execution Model | Sequential | Single-Cycle | Pipelined |
| Input Format | Text Assembly | Binary | Binary |
| Registers | 10 custom | 32 MIPS | 32 MIPS |
| Pipeline Stages | None | 5 (sequential) | 5 (parallel) |
| Hazard Detection | N/A | N/A | Yes |
| CPI | Variable | 5 | ~1-2 |
| Memory Model | Abstract | Big-endian | Big-endian |
| Control Unit | Switch-case | Full control signals | Full + hazard logic |
| Performance | Slowest | Moderate | Fastest |
| Complexity | Low | Medium | High |
| Instructions Supported | 12 basic | 20+ MIPS | 20+ MIPS |
| Branch Handling | Immediate | Immediate | With stalls |
| Language | C | C++ | C++ |
-
HW1 → HW2: Transition from educational simulator to realistic processor
- Added proper MIPS ISA support
- Implemented control unit and datapath
- Binary instruction execution
- Full register file
-
HW2 → HW3: Optimization through pipelining
- Parallel instruction execution
- Pipeline registers for stage separation
- Hazard detection and resolution
- Significant performance improvement
- Closer to real processor design
All three versions are configured as Visual Studio C/C++ projects.
Visual Studio:
- Open
HW1_[Min]/CA HW1.sln - Build the solution (F7)
- Executable:
HW1_[Min]/x64/Debug/CA HW1.exe
Command Line:
gcc -o hw1_simulator "HW1_[Min]/CA HW1/HW1_[3223763]_[seok_min_Lim].c" -lm
./hw1_simulatorVisual Studio:
- Open
HW2_[Min]/CA HW2/CA HW2.vcxproj - Build the solution (F7)
- Executable:
HW2_[Min]/CA HW2/x64/Debug/CA HW2.exe
Command Line:
g++ -o hw2_simulator "HW2_[Min]/CA HW2/소스.cpp" -lm
./hw2_simulatorVisual Studio:
- Open
HW3_[Min]/CA HW3.vcxproj - Build the solution (F7)
- Executable:
HW3_[Min]/x64/Debug/CA HW3.exe
Command Line:
g++ -o hw3_simulator "HW3_[Min]/소스.cpp" -lm
./hw3_simulatorInput: Text file with assembly instructions
- Create or modify
example.txtwith assembly code - Update filename in source code if needed (line 56):
const char* filename = "example.txt";
- Run:
./hw1_simulator
Input Format: Line-numbered assembly instructions
line_number: OPCODE operand1 operand2 operand3
Example (example.txt):
1: LW r0 0x1
2: LW r1 0x2
3: ADD r2 r0 r1
4: SW r2 STDOUT
Input: Binary machine code files (.bin)
- Prepare MIPS binary file (or use provided
input1.bin,input2.bin) - Update filename in source code if needed:
fopen_s(&file, "input2.bin", "rb")
- Run:
./hw2_simulatoror./hw3_simulator
Input Format: MIPS 32-bit instructions in big-endian binary format
Note: HW2 and HW3 use the same input format but produce different execution traces due to pipelining
The HW1 simulator prints each instruction as it executes, along with register value changes:
1: LW r0 0x1
LW r[0]: -1 -> 1
2: LW r1 0x2
LW r[1]: -1 -> 2
3: ADD r2 r0 r1
ADD r[2]: -1 -> 3
...
For branch instructions, it indicates whether the branch was taken:
BEQ # Branch taken
skip BEQ # Branch not taken
HW2 shows detailed execution through each stage with control signals:
0
pc: 0 inst: 3c011001
addiu rs:0 rt:1 imm:4097
data1: 0 data2:1001
ALU Result: 1001
No memory access
reg[1] write data: 1001
Next Pc: 4
=====================================
Each cycle shows:
- Program counter and fetched instruction
- Decoded instruction with operands
- ALU inputs and result
- Memory operations (if any)
- Register write-back
- Next PC value
HW3 displays pipeline stage execution with hazard detection:
Cycle[0]
[IF]
[IM] PC:0X0 -> 0X3C011001
[PC Update] PC <- 0X4 = 0X0+4
[ID]
[EX]
[MEM]
None
[WB]
None
=====================================
Cycle[1]
[IF]
[IM] PC:0X4 -> 0X34210000
[PC Update] PC <- 0X8 = 0X4+4
[ID]
type: I, opcode: 0XF (Addiu), rs: 0X0 (R[0]=0X0), rt: 0X1 (R[1]=0X0), imm: 0X1001
[EX]
[MEM]
None
[WB]
None
=====================================
Cycle[2]
...
[Data Hazard] Stall # Pipeline stall detected
...
Shows:
- Cycle-by-cycle pipeline progress
- Each stage's operations
- Data hazard detection and stalls
- Register updates at write-back stage
struct instruction {
enum opcodes opcode;
int jump; // Target line number
int present; // Current line number
int r[3]; // Register indices
int registers[13]; // Register values (0-9 general, 10-12 temp)
};Execution Flow:
- Read text instruction from file
- Parse instruction with
buffer2Inst() - Execute with
process()using switch-case - Update registers and jump counter
- Move to next instruction or jump target
typedef struct {
// Control signals for datapath
unsigned int RegDst, ALUSrc, MemtoReg;
unsigned int RegWrite, MemRead, MemWrite;
unsigned int Branch, Bcond, ALUOp, Jump;
} ControlSignals;
typedef struct {
// Decoded instruction fields
unsigned int opcode, rs, rt, rd;
unsigned int shamt, funct, imm, addr;
} Operands;Execution Flow (Sequential 5-stage):
- F1_If: Fetch 32-bit instruction from memory
- F2_Id: Decode opcode, extract operands, generate control signals
- F3_Ex: Execute ALU operation, evaluate branches
- F4_Mem: Load/store data in memory
- F5_Wb: Write result to register file, update PC
// Pipeline registers to pass data between stages
typedef struct {
unsigned int in; // Valid bit
unsigned int savePC; // Saved program counter
unsigned int inst; // Instruction
} IF_ID;
typedef struct {
unsigned int in;
ControlSignals* cs;
unsigned int savePC, rt, rd, imm;
unsigned int readData1, readData2;
unsigned int shamt, funct, addr, opcode;
} ID_EX;
typedef struct {
unsigned int in;
ControlSignals* cs;
unsigned int savePC, aluResult, zero;
unsigned int writeData, writeRegister;
unsigned int opcode, jumpAddr;
} EX_MEM;
typedef struct {
unsigned int in;
ControlSignals* cs;
unsigned int readData, aluResult;
unsigned int writeRegister, opcode;
} MEM_WB;Pipelined Execution (Parallel 5-stage):
- F1_If: Fetch instruction, increment PC
- F2_Id: Decode, detect hazards, possibly stall
- F3_Ex: Execute ALU operation
- F4_Mem: Memory access, handle branches
- F5_Wb: Write back to registers
Hazard Detection Logic:
- Compares source registers with destination registers in pipeline
- Inserts stalls (bubbles) when hazards detected
- Prevents incorrect data reads from pipeline registers
- HW2 and HW3 use 4GB memory allocation (may require 64-bit system)
- All versions support big-endian byte ordering for memory operations
- Register 0 ($zero) is hardwired to 0 in HW2 and HW3
- Register 31 ($ra) initialized to 0xFFFFFFFF for return address
- Register 29 ($sp) initialized to 0x1000000 for stack pointer
- All registers initialized to -1
- Immediate values can be hexadecimal (0x prefix) or decimal
- Division by zero not handled
- Line numbers required in input file
- Requires binary machine code input
- Supports sign extension for immediate values
- Proper handling of signed/unsigned operations
- Memory addresses must be word-aligned (4-byte boundaries)
- HW1: Best for learning basic ISA concepts
- HW2: Educational tool for understanding processor stages
- HW3: Demonstrates real-world processor optimizations
- HW3 significantly outperforms HW2 on large programs due to pipelining
This is an academic project for Computer Architecture coursework.
Seok Min Lim (Student ID: 3223763)