The CPU and fetch-execute
The ALU, control unit, registers and buses, and tracing the fetch-decode-execute cycle.
Do this lesson in the simulatorThe CPU, the central processing unit, is the part of a computer that runs programs. BugBot's is the processor on its top board, and it carries out millions of instructions every second. Each one goes through the same three steps, over and over: fetch an instruction from memory, decode it, and execute it. This lesson opens the CPU up and runs the cycle by hand.
Inside the CPU
| Part | What it does |
|---|---|
| Control unit (CU) | decodes each instruction and sends signals to make the other parts carry it out; controls the timing of the cycle |
| Arithmetic logic unit (ALU) | does the arithmetic (adding, subtracting) and the logic (comparisons, AND, OR) |
| Cache | a small amount of very fast memory inside the CPU, holding data it needs often |
| Registers | tiny, very fast stores inside the CPU, each holding one value |
| Clock | sends a regular pulse; each tick moves the cycle on |
The registers each have one job:
| Register | Holds |
|---|---|
| Program counter (PC) | the address of the next instruction to fetch |
| Memory address register (MAR) | the address about to be read from or written to |
| Memory data register (MDR) | the data or instruction just read from memory, or about to be written |
| Accumulator (ACC) | the result of the last calculation |
Buses
The CPU talks to memory along three buses, sets of parallel wires:
- the address bus carries the address to use, from the CPU to memory;
- the data bus carries the data or instruction, in either direction;
- the control bus carries signals such as "read" or "write", and the clock.
The fetch-decode-execute cycle
- Fetch: the address in the PC is copied to the MAR. The instruction at that address is sent along the data bus to the MDR. The PC goes up by 1, ready for the next instruction.
- Decode: the control unit works out what the instruction in the MDR means.
- Execute: the instruction is carried out. A calculation happens in the ALU and its result goes to the accumulator; a load or store uses the MAR and MDR again.
Then back to fetch. Here is the tiny computer from the last lesson, with every register shown as the cycle runs:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
memory = ["LOAD 5", "ADD 6", "STORE 7", "OUT 7", "HALT", 20, 22, 0]
pc = mar = acc = 0
mdr = None
while True:
# fetch
mar = pc
mdr = memory[mar]
pc = pc + 1
print(f"fetch PC={pc} MAR={mar} MDR={mdr!r}")
# decode
op, *arg = mdr.split()
# execute
if op == "LOAD":
mar = int(arg[0]); mdr = memory[mar]; acc = mdr
elif op == "ADD":
mar = int(arg[0]); mdr = memory[mar]; acc = acc + mdr # the ALU adds
elif op == "STORE":
mar = int(arg[0]); mdr = acc; memory[mar] = mdr
elif op == "OUT":
print("output:", memory[int(arg[0])])
tone(440 + memory[int(arg[0])] * 10, 0.3)
elif op == "HALT":
break
print(f"execute {op:5} ACC={acc} MAR={mar} MDR={mdr}")
Read the printout as the processor's diary. Notice the PC is always one ahead of the instruction being run, because it is updated during the fetch.
Instructions are numbers too
Real memory holds only binary. So each instruction is a number: part of it says which operation (the opcode) and part says which address or value (the operand). In a made-up 8-bit instruction set, LOAD 5 might be the opcode 0001 and operand 0101: the byte 00010101. The control unit decodes the opcode to know what to do.
Task: trace the cycle
Complete the fetch-decode-execute loop so it runs the program in memory and supports LOAD, ADD, SUB, STORE, OUT and HALT. During each fetch, print fetch <MAR>: <instruction> (for example fetch 0: LOAD 6). The program outputs output: 35.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
memory = ["LOAD 6", "SUB 7", "ADD 8", "STORE 9", "OUT 9", "HALT", 40, 12, 7, 0]
pc = mar = acc = 0
mdr = None
Challenges
- Add a
JUMPZ ainstruction that jumps to addressaif the accumulator is 0. Write a countdown program with it. - Count how many fetch-execute cycles the program takes, and how many would run in one second at 1 GHz.
- Encode each instruction as an 8-bit number, with a 4-bit opcode and a 4-bit address, and print the memory as binary.