A C-based simulator for a custom 8-bit Harvard-architecture processor with a 3-stage instruction pipeline. The simulator reads assembly programs, encodes them into 16-bit machine instructions, executes them cycle by cycle, and prints the full processor state after each clock cycle.
This project models the behavior of a small educational CPU designed for computer architecture coursework. It demonstrates how assembly instructions move through a pipelined processor, how registers and memory are updated, and how control-flow changes affect the pipeline.
The simulator includes an assembler-style parser, instruction encoder, register file, separate instruction and data memories, status flag updates, data hazard detection, branch/jump handling, and detailed execution traces.
Modern processors improve throughput by overlapping instruction execution across pipeline stages. This simulator makes that behavior observable by implementing a simplified CPU that:
- Loads assembly instructions from a text file.
- Converts instructions into binary machine code.
- Stores instructions and data in separate Harvard-architecture memories.
- Executes instructions through Fetch, Decode, and Execute stages.
- Detects read-after-write data hazards and inserts stalls.
- Flushes incorrect pipeline stages after taken branches and jumps.
- Prints registers, memory, status flags, program counter, and pipeline stages each cycle.
- 3-stage pipeline: Fetch, Decode, Execute
- Harvard architecture with separate instruction and data memories
- 1024-entry instruction memory with 16-bit encoded instructions
- 2048-byte data memory
- 64 general-purpose 8-bit registers
- 16-bit program counter
- 8-bit status register using the lower five flags: C, V, N, S, Z
- Assembly parser with support for comments, whitespace normalization, commas, uppercase/lowercase input, decimal immediates, and hexadecimal immediates
- R-type and I-type instruction encoding
- Data hazard detection for register dependencies
- Pipeline stalls for read-after-write hazards
- Pipeline flushing for taken branches and jumps
- Cycle-by-cycle execution trace
- Final processor-state dump
| Instruction | Type | Description |
|---|---|---|
ADD R1 R2 |
R-type | Adds R2 to R1 and stores the result in R1. |
SUB R1 R2 |
R-type | Subtracts R2 from R1 and stores the result in R1. |
MUL R1 R2 |
R-type | Multiplies R1 by R2 and stores the low 8 bits in R1. |
LDI R1 IMM |
I-type | Loads a 6-bit signed immediate into R1. |
BEQZ R1 IMM |
I-type | Branches relative to the current instruction if R1 is zero. |
AND R1 R2 |
R-type | Performs bitwise AND and stores the result in R1. |
OR R1 R2 |
R-type | Performs bitwise OR and stores the result in R1. |
JR R1 R2 |
R-type | Jumps to the address formed by concatenating R1 and R2. |
SAL R1 IMM |
I-type | Shifts R1 left by the immediate value. |
SAR R1 IMM |
I-type | Performs arithmetic right shift on R1. |
LB R1 ADDR |
I-type | Loads a byte from data memory into R1. |
SB R1 ADDR |
I-type | Stores R1 into data memory. |
Assembly File
|
v
Parser and Encoder
|
v
Instruction Memory (1024 x 16)
|
v
+---------+ +---------+ +----------+
| Fetch | --> | Decode | --> | Execute |
+---------+ +---------+ +----------+
|
v
Register File | SREG Flags | Data Memory | Program Counter
uint16_tis used for instruction memory because each encoded instruction is 16 bits.uint8_tis used for registers and data memory to match the simulated 8-bit processor width.- Each pipeline stage stores an
Instructionstruct containing decoded fields and the original source text, which makes tracing easier. - Branches and jumps are resolved in the Execute stage, then Fetch and Decode are flushed when control flow changes.
- The simulator detects read-after-write hazards between Decode and Execute and inserts a one-cycle stall.
- C
- Standard C library
- Fixed-width integer types from
stdint.h - Command-line file input
- Assembly-style text parsing
Clone the repository:
git clone https://github.com/basmalaallam/Processor-Simulator.git
cd Processor-SimulatorCompile with GCC or Clang:
gcc src/main.c -o simulatorOn Windows with Microsoft Visual C++:
cl src\main.c /Fe:simulator.exeRun the included sample program:
./simulator examples/program.asmOn Windows PowerShell:
.\simulator.exe examples\program.asmRun your own assembly file:
./simulator path/to/program.asmIf no file path is provided, the simulator looks for program.asm in the current working directory.
LDI R1 5
LDI R2 10
ADD R1 R2
SB R1 20
LB R3 20
SUB R3 R1
BEQZ R3 1
LDI R4 99
LDI R4 7Expected final highlights:
R1 = 15R2 = 10R3 = 0R4 = 7DataMemory[20] = 15
.
|-- README.md
|-- .gitignore
|-- examples/
| `-- program.asm
`-- src/
`-- main.c
Watch the project walkthrough and simulator demonstration here:
Processor Simulator Demo Video
- Arrays model instruction memory, data memory, and the register file.
- A structured
Instructionrecord stores parsed operands, opcode, program counter, machine code, and source text. - A finite pipeline state is represented with three instruction-stage variables.
- Bit masking and shifting are used for instruction encoding, flag updates, register concatenation, and immediate sign extension.
- Read-after-write dependency detection determines when to stall the pipeline.
- Control-flow instructions update the program counter and flush younger instructions.
- Encoding human-readable assembly into compact 16-bit machine instructions.
- Simulating overlapping instruction execution with explicit pipeline state.
- Preserving correct execution when dependencies exist between adjacent instructions.
- Implementing branch and jump behavior without allowing wrong-path instructions to commit.
- Updating status flags consistently across arithmetic, logical, and shift operations.
- Producing a readable trace that helps debug processor behavior cycle by cycle.
- Add automated regression tests for parser, encoder, execution, hazards, and branch behavior.
- Add a
Makefileor CMake project for simpler cross-platform builds. - Split parser, CPU state, instruction execution, and printing into separate modules.
- Add command-line flags for quiet mode, full memory printing, maximum cycles, and trace output files.
- Add labels in assembly programs instead of requiring numeric branch offsets.
- Add CI with compiler warnings enabled.
- Add sample programs that demonstrate every instruction and edge case.
- Computer architecture fundamentals: pipelines, hazards, status flags, instruction formats, and Harvard memory organization.
- Low-level C programming with fixed-width integer types and bitwise operations.
- Designing debuggable simulations with clear state transitions.
- Translating assembly-like input into executable machine-level behavior.
- Explaining technical systems in a way that is readable to both engineers and reviewers.