The CPU and fetch-execute

The ALU, control unit, registers and buses, and tracing the fetch-decode-execute cycle.

F9.4Logic and computer systemsGCSE20 min

Do this lesson in the simulator

The 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

  1. 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.
  2. Decode: the control unit works out what the instruction in the MDR means.
  3. 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}")

Run this in the simulator

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

  1. Add a JUMPZ a instruction that jumps to address a if the accumulator is 0. Write a countdown program with it.
  2. Count how many fetch-execute cycles the program takes, and how many would run in one second at 1 GHz.
  3. Encode each instruction as an 8-bit number, with a 4-bit opcode and a 4-bit address, and print the memory as binary.