CPU Simulator + C code

Ahmet Göker
9 min readJul 12, 2024

--

Hello, everyone. Welcome to my blog. Today, I want to cover the topic of CPUs and write a CPU simulator in C code. This aims to help people gain a better understanding of CPUs. The reason why I will be coding a CPU simulator is to demonstrate a different method. If you are ready, let me start by explaining what a CPU is, why it is useful, and how it works in C.

Introduction

CPU or the central processing unit is another important component of any computing device; known by its popular name the ‘Brain’ of the computer. Starting from personal computers, portable gadgets, up to the supercomputers, servers, etc., the CPU is the core that manages the operations by providing the results of corresponding instructions and data. Here is what this blog is going to cover: what is a CPU; why is it significant; how does it perform instructions; and what algorithms are behind it?

What is a CPU?

The most important hardware on the computer is the CPU or the Central Processing Unit, which is a microprocessor that resides in the computer’s motherboard; it is the one that computes the arithmetic, logic, control, and input/output operations as instructed by the programs that are in the computer. The components make up its architecture consist of the Control Unit (CU), the Arithmetic Logic Unit (ALU), and some registers that enable it to process data and instructions. The performance of the CPU as one of the key components of the computer plays a role in the functionality.

https://www.redhat.com/sysadmin/cpu-components-functionality

Components and Their Functions:

  1. RAM (Random Access Memory): This is the main memory where the CPU fetches instructions and data. It is volatile memory, meaning it loses its content when power is off.
  2. System Clock and CPU Clock: The system clock generates a clock signal that synchronizes the timing of all operations in the CPU and the rest of the system. The CPU clock specifically synchronizes the CPU operations.
  3. Control Unit: This component controls the flow of data within the CPU. It interprets the instructions from the program and signals other parts of the CPU to execute them.
  4. Memory Management Unit (MMU): This unit manages the translation of virtual memory addresses to physical memory addresses, ensuring that each process has its own memory space.
  5. L1, L2, and L3 Cache: These are small, fast memory locations closer to the CPU cores. L1 cache is the smallest and fastest, while L2 and L3 are larger and slower. The cache stores frequently accessed data to speed up processing.
  6. Registers: These are small, fast storage locations within the CPU used to hold data temporarily during execution.
  • A Register and B Register: These are general-purpose registers used for various operations.
  • Accumulator: A special-purpose register used for arithmetic and logic operations.
  • Instruction Pointer (IP): Holds the address of the next instruction to be executed.
  • Instruction Register: Holds the current instruction being executed.

Importance of the CPU

It is significant to recognize the role of CPU in the computer system mainly because it is the main component that is charge of the execution of instructions, processing of data, controlling of other hardware commodities, and managing of multitasking. Firstly, general purpose or central processing unit is responsible for the execution of a sequence of stored instructions known as the programme, which helps the computer solve large problems by reducing them into large no. of small and simple problems. Secondly, it manipulates data by calculating and making logical decisions required in simple arithmetic and in complex computations, modelling, data analysis among other functions. Third, the CPU regulates other devices of the computers through signaling and often supervises the running of program by fetching and decoding instructions and then performing the requisite steps systematically. Finally, the present brand CPUs are made to work multi-practiced, that is, the modern CPU is able to run multiple programs without much hindering the rest programs that are running at the background thus improving the computer’s ability to multitask.

How the CPU Executes Instructions

Like any other processor, the CPU also goes through a simple loop that is referred to as the fetch-decode-execute cycle to process instruction and this can be broken down into many sub-steps that would help to enhance processing of a certain task. This cycle includes the following stages: searching for the instruction within the memory, interpreting the instruction to know what needs to be done with it, performing the necessary action and , if necessary, storing the results back in memory.

Fetch stage:
At the fetch stage of the cycle the Program Counter (PC) contains the address of the next instruction to be executed. Instruction is also fetched from the memory location referenced by the content of the PC and after that, the fetched instruction is located in the IR. Afterward, the PC is incremented to the next instruction to enable the CPU prepare for the next cycle’s operation.

Decode Stage:
In the decode stage, the CU takes meaning of an instruction that is stored in the IR. The CU produces control signals that enable the other parts of the CPU to perform the required activities like loading data from memory to perform ARITHMETIC LOGIC UNIT ALU operations in accordance with the instructions to be executed.

Execute Stage:
In the execute stage, the arithmetic logic unit ALU carries out an arithmetic or logical operation according to the decoded instruction. If the instruction is a data transfer type, the CPU loads from or stores to the main memory. In control type operations including the jump or branch instructions, the PC is updated to correspond to the new instructional sequence.

