A 32-bit 5-Stage Pipelined Processor in VHDL
Complete with Hazard Detection, Data Forwarding, Interrupts, and a Custom Assembler
PipeLord is a fully-functional 32-bit pipelined RISC processor implemented in VHDL. It features a classic 5-stage pipeline architecture with comprehensive hazard handling, interrupt support, and a Python-based assembler for program development.
| Feature | Description |
|---|---|
| 5-Stage Pipeline | IF → ID → EX → MEM → WB stages for maximum throughput |
| Von Neumann Architecture | Unified memory with split-cycle access for instruction fetch and data operations |
| 8 General-Purpose Registers | R0–R7, each 32-bits wide |
| 32-bit Immediate Support | Two-word instructions with full 32-bit immediate values |
| Data Forwarding | EX→EX and MEM→EX forwarding to minimize stalls |
| Hazard Detection | Automatic stalling for load-use hazards |
| Hardware & Software Interrupts | INT instruction + external interrupt line with shadow flag register |
| Stack Operations | Hardware stack pointer with PUSH/POP support (initialized at 0x3FFFF, the highest address in the 18-bit 256K-word space, and growing downward) |
| Custom Assembler | Python tool to convert assembly to machine code |
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ FETCH │──▶│ DECODE │──▶│ EXECUTE │──▶│ MEMORY │──▶│WRITEBACK│
│ (IF) │ │ (ID) │ │ (EX) │ │ (MEM) │ │ (WB) │
└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
│ │ │ │ │
IF/ID ID/EX EX/MEM MEM/WB RegFile
Reg Reg Reg Reg Write
| Stage | Key Components | Responsibilities |
|---|---|---|
| Fetch (IF) | PC Register, HLT Module | Fetches instruction from memory, manages PC (halt, jump, interrupt) |
| Decode (ID) | Control Unit, Register File | Decodes opcode, reads registers, generates all control signals |
| Execute (EX) | ALU, ALU Controller, Muxes | Performs arithmetic/logic operations, branch evaluation, address calculation |
| Memory (MEM) | Memory Interface, SP Module | Accesses data memory for LDD/STD/PUSH/POP, manages stack pointer |
| Writeback (WB) | Writeback Mux, Swap FSM | Writes results back to register file, handles 2-cycle SWAP instruction |
Each pipeline register can be stalled (hold value) or flushed (insert NOP bubble) for hazard handling:
| Register | Width | Key Fields |
|---|---|---|
IF_ID_Reg |
~64b | Instruction, PC+1 |
ID_EX_Reg |
~180b | Operands, control signals, immediate, register addresses |
EX_MEM_Reg |
~120b | ALU result, write data, control signals |
MEM_WB_Reg |
~80b | Memory data, ALU result, destination register |
PipeLord uses a custom 32-bit instruction format with 5-bit opcode. Instructions requiring immediate values or offsets use two consecutive 32-bit words.
Single-Word Instructions:
┌───────────┬───────┬───────┬───────┬────────────────────────┐
│ Opcode │ Rd │ Rs1 │ Rs2 │ (unused) │
│ [31:27] │[26:24]│[23:21]│[20:18]│ [17:0] │
└───────────┴───────┴───────┴───────┴────────────────────────┘
Two-Word Instructions (with 32-bit Immediate):
Word 1:
┌───────────┬───────┬───────┬───────┬────────────────────────┐
│ Opcode │ Rd │ Rs1 │ Rs2 │ (unused) │
└───────────┴───────┴───────┴───────┴────────────────────────┘
Word 2:
┌────────────────────────────────────────────────────────────┐
│ 32-bit Immediate / Offset │
└────────────────────────────────────────────────────────────┘
| Mnemonic | Opcode | Format | Description |
|---|---|---|---|
MOV Rd, Rs |
01011 |
R-Type | Copy Rs → Rd |
LDM Rd, Imm |
01110 |
I-Type (2-word) | Load 32-bit immediate → Rd |
LDD Rd, Off(Rs) |
01111 |
I-Type (2-word) | Load M[Rs + Offset] → Rd |
STD Rs, Off(Rb) |
10000 |
I-Type (2-word) | Store Rs → M[Rb + Offset] |
IN Rd |
00110 |
Special | Read input port → Rd |
OUT Rs |
00101 |
Special | Write Rs → output port |
PUSH Rs |
01100 |
Stack | Push Rs to stack (SP decrements) |
POP Rd |
01101 |
Stack | Pop from stack → Rd (SP increments) |
| Mnemonic | Opcode | Format | Description | Flags |
|---|---|---|---|---|
ADD Rd, Rs1, Rs2 |
01000 |
R-Type | Rd = Rs1 + Rs2 | Z, N, C |
SUB Rd, Rs1, Rs2 |
01001 |
R-Type | Rd = Rs1 - Rs2 | Z, N, C |
AND Rd, Rs1, Rs2 |
01010 |
R-Type | Rd = Rs1 AND Rs2 | Z, N |
NOT Rd |
00011 |
R-Type | Rd = NOT Rd | Z, N |
INC Rd |
00100 |
R-Type | Rd = Rd + 1 | Z, N, C |
IADD Rd, Rs, Imm |
11001 |
I-Type (2-word) | Rd = Rs + Immediate | Z, N, C |
SETC |
00010 |
Special | Set Carry Flag = 1 | C |
SWAP R1, R2 |
00111 |
R-Type | Exchange R1 ↔ R2 | — |
| Mnemonic | Opcode | Format | Description |
|---|---|---|---|
JMP Addr |
10100 |
J-Type (2-word) | Unconditional jump |
JZ Addr |
10001 |
J-Type (2-word) | Jump if Zero flag = 1 |
JN Addr |
10010 |
J-Type (2-word) | Jump if Negative flag = 1 |
JC Addr |
10011 |
J-Type (2-word) | Jump if Carry flag = 1 |
CALL Addr |
10101 |
J-Type (2-word) | Call subroutine (push address of next instruction) |
RET |
10110 |
Special | Return from subroutine (pop PC) |
NOP |
00000 |
Special | No operation |
HLT |
00001 |
Special | Halt processor |
| Mnemonic | Opcode | Format | Description |
|---|---|---|---|
INT Index |
11000 |
Special | Software interrupt (push PC+1, save flags) |
RTI |
10111 |
Special | Return from interrupt (pop PC, restore flags) |
PipeLord implements full data forwarding to minimize pipeline stalls:
| Hazard Type | Solution | Implementation |
|---|---|---|
| EX→EX | Forward from EX/MEM to EX | Forwarding_Unit.vhd |
| MEM→EX | Forward from MEM/WB to EX | Forwarding_Unit.vhd |
| Load-Use | Stall 1 cycle | Hazard_Unit.vhd |
Forwarding Datapath:
┌────────────────┐
│ Forwarding Unit│
└───────┬────────┘
│ Forward Sel
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌───────┐ ┌─────────┐ ┌─────────┐
│ ID/EX │────────▶ │ ALU │ ◀────── │ EX/MEM │
└───────┘ └─────────┘ └─────────┘
▲
│
┌─────────┐
│ MEM/WB │
└─────────┘
| Hazard Type | Solution | Penalty |
|---|---|---|
| Unconditional Jump | Jump resolved in ID, flush IF/ID | 1 cycle |
| Conditional Branch | Branch resolved in EX, flush IF/ID & ID/EX | 2 cycles |
| RET/RTI | Stall until PC loaded from memory | 3+ cycles |
The Von Neumann Memory is shared between Fetch and Memory stages. This is handled via split-cycle access:
- First half of clock: Instruction Fetch
- Second half of clock: Data Memory Access (LDD/STD)
| Vector | Address | Purpose |
|---|---|---|
| 0 | M[0] |
Reset vector |
| 1 | M[1] |
Hardware interrupt |
Software Interrupt (INT Index):
- Push PC+1 to stack
- Save flags to shadow register
- Jump to
M[Index]
Hardware Interrupt (External intr Signal):
- Insert
HW_INTinstruction into pipeline - Push PC+1 to stack
- Save flags to shadow register
- Jump to
M[1]
Return from Interrupt (RTI):
- Restore flags from shadow register
- Pop return address from stack
- Resume execution
pipelord/
├── 📄 README.md # This file
├── 📁 design/
│ └── 🖼️ design.png # Architecture diagram
├── 📁 src/
│ ├── 📄 Processor.vhd # Top-level processor entity
│ ├── 📄 Memory.vhd # Main memory module
│ ├── 📁 stages/
│ │ ├── 📁 1-fetch/rtl/ # Fetch stage (PC, HLT)
│ │ ├── 📁 2-decode/rtl/ # Decode stage (Control, RegFile)
│ │ ├── 📁 3-execute/rtl/ # Execute stage (ALU, ALU Controller)
│ │ ├── 📁 4-memory/rtl/ # Memory stage (SP Module)
│ │ └── 📁 5-writeback/rtl/ # Writeback stage (Mux, SWAP FSM)
│ ├── 📁 pipeline_regs/ # IF/ID, ID/EX, EX/MEM, MEM/WB registers
│ ├── 📁 hazard_control/ # Hazard Unit, Forwarding Unit
│ └── 📁 utils/rtl/ # Muxes, Adders
├── 📁 Assembler/
│ ├── 📄 assembler.py # Python assembler
│ ├── 📄 README.md # Assembler documentation
│ └── 📁 tests/ # Assembly test programs
├── 📁 do_files/ # ModelSim simulation scripts
├── 📄 program.mem # Compiled program (hex)
└── 📄 program.bin # Compiled program (binary)
- VHDL Simulator: ModelSim, GHDL, or Vivado
- Python 3.x: For the assembler
Create a file myprogram.asm:
.ORG 0
JMP Main
.ORG 10
Main:
LDM R1, 100 # Load 100 into R1
LDM R2, 200 # Load 200 into R2
ADD R3, R1, R2 # R3 = R1 + R2 = 300
OUT R3 # Output R3
HLT # Stoppython3 Assembler/assembler.py myprogram.asmThis generates program.mem (hex format). For binary output:
python3 Assembler/assembler.py myprogram.asm --bin# Navigate to project directory
cd pipelord
# Start ModelSim and run the testbench
vsim -do do_files/Processor_tb.do- View waveforms in ModelSim
- Check register values, memory contents, and output port
- Verify pipeline behavior with forwarding and stalls
The project includes comprehensive testbenches for each component:
| Component | Testbench |
|---|---|
| ALU | src/stages/3-execute/tb/ALU_tb.vhd |
| Register File | src/stages/2-decode/tb/RegisterFile_tb.vhd |
| Control Unit | src/stages/2-decode/tb/Control_Unit_tb.vhd |
| Full Processor | src/tb/Processor_tb.vhd |
Run all tests:
vsim -do do_files/run_all_tests.do| Metric | Value |
|---|---|
| Pipeline Depth | 5 stages |
| Clock Cycles per Instruction (CPI) | ~1.0 (ideal) |
| Data Hazard Penalty | 0 cycles (forwarded) |
| Load-Use Penalty | 1 cycle |
| Branch Penalty | 1-2 cycles |
| Memory Width | 32 bits |
| Address Space | 256K words (18-bit address) |
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is developed for educational purposes as part of a Computer Architecture course.