This project implements a MIPS-style multi-cycle CPU in Verilog. The design splits instruction execution into classical stages (fetch, decode, execute, memory, and write-back) and uses a centralized control unit plus hazard detection and forwarding logic to keep the pipeline correct and efficient. The processor includes a simple on-chip cache and synchronous memory interface to accelerate load/store operations.
The goal of this project is to show how a complete CPU can be built from basic digital building blocks, and how microarchitectural choices (like pipelining, forwarding, and caching) affect performance and correctness.
- MIPS-style instruction set subset (integer arithmetic, logical ops, loads/stores, branches, and jumps)
- Multi-cycle pipeline with separated stages:
- Instruction fetch
- Decode and register read
- Execute / ALU
- Memory access
- Write-back
- Central control unit built around finite-state machines
- Hazard detection and forwarding logic for:
- Data hazards (register dependencies between back-to-back instructions)
- Control hazards (branches, jumps)
- On-chip data cache to reduce average memory access latency
- Synchronous memory interface for instruction and data memory
- Simulation testbench to verify functionality and run example programs
At a high level, the CPU is divided into a datapath and a control block.
The datapath is responsible for moving and transforming data through the pipeline. Key components include:
- Program counter (PC) and PC update logic
- Instruction memory interface
- Register file with two read ports and one write port
- Arithmetic Logic Unit (ALU) for integer operations
- Pipeline registers between stages (IF/ID, ID/EX, EX/MEM, MEM/WB)
- Data memory / cache interface
- Sign/zero extension units and shifters for immediates and branch targets
- Multiplexers for selecting ALU operands, next PC, and write-back data
The control unit generates control signals based on the current instruction opcode, function bits, and pipeline state. Typical responsibilities:
- Decode instructions and generate high-level control (ALUSrc, RegWrite, MemRead, MemWrite, MemToReg, Branch, Jump)
- Drive pipeline register enables and flush signals
- Coordinate with the hazard detection unit when stalls or flushes are required
Internally, the control logic can be implemented as one or more finite-state machines (FSMs) that step through the phases of instruction execution and handle special cases (e.g., branches, load-use hazards).
To maintain correctness in the presence of overlapping instructions, the processor includes:
-
Hazard detection logic that:
- Detects when an instruction uses a register that is not yet written back
- Inserts stalls (bubbles) into the pipeline when needed, especially for load-use hazards
- Flushes instructions after taken branches or jumps
-
Forwarding unit that:
- Bypasses results from later pipeline stages (EX/MEM, MEM/WB) back to the ALU inputs
- Allows dependent instructions to execute without waiting for the value to be written back to the register file
The memory system is modeled as a synchronous memory with a small on-chip cache to improve performance:
- Data cache organized as a small set of cache lines, each storing:
- Valid bit and tag
- Cached data block
- Hit/miss detection using tag comparison and valid bits
- On a cache hit, the data is returned in one cycle (matching the multi-cycle pipeline timing)
- On a cache miss, the cache is refilled from backing memory (modeled in simulation)
Instruction memory may be modeled as:
- A separate instruction ROM/IMEM loaded with assembled machine code
- Or a unified memory model with a dedicated instruction port
The design includes a testbench that:
- Resets the CPU
- Loads a small program into instruction memory (e.g., arithmetic, loads/stores, branches)
- Runs the CPU for a fixed number of cycles
- Checks final register and/or memory state to verify correctness
Typical simulation flow:
- Compile RTL and testbench in your simulator (e.g., ModelSim, Questa, or Icarus Verilog).
- Run the simulation to observe:
- Waveforms (PC, instruction, control signals, ALU outputs, register file writes, cache hits/misses)
- Final architectural state (registers and memory)
Example (adjust file names to match your repo):
# Compile
vlog src/*.v tb/tb_cpu.v
# Run
vsim tb_cpu
run -all