The fetch-decode-execute cycle in detail

Register transfer notation, the buses at each step, a full register trace, and a stored program that plays notes.

A9.3Computer architectureA level25 min

Covers OCR H446 1.1.1 Structure and function of the processor · AQA 7517 4.7.3.2 The Fetch-Execute cycle and the role of registers within it · Eduqas A500QS 2.1 Hardware and communication

Do this lesson in the simulator

At GCSE (F9.4) you described the cycle in three words: fetch, decode, execute. At A level you must describe it as a sequence of register transfers: exactly which value is copied into which register at each step, and what each bus carries. Exam questions give a starting state and ask you to fill in the registers as the cycle runs, so this lesson traces it step by step.

Register transfer notation

Textbooks and mark schemes write each step in a short notation:

Notation Means
[PC] the contents of the PC
MAR ← [PC] copy the contents of the PC into the MAR
[Memory]addressed the contents of the memory location whose address is in the MAR
PC ← [PC] + 1 add 1 to the PC's contents and put the answer back in the PC

Square brackets mean "the contents of". Without them, PC names the register itself.

Fetch

Step Transfer What happens on the buses
1 MAR ← [PC] the address of the next instruction is copied to the MAR, which puts it on the address bus
2 PC ← [PC] + 1 the PC is incremented so it points at the following instruction; this can happen at the same time as step 3
3 MDR ← [Memory]addressed the control unit sends a memory read signal on the control bus; memory puts the instruction on the data bus, and it is copied into the MDR
4 CIR ← [MDR] the instruction is copied to the CIR, freeing the MDR for data during execution

AQA writes MBR for the MDR; the steps are the same.

The four fetch transfers: PC to MAR, PC plus 1, memory to MDR over the data bus, MDR to CIRprocessorPC0 then 1MAR0MDRLOAD 4CIRLOAD 41+12address busmemory readcontrol busdata bus34addressmain memory0LOAD 41ADD 52STORE 63HALT42052260
The first fetch of the lesson's program, in step order. The PC's 0 goes to the MAR and out on the address bus, the PC moves on to 1, memory sends back LOAD 4 on the data bus, and it is copied to the CIR.

Decode

The control unit splits the instruction in the CIR into its opcode (what to do) and its operand (what to do it to). From the opcode it works out which sequence of control signals will carry the instruction out.

Execute

What happens now depends on the instruction. Four common ones:

Instruction Transfers
LOAD 4: copy the value at address 4 into the accumulator MAR ← [CIR(operand)], then MDR ← [Memory]addressed, then ACC ← [MDR]
ADD 5: add the value at address 5 to the accumulator MAR ← [CIR(operand)], then MDR ← [Memory]addressed, then ACC ← [ACC] + [MDR], done by the ALU
STORE 6: copy the accumulator into address 6 MAR ← [CIR(operand)], then MDR ← [ACC], then [Memory]addressed ← [MDR], with a memory write signal on the control bus
BRANCH 0: jump to address 0 PC ← [CIR(operand)]: the next fetch uses the new address

A branch is just an execute step that writes to the PC. That is the whole mechanism behind every loop and if in every program.

After execute, the processor checks whether an interrupt is waiting (lesson A9.7), then starts the next fetch.

Fetch, decode, execute, then a check for interrupts, and round againfetchfour registertransfersdecodesplit opcodeand operandexecutedepends on the opcode;a branch loads the PCinterruptwaiting?nonext instructionyes: lesson A9.7
The cycle repeats for every instruction. A branch does not leave the loop: it only changes the PC during execute, so the next fetch comes from somewhere else.

A full trace

Memory holds this program, starting at address 0:

Address 0 1 2 3 4 5 6
Contents LOAD 4 ADD 5 STORE 6 HALT 20 22 0

The trace shows every register after each transfer. A blank means unchanged.