Store Stage:
In the store stage, the completion of the executed instruction is written back to the memory or registers based on the CPU; this is important to keep the outcome available for other instructions or processing.

CPU algorithms

This indicates that the efficiency of a CPU’s operations depends on a variety of algorithms that is integrated within it. These algorithms increase CPU performance since they improve the processing of instructions and resource management by the CPU. These are instruction pipelining, branch prediction, cache and out of order executions.

Instruction Pipelining:
It is also known as instruction Pirate, which enhance the speed of a CPU since they helps it to run at the same time in a single cycle. It divides the fetch-decode-execute cycle into various forms which help the CPU to work on several instructions which are in different forms of completion at one time. This in turn raises the throughput of the CPU, in turn you observe that more instructions are processed at a given time.

Branch Prediction:
The branch prediction seeks to reduce down time occasioned by branch delay, for example in loop or conditional instructions. These algorithms forecast the occurrence of a branch instruction and fetch consequent instructions, avoiding idle time and maintaining the CPU’s continuous execution of instructions as there may be fluctuations in the control flow of a program.

Cache Management:
Cache memory is a component contained in CPUs whereby quite often accessed data and instructions are temporarily stored so that less time is spent in accessing the main memory. Replacement algorithms decide which data is stored in the cache and when the cache should be updated; this decreases latency because commonly accessed data is stored in the cache.

Out-of-Order Execution:
Out of order execution enables the CPU to perform instructions in an arbitrary order, thus reducing time Familiarity with acronyms: wasted by trying to adhere to a strict order between instructions. This technique helps to prevent the stalls caused by the data dependencies or other holds and allows the execution units of the CPU to continue operating at the fastest pace possible while preserving the correct order of instructions’ completion.

CPU simulator in C code

In this section, I will code a C simulator to demonstrate how the CPU works. This project can be a great opportunity to enhance your skills in pointers and structs, as we will use them extensively. Do not worry if you are not quite familiar with C; after showing the code, I will explain how it works. Let’s get started.

To build a CPU simulator in C, it is necessary to model the components and the operations of the CPU which are; the instruction set, registers, memory as well as the controllers. Here is a simplified example of how you might start building a basic CPU simulator in C:

Step 1: Include Libraries and Define Constants

  • MEMORY_SIZE: Defines the size of the CPU's memory as 256 units.
  • NUM_REGISTERS: Defines the number of general-purpose registers as 4

Step 2: Define the Instruction Set

  • registers: An array representing the general-purpose registers.
  • pc: The program counter, which holds the address of the next instruction to execute.
  • memory: An array representing the CPU's memory
https://www.eeweb.com/building-a-4-bit-computer-the-instruction-set/

Step 3: Implement the Instruction Execution Function

  • This function takes a CPU structure, an instruction, and operands as arguments.
  • The switch statement executes different actions based on the instruction type:
  • ADD and SUB: Perform arithmetic operations on the specified registers.
  • LOAD and STORE: Transfer data between memory and registers.
  • JUMP, JZ, JNZ: Control the flow of the program by altering the program counter.
  • HALT: Stops the execution.
  • The cpu->pc += 4 statement advances the program counter to the next instruction.

Step 5: Implement the CPU Run Function

  • This function runs the program loaded into the CPU.
  • It iterates through the program, fetching and executing each instruction until the program counter reaches the end of the program.

Step 6: Implement the Main Function

  • Initializes a CPU structure with all registers and memory set to zero, and the program counter set to zero.
  • Defines a sample program that performs a series of operations:
  • LOAD R0, 0x10: Loads the value from memory address 0x10 into register R0.
  • ADD R0, R1: Adds the value in R1 to R0.
  • JZ R0, 0x18: If R0 is zero, jumps to the instruction at address 0x18.
  • SUB R0, R1: Subtracts the value in R1 from R0.
  • STORE R0, 0x20: Stores the value in R0 into memory address 0x20.
  • JUMP 0x0C: Jumps to the instruction at address 0x0C.
  • HALT: Halts the CPU.

Let’s see the output:

If everything is working correctly, the program should halt after executing the HALT instruction, and you should see Memory[0x20]: 5 displayed as the last value, which means that such instruction as STORE acted correctly. In case if you still have some problems, then it is highly recommended to revisit instruction definitions, memory and register initialization as well as the overall flow of execution in comparison to the intended program’s plan.

I hope that this blog fits your needs. There will be more blogs about low-level programming. I hope you understood this. If you have any questions, feel free to ask me. Thanks!

--

--

Ahmet Göker
Ahmet Göker

Written by Ahmet Göker

Full stack Reverser | Linux-Kernel | Windows API

Responses (4)