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

Covers OCR J277 1.1.1 Architecture of the CPU · AQA 8525 3.4.5 Systems architecture · Edexcel 1CP2 3.1.1 The von Neumann stored program concept

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.
Inside the CPU: control unit, ALU, cache, clock and the four registers; the address, data and control buses join it to main memoryCPUcontrol unitdecodes, andsends signalsALUadds, comparescachefast copiesregistersPCnext addressACClast resultMARaddress to useMDRdata just readresultclockticksmainmemory(RAM)address busone waydata busboth wayscontrol busread, write
The parts of the CPU from the tables, and where they connect. Addresses leave from the MAR on the address bus, which only goes out to memory. Data and instructions come and go through the MDR on the data bus, in both directions. The control bus carries signals such as read and write.

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.
The fetch-decode-execute cycle as a loop, showing the registers for the first instruction, LOAD 51. fetchPC copied to MAR: MAR = 0memory[0] to MDR: LOAD 5PC goes up by 1: PC = 12. decodethe control unit readsLOAD 5: opcode LOAD,address 53. executeMAR = 5memory[5] to MDR: 20ACC = 20back to fetch,now at PC = 1
One trip round the cycle, for the first instruction of the lesson's program. The PC has already moved on to 1 during the fetch, before LOAD 5 is even decoded, so the next fetch takes the instruction at address 1.

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.

The instruction byte 00010101: the first four bits are the opcode, the last four the operand00010101opcode 0001which operation: LOADoperand 0101which address: 5
LOAD 5 as one byte, 00010101. The control unit decodes the first four bits to know what to do, and the last four say where: 0101 in binary is 5.

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.