Transfer PC MAR MDR CIR ACC
start 0 0 0
MAR ← [PC] 0
PC ← [PC] + 1 1
MDR ← [Memory]addressed LOAD 4
CIR ← [MDR] LOAD 4
MAR ← [CIR(operand)] 4
MDR ← [Memory]addressed 20
ACC ← [MDR] 20
MAR ← [PC] 1
PC ← [PC] + 1 2
MDR ← [Memory]addressed ADD 5
CIR ← [MDR] ADD 5
MAR ← [CIR(operand)] 5
MDR ← [Memory]addressed 22
ACC ← [ACC] + [MDR] 42
MAR ← [PC] 2
PC ← [PC] + 1 3
MDR ← [Memory]addressed STORE 6
CIR ← [MDR] STORE 6
MAR ← [CIR(operand)] 6
MDR ← [ACC] 42
[Memory]addressed ← [MDR]
MAR ← [PC] 3
PC ← [PC] + 1 4
MDR ← [Memory]addressed HALT
CIR ← [MDR] HALT

At the end, address 6 holds 42. Notice that the MAR and MDR change far more often than the PC: they are used for every memory access, instructions and data alike. Notice too that while HALT is in the CIR, the PC already holds 4: the PC always points one instruction ahead.

Here is the same machine in Python, printing each transfer as it happens:

memory = ["LOAD 4", "ADD 5", "STORE 6", "HALT", 20, 22, 0]
pc, mar, mdr, cir, acc = 0, 0, None, None, 0

while True:
    mar = pc;           print(f"MAR <- [PC]          MAR={mar}")
    pc = pc + 1;        print(f"PC <- [PC] + 1       PC={pc}")
    mdr = memory[mar];  print(f"MDR <- [Memory]      MDR={mdr}")
    cir = mdr;          print(f"CIR <- [MDR]         CIR={cir}")
    opcode, *operand = cir.split()
    if opcode == "HALT":
        break
    mar = int(operand[0]); print(f"MAR <- operand       MAR={mar}")
    if opcode == "STORE":
        mdr = acc;         print(f"MDR <- [ACC]         MDR={mdr}")
        memory[mar] = mdr; print(f"[Memory] <- [MDR]    memory[{mar}]={mdr}")
    else:
        mdr = memory[mar]; print(f"MDR <- [Memory]      MDR={mdr}")
        acc = mdr if opcode == "LOAD" else acc + mdr
        print(f"ACC <- ...           ACC={acc}")
print("memory:", memory)

Run this in the simulator

Memory-mapped output

A processor with no special output instructions can still control hardware. Some addresses are wired not to memory but to an I/O controller, so writing to that address sends a value to a device. This is memory-mapped I/O. The task keeps things simpler with a dedicated OUT instruction that plays the accumulator as a note on the robot's piezo: a stored program making a sound. Challenge 2 does the same job the memory-mapped way.

The address on the address bus is decoded: most addresses select RAM, one selects the piezo's I/O controllerprocessorSTORE naddressaddressdecoderis it 99?0 to 98RAMstores the value99piezo controllerplays the valuesame data bus,same STORE
Writing to address 99 uses exactly the same STORE and the same buses as writing to memory. The address decoder sends it to the piezo's controller instead of RAM, so the value becomes a note.

Task: fetch in register transfers

The program below already decodes and executes. Write the fetch as four register transfers, in the order in the table above, using the variables pc, mar, mdr and cir. Straight after the fetch, before decoding, print the four registers in the form PC=1 MAR=0 MDR=LOAD 7 CIR=LOAD 7.

Memory holds LOAD 7, OUT, ADD 8, OUT, ADD 8, OUT, HALT, then the data 300 and 100. OUT plays the accumulator for 0.2 seconds, so a working fetch plays 300, 400 and 500 Hz and prints seven lines, the last PC=7 MAR=6 MDR=HALT CIR=HALT. The robot does not move.

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

memory = ["LOAD 7", "OUT", "ADD 8", "OUT", "ADD 8", "OUT", "HALT", 300, 100]
pc, mar, mdr, cir, acc = 0, 0, 0, "", 0

while True:
    # fetch: four register transfers, then print the registers
    cir = "HALT"

    # decode
    parts = cir.split()
    opcode = parts[0]
    # execute
    if opcode == "LOAD":
        mar = int(parts[1]); mdr = memory[mar]; acc = mdr
    elif opcode == "ADD":
        mar = int(parts[1]); mdr = memory[mar]; acc = acc + mdr
    elif opcode == "OUT":
        tone(acc, 0.2)
    elif opcode == "HALT":
        break

Challenges

  1. Add a BRANCH n instruction that sets the PC, and make the program play its three notes twice.
  2. Add STORE n with memory-mapped output: storing to address 99 plays the value as a note.
  3. Write out the register transfers for executing SUB 9.