Skip to content
Informatics

Computer architecture: von Neumann, memory and compilation

CPU, memory, input and output, the fetch-decode-execute cycle, the memory hierarchy and caches, from C code to executable, the Harvard architecture of microcontrollers.

Before writing programs it helps to know what happens inside the machine that runs them. From the microcontroller in a washing machine to a data centre server, almost every computer follows the same scheme, proposed by von Neumann in 1945: a processor, a memory holding both data and instructions, and input and output devices, connected by a bus. In this lesson we see how it is built, how it runs a program and what happens when a C program is compiled.

The von Neumann architecture

The von Neumann architecture with CPU, memory and input output connected by a bus
The CPU, with the arithmetic logic unit (ALU), the control unit (CU) and the registers, communicates over the bus with the RAM, which holds instructions and data at numbered addresses, and with the input and output devices.
  • CPU (central processing unit). The ALU performs arithmetic and logic operations; the control unit reads instructions and coordinates everything else; the registers are a few very fast memory cells inside the processor. Two special registers: the program counter (PC), holding the address of the next instruction, and the instruction register (IR), holding the one being executed.
  • Main memory (RAM). A long sequence of cells, each with an address. Each cell holds one byte (8 bits). It is volatile: it is wiped when power is lost.
  • Input and output. Keyboard, screen, network, disk, but also sensors and actuators in an embedded system.
  • Bus. The connections between the parts: address, data and control signals.

The fetch-decode-execute cycle

The processor repeats three steps forever, paced by the clock:

  1. fetch: read from memory the instruction at the address in the PC, and increment the PC;
  2. decode: the control unit interprets the instruction (which operation, on which registers or addresses);
  3. execute: the ALU performs the operation, and the result goes to a register or to memory.

A jump instruction changes the PC, and that is how conditions and loops are made. A 3 GHz processor runs billions of these cycles per second, and modern processors overlap several of them (pipelining) and execute several instructions at once.

The memory hierarchy

Fast memory is expensive, large memory is slow. That is why there are several levels:

LevelTypical sizeAccess time
registersa few hundred bytesunder 1 ns
L1, L2, L3 cachetens of kB to tens of MB1 to 40 ns
RAMgigabytesabout 100 ns
SSDterabytesabout 100 µs
hard diskterabytesabout 10 ms

A hard disk access is a hundred thousand times slower than a RAM access. Caches work because programs tend to reuse data they have just used (temporal locality) and nearby data (spatial locality): walking through an array in order is much faster than jumping around memory at random.

From source code to executable

The processor understands only machine language: bit sequences encoding the instructions, different for each processor family (x86, ARM, RISC-V). Writing machine language, or its readable form, assembly, is possible but slow. That is why high-level languages such as C are used, with a compiler to translate them.

The path of a C program, for example with gcc:

  1. preprocessor: expands the #include and #define directives;
  2. compiler: translates the C code into assembly and then into object code (.o files);
  3. linker: joins the object files together and with the libraries (such as the one containing printf) and produces the executable.
#include <stdio.h>

int main(void)
{
    int a = 3, b = 4;
    int c = a + b;          /* becomes a few machine instructions: load, add, store */
    printf("%d\n", c);
    return 0;
}

With gcc -S you can see the generated assembly: the sum becomes an add instruction between two registers. C is close to the machine: every construct has a predictable translation, which is why it is still the language of operating systems, drivers and microcontrollers.

Compiler and interpreter. Languages such as Python are instead interpreted: a program, the interpreter, reads the code and executes the statements one at a time. It is more convenient, but slower, often by tens of times.

Harvard and microcontrollers

Many microcontrollers, such as Arduino’s AVRs and PICs, use the Harvard architecture: separate memories and buses for instructions (Flash, non-volatile) and for data (RAM). The processor can read an instruction and a data item in the same cycle. ARM Cortex-M processors combine the two ideas: separate buses, but a single address space holding Flash, RAM and the peripheral registers, which are read and written like ordinary memory cells. This is what lets C control hardware directly, as we will see in the last lesson of the course.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
0
Would love your thoughts, please comment.x
()
x