Your technical library
Computer Architecture
13 modules · 64 topics
About this book
Computer Architecture
This course teaches what the processor underneath your code actually does — how a line of Python becomes instructions, how a chip made of switches executes them, why the same loop runs ten times faster when its data is sorted or laid out in an array, why adding cores stopped being free, and why a laptop still starts up pretending to be a 1978 microprocessor — so that you can read a profile or a disassembly, reason about performance with numbers instead of folklore, and understand the hardware the operating system is built to share. It is written for any software engineer who was never taught what the machine does with their code. No hardware background is assumed, and nothing is used before the topic that teaches it. It builds on three other courses on this site: System Design (the latency numbers and Amdahl's law it assumes), Data Structures (counting operations as a cost) and, where logic or expectation appears, Mathematics. It goes underneath the Operating Systems course: where that course teaches a piece of hardware as the kernel and your programs meet it — the stack, caches, coherence, memory ordering, speculation — this course shows how the hardware is built to behave that way, and links back to the lesson where it is applied. It is the recommended predecessor of Operating Systems: that course's lessons keep the programmer's view and link the topic here that builds each mechanism, so nothing below assumes you have read Operating Systems first.
The course runs top-down first, then bottom-up. The computer from the top draws the map: the levels of abstraction from transistors to applications, the stored-program design every computer since 1945 follows, the journey from source code through assembly and machine code into RAM — with the compiler and the operating system placed on the map — and the one equation that says what makes a program fast. The language of bits shows how numbers, signs and bytes are encoded. The instruction set teaches the contract between software and hardware on RISC-V — arithmetic, memory, branches, function calls and encodings — and ends by reading real compiler output. Then the course goes underneath the contract and builds a machine that honours it: logic and arithmetic circuits from transistors to adders, state and memory cells from flip-flops to DRAM, and building a processor from those parts, including the microcode that ran the 8086.
The next modules make that processor fast. Pipelining overlaps instructions and deals with the hazards that follow, including branch prediction and exceptions caught mid-flight; instruction-level parallelism issues several instructions per cycle, out of order and speculatively, and shares one core between two threads. The memory hierarchy shows why memory, not arithmetic, usually decides speed: caches, their misses, prefetching and DRAM, closing with the processor's own performance counters, which tell you which of the course's stalls a real program is hitting. Multicore explains why chips grew cores instead of gigahertz and what keeps their caches and memory consistent. Data parallelism covers SIMD instructions and GPUs. The x86 lineage follows the 8086's segments, modes and variable-length instructions into today's x86-64 cores, and buses, devices and power-on connects the processor to the outside world, follows it to the moment firmware hands the machine to an operating system, and closes by comparing a microcontroller such as an Arduino, which runs one program with no operating system, with a Raspberry Pi or laptop, which boots one — where the Operating Systems course begins. Along the way each topic says what it inherits, what it leaves open, who designed the idea and when, and how it shows up in decisions engineers make.
64 topics across 13 modules, in dependency order.
64 of 64 topics are authored. Read in dependency order. Each topic opens with the problem it inherits and closes with what it leaves open. Planned lessons are labelled and excluded from progress; references cover authored material.
Read at your own pace. Each topic is one continuous page; mark it done as you finish.
Start with 01.1 · Levels of abstraction: from applications down to transistorsReview what you have finished↷ 0 skipped topics- 01.1Levels of abstraction: from applications down to transistors
- 01.2The stored-program computer: instructions and data in one memory
- 01.3From source code to a running program: compiler, assembly, machine code, the OS and RAM
- 01.4What makes a program fast: instructions, cycles per instruction and clock period
- 03.1The instruction set architecture: the contract between software and hardware
- 03.2Arithmetic and logic instructions: computing in registers
- 03.3Loads, stores and addressing modes: arrays and structs in memory
- 03.4Branches and jumps: how conditionals and loops become instructions
- 03.5Functions and calling conventions: how a call returns
- 03.6Instruction encoding: how an instruction becomes a 32-bit number
- 03.7Reading compiler output: from C to RISC-V and x86-64
- 04.1Transistors and logic gates: switches that compute
- 04.2Combinational building blocks: decoders, multiplexers and the ALU
- 04.3Adders: from ripple carry to carry lookahead
- 04.4Multiplication hardware: shift-and-add, Booth and Wallace trees
- 04.5Division hardware: why division is slow, and dividing by a constant without dividing
- 04.6Floating-point addition in hardware: align, add, normalise, round
- 07.1Pipelining: overlapping instructions like an assembly line
- 07.2Data hazards and forwarding: using a result before it is written back
- 07.3Control hazards: what to fetch before a branch resolves
- 07.4Branch prediction: guessing which way the code goes
- 07.5Precise exceptions: stopping a pipeline as if instructions ran one at a time
- 07.6RISC-V trap entry: the CSRs a trap writes and mret
- 07.7Pipeline performance: where the cycles actually go
- 09.1The memory wall and locality: why a small fast memory works
- 09.2Cache organisation: lines, tags, sets and associativity
- 09.3Cache writes and replacement: write-back, write-through and LRU
- 09.4Classifying cache misses: compulsory, capacity and conflict
- 09.5Multi-level caches and average memory access time
- 09.6Hardware prefetching: fetching data before it is asked for
- 09.7DRAM organisation: channels, banks, rows and the row buffer
- 09.8Hardware performance counters: finding which stall a program pays for
- 10.1The power wall: why chips grew cores instead of gigahertz
- 10.2Shared-memory multiprocessors: private caches, shared caches and the on-chip network
- 10.3Cache coherence: keeping every core's copy of a line consistent
- 10.4Directory coherence: tracking sharers instead of broadcasting
- 10.5Memory consistency models: sequential consistency, TSO and fences
- 10.6Atomic operations in hardware: how read-modify-write stays indivisible
- 13.1Buses and interconnects: from a shared bus to point-to-point links
- 13.2I/O addressing and the physical address map
- 13.3I/O organisation: programmed I/O, interrupts, DMA and channels
- 13.4Power-on and firmware: from reset to a machine that can describe itself
- 13.5Handing off to an operating system: BIOS, UEFI and the boot loader
- 13.6Microcontrollers and microprocessors: an Arduino, a Raspberry Pi and where the OS fits
01.2The stored-program computer: instructions and data in one memory
01.3From source code to a running program: compiler, assembly, machine code, the OS and RAM
01.4What makes a program fast: instructions, cycles per instruction and clock period
02.1Binary and hexadecimal: positional numbers in base two
02.2Two's complement: signed integers one adder can handle
02.3Bitwise operations and shifts: masks, flags and fast arithmetic
02.4Byte order and alignment: how a word sits in memory
03.1The instruction set architecture: the contract between software and hardware
03.2Arithmetic and logic instructions: computing in registers
03.3Loads, stores and addressing modes: arrays and structs in memory
03.4Branches and jumps: how conditionals and loops become instructions
03.5Functions and calling conventions: how a call returns
03.6Instruction encoding: how an instruction becomes a 32-bit number
03.7Reading compiler output: from C to RISC-V and x86-64
04.1Transistors and logic gates: switches that compute
04.2Combinational building blocks: decoders, multiplexers and the ALU
04.3Adders: from ripple carry to carry lookahead
04.4Multiplication hardware: shift-and-add, Booth and Wallace trees
04.5Division hardware: why division is slow, and dividing by a constant without dividing
04.6Floating-point addition in hardware: align, add, normalise, round
05.1Latches and flip-flops: circuits that remember a bit
05.2Clocks and timing: what sets a processor's frequency
05.3SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy
06.1The single-cycle datapath: one instruction from fetch to write-back
06.2Hardwired control: decoding an instruction into control signals
06.3Microprogrammed control: a processor run by a program inside it
07.1Pipelining: overlapping instructions like an assembly line
07.2Data hazards and forwarding: using a result before it is written back
07.3Control hazards: what to fetch before a branch resolves
07.4Branch prediction: guessing which way the code goes
07.5Precise exceptions: stopping a pipeline as if instructions ran one at a time
07.6RISC-V trap entry: the CSRs a trap writes and mret
07.7Pipeline performance: where the cycles actually go
08.1Superscalar and VLIW: more than one instruction per cycle
08.2Out-of-order execution: register renaming and dynamic scheduling
08.3The reorder buffer and speculation: executing ahead, committing in order
08.4Simultaneous multithreading: two threads sharing one core
09.1The memory wall and locality: why a small fast memory works
09.2Cache organisation: lines, tags, sets and associativity
09.3Cache writes and replacement: write-back, write-through and LRU
09.4Classifying cache misses: compulsory, capacity and conflict
09.5Multi-level caches and average memory access time
09.6Hardware prefetching: fetching data before it is asked for
09.7DRAM organisation: channels, banks, rows and the row buffer
09.8Hardware performance counters: finding which stall a program pays for
10.1The power wall: why chips grew cores instead of gigahertz
10.2Shared-memory multiprocessors: private caches, shared caches and the on-chip network
10.3Cache coherence: keeping every core's copy of a line consistent
10.4Directory coherence: tracking sharers instead of broadcasting
10.5Memory consistency models: sequential consistency, TSO and fences
10.6Atomic operations in hardware: how read-modify-write stays indivisible
11.1SIMD and vector instructions: one instruction, many values
11.2Vector-length-agnostic code: strip mining, RISC-V V and auto-vectorisation
11.3GPU architecture: thousands of threads to hide memory latency
12.1The 8086: segmented addressing and the start of x86
12.2x86 operating modes: real, protected and long mode
12.3x86 decoding and micro-operations: a CISC front end on a RISC-like core
13.1Buses and interconnects: from a shared bus to point-to-point links
13.2I/O addressing and the physical address map
13.3I/O organisation: programmed I/O, interrupts, DMA and channels
13.4Power-on and firmware: from reset to a machine that can describe itself
13.5Handing off to an operating system: BIOS, UEFI and the boot loader
13.6Microcontrollers and microprocessors: an Arduino, a Raspberry Pi and where the OS fits
Skipped topics
Moved past without finishing. A skipped topic still closes its module, and Continue goes on without it — open one to read it or mark it understood.
Glossary
Terms as the lessons define them, each linked to the lesson that defines it.
abstraction
An abstraction is a simplified description of something that lets you use it without knowing how it works inside.
Go to Levels of abstraction: from applications down to transistors →Level of abstraction
A level of abstraction is one layer of a system described that way: it offers an interface — a set of operations and the promised effect of each — to the layer above, and it is built from the interface of the layer below.
Go to Levels of abstraction: from applications down to transistors →interface
A level of abstraction is one layer of a system described that way: it offers an interface — a set of operations and the promised effect of each — to the layer above, and it is built from the interface of the layer below.
Go to Levels of abstraction: from applications down to transistors →architecture
The architecture of a computer is its attributes as seen by the programmer — the conceptual structure and functional behaviour — as distinct from the organisation of its data flow and controls, its logical design and its physical implementation.
Go to Levels of abstraction: from applications down to transistors →microarchitecture
The instruction set architecture (ISA) is the architecture in the System/360 sense; the microarchitecture is the organisation of one chip that implements it.
Go to Levels of abstraction: from applications down to transistors →stored-program computer
A stored-program computer keeps its instructions in the same memory as its data, encoded as ordinary memory values, and executes them by fetching each one from memory at the address held in a program counter.
Go to The stored-program computer: instructions and data in one memory →program counter
A stored-program computer keeps its instructions in the same memory as its data, encoded as ordinary memory values, and executes them by fetching each one from memory at the address held in a program counter.
Go to The stored-program computer: instructions and data in one memory →von Neumann architecture
Von Neumann wrote it up in the "First Draft of a Report on the EDVAC", distributed on 30 June 1945 with only his name on it — which is why the design is called the von Neumann architecture, and why the name is disputed.
Go to The stored-program computer: instructions and data in one memory →memory
The memory holds both the program and its data, as numbers at numbered addresses.
Go to The stored-program computer: instructions and data in one memory →control unit
The control unit fetches the instruction the program counter points to, works out what it asks for, and directs the other parts to do it.
Go to The stored-program computer: instructions and data in one memory →Arithmetic logic unit
The arithmetic logic unit (ALU; the report's central arithmetic part) does the arithmetic (add, subtract) and the logic (AND, OR, compare) the instruction asks for.
Go to The stored-program computer: instructions and data in one memory →central processing unit
The control unit and the ALU together are the central processing unit, or processor.
Go to The stored-program computer: instructions and data in one memory →Harvard architecture
The Mark I's arrangement — instructions and data in separate memories, reached by separate paths — is called the Harvard architecture.
Go to The stored-program computer: instructions and data in one memory →Register
With several registers — small, fast storage places inside the processor, named by the instruction — the processor reads each input once, keeps every intermediate value inside, and writes once.
Go to The stored-program computer: instructions and data in one memory →stack machine
A stack machine keeps operands on a stack inside the processor, and its arithmetic instructions name no operands at all — they take the top two values and push the result.
Go to The stored-program computer: instructions and data in one memory →Bytecode
That makes code compact, and survives today in virtual machines: Java bytecode, CPython's bytecode and WebAssembly all describe a stack machine. (Bytecode is a compact instruction format that a language's runtime executes instead of the processor; CPython is the standard Python implementation; WebAssembly is a portable low-level instruction format that browsers run.) The runtime either interprets such instructions…
Go to The stored-program computer: instructions and data in one memory →von Neumann bottleneck
John Backus, who had led the team that created Fortran, named this in his 1977 Turing Award lecture, published in 1978: the von Neumann bottleneck, the narrow channel through which a computer must pass every word it works on.
Go to The stored-program computer: instructions and data in one memory →compiler
A compiler is a program that translates source code in a language like C into the instructions of one instruction set.
Go to From source code to a running program: compiler, assembly, machine code, the OS and RAM →assembly language
That is assembly language: the instructions written as text, one line per instruction, with names for operations (addi) and registers (a1).
Go to From source code to a running program: compiler, assembly, machine code, the OS and RAM →assembler
An assembler is the program that turns that text into machine code — the same instructions as the binary numbers the processor actually decodes.
Go to From source code to a running program: compiler, assembly, machine code, the OS and RAM →machine code
An assembler is the program that turns that text into machine code — the same instructions as the binary numbers the processor actually decodes.
Go to From source code to a running program: compiler, assembly, machine code, the OS and RAM →execution time
So define the execution time of a program on a machine as the elapsed time from its start to its finish, and define performance as its reciprocal: a machine that runs the program in half the time has twice the performance.
Go to What makes a program fast: instructions, cycles per instruction and clock period →clock
A processor steps forward on the ticks of a clock, a signal that alternates at a fixed rate; the span from one tick to the next is one clock cycle.
Go to What makes a program fast: instructions, cycles per instruction and clock period →clock period
The time between ticks is the clock period and its reciprocal is the clock frequency : a 3 GHz processor has ticks per second and ns (nanoseconds, billionths of a second) — about the time light takes to travel ten centimetres in a vacuum, and a tiny fraction of the latency numbers System Design works with.
Go to What makes a program fast: instructions, cycles per instruction and clock period →clock frequency
The time between ticks is the clock period and its reciprocal is the clock frequency : a 3 GHz processor has ticks per second and ns (nanoseconds, billionths of a second) — about the time light takes to travel ten centimetres in a vacuum, and a tiny fraction of the latency numbers System Design works with.
Go to What makes a program fast: instructions, cycles per instruction and clock period →dynamic instruction count
The first ratio is the dynamic instruction count : the number of instructions the processor actually executes on this input — a loop body run a million times counts a million times — not the number of instructions in the executable file.
Go to What makes a program fast: instructions, cycles per instruction and clock period →cycles per instruction
The second is the average cycles per instruction, , averaged over that same execution.
Go to What makes a program fast: instructions, cycles per instruction and clock period →Latency
Latency is the time to finish one task — the above, or one request's response time.
Go to What makes a program fast: instructions, cycles per instruction and clock period →Throughput
Throughput is how many tasks finish per unit time.
Go to What makes a program fast: instructions, cycles per instruction and clock period →bit
A bit is a binary digit: a symbol that takes one of two values.
Go to Binary and hexadecimal: positional numbers in base two →word width
Write a pattern , where is the word width — the number of bits the encoding uses — and each is 0 or 1.
Go to Binary and hexadecimal: positional numbers in base two →most significant bit
The leftmost bit carries the largest weight and is the most significant bit (MSB); is the least significant bit (LSB).
Go to Binary and hexadecimal: positional numbers in base two →least significant bit
The leftmost bit carries the largest weight and is the most significant bit (MSB); is the least significant bit (LSB).
Go to Binary and hexadecimal: positional numbers in base two →unsigned overflow
When the true sum exceeds , that dropped carry is the unsigned overflow: the stored result is correct modulo and wrong as a plain number.
Go to Binary and hexadecimal: positional numbers in base two →nibble
The fix exploits a coincidence of bases: sixteen is , so one hexadecimal digit encodes exactly four bits (a group of four bits is a nibble), and converting between binary and hex needs only a lookup per digit, never arithmetic across digit boundaries.
Go to Binary and hexadecimal: positional numbers in base two →octal
Older documentation also uses octal, base eight — three bits per digit — which survives today in chmod 755 (chmod, "change mode", is the Unix command that sets who may read, write or execute a file), where each octal digit is literally the three permission bits rwx (read, write, execute).
Go to Binary and hexadecimal: positional numbers in base two →byte
A byte is a group of bits treated as one unit of data — eight bits almost everywhere since the IBM System/360 of 1964 made that size the standard.
Go to Binary and hexadecimal: positional numbers in base two →binary prefixes
In December 1998 the IEC adopted distinct binary prefixes: KiB (kibibyte) is , MiB is , GiB is , while kB, MB and GB keep their strict decimal meaning.
Go to Binary and hexadecimal: positional numbers in base two →sign-magnitude
The obvious scheme is sign-magnitude: dedicate the top bit to the sign and read the rest as the magnitude, so 4-bit is and is .
Go to Two's complement: signed integers one adder can handle →Ones' complement
Ones' complement fixes the operation: negate by flipping every bit, so is .
Go to Two's complement: signed integers one adder can handle →biased encoding
A biased encoding shifts the whole range instead of the sign: the stored pattern is value plus a fixed bias — with bias 7 on 4 bits, pattern encodes and all-ones encodes .
Go to Two's complement: signed integers one adder can handle →two's complement
In -bit two's complement, a pattern stands for : the top bit carries a negative weight while the rest stay positive.
Go to Two's complement: signed integers one adder can handle →sign extension
For unsigned numbers the answer is zeros; for two's complement it is copies of the sign bit, called sign extension.
Go to Two's complement: signed integers one adder can handle →Signed overflow
Signed overflow happens precisely when those two carries differ — equivalently, when two same-sign operands produce an opposite-sign result.
Go to Two's complement: signed integers one adder can handle →time
The harder case is time.
Go to Two's complement: signed integers one adder can handle →Bitwise operation
A bitwise operation applies one of those operators to a whole word at once: bit of the result is computed from bit of each input, independently of every other position.
Go to Bitwise operations and shifts: masks, flags and fast arithmetic →mask
A mask is a word whose bits mark the positions of interest.
Go to Bitwise operations and shifts: masks, flags and fast arithmetic →shift
A shift moves every bit by positions.
Go to Bitwise operations and shifts: masks, flags and fast arithmetic →Endianness
Which byte goes at which address is the byte order, or endianness.
Go to Byte order and alignment: how a word sits in memory →little-endian
In little-endian order the least significant byte — the one worth in the value — sits at the lowest address; in big-endian order the most significant byte does.
Go to Byte order and alignment: how a word sits in memory →big-endian
In little-endian order the least significant byte — the one worth in the value — sits at the lowest address; in big-endian order the most significant byte does.
Go to Byte order and alignment: how a word sits in memory →Network byte order
Big-endian survives as the order of interchange: network byte order, fixed by the Internet's protocol convention (RFC 1700, 1994), transmits every multi-octet field most significant octet first (an octet is a byte, the name networking standards use because "byte" once varied in size).
Go to Byte order and alignment: how a word sits in memory →Natural alignment
A value of bytes is naturally aligned when its address is a multiple of : a 4-byte integer at an address divisible by 4, an 8-byte double at one divisible by 8.
Go to Byte order and alignment: how a word sits in memory →padding
C guarantees that struct members lie at increasing addresses in declaration order, each at an offset divisible by its alignment; the compiler may insert padding between members and after the last one — never before the first.
Go to Byte order and alignment: how a word sits in memory →Architectural state
This is the architectural state: every bit of the machine that software can see or change.
Go to The instruction set architecture: the contract between software and hardware →instruction set architecture
An instruction set architecture (ISA) is the precise interface between software and hardware: the architectural state a program may observe — registers, the program counter and byte-addressed memory — together with the effect of every instruction on that state.
Go to The instruction set architecture: the contract between software and hardware →Immediate value
Sources may be registers or immediates: constant values spelled inside the instruction itself.
Go to The instruction set architecture: the contract between software and hardware →Reduced instruction set computer
A reduced instruction set computer (RISC) offers few, regular instructions: arithmetic operates on registers only, memory is reached solely by load and store, and every instruction has one fixed length.
Go to The instruction set architecture: the contract between software and hardware →Complex instruction set computer
A complex instruction set computer (CISC) offers many — hundreds — of irregular instructions, including ones that take memory operands and implement statement-level operations.
Go to The instruction set architecture: the contract between software and hardware →RISC-V base ISA
A chip picks a base ISA — RV32I, RV64I or RV128I, fixing the register and address width, with RV32E/RV64E as 16-register embedded variants — then adds lettered extensions for what it needs: M for integer multiply and divide, A for atomics (read-modify-write instructions that other cores cannot interleave with, met in atomic operations in hardware), F and D for single- and double-precision floating point, C for com…
Go to The instruction set architecture: the contract between software and hardware →RISC-V extension
A chip picks a base ISA — RV32I, RV64I or RV128I, fixing the register and address width, with RV32E/RV64E as 16-register embedded variants — then adds lettered extensions for what it needs: M for integer multiply and divide, A for atomics (read-modify-write instructions that other cores cannot interleave with, met in atomic operations in hardware), F and D for single- and double-precision floating point, C for com…
Go to The instruction set architecture: the contract between software and hardware →Pseudo-instruction
A pseudo-instruction is an assembly mnemonic with no encoding of its own: the assembler expands it into a real instruction (or sequence) before the hardware ever sees it. mv (move), nop (no operation), not, li (load immediate) and ret (return from a function, built in functions and calling conventions) are all pseudo-instructions — conveniences that cost the ISA no opcodes because they are special cases of instruc…
Go to Arithmetic and logic instructions: computing in registers →RISC-V M extension
Multiplication and division are not in the base ISA at all — they are the standard M extension (version 2.0), kept outside so that a low-end core can ship RV32I without a divider; a multiply-only subset, Zmmul (one of the Z-named extensions the previous topic mentioned), exists for chips that want products but not quotients.
Go to Arithmetic and logic instructions: computing in registers →Flags register
And most arithmetic instructions silently write a second destination: the flags register eflags (extended flags), whose bits — zero, sign, carry, overflow — a later conditional jump reads.
Go to Arithmetic and logic instructions: computing in registers →Load instruction
An ISA in which arithmetic instructions operate only on registers and only dedicated load and store instructions access memory.
Go to Loads, stores and addressing modes: arrays and structs in memory →Store instruction
An ISA in which arithmetic instructions operate only on registers and only dedicated load and store instructions access memory.
Go to Loads, stores and addressing modes: arrays and structs in memory →effective address
The operand imm(rs1) reads "the effective address formed by adding imm to register rs1".
Go to Loads, stores and addressing modes: arrays and structs in memory →Addressing mode
Every field is optional, and each combination that is left gets its own addressing mode — a rule by which the instruction computes where its operand lives:
Go to Loads, stores and addressing modes: arrays and structs in memory →conditional branch
A conditional branch breaks the pattern: it compares two registers, and only if the comparison holds does it write a different value into the PC — a target address.
Go to Branches and jumps: how conditionals and loops become instructions →PC-relative addressing
Addressing by distance from the current instruction is PC-relative addressing, and it buys a useful property: a whole block of code can be relocated without touching any of its branches, because the target moves with the branch itself.
Go to Branches and jumps: how conditionals and loops become instructions →Jump instruction
The unconditional jump exists for everything a branch cannot express: skipping the else arm, jumping back to a loop head, leaving the region entirely. jal rd, label — jump and link, "link" meaning it saves a link back to where it came from — jumps to PC + sext(imm) with a 20-bit two-byte-unit field, reaching MiB, and also writes PC+4 into rd: the address of the instruction that comes after the jump — the…
Go to Branches and jumps: how conditionals and loops become instructions →jump and link
The unconditional jump exists for everything a branch cannot express: skipping the else arm, jumping back to a loop head, leaving the region entirely. jal rd, label — jump and link, "link" meaning it saves a link back to where it came from — jumps to PC + sext(imm) with a 20-bit two-byte-unit field, reaching MiB, and also writes PC+4 into rd: the address of the instruction that comes after the jump — the…
Go to Branches and jumps: how conditionals and loops become instructions →Compare instruction
On x86 (again 32-bit, Intel syntax; eax, ebx and edx are general registers) a dedicated compare instruction does the arithmetic first: cmp eax, ebx computes eax − ebx and throws the result away, keeping only what the subtraction did to a set of one-bit flags in a register called EFLAGS — ZF set if the result was zero (the operands were equal), SF copying its sign bit, CF the borrow, OF the overflow.
Go to Branches and jumps: how conditionals and loops become instructions →Condition codes
Condition codes (or status flags) are a small set of bits — typically sign, zero, carry, overflow — that arithmetic instructions write as a side effect and later instructions read.
Go to Branches and jumps: how conditionals and loops become instructions →delay slot
Another RISC family took the opposite escape: MIPS — the RISC design John Hennessy's group began at Stanford in 1981, later a commercial processor family — declared that the instruction after a branch always executes (the famous delay slot), exposing the pipeline's fetch-ahead in the contract; RISC-V forbids architecturally visible delay slots and keeps the contract clean of implementation.
Go to Branches and jumps: how conditionals and loops become instructions →return address
The return address is that saved address — on RISC-V, always the current PC plus 4, the next 32-bit instruction.
Go to Functions and calling conventions: how a call returns →closed subroutine
A closed subroutine — David Wheeler's term in the programming manual of 1950 for EDSAC, the Cambridge stored-program computer of 1949 (the stored-program computer) — is a block of code that can be entered from anywhere and returns to the instruction after the call.
Go to Functions and calling conventions: how a call returns →activation record
An activation record (or stack frame) is the block of memory holding one call's private state: the saved return address, the callee-saved registers it will overwrite, its local variables and spill slots, and room for arguments it passes to further calls.
Go to Functions and calling conventions: how a call returns →stack frame
An activation record (or stack frame) is the block of memory holding one call's private state: the saved return address, the callee-saved registers it will overwrite, its local variables and spill slots, and room for arguments it passes to further calls.
Go to Functions and calling conventions: how a call returns →call stack
The call stack is the contiguous region of memory that holds the chain of live frames — one per call that has started and not yet returned.
Go to Functions and calling conventions: how a call returns →prologue
A frame's contents arrive in a fixed pattern — the prologue allocates the frame and saves what the callee will use; the epilogue restores it and releases the frame:
Go to Functions and calling conventions: how a call returns →epilogue
A frame's contents arrive in a fixed pattern — the prologue allocates the frame and saves what the callee will use; the epilogue restores it and releases the frame:
Go to Functions and calling conventions: how a call returns →frame pointer
Some compilers also keep a frame pointer — a second register (s0 reused for the job) that points at the top of the frame so locals have stable offsets while sp moves.
Go to Functions and calling conventions: how a call returns →calling convention
Anything either side silently relies on must be agreed in advance, once, for everyone: that agreement is the calling convention — the part of the ABI (application binary interface) that divides the registers into jobs and says who preserves what across a call.
Go to Functions and calling conventions: how a call returns →ABI
Anything either side silently relies on must be agreed in advance, once, for everyone: that agreement is the calling convention — the part of the ABI (application binary interface) that divides the registers into jobs and says who preserves what across a call.
Go to Functions and calling conventions: how a call returns →caller-saved
The preserved? column is the clever part — the split between caller-saved registers (ra, t0–t6, a0–a7: a call may clobber them, so the caller must save any value it needs) and callee-saved registers (s0–s11, sp: a call must leave them as it found them, so the callee saves before using).
Go to Functions and calling conventions: how a call returns →callee-saved
The preserved? column is the clever part — the split between caller-saved registers (ra, t0–t6, a0–a7: a call may clobber them, so the caller must save any value it needs) and callee-saved registers (s0–s11, sp: a call must leave them as it found them, so the callee saves before using).
Go to Functions and calling conventions: how a call returns →inlining
The same reasoning explains inlining — copying a function's body into its caller: at -O1 without -fno-inline, GCC simply deletes the calls and produces mulw a1,a1,a1; mulw a0,a0,a0; addw a0,a1,a0; ret — the whole contract's frame-and-shuffle machinery avoided by doing the callee's work in place.
Go to Functions and calling conventions: how a call returns →red zone
Two details differ quietly underneath: the SysV ABI keeps a 128-byte red zone below rsp that a leaf function may use without moving the pointer (RISC-V's psABI defines none), and the RISC-V convention carries the push/pop hint for the return-address predictor inside the register numbers rather than in dedicated instructions.
Go to Functions and calling conventions: how a call returns →accumulator
One-address (accumulator) machines name a single operand; the other source and the destination are an implied register, the accumulator: add B means acc := acc + [B].
Go to Instruction encoding: how an instruction becomes a 32-bit number →format
A format is a fixed assignment of bit positions to fields; the bottom seven bits of every base instruction are its opcode, which names the format — the decoder reads the opcode first and learns which of six layouts the remaining 25 bits follow.
Go to Instruction encoding: how an instruction becomes a 32-bit number →opcode
A format is a fixed assignment of bit positions to fields; the bottom seven bits of every base instruction are its opcode, which names the format — the decoder reads the opcode first and learns which of six layouts the remaining 25 bits follow.
Go to Instruction encoding: how an instruction becomes a 32-bit number →immediate
Instructions that carry a constant — an immediate — replace the rightmost fields.
Go to Instruction encoding: how an instruction becomes a 32-bit number →expanding opcode
An expanding opcode (variable-length opcode) does the latter: one opcode pattern is reserved as an escape code, and when it appears the hardware reads the unused operand field as a second opcode.
Go to Instruction encoding: how an instruction becomes a 32-bit number →escape code
An expanding opcode (variable-length opcode) does the latter: one opcode pattern is reserved as an escape code, and when it appears the hardware reads the unused operand field as a second opcode.
Go to Instruction encoding: how an instruction becomes a 32-bit number →ModR/M
Nothing in nature says instructions must share one length. x86-64 — which this course contrasts throughout — encodes an instruction as a sequence of 1 to 15 bytes: optional prefixes (0–4 bytes that modify the instruction, for instance changing the operand size), an opcode (1–3), a ModR/M byte (which registers or memory operands), an optional SIB byte (scaled index + base addressing), an optional displacement, an o…
Go to Instruction encoding: how an instruction becomes a 32-bit number →SIB
Nothing in nature says instructions must share one length. x86-64 — which this course contrasts throughout — encodes an instruction as a sequence of 1 to 15 bytes: optional prefixes (0–4 bytes that modify the instruction, for instance changing the operand size), an opcode (1–3), a ModR/M byte (which registers or memory operands), an optional SIB byte (scaled index + base addressing), an optional displacement, an o…
Go to Instruction encoding: how an instruction becomes a 32-bit number →disassembler
The text form of those numbers — one line per instruction, mnemonics for opcodes, names for registers — is the assembly language of the ISA, and a disassembler recovers it from a compiled binary by running the encoding tables backwards.
Go to Reading compiler output: from C to RISC-V and x86-64 →Compiler Explorer
The practical way to read compiler output is Compiler Explorer, the public website at godbolt.org: paste source on the left, pick a compiler and version, and the right pane shows the assembly it emits — the line that produced each instruction highlighted against the source.
Go to Reading compiler output: from C to RISC-V and x86-64 →leaf function
The two frames show the convention's price and its discount: sum_elems saves ra because its call will overwrite it, while elem — a leaf function, one that calls nothing — saves only the s0 it uses.
Go to Reading compiler output: from C to RISC-V and x86-64 →strength reduction
That is strength reduction — the multiply-per-iteration replaced by an add — and induction-variable elimination rolled into one, and the result is worth staring at: lw / addi / add / bne, four instructions per iteration, character-for-character the pointer loop the branches lesson derived by hand as the best it could write.
Go to Reading compiler output: from C to RISC-V and x86-64 →transistor
The modern form is the transistor: the first working one was the point-contact device John Bardeen and Walter Brattain demonstrated at Bell Telephone Laboratories in December 1947, in William Shockley's group — work the three shared the 1956 Nobel Prize in Physics for — and the variant in every chip you have ever used is the MOS transistor, first made to work reliably by Mohamed Atalla and Dawon Kahng at Bell Labs…
Go to Transistors and logic gates: switches that compute →semiconductor
Silicon is a semiconductor: it conducts far worse than a metal and far better than an insulator, and its conductance can be tuned deliberately.
Go to Transistors and logic gates: switches that compute →N-type semiconductor
Mixing in an atom like phosphorus, which carries a spare electron, gives n-type silicon, whose carriers are electrons; mixing in boron leaves a deficit of electrons — mobile empty sites called holes — and gives p-type silicon, whose carriers behave as positive charges.
Go to Transistors and logic gates: switches that compute →P-type semiconductor
Mixing in an atom like phosphorus, which carries a spare electron, gives n-type silicon, whose carriers are electrons; mixing in boron leaves a deficit of electrons — mobile empty sites called holes — and gives p-type silicon, whose carriers behave as positive charges.
Go to Transistors and logic gates: switches that compute →MOS transistor
Building one is not our concern; the MOS transistor (metal–oxide–semiconductor field-effect transistor) is what doping buys, and it works like this.
Go to Transistors and logic gates: switches that compute →source
An NMOS transistor (n-type MOS: its working carriers are electrons) sits on p-type silicon with two n-type regions, the source and the drain, separated by a channel.
Go to Transistors and logic gates: switches that compute →drain
An NMOS transistor (n-type MOS: its working carriers are electrons) sits on p-type silicon with two n-type regions, the source and the drain, separated by a channel.
Go to Transistors and logic gates: switches that compute →gate
Over the channel lies a conducting plate, the gate, insulated from the silicon by a thin film of silicon dioxide — so no current flows into the gate itself.
Go to Transistors and logic gates: switches that compute →CMOS
The complementary scheme is called CMOS (complementary MOS), and it was invented for exactly this reason.
Go to Transistors and logic gates: switches that compute →logic gate
Each is a logic gate: a device that implements a fixed Boolean function (a function whose inputs and output are each 0 or 1), whatever it is built from — silicon today, relays and vacuum tubes before it.
Go to Transistors and logic gates: switches that compute →truth table
A table like this one, listing a function's output for every input combination, is a truth table — the specification any circuit must match.
Go to Transistors and logic gates: switches that compute →Boolean algebra
A Boolean algebra, in the two-valued form circuits use, is the set with operations AND (), OR () and NOT (), governed by the rules below: identity, annulment, idempotence, complementarity, commutativity, associativity, distributivity and double negation.
Go to Transistors and logic gates: switches that compute →minterm
Each 1-row contributes one AND of all three inputs — a minterm, in which every variable appears once, plain or complemented, so that it is 1 on exactly that row.
Go to Transistors and logic gates: switches that compute →Sum of products
This is the sum-of-products (or canonical) form, and it is a construction recipe, not just a formula: wire each minterm to an AND gate and OR the outputs.
Go to Transistors and logic gates: switches that compute →combinational circuit
A combinational circuit is a network of gates with no loops, whose outputs are a function of its current inputs alone — feed it the same inputs and it settles to the same outputs, always.
Go to Combinational building blocks: decoders, multiplexers and the ALU →decoder
A decoder takes such an input and asserts (drives to 1) exactly one of output lines — the one whose index the input encodes — a one-hot answer to "which one?".
Go to Combinational building blocks: decoders, multiplexers and the ALU →One-hot encoding
A decoder takes such an input and asserts (drives to 1) exactly one of output lines — the one whose index the input encodes — a one-hot answer to "which one?".
Go to Combinational building blocks: decoders, multiplexers and the ALU →multiplexer
The multiplexer (mux) asks the opposite question: given data inputs and an -bit select, copy input number to the single output — .
Go to Combinational building blocks: decoders, multiplexers and the ALU →encoder
The encoder is the decoder's exact inverse: input lines, of which exactly one is asserted, produce the -bit index of that line.
Go to Combinational building blocks: decoders, multiplexers and the ALU →priority encoder
The priority encoder answers instead with the index of the highest-priority asserted line.
Go to Combinational building blocks: decoders, multiplexers and the ALU →ALU slice
An arithmetic logic unit slice handles one bit of a word: it computes every offered operation on that bit and lets a mux pick the answer.
Go to Combinational building blocks: decoders, multiplexers and the ALU →propagation delay
A circuit's propagation delay is the worst-case time from an input change to the corresponding settled output.
Go to Combinational building blocks: decoders, multiplexers and the ALU →critical path
The path that sets it — the longest chain of gates any signal must traverse — is the critical path.
Go to Combinational building blocks: decoders, multiplexers and the ALU →sum
Adding two bits and produces two outputs: a sum bit and a carry bit .
Go to Adders: from ripple carry to carry lookahead →carry
Adding two bits and produces two outputs: a sum bit and a carry bit .
Go to Adders: from ripple carry to carry lookahead →half adder
A half adder is the two-gate circuit computing and from two input bits.
Go to Adders: from ripple carry to carry lookahead →full adder
A full adder adds three bits — , and a carry-in — and produces a sum and a carry-out.
Go to Adders: from ripple carry to carry lookahead →Ripple-carry adder
This is the ripple-carry adder, and it is exactly the column addition you learned at school: work right to left, carry into the next column.
Go to Adders: from ripple carry to carry lookahead →generate
Define two signals per bit — the generate and the propagate — and the carry recurrence collapses to one line:
Go to Adders: from ripple carry to carry lookahead →propagate
Define two signals per bit — the generate and the propagate — and the carry recurrence collapses to one line:
Go to Adders: from ripple carry to carry lookahead →Carry-lookahead adder
That is the carry-lookahead adder.
Go to Adders: from ripple carry to carry lookahead →partial product
Each row is a partial product — — and the product is their sum.
Go to Multiplication hardware: shift-and-add, Booth and Wallace trees →shift register
Together they form one long shift register — a register whose contents can all move one position sideways in a single step.
Go to Multiplication hardware: shift-and-add, Booth and Wallace trees →Carry-save adder
This carry-save adder computes in constant depth — the carry is not propagated, just handed to the next level as another summand, shifted one place.
Go to Multiplication hardware: shift-and-add, Booth and Wallace trees →restoring division
The "undo" of a failed subtraction is the restore step that names restoring division:
Go to Division hardware: why division is slow, and dividing by a constant without dividing →Non-restoring division
Non-restoring division removes the waste by a one-step identity: restoring computes , then restores to , then next step computes .
Go to Division hardware: why division is slow, and dividing by a constant without dividing →Magic number
The constant is a magic number: a reciprocal scaled up by and rounded up to an integer.
Go to Division hardware: why division is slow, and dividing by a constant without dividing →barrel shifter
A barrel shifter — a shifter built from rows of multiplexers that moves by any amount in one pass rather than one place per step — does this, and the bits shifted out are not discarded but kept in guard, round and sticky positions for the last stage.
Go to Floating-point addition in hardware: align, add, normalise, round →leading-zero counter
If it overflowed its top bit, shift right once and add one to the exponent; if it went below 1 (cancellation shrank it), shift left — a leading-zero counter, a small circuit that counts how many 0 bits precede the first 1, tells the shifter how far — and subtract the same amount from the exponent.
Go to Floating-point addition in hardware: align, add, normalise, round →guard bit
the guard bit , the first bit beyond the stored significand — the one that says whether the residue is at least a half;
Go to Floating-point addition in hardware: align, add, normalise, round →round bit
the round bit , the second bit beyond — kept so that a one-place left shift (met below) can promote into the significand and still leave a guard bit behind;
Go to Floating-point addition in hardware: align, add, normalise, round →sticky bit
the sticky bit , the OR of every bit further down: once a 1 is shifted into it, it stays 1 however much further the operand shifts.
Go to Floating-point addition in hardware: align, add, normalise, round →latch
A latch is a level-sensitive storage element built by feeding one or more gate outputs back into their inputs: so long as its enabling condition holds, its outputs follow its inputs, and when the condition releases it holds the last value it saw.
Go to Latches and flip-flops: circuits that remember a bit →bistable
Its underlying bistable structure — a feedback loop with exactly two stable resting states — is what makes the stored bit self-sustaining.
Go to Latches and flip-flops: circuits that remember a bit →SR latch
The cross-coupled pair of NOR gates above is the SR latch, for set and reset.
Go to Latches and flip-flops: circuits that remember a bit →enable
In a machine, state should change only at moments we choose, so the first refinement is an enable: AND each control input with an enable line, giving and .
Go to Latches and flip-flops: circuits that remember a bit →D latch
This is the D latch (D for data): while , follows a couple of gate delays behind it; when , keeps whatever was at the moment fell.
Go to Latches and flip-flops: circuits that remember a bit →master
The first latch — the master — is transparent while the clock is high; the second — the slave — is transparent while the clock is low:
Go to Latches and flip-flops: circuits that remember a bit →slave
The first latch — the master — is transparent while the clock is high; the second — the slave — is transparent while the clock is low:
Go to Latches and flip-flops: circuits that remember a bit →flip-flop
A flip-flop is an edge-triggered storage element: it samples its data input at a clock edge — the instant the clock signal changes, either from low to high (rising) or from high to low (falling), by design — and its output changes only in response to that edge.
Go to Latches and flip-flops: circuits that remember a bit →register file
The part that does this is the register file, and it is assembled entirely from pieces already on the table:
Go to Latches and flip-flops: circuits that remember a bit →synchronous
A design where every storage element obeys this contract is synchronous: the machine's state changes only at edges, never in between, so the cycle is the unit of progress and the whole system can be reasoned about one register-to-register hop at a time.
Go to Clocks and timing: what sets a processor's frequency →asynchronously
Real machines cannot live inside that assumption: a key press, a network packet, a sensor interrupt (a signal asking the processor's attention), a signal driven by a different oscillator — all arrive asynchronously, on no schedule at all.
Go to Clocks and timing: what sets a processor's frequency →metastability
This is metastability.
Go to Clocks and timing: what sets a processor's frequency →synchronizer
The standard defence is the synchronizer — two flip-flops in series between the asynchronous input and the logic that consumes it.
Go to Clocks and timing: what sets a processor's frequency →bit line
To make that loop writable and readable, connect each internal node to an outside wire — a bit line, one for each node — through one more transistor whose gate is driven by a word line: while the word line is low the cell is sealed and remembers; while it is high, the two access transistors open and the cell can be sensed or forced.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →word line
To make that loop writable and readable, connect each internal node to an outside wire — a bit line, one for each node — through one more transistor whose gate is driven by a word line: while the word line is low the cell is sealed and remembers; while it is high, the two access transistors open and the cell can be sensed or forced.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →access transistors
To make that loop writable and readable, connect each internal node to an outside wire — a bit line, one for each node — through one more transistor whose gate is driven by a word line: while the word line is low the cell is sealed and remembers; while it is high, the two access transistors open and the cell can be sensed or forced.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →SRAM cell
Six transistors in total — this is the SRAM cell.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →write drivers
The bit lines are driven as complements — to the value being written, to its opposite — by write drivers built deliberately stronger than the cell's small inverters.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →sense amplifier
A sense amplifier — a comparator watching the pair — waits only until the difference crosses a small threshold, not until either line reaches a rail.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →row decoder
The high bits feed a row decoder that asserts exactly one word line, waking a whole row of cells onto their bit lines; the low bits feed a column decoder whose multiplexers pass only the wanted columns on to the sense amplifiers and write drivers.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →column decoder
The high bits feed a row decoder that asserts exactly one word line, waking a whole row of cells onto their bit lines; the low bits feed a column decoder whose multiplexers pass only the wanted columns on to the sense amplifiers and write drivers.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →SRAM
SRAM (static random-access memory) is a memory built from bistable cells — typically the six-transistor cell above — that hold their value for as long as power is applied, with no periodic maintenance. "Static" names the storage contract, not the interface: the value does not decay.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →DRAM cell
In 1966 Robert Dennard at IBM asked what a bit of storage minimally needs, and his answer fits in two components: a DRAM cell is one access transistor plus one capacitor, a component that stores electric charge like a tiny bucket.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →refresh
The fix is periodic refresh: every cell must be read and written back before its charge fades, and the standard that DRAM chips follow, set by the industry body JEDEC, fixes that deadline at per row at normal operating temperatures for the DDR3 and DDR4 generations (half that when the chip runs hot, and tighter again in newer generations).
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →DRAM
DRAM (dynamic random-access memory) is a memory whose cells store each bit as charge on a capacitor guarded by one transistor.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →Row buffer
Because the sense amplifiers on the array's edge hold the entire activated row after a read — hundreds to thousands of bits, called the row buffer or DRAM page — a second access into the same row skips the slow cell-sensing step entirely and is served straight out of the amplifiers.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →page
Because the sense amplifiers on the array's edge hold the entire activated row after a read — hundreds to thousands of bits, called the row buffer or DRAM page — a second access into the same row skips the slow cell-sensing step entirely and is served straight out of the amplifiers.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →SDRAM
Early DRAM answered whenever it was asked, but modern parts are SDRAM — synchronous DRAM, marched by the same clock discipline as everything else — and since DDR ("double data rate") they transfer data on both clock edges.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →content-addressable memory
A hash table in software does this by computing where a key should be; a content-addressable memory (CAM) does it by asking every row at once.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →CAM cell
A CAM cell is an SRAM cell with four extra transistors — ten in total — forming a tiny comparator: if the stored bit differs from the search bit broadcast on dedicated lines, the transistors open a conducting path that discharges the row's shared match line.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →match line
A CAM cell is an SRAM cell with four extra transistors — ten in total — forming a tiny comparator: if the stored bit differs from the search bit broadcast on dedicated lines, the transistors open a conducting path that discharges the row's shared match line.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →TLB
They earn their place where a lookup must be single-cycle and exhaustive: the TLB, which maps virtual pages to physical frames (the operating system gives each program imaginary, virtual, addresses, and the hardware translates them to real, physical, memory locations) and is searched by page number on every memory access — a CAM wherever it is built fully associative, as small first-level TLBs often are (the Opera…
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →Non-volatile memory
These want non-volatile memory, and the simplest form is the simplest cell of all: a mask ROM is a DRAM-shaped array where each cell's transistor is hardwired at manufacture (by the masks, the patterns that define each layer of the chip, made for that one design) to supply or to ground — the "capacitor" is a permanent connection that decides the bit once, forever.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →mask ROM
These want non-volatile memory, and the simplest form is the simplest cell of all: a mask ROM is a DRAM-shaped array where each cell's transistor is hardwired at manufacture (by the masks, the patterns that define each layer of the chip, made for that one design) to supply or to ground — the "capacitor" is a permanent connection that decides the bit once, forever.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →PROM
One step softer is the PROM (programmable ROM): each cell contains a one-time link between its transistor and ground — originally a fuse that a strong programming pulse burns open, in modern parts often its inverse, an antifuse that ships as an insulator and becomes a conductor when pulsed — so the customer, not the fab (the factory that makes the chip), fixes the bits, exactly once.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →antifuse
One step softer is the PROM (programmable ROM): each cell contains a one-time link between its transistor and ground — originally a fuse that a strong programming pulse burns open, in modern parts often its inverse, an antifuse that ships as an insulator and becomes a conductor when pulsed — so the customer, not the fab (the factory that makes the chip), fixes the bits, exactly once.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →floating-gate
Reusability arrived with the floating-gate transistor — a MOSFET with a second, completely insulated gate that can hold electrons for years; charge parked on it shifts the transistor's threshold, and that shift is the stored bit.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →flash
Then Fujio Masuoka at Toshiba removed the per-byte select transistor and let a whole block be erased at once — much denser and much faster to erase; a colleague said it cleared "in a flash," and the name stayed.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →programmable logic array
The same PROM-style cell also turns up outside storage: rows of programmable links arranged into an AND plane feeding an OR plane — a programmable logic array — compute any sum of products, an early way to build combinational logic that could be reconfigured without rewiring silicon.
Go to SRAM, DRAM and ROM: the memory cells behind every level of the hierarchy →datapath
The datapath is the registers, memories and combinational blocks that data flows through while an instruction executes — everything the instruction's bits are about.
Go to The single-cycle datapath: one instruction from fetch to write-back →Control path
What remains is the control path: the much smaller collection of wires that tells each multiplexer which input to pass and each write port when to fire.
Go to The single-cycle datapath: one instruction from fetch to write-back →hardwired control
When the translation is a fixed combinational function etched in gates, the design is hardwired control.
Go to Hardwired control: decoding an instruction into control signals →Micro-operation
He called each such step a micro-operation, and a machine instruction is then nothing more than a sequence of micro-operations — a program, one level down.
Go to Microprogrammed control: a processor run by a program inside it →Microinstruction
A control store holds one microinstruction per entry — the bit pattern that says which gates pulse this step — and a sequencer steps through the entries, with branches so that the sequence can test machine state.
Go to Microprogrammed control: a processor run by a program inside it →Microprogramming
Microprogramming is implementing each machine instruction as a sequence of primitive control steps — a microprogram — held in a store and executed by a sequencer, rather than deriving the control signals directly from the opcode in hardwired logic.
Go to Microprogrammed control: a processor run by a program inside it →Control store
The store is the control store; each entry is a microinstruction; the sequencer is a microsequencer driven by a micro program counter, the .
Go to Microprogrammed control: a processor run by a program inside it →Microsequencer
The store is the control store; each entry is a microinstruction; the sequencer is a microsequencer driven by a micro program counter, the .
Go to Microprogrammed control: a processor run by a program inside it →preamble
Every machine instruction begins with the same four — the preamble: fetch the instruction, decode its fields, advance pc by one instruction, and dispatch on the opcode:
Go to Microprogrammed control: a processor run by a program inside it →Vertical microcode
A vertical microinstruction encodes an operation in fields — type, registers, immediates — which logic below the store decodes into control signals: narrow words, a decode stage in the path, and a microassembly language that stays stable as long as the internal register set does.
Go to Microprogrammed control: a processor run by a program inside it →Horizontal microcode
A horizontal microinstruction carries the control signals directly, one bit each: wider words, no decode, micro-operations that can overlap, and a microassembler that must know the machine's wiring to the last signal.
Go to Microprogrammed control: a processor run by a program inside it →Pipelining
Pipelining is the fix — cut the datapath into stages separated by registers, start a new instruction every cycle, and let five instructions occupy the machine at once.
Go to Pipelining: overlapping instructions like an assembly line →pipeline registers
flowchart TB IF["IF · instruction fetchPC → instruction memory"] P1[["pipeline registerIF·OF"]] OF["OF · operand fetchregister file, immediate field"] P2[["pipeline registerOF·EX"]] EX["EX · executeALU, branch unit"] P3[["pipeline registerEX·MA"]] MA["MA · memory accessdata memory read/write"] P4[["pipeline registerMA·RW"]] RW["RW · register writeregister file"] IF --> P1 --> OF --> P2 --> EX --> P3 --> MA --> P4 …
Go to Pipelining: overlapping instructions like an assembly line →Instruction packet
What a pipeline register actually stores between stages is the instruction packet: everything later stages will need to finish this instruction's work.
Go to Pipelining: overlapping instructions like an assembly line →Bus Interface Unit
The Intel 8086 of 1978 — the 16-bit processor that started the x86 line — did the smallest useful version: a Bus Interface Unit that prefetches code into a 6-byte queue while the separate Execution Unit runs — two half-stages, in effect.
Go to Pipelining: overlapping instructions like an assembly line →Data hazard
A data hazard is a dependence between instructions that the pipeline's overlap can violate.
Go to Data hazards and forwarding: using a result before it is written back →Read-after-write hazard
Only RAW — the consumer's OF racing ahead of the producer's RW — is a real threat.
Go to Data hazards and forwarding: using a result before it is written back →Load delay slot
The MIPS I architecture — the Stanford lineage commercialised as the R2000 in 1985 — wrote the discipline into the ISA itself: the instruction immediately after a load was forbidden from using the load's destination register (the load delay slot), and the assembler quietly inserted a nop when it could find nothing useful to fill it.
Go to Data hazards and forwarding: using a result before it is written back →interlock
An interlock is the hardware that detects a hazard and holds the pipeline until it clears: the dependent instruction waits in its stage, everything behind it waits too, and bubbles — injected nops — carry the empty slots downstream.
Go to Data hazards and forwarding: using a result before it is written back →Bubble
An interlock is the hardware that detects a hazard and holds the pipeline until it clears: the dependent instruction waits in its stage, everything behind it waits too, and bubbles — injected nops — carry the empty slots downstream.
Go to Data hazards and forwarding: using a result before it is written back →Forwarding
Forwarding — called bypassing in some texts — is a dedicated wire from a pipeline register back to an earlier stage's input multiplexer (mux), so a consumer takes its operand from wherever the producer's result currently sits.
Go to Data hazards and forwarding: using a result before it is written back →wrong-path instructions
If it resolves taken, they are instructions the program should never execute — wrong-path instructions.
Go to Control hazards: what to fetch before a branch resolves →control hazard
A control hazard is the possibility that a pipeline executes instructions on the wrong path of a branch — fetched before the branch's outcome was known — and lets them write results the program never asked for.
Go to Control hazards: what to fetch before a branch resolves →branch penalty
That count is the branch penalty: the number of instructions fetched between a branch entering fetch and its outcome arriving — two in this pipeline, and larger the deeper the pipe.
Go to Control hazards: what to fetch before a branch resolves →structural hazard
A structural hazard is the possibility that instructions in different pipeline stages need the same resource in the same cycle — a memory port (one access path into a memory, able to serve one read or write per cycle), a functional unit (an adder or a multiplier, say), a register-file port — and the resource cannot serve them all.
Go to Control hazards: what to fetch before a branch resolves →predict-not-taken
This is predict-not-taken: wrong exactly when the branch is taken, free otherwise.
Go to Control hazards: what to fetch before a branch resolves →delayed branch
Declare that the instructions fetched after a branch always execute, and make filling them the compiler's job: the delayed branch and its delay slot (met in branches and jumps).
Go to Control hazards: what to fetch before a branch resolves →bimodal predictor
This is the bimodal predictor (some texts reserve the name for the two-bit version below), and it already turns a corner: a loop's back-edge is taken on all but the last iteration, so it gets most iterations right.
Go to Branch prediction: guessing which way the code goes →saturating counter
A saturating counter predicts a branch from a small counter that increments on taken outcomes and decrements on not-taken ones, clamped at both ends instead of wrapping.
Go to Branch prediction: guessing which way the code goes →destructive interference
First, the table is indexed by PC bits with no tag, so two branches can collide on one counter — destructive interference, or branch aliasing: each trains the entry against the other.
Go to Branch prediction: guessing which way the code goes →loop predictor
A loop predictor handles exactly that case: a counter that loads the trip count and counts down to predict the exit precisely.
Go to Branch prediction: guessing which way the code goes →branch target buffer
A branch target buffer is a small tagged table indexed by PC whose entries remember what a previous decode saw: that this address is a branch, of which kind — conditional, unconditional, call, return — and where it went.
Go to Branch prediction: guessing which way the code goes →return address stack
So the fetch engine keeps a small hardware stack: a return address stack that records the fall-through address at every call; when the BTB says "this instruction is a return", the stack's top is the target — and a confirmed return pops it.
Go to Branch prediction: guessing which way the code goes →global history register
The machine that captures this is a shift register (a row of bits that moves along one place at each update) holding the last branch outcomes — the global history register, GHR (also written BHR, for branch history register): every resolved branch shifts its outcome in, newest into the top bit.
Go to Branch prediction: guessing which way the code goes →pattern history table
The first level records a pattern of recent outcomes; the second, a pattern history table of saturating counters indexed by that pattern combined with PC bits, learns what this branch does after that pattern.
Go to Branch prediction: guessing which way the code goes →gshare
Concatenating history bits with PC bits costs a wide index; folding them together with XOR — the gshare predictor — keeps the index short while still giving nearly every (address, history) pair its own counter.
Go to Branch prediction: guessing which way the code goes →tournament predictor
That is the tournament predictor, so called because the chooser picks the predictor that has been winning.
Go to Branch prediction: guessing which way the code goes →TAGE
TAGE — TAgged GEometric history length — keeps a small untagged predictor for the default answer plus a ladder of partially tagged tables, each indexed by a history length from a geometric series: one table trained on the last few branches, the next on a few more, the next on many.
Go to Branch prediction: guessing which way the code goes →conditional move
When both arms are a couple of instructions, a conditional move selects between two computed values instead of jumping — x86's cmov (conditional move; introduced with the Pentium Pro in 1995), AArch64's csel (conditional select; AArch64 is the 64-bit ARM instruction set), and the czero.eqz/czero.nez pair of RISC-V's Zicond (integer conditional operations) extension, which zero a value under a condition (czero: "co…
Go to Branch prediction: guessing which way the code goes →interrupt
An interrupt is asynchronous: a signal raised by hardware outside the program — a timer, a disk controller, a key — that could arrive at any moment and has no author in the instruction stream.
Go to Precise exceptions: stopping a pipeline as if instructions ran one at a time →exception
An exception is raised by an instruction of the program itself: a load or store whose address misses the page tables (the operating system's tables recording where each page lives), an illegal opcode (an instruction word the processor does not recognise), a deliberately planted system call asking for a kernel service.
Go to Precise exceptions: stopping a pipeline as if instructions ran one at a time →trap
Terminology varies — some texts use trap for the deliberate kind and keep "exception" for the accidental ones, and RISC-V's manual uses it for the transfer of control that either an exception or an interrupt causes (traps and exceptions separates the words) — but the hardware treats them as one family.
Go to Precise exceptions: stopping a pipeline as if instructions ran one at a time →interrupt handler
Both end at the same place: the machine stops the user stream, records where it stopped, and starts executing kernel code — an interrupt handler whose address is looked up in a handler table indexed by an event code (a disk completion and a page fault are different rows).
Go to Precise exceptions: stopping a pipeline as if instructions ran one at a time →handler table
Both end at the same place: the machine stops the user stream, records where it stopped, and starts executing kernel code — an interrupt handler whose address is looked up in a handler table indexed by an event code (a disk completion and a page fault are different rows).
Go to Precise exceptions: stopping a pipeline as if instructions ran one at a time →precise
An exception mechanism is precise if, when the handler is entered: (1) every instruction before has executed and made its state change; (2) no instruction after has executed or modified any state the program can observe; and (3) the saved PC identifies exactly the instruction where execution stopped.
Go to Precise exceptions: stopping a pipeline as if instructions ran one at a time →exception unit
When the marked packet reaches RW, a single block of logic at the tail, the exception unit, takes over: it suppresses the write-back of a faulting instruction (its result does not exist), saves the resume state described next, reads the handler's address out of the table at the row given by the packet's event code, loads that address into the PC, and fetching restarts — now in kernel code.
Go to Precise exceptions: stopping a pipeline as if instructions ran one at a time →program state
Resuming needs the program state the sequential contract promises: the register file, the PC, the flags, and memory — everything the next instruction is entitled to observe.
Go to Precise exceptions: stopping a pipeline as if instructions ran one at a time →context
The subset the machine must squirrel away before the handler clobbers it is the context: PC, flags, registers.
Go to Precise exceptions: stopping a pipeline as if instructions ran one at a time →control and status registers
RISC-V keeps the trap state outside its thirty-two general registers, in a separate file of up to 4096 control and status registers (CSRs): special registers that control or report the state of the hart — the RISC-V term for one hardware thread, here simply "the processor core's instruction stream".
Go to RISC-V trap entry: the CSRs a trap writes and mret →stall rate
The accounting splits every cause of lost cycles into a stall rate — how often, per instruction, the cause strikes — and a stall penalty — the cycles each strike costs — so that is the ideal plus the sum of their products.
Go to Pipeline performance: where the cycles actually go →stall penalty
The accounting splits every cause of lost cycles into a stall rate — how often, per instruction, the cause strikes — and a stall penalty — the cycles each strike costs — so that is the ideal plus the sum of their products.
Go to Pipeline performance: where the cycles actually go →activity factor
Not every gate switches every cycle — each block gets an activity factor , the fraction of its capacitance that moves per cycle — and the total dissipation is the sum over all of them, times how often a cycle occurs.
Go to Pipeline performance: where the cycles actually go →Dynamic power
Dynamic power is the power a circuit burns because its signals keep changing: every input or output transition on a transistor dissipates a packet of energy, and the total is the sum across all of them — the price of bits moving, as distinct from leakage, the current transistors leak while merely holding a value, which on modern processes is a sizeable minority of total power.
Go to Pipeline performance: where the cycles actually go →leakage
Dynamic power is the power a circuit burns because its signals keep changing: every input or output transition on a transistor dissipates a packet of energy, and the total is the sum across all of them — the price of bits moving, as distinct from leakage, the current transistors leak while merely holding a value, which on modern processes is a sizeable minority of total power.
Go to Pipeline performance: where the cycles actually go →superscalar
A superscalar processor issues more than one instruction per clock cycle, dynamically, under hardware control.
Go to Superscalar and VLIW: more than one instruction per cycle →Instruction-level parallelism
Instruction-level parallelism (ILP) is the count of instructions in a program that can execute simultaneously without violating a data dependence — a property of the program and its input, not of the machine.
Go to Superscalar and VLIW: more than one instruction per cycle →VLIW
A VLIW machine executes one long instruction word per cycle; each word carries several operations destined for different functional units, packed by the compiler.
Go to Superscalar and VLIW: more than one instruction per cycle →Tomasulo's algorithm
Tomasulo's algorithm executes instructions when their operands are ready, using three mechanisms.
Go to Out-of-order execution: register renaming and dynamic scheduling →Reservation station
Reservation stations — small buffers in front of each functional unit — hold a dispatched instruction (one sent into the machine's waiting area) together with its operand values or the tags of their producers; the Model 91's floating-point unit (the part that does arithmetic on non-integer numbers) ran three stations ahead of its adder and two ahead of its multiplier-divider.
Go to Out-of-order execution: register renaming and dynamic scheduling →Common data bus
A common data bus (CDB) broadcasts every unit's result together with its tag — the name of the station that produced it — to all stations and registers at once; any station waiting on that tag captures the value that cycle.
Go to Out-of-order execution: register renaming and dynamic scheduling →Register renaming
Register renaming maps each architectural register to a physical register through a rename map table — the RAT — drawn from a free list of unused physical registers.
Go to Out-of-order execution: register renaming and dynamic scheduling →Rename map table
Register renaming maps each architectural register to a physical register through a rename map table — the RAT — drawn from a free list of unused physical registers.
Go to Out-of-order execution: register renaming and dynamic scheduling →free list
Register renaming maps each architectural register to a physical register through a rename map table — the RAT — drawn from a free list of unused physical registers.
Go to Out-of-order execution: register renaming and dynamic scheduling →Reorder buffer
The reorder buffer is a circular queue, entered in program order as each instruction is decoded and renamed (the step Smith and Pleszkun call issue) and drained at the head in program order, whose entries hold an instruction's result and status until the machine is certain the instruction belongs in the program's past.
Go to The reorder buffer and speculation: executing ahead, committing in order →Commit
An instruction commits — retires — when it reaches the head with no pending exception: the commit is the point of no return, the one moment its effects become permanent.
Go to The reorder buffer and speculation: executing ahead, committing in order →Simultaneous multithreading
Simultaneous multithreading is the organisation in which several independent hardware threads may issue instructions into a superscalar's issue slots in the same cycle — the binding between thread and functional unit is fully dynamic, cycle by cycle.
Go to Simultaneous multithreading: two threads sharing one core →hardware thread
A hardware thread deserves a precise word of its own: it is a complete execution context living on the pipeline — its own program counter, its own architectural register state, its own view of where its program is.
Go to Simultaneous multithreading: two threads sharing one core →logical CPU
What the operating system sees and schedules as a CPU — a core, or one hardware thread of a core — is a logical CPU; that is what nproc (number of processing units) counts, and the Operating Systems course's multicore processors uses the same term.
Go to Simultaneous multithreading: two threads sharing one core →cache
Projected forward, memory access time dominates everything within about a decade — a "memory wall" that no cache cleverness can remove, because they assumed the cache was already perfect.
Go to The memory wall and locality: why a small fast memory works →hit
An access that finds its data in the cache is a hit; one that does not is a miss, and must go on to the slower memory behind.
Go to The memory wall and locality: why a small fast memory works →miss
An access that finds its data in the cache is a hit; one that does not is a miss, and must go on to the slower memory behind.
Go to The memory wall and locality: why a small fast memory works →L1
The caches are named L1, L2 and so on, numbered outward from the processor: L1 is the smallest and fastest, and each higher number is larger and slower.
Go to The memory wall and locality: why a small fast memory works →Memory wall
The memory wall is the point where average memory access time (the expected time of one access, which multi-level caches develops in full) — not processor speed — determines how fast programs run.
Go to The memory wall and locality: why a small fast memory works →Locality
The phenomenon has a name — locality — and Peter Denning's working-set model of program behaviour (published 1968, while he was at MIT) gave it a usable definition: at any moment a program concentrates its references on a small, slowly changing working set.
Go to The memory wall and locality: why a small fast memory works →Working set
The phenomenon has a name — locality — and Peter Denning's working-set model of program behaviour (published 1968, while he was at MIT) gave it a usable definition: at any moment a program concentrates its references on a small, slowly changing working set.
Go to The memory wall and locality: why a small fast memory works →Temporal locality
Temporal locality is the tendency of an address accessed once to be accessed again soon — because the loop runs again, the stack frame is re-entered, the hot variable is read and written every iteration.
Go to The memory wall and locality: why a small fast memory works →Spatial locality
Spatial locality is the tendency of an access to be followed by accesses to nearby addresses — because instructions fetch sequentially, arrays are walked element by element, and the fields of one struct sit together.
Go to The memory wall and locality: why a small fast memory works →Reuse distance
For temporal locality the natural instrument is reuse distance: at each access to an address, count how many distinct addresses were touched since that same address was last used.
Go to The memory wall and locality: why a small fast memory works →Heavy-tailed distribution
On the real thing — a million data accesses of perlbench (the Perl interpreter, one of the programs in the SPEC CPU2006 benchmark suite) after a million-access warm-up, profiled by Smruti Sarangi, author of the textbook this module draws on — land at distances , over two thirds below , and the tail, while thin, never quite dies: a heavy-tailed distribution, typical of workloads.
Go to The memory wall and locality: why a small fast memory works →Address distance
Spatial locality has a matching instrument: the address distance, the gap between the current access and the closest of the last accessed addresses ( works well — roughly one loop iteration's worth).
Go to The memory wall and locality: why a small fast memory works →Cache block
They fetch a cache block — also called a line — a fixed-size aligned chunk of memory, typically bytes and always a power of two, bytes being the standard for decades.
Go to The memory wall and locality: why a small fast memory works →Cache line
They fetch a cache block — also called a line — a fixed-size aligned chunk of memory, typically bytes and always a power of two, bytes being the standard for decades.
Go to The memory wall and locality: why a small fast memory works →Block offset
A -bit address is carved into three fields: the low bits are the offset, picking a byte within a -byte block; the next bits are the index, choosing one of sets; the remaining bits are the tag, identifying which block the set currently holds.
Go to Cache organisation: lines, tags, sets and associativity →Set index
A -bit address is carved into three fields: the low bits are the offset, picking a byte within a -byte block; the next bits are the index, choosing one of sets; the remaining bits are the tag, identifying which block the set currently holds.
Go to Cache organisation: lines, tags, sets and associativity →tag
A -bit address is carved into three fields: the low bits are the offset, picking a byte within a -byte block; the next bits are the index, choosing one of sets; the remaining bits are the tag, identifying which block the set currently holds.
Go to Cache organisation: lines, tags, sets and associativity →Data array
A cache stores each block in two side-by-side structures: a data array holding the block's bytes, and a tag array holding the tag plus a valid bit.
Go to Cache organisation: lines, tags, sets and associativity →Tag array
A cache stores each block in two side-by-side structures: a data array holding the block's bytes, and a tag array holding the tag plus a valid bit.
Go to Cache organisation: lines, tags, sets and associativity →Valid bit
A cache stores each block in two side-by-side structures: a data array holding the block's bytes, and a tag array holding the tag plus a valid bit.
Go to Cache organisation: lines, tags, sets and associativity →Direct-mapped cache
In a direct-mapped cache the index names the single entry a block may occupy: for the 8 KB, 64-byte-block example there are entries, so index block mod — the 7 index bits — and the tag is the remaining bits of block address.
Go to Cache organisation: lines, tags, sets and associativity →Fully associative cache
The opposite extreme places no restriction at all: a fully associative cache lets any block occupy any entry, so the tag is the whole block address and every entry must be compared at once. "At once" is the operative phrase — one comparator per entry plus an encoder and a multiplexer wide enough to pick the survivor.
Go to Cache organisation: lines, tags, sets and associativity →Set-associative cache
The set-associative cache takes the middle position: the index selects not one entry but a set of entries — the ways — and all of them are checked in parallel. is the associativity: direct-mapped is , fully associative is , and real caches live in between.
Go to Cache organisation: lines, tags, sets and associativity →set
The set-associative cache takes the middle position: the index selects not one entry but a set of entries — the ways — and all of them are checked in parallel. is the associativity: direct-mapped is , fully associative is , and real caches live in between.
Go to Cache organisation: lines, tags, sets and associativity →ways
The set-associative cache takes the middle position: the index selects not one entry but a set of entries — the ways — and all of them are checked in parallel. is the associativity: direct-mapped is , fully associative is , and real caches live in between.
Go to Cache organisation: lines, tags, sets and associativity →Associativity
The set-associative cache takes the middle position: the index selects not one entry but a set of entries — the ways — and all of them are checked in parallel. is the associativity: direct-mapped is , fully associative is , and real caches live in between.
Go to Cache organisation: lines, tags, sets and associativity →write-through
Under a write-through policy every store updates the cache line (if present) and is also sent to the next level of the hierarchy in the same stroke.
Go to Cache writes and replacement: write-back, write-through and LRU →write-back
Under a write-back policy a store updates only the cache line and sets a per-line flag — the dirty bit (Sarangi's text calls it the modified bit), one extra bit in the tag array beside the valid bit.
Go to Cache writes and replacement: write-back, write-through and LRU →dirty bit
Under a write-back policy a store updates only the cache line and sets a per-line flag — the dirty bit (Sarangi's text calls it the modified bit), one extra bit in the tag array beside the valid bit.
Go to Cache writes and replacement: write-back, write-through and LRU →write-through cache
A write-through cache propagates every store to the next level of the hierarchy as it executes; a resident line is always clean and may be dropped at will.
Go to Cache writes and replacement: write-back, write-through and LRU →write-back cache
A write-back cache lets a store modify only the cached copy and records that fact in the line's dirty bit; the lower level is updated lazily, at eviction — or earlier if the bus is idle and the controller chooses to pre-clean the line.
Go to Cache writes and replacement: write-back, write-through and LRU →write-allocate
Under write-allocate the missing line is fetched first, the store lands inside it, and (for a write-back cache) the line sits dirty until eviction.
Go to Cache writes and replacement: write-back, write-through and LRU →no-write-allocate
Under no-write-allocate the store is forwarded to the next level without fetching, and the cache pretends nothing happened.
Go to Cache writes and replacement: write-back, write-through and LRU →write-combining
The kernel can mark a physical range write-combining — used for device RAM such as a graphics card's frame buffer (the memory holding the image on screen) — under which word-sized stores to the same line are gathered in a buffer and flushed as one whole-line transaction: no-write-allocate taken to its logical extreme, trading latency for burst efficiency where each transfer is expensive.
Go to Cache writes and replacement: write-back, write-through and LRU →uncacheable
At the other extreme a range can be marked uncacheable, so every access reaches the device — the correct treatment for registers that change on their own, like the memory-mapped hardware registers — device control registers that appear at ordinary memory addresses — of device registers and MMIO.
Go to Cache writes and replacement: write-back, write-through and LRU →write buffer
The decoupling device is the write buffer — a small queue, typically 4–8 entries, sitting between the cache and the level below.
Go to Cache writes and replacement: write-back, write-through and LRU →least recently used
The natural approximation is least recently used: evict the way touched longest ago, on the argument that recent use is the best available predictor of future use.
Go to Cache writes and replacement: write-back, write-through and LRU →tree pseudo-LRU
The standard cheat is tree pseudo-LRU: organise the ways as the leaves of a binary tree and keep one bit per internal node — bits total.
Go to Cache writes and replacement: write-back, write-through and LRU →compulsory
The misses that remain in a cache of infinite capacity with the same line size are compulsory misses — first touches, also called cold misses; they are the floor no geometry moves.
Go to Classifying cache misses: compulsory, capacity and conflict →capacity
The misses a fully associative cache of the same capacity, line size and replacement policy still suffers — misses on lines that were evicted for sheer lack of room — are capacity misses.
Go to Classifying cache misses: compulsory, capacity and conflict →conflict
Everything left over — misses the fully-associative reference avoided because its lines can go anywhere — are conflict misses, caused by the set-mapping alone.
Go to Classifying cache misses: compulsory, capacity and conflict →Average memory access time
Average memory access time, written AMAT, is the expected time a memory reference takes: where is the cycles or seconds a hit costs, is the miss rate (misses per memory reference), and is the additional time a miss costs.
Go to Multi-level caches and average memory access time →Inclusive cache
An inclusive hierarchy says yes by rule: every line in L1 also resides in L2, every line in L2 in L3.
Go to Multi-level caches and average memory access time →Exclusive cache
An exclusive hierarchy is the opposite rule — a line lives in exactly one level, so on an L1 miss filled from L2 the line moves up and the victim it displaced drops into L2 (the levels trade lines rather than copy them).
Go to Multi-level caches and average memory access time →Cache pollution
A useless one spends a line of bandwidth — on a -byte line, a prefetcher that guesses wrong as often as it guesses right doubles the stream's memory traffic — occupies a fill buffer (a small hardware table tracking each line on its way into the cache) and a cache slot that a wanted line could have held, and can evict useful data: pollution.
Go to Hardware prefetching: fetching data before it is asked for →memory controller
Every miss that survives the last cache level becomes a real request — read or write, 64 bytes, a physical address — queued at the memory controller, the logic that drives the DRAM chips.
Go to DRAM organisation: channels, banks, rows and the row buffer →bank
So a chip carries many arrays, each an independent bank with its own row buffer: bank 3 can be activated while bank 0 transfers data.
Go to DRAM organisation: channels, banks, rows and the row buffer →rank
That ganged set of chips is a rank, selected by a chip-select line.
Go to DRAM organisation: channels, banks, rows and the row buffer →channel
The 64-bit data bus plus its address-and-command wiring is a channel, run by its own memory controller.
Go to DRAM organisation: channels, banks, rows and the row buffer →Rowhammer
The phenomenon is now called Rowhammer; mitigations in the chip (refreshing neighbours probabilistically, or tracking hot rows) are part of newer standards.
Go to DRAM organisation: channels, banks, rows and the row buffer →performance-monitoring unit
The performance-monitoring unit (PMU) is the on-core hardware that counts selected events: a small set of generic counters programmable to any event — an Intel core typically has four — plus a few fixed counters hard-wired to the essential ones (cycles, instructions retired, reference cycles).
Go to Hardware performance counters: finding which stall a program pays for →pipeline slot
A pipeline slot is one opportunity to send a micro-op from the front end into the back end: a machine with issue width has slots per cycle, so a run of cycles contains slots in total.
Go to Hardware performance counters: finding which stall a program pays for →slots
A pipeline slot is one opportunity to send a micro-op from the front end into the back end: a machine with issue width has slots per cycle, so a run of cycles contains slots in total.
Go to Hardware performance counters: finding which stall a program pays for →process generation
For thirty years, each new process generation — a new, finer manufacturing technology, arriving every couple of years — had made transistors smaller, faster and no hotter per unit area, so software kept speeding up without being rewritten.
Go to The power wall: why chips grew cores instead of gigahertz →Moore's law
The observation — Moore's law — held for five decades, but on its own it only explains bigger chips, not faster ones.
Go to The power wall: why chips grew cores instead of gigahertz →die
Twice the transistors at the same speed is just more die (the single slice of silicon a chip is cut from).
Go to The power wall: why chips grew cores instead of gigahertz →Dennard scaling
Dennard scaling is the proportional scaling rule under which every linear dimension, the supply voltage and the current all shrink by per process generation, so that delay falls by , power per circuit by , and power density stays constant.
Go to The power wall: why chips grew cores instead of gigahertz →leaks
The chip also leaks: a transistor held "off" still passes a trickle called the subthreshold current, which grows exponentially as the supply approaches the threshold voltage (the gate voltage at which a transistor starts to conduct), plus tunnelling current (electrons crossing an insulator so thin that it barely blocks them) through a gate oxide now only a few atoms thick.
Go to The power wall: why chips grew cores instead of gigahertz →threshold voltage
The chip also leaks: a transistor held "off" still passes a trickle called the subthreshold current, which grows exponentially as the supply approaches the threshold voltage (the gate voltage at which a transistor starts to conduct), plus tunnelling current (electrons crossing an insulator so thin that it barely blocks them) through a gate oxide now only a few atoms thick.
Go to The power wall: why chips grew cores instead of gigahertz →dynamic voltage and frequency scaling
In real systems: chips exploit the quadratic by running many operating points — dynamic voltage and frequency scaling (DVFS).
Go to The power wall: why chips grew cores instead of gigahertz →power wall
That is the power wall: the point where the next increment of clock rate costs more power than the chip can shed.
Go to The power wall: why chips grew cores instead of gigahertz →dark silicon
The cubic makes a lone fast core ruinously expensive and a fleet of slow ones cheap — and it is also why chips now carry more transistors than can be powered at full speed at once, a spareness the industry calls dark silicon (Taylor, 2012): accelerators and idle regions are the budget being spent only when needed.
Go to The power wall: why chips grew cores instead of gigahertz →threads
Divided work is threads sharing an address space: a thread is one stream of instructions with its own program counter and registers (the Operating Systems course builds them properly), and the threads of one program all see the same memory — a sort split four ways shares the array, a web server's threads share the request table.
Go to Shared-memory multiprocessors: private caches, shared caches and the on-chip network →SISD
SISD (single instruction, single data) — one instruction stream, one data stream — is the single core the whole course has been building.
Go to Shared-memory multiprocessors: private caches, shared caches and the on-chip network →SIMD
SIMD (single instruction, multiple data) — one instruction stream, many data streams — applies the same operation to a vector of values; it is the subject of the data-parallelism module (SIMD and vector instructions) and of GPU architecture.
Go to Shared-memory multiprocessors: private caches, shared caches and the on-chip network →MISD
MISD (multiple instruction, single data) — many instruction streams over one data stream — never found a real niche.
Go to Shared-memory multiprocessors: private caches, shared caches and the on-chip network →MIMD
MIMD (multiple instruction, multiple data) — many independent instruction streams, each on its own data — is the multicore chip: every core fetches its own program counter's stream and runs its own thread.
Go to Shared-memory multiprocessors: private caches, shared caches and the on-chip network →Flynn's taxonomy
Flynn's taxonomy classifies machines as SISD, SIMD, MISD or MIMD by the multiplicity of their instruction and data streams.
Go to Shared-memory multiprocessors: private caches, shared caches and the on-chip network →shared-memory multiprocessor
A shared-memory multiprocessor (or multicore, at chip scale) is an MIMD machine whose cores see one physical address space: any core can load or store any address, and communication between threads is just memory traffic.
Go to Shared-memory multiprocessors: private caches, shared caches and the on-chip network →slice
But a monolithic LLC cannot be near anyone either, so it is sliced: the address space is interleaved across banks of the cache (consecutive lines go to different banks in rotation), each slice owning a stripe of addresses, and each slice lives beside one core.
Go to Shared-memory multiprocessors: private caches, shared caches and the on-chip network →tile
The natural unit of layout becomes the tile — one core, its private L1 and L2, one LLC slice, and a router — repeated across the die.
Go to Shared-memory multiprocessors: private caches, shared caches and the on-chip network →network-on-chip
Tiles communicate over a network-on-chip (NoC): each router is a small pipelined switch that forwards messages toward their destination tile — input buffers to hold arriving messages, a route computation to pick the outgoing link, an arbiter to choose among messages competing for the same link, and a crossbar to connect inputs to outputs — costing roughly 3–5 pipeline stages plus a link traversal per hop.
Go to Shared-memory multiprocessors: private caches, shared caches and the on-chip network →router
Tiles communicate over a network-on-chip (NoC): each router is a small pipelined switch that forwards messages toward their destination tile — input buffers to hold arriving messages, a route computation to pick the outgoing link, an arbiter to choose among messages competing for the same link, and a crossbar to connect inputs to outputs — costing roughly 3–5 pipeline stages plus a link traversal per hop.
Go to Shared-memory multiprocessors: private caches, shared caches and the on-chip network →diameter
Its diameter is the worst-case hop count between two nodes — the latency floor for the unluckiest request.
Go to Shared-memory multiprocessors: private caches, shared caches and the on-chip network →bisection bandwidth
Its bisection bandwidth is the smallest number of links whose removal splits the network in half — the throughput the whole fabric can push when half the traffic crosses the machine.
Go to Shared-memory multiprocessors: private caches, shared caches and the on-chip network →non-uniform memory access
A machine has non-uniform memory access when the latency of a load depends on which physical memory owns the address — fast for the socket holding the line's DRAM, slower through the inter-socket link for a remote address.
Go to Shared-memory multiprocessors: private caches, shared caches and the on-chip network →cache coherence protocol
Making the caches behave like one shared memory again, without giving the private caches up, is the job of a cache coherence protocol — a small state machine attached to every cache line, and the subject of this topic.
Go to Cache coherence: keeping every core's copy of a line consistent →coherent
A memory system that keeps both is coherent.
Go to Cache coherence: keeping every core's copy of a line consistent →cache controller
The simplest interconnect that can carry coherence is a shared bus: one wire (logically) that every cache controller — the small piece of hardware that manages one cache's lines — listens to, and on which only one transaction happens at a time.
Go to Cache coherence: keeping every core's copy of a line consistent →snoops
Each cache controller therefore runs the same per-line state machine twice over: once for its own core's loads and stores, and once for the transactions it overhears — snoops — on the bus.
Go to Cache coherence: keeping every core's copy of a line consistent →snooping protocol
A protocol built this way is a snooping protocol, and the bus that carries the traffic is a snoopy bus.
Go to Cache coherence: keeping every core's copy of a line consistent →write-update
A write-update protocol broadcasts the written value on every store, so shared copies are refreshed rather than invalidated.
Go to Cache coherence: keeping every core's copy of a line consistent →write-invalidate
Real protocols write-invalidate: a write kills the other copies, and the next reader pays a miss to get the fresh value.
Go to Cache coherence: keeping every core's copy of a line consistent →transient states
Protocols add transient states for the waiting — for example, : "heading for S, waiting for Data" — and a line in a transient state may not be read or written.
Go to Cache coherence: keeping every core's copy of a line consistent →directory
A directory keeps, per memory line, a list of which caches hold it, and turns every broadcast into a handful of point-to-point messages.
Go to Directory coherence: tracking sharers instead of broadcasting →home
Place the directory beside the shared memory (or the shared last-level cache) that owns the line's home.
Go to Directory coherence: tracking sharers instead of broadcasting →GetS
The caches keep their ordinary MESI states; the difference is that misses are no longer broadcast but sent to the directory as named requests — a read miss sends a GetS (get the line shared) and a write miss or upgrade a GetM (get it modified), the point-to-point counterparts of the bus's BusRd and BusRdX/BusUpgr — and the directory is now the serialization point: the order in which the directory processes request…
Go to Directory coherence: tracking sharers instead of broadcasting →GetM
The caches keep their ordinary MESI states; the difference is that misses are no longer broadcast but sent to the directory as named requests — a read miss sends a GetS (get the line shared) and a write miss or upgrade a GetM (get it modified), the point-to-point counterparts of the bus's BusRd and BusRdX/BusUpgr — and the directory is now the serialization point: the order in which the directory processes request…
Go to Directory coherence: tracking sharers instead of broadcasting →serialization point
The caches keep their ordinary MESI states; the difference is that misses are no longer broadcast but sent to the directory as named requests — a read miss sends a GetS (get the line shared) and a write miss or upgrade a GetM (get it modified), the point-to-point counterparts of the bus's BusRd and BusRdX/BusUpgr — and the directory is now the serialization point: the order in which the directory processes request…
Go to Directory coherence: tracking sharers instead of broadcasting →litmus test
Here is a litmus test — a tiny program built to expose exactly one ordering question, then run on many cores to see which outcomes occur: cores A and B each store to a different zero-initialised location (A stores , B stores ) and then load the other location — and on ordinary hardware both loads can return 0.
Go to Memory consistency models: sequential consistency, TSO and fences →memory consistency model
What is needed is a stated rule about which outcomes are legal: a memory consistency model, the contract between the memory system and every program that runs above it.
Go to Memory consistency models: sequential consistency, TSO and fences →sequentially consistent
A multiprocessor is sequentially consistent (SC) if the result of any execution is the same as if the operations of all the processors were executed in some sequential order, and the operations of each individual processor appear in this sequence in the order specified by its program.
Go to Memory consistency models: sequential consistency, TSO and fences →store buffer
The first of those structures is the store buffer: a first-in-first-out (FIFO) queue — entries leave in the order they arrived — sitting between a core's commit point and its cache.
Go to Memory consistency models: sequential consistency, TSO and fences →store-to-load forwarding
A load searches the buffer for a pending store to its own address and forwards the newest such value, because a core must observe its own store even before publication; this store-to-load forwarding is what keeps single-threaded semantics exact.
Go to Memory consistency models: sequential consistency, TSO and fences →Total store order
Total store order (TSO) keeps every SC ordering except store→load: a load may complete before an earlier store to a different address becomes visible — the load is ordered first in , which is exactly what "the store is still in the buffer" means.
Go to Memory consistency models: sequential consistency, TSO and fences →write atomicity
One property worth naming because the weak models vary on it: write atomicity — whether a store becomes visible to all other cores at once (the storing core itself may see it earlier, through forwarding).
Go to Memory consistency models: sequential consistency, TSO and fences →atomic read-modify-write
An atomic read-modify-write (RMW) is an operation whose read and write parts must appear to occupy consecutive positions in the memory order — under sequential consistency, no other operation may interpose between them in at all; weaker models relax it to "no other store to the same address may interpose", which is sufficient for synchronisation.
Go to Atomic operations in hardware: how read-modify-write stays indivisible →cache locking
Since the P6 family (Intel's Pentium Pro generation, from 1995), when the locked operand is in write-back cacheable memory and fits inside one cache line, the processor does not assert the bus-lock signal at all: it performs the RMW inside its cache — a mechanism Intel's software developer's manual calls cache locking (SDM Vol. 3A, §9.1).
Go to Atomic operations in hardware: how read-modify-write stays indivisible →split lock
Two cases still fall back to the real bus lock: an operand split across two cache lines (a split lock) or a locked access to non-cacheable memory (memory the processor is told never to hold in a cache, as with some device registers) — both rare, both orders of magnitude slower, and the reason lock-free code insists on aligned operands.
Go to Atomic operations in hardware: how read-modify-write stays indivisible →reservation set
The RISC-V A extension (A for atomics, one of the instruction set's optional modules; its current specification names the load-reserved/store-conditional part Zalrsc and the single-instruction part Zaamo) splits the compound operation into two instructions and lets the coherence protocol detect interference instead of preventing it. lr.w (load-reserved word) loads a word and registers a reservation set — a set of …
Go to Atomic operations in hardware: how read-modify-write stays indivisible →constrained LR/SC loop
A constrained LR/SC loop is one with:
Go to Atomic operations in hardware: how read-modify-write stays indivisible →data parallelism
That property has a name: a problem has data parallelism when the same operation can be applied to many data items independently, so the order among items does not matter.
Go to SIMD and vector instructions: one instruction, many values →data lanes
A 128-bit register splits into four 32-bit data lanes, and the instruction addps — add packed single-precision, where "packed" means several values side by side in one register — performs all four additions at once.
Go to SIMD and vector instructions: one instruction, many values →SIMD instruction
A SIMD instruction applies one operation to every lane of its operand registers in parallel: one fetch, one decode, results.
Go to SIMD and vector instructions: one instruction, many values →intrinsics
Here is the same loop written with SSE intrinsics — C functions, each of which the compiler turns into exactly one named instruction — followed by what GCC emits at -O3 (its most aggressive standard optimisation level):
Go to SIMD and vector instructions: one instruction, many values →arithmetic intensity
Each element of c[i]=a[i]+b[i] moves 12 bytes (two reads, one write) and performs one floating-point operation (FLOP) — an arithmetic intensity (FLOPs performed per byte moved to or from memory) of FLOP per byte.
Go to SIMD and vector instructions: one instruction, many values →Predication
Predication executes both sides of a data-dependent choice under masks, so the instruction stream stays straight: the lanes on the wrong side of an if are turned into no-ops instead of branching away.
Go to SIMD and vector instructions: one instruction, many values →Strip mining
Strip mining processes a long array in hardware-sized strips: loop over the data in chunks of at most the machine's vector length, setting the length for each chunk, and let the last chunk be whatever is left.
Go to Vector-length-agnostic code: strip mining, RISC-V V and auto-vectorisation →Vector-length-agnostic
Vector-length-agnostic code never names the machine's vector width: it asks the hardware, each strip, how many elements to process and is correct for every answer.
Go to Vector-length-agnostic code: strip mining, RISC-V V and auto-vectorisation →streaming multiprocessor
The streaming multiprocessor (SM) is the GPU's core unit: a small in-order machine of simple lanes — eight scalar SP (streaming processor) cores plus two special-function units (SFUs, which compute reciprocals, square roots and trigonometric functions) on Tesla; a modern SM is four partitions, each with its own warp scheduler, register file and a mix of FP32, INT32, FP64 (32-bit float, 32-bit integer, 64-bit float…
Go to GPU architecture: thousands of threads to hide memory latency →shared memory
The streaming multiprocessor (SM) is the GPU's core unit: a small in-order machine of simple lanes — eight scalar SP (streaming processor) cores plus two special-function units (SFUs, which compute reciprocals, square roots and trigonometric functions) on Tesla; a modern SM is four partitions, each with its own warp scheduler, register file and a mix of FP32, INT32, FP64 (32-bit float, 32-bit integer, 64-bit float…
Go to GPU architecture: thousands of threads to hide memory latency →SIMT
SIMT — single instruction, multiple threads — executes a group of 32 threads called a warp in lock-step (the name is a weaving metaphor: a warp is the set of parallel threads on a loom): one instruction is broadcast per issue, and every active lane executes it on its own data.
Go to GPU architecture: thousands of threads to hide memory latency →warp
SIMT — single instruction, multiple threads — executes a group of 32 threads called a warp in lock-step (the name is a weaving metaphor: a warp is the set of parallel threads on a loom): one instruction is broadcast per issue, and every active lane executes it on its own data.
Go to GPU architecture: thousands of threads to hide memory latency →reconverges
The hardware's answer is serialised predication: the warp executes the then-path with the else-lanes masked off, then the else-path with the then-lanes off, then reconverges — a branch synchronization stack tracks the nesting.
Go to GPU architecture: thousands of threads to hide memory latency →kernel
A CUDA kernel is an ordinary-looking C function for one thread; launching it creates a grid of thread blocks (CTAs — cooperative thread arrays) of up to 512 threads on Tesla (1,024 on current hardware), and the hardware does the rest:
Go to GPU architecture: thousands of threads to hide memory latency →grid
A CUDA kernel is an ordinary-looking C function for one thread; launching it creates a grid of thread blocks (CTAs — cooperative thread arrays) of up to 512 threads on Tesla (1,024 on current hardware), and the hardware does the rest:
Go to GPU architecture: thousands of threads to hide memory latency →thread blocks
A CUDA kernel is an ordinary-looking C function for one thread; launching it creates a grid of thread blocks (CTAs — cooperative thread arrays) of up to 512 threads on Tesla (1,024 on current hardware), and the hardware does the rest:
Go to GPU architecture: thousands of threads to hide memory latency →segment
A segment is a contiguous region of memory up to 64 KiB long, beginning on a paragraph boundary — a physical address evenly divisible by 16, i.e. with its low four bits zero.
Go to The 8086: segmented addressing and the start of x86 →paragraph boundary
A segment is a contiguous region of memory up to 64 KiB long, beginning on a paragraph boundary — a physical address evenly divisible by 16, i.e. with its low four bits zero.
Go to The 8086: segmented addressing and the start of x86 →logical address
A logical address names a byte as the pair : the high 16 bits of the segment's start, held in a segment register, plus a 16-bit byte offset into the segment.
Go to The 8086: segmented addressing and the start of x86 →physical address
A segment is a contiguous region of memory up to 64 KiB long, beginning on a paragraph boundary — a physical address evenly divisible by 16, i.e. with its low four bits zero.
Go to The 8086: segmented addressing and the start of x86 →segment registers
The 8086 stores each segment's anchor in one of four 16-bit segment registers — CS (code segment), DS (data segment), SS (stack segment) and ES (extra segment, a spare).
Go to The 8086: segmented addressing and the start of x86 →segment-override prefix
A one-byte segment-override prefix in front of an instruction swaps the default data segment for another — so mov ax, es:[bx] reads through ES instead of DS. (This is Intel syntax, used throughout this course's x86 listings, as in reading compiler output: mov copies, the destination ax comes first, and square brackets mean "the memory at this address", here the offset held in BX inside segment ES.) Two reference k…
Go to The 8086: segmented addressing and the start of x86 →execution unit
The execution unit (EU) holds the eight 16-bit general registers (AX, BX, CX, DX, SP, BP, SI, DI — the first four are the accumulator, base, count and data registers, and each splits into two 8-bit halves, AH and AL for AX, and so on), the 16-bit ALU and the flags, and has no connection to the system bus — every address it manipulates is a 16-bit offset.
Go to The 8086: segmented addressing and the start of x86 →instruction stream queue
It maintains a small RAM — the instruction stream queue, six bytes on the 8086 — and keeps it full: whenever at least two bytes are free and the EU has not asked for the bus, the BIU reads the next two bytes of instruction stream.
Go to The 8086: segmented addressing and the start of x86 →interrupt vector table
The 8086 reserves the lowest kilobyte of memory — physical addresses – — for the interrupt vector table (IVT): 256 entries of four bytes each.
Go to The 8086: segmented addressing and the start of x86 →far pointer
Entry is a far pointer — a segment and an offset stored side by side — to the handler, the routine that deals with interrupt type : the low word is the offset, loaded into IP; the high word is the segment, loaded into CS.
Go to The 8086: segmented addressing and the start of x86 →type
Entry is a far pointer — a segment and an offset stored side by side — to the handler, the routine that deals with interrupt type : the low word is the offset, loaded into IP; the high word is the segment, loaded into CS.
Go to The 8086: segmented addressing and the start of x86 →8259A programmable interrupt controller
The INTR pin is shared by every device, so the real machine hangs an 8259A programmable interrupt controller in front of it: a separate chip that accepts up to eight device lines, resolves which request has priority, and only then drives INTR.
Go to The 8086: segmented addressing and the start of x86 →8088
A year after the 8086, Intel shipped the 8088 (1979): the same EU and the same instruction set, but an 8-bit external data bus and a four-byte queue that fetches one byte whenever one is free.
Go to The 8086: segmented addressing and the start of x86 →real-address mode
A hardware reset drops every x86 processor into real-address mode (usually just "real mode"): the 8086 execution environment, plus the extensions later generations added.
Go to x86 operating modes: real, protected and long mode →protected mode
In protected mode a segment register holds a selector — an index into a system table — and the 64-bit record it points at, a segment descriptor, carries the base address, the segment limit and the access rights.
Go to x86 operating modes: real, protected and long mode →selector
In protected mode a segment register holds a selector — an index into a system table — and the 64-bit record it points at, a segment descriptor, carries the base address, the segment limit and the access rights.
Go to x86 operating modes: real, protected and long mode →segment descriptor
In protected mode a segment register holds a selector — an index into a system table — and the 64-bit record it points at, a segment descriptor, carries the base address, the segment limit and the access rights.
Go to x86 operating modes: real, protected and long mode →rings
Four privilege levels — rings 0 through 3, 0 for the kernel — replace the 8086's polite fiction that all code is equally trusted.
Go to x86 operating modes: real, protected and long mode →paging
The Intel386 widened everything the 286 had made awkward: registers and offsets to 32 bits (the E in EIP and EFLAGS, the 32-bit flags register), so a flat space of GiB fits in one pointer; the descriptor's base to 32 bits and its limit to 20 bits times an optional 4 KiB granularity, so a single segment can legally cover the whole 4 GiB space; and — the genuinely new mechanism — paging: the linear ad…
Go to x86 operating modes: real, protected and long mode →virtual-8086 mode
Setting the VM flag (virtual-8086 mode) in EFLAGS marks a task as virtual-8086 mode — strictly a per-task attribute of protected mode rather than a separate mode, though everyone calls it one — it uses real-mode address translation and runs at privilege level 3, while the protected-mode operating system — the "monitor" — holds the page tables and the interrupt table underneath.
Go to x86 operating modes: real, protected and long mode →long mode
The new long mode (Intel's term: IA-32e mode) is deliberately not a separate universe but a sub-structure of protected mode with two faces, selected per code segment by the L ("long") bit in that segment's descriptor:
Go to x86 operating modes: real, protected and long mode →A20
The most famous casualty of a wider address bus is a single line: A20, address bit 20 counting from bit 0, the first bit beyond the 8086's megabyte.
Go to x86 operating modes: real, protected and long mode →system management mode
A system-management interrupt — the SMI# pin or an APIC message (the APIC, Advanced Programmable Interrupt Controller, is the circuit on each x86 core that receives interrupts and lets cores signal one another) — suspends whatever is running (any mode) and drops the processor into system management mode, with its own separate address space invisible to the operating system; RSM returns to the interrupted state.
Go to x86 operating modes: real, protected and long mode →µop (micro-operation)
A micro-operation, written µop or uop, is one fixed-format operation in the core's internal instruction set — one register-to-register operation, one load, one store — produced by the decoders or by the microcode sequencer.
Go to x86 decoding and micro-operations: a CISC front end on a RISC-like core →stack engine
On recent cores a small stack engine folds the pointer arithmetic out of the µop stream entirely, so stack traffic costs the schedulers almost nothing.
Go to x86 decoding and micro-operations: a CISC front end on a RISC-like core →instruction-length decoder
Between fetch and the decoders sits an instruction-length decoder that resolves the lengths of three instructions per cycle — in the reconstruction by Agner Fog (a performance engineer whose measured microarchitecture manuals are the standard public reference), probably by tentatively decoding all sixteen possible start offsets in a chunk in parallel.
Go to x86 decoding and micro-operations: a CISC front end on a RISC-like core →predecoding
Later cores moved the same work upstream into the instruction cache: when a line is filled, the machine annotates where instructions begin — predecoding — so the decoders see pre-cut instructions instead of raw bytes (the front end of Sandy Bridge, Intel's core design of 2011, works this way, and AMD cores have long marked boundaries in the code cache).
Go to x86 decoding and micro-operations: a CISC front end on a RISC-like core →microcode sequencer
Above four µops the decoders give up, and the microcode sequencer takes over — Intel's diagrams label it MSROM.
Go to x86 decoding and micro-operations: a CISC front end on a RISC-like core →execution trace cache
The Pentium 4 (NetBurst, November 2000) replaced the L1 instruction cache with an execution trace cache holding roughly twelve thousand decoded µops — stored in execution order, traces stitched through predicted branches rather than in code-layout order.
Go to x86 decoding and micro-operations: a CISC front end on a RISC-like core →µop cache
The idea returned in disciplined form on Sandy Bridge (2011): the µop cache is an ordinary cache indexed by code address — 32 sets × 8 ways × 6 µops, about 1,500 µops, at most three lines per aligned 32-byte block of code.
Go to x86 decoding and micro-operations: a CISC front end on a RISC-like core →loop buffer
Skylake (2015) added a third, smaller source beneath it: the sixty-four-entry µop queue doubles as a loop buffer, recycling loops of up to roughly thirty to forty µops at a flat four µops per cycle — a hot loop runs off a queue with fetch, decode and the µop cache all idle.
Go to x86 decoding and micro-operations: a CISC front end on a RISC-like core →bus
A bus is a communication path shared by several hardware components, together with the protocol that decides who may drive it, when, and for how long.
Go to Buses and interconnects: from a shared bus to point-to-point links →address lines
Conventionally its wires come in three functional groups: address lines that name the target and location, data lines that carry the payload, and control lines that carry everything else — read-versus-write, memory-versus-I/O, timing strobes and status.
Go to Buses and interconnects: from a shared bus to point-to-point links →data lines
Conventionally its wires come in three functional groups: address lines that name the target and location, data lines that carry the payload, and control lines that carry everything else — read-versus-write, memory-versus-I/O, timing strobes and status.
Go to Buses and interconnects: from a shared bus to point-to-point links →control lines
Conventionally its wires come in three functional groups: address lines that name the target and location, data lines that carry the payload, and control lines that carry everything else — read-versus-write, memory-versus-I/O, timing strobes and status.
Go to Buses and interconnects: from a shared bus to point-to-point links →bus master
A bus master is an agent allowed to initiate a transaction — drive the address and command — while a target only answers.
Go to Buses and interconnects: from a shared bus to point-to-point links →target
A bus master is an agent allowed to initiate a transaction — drive the address and command — while a target only answers.
Go to Buses and interconnects: from a shared bus to point-to-point links →bus cycle
The chip's job for the outside world is run by its Bus Interface Unit — the same unit that keeps the six-byte prefetch queue fed — and every access it makes, instruction fetch or operand, takes one bus cycle of at least four clock periods, named T1 through T4.
Go to Buses and interconnects: from a shared bus to point-to-point links →time-multiplexes
Forty pins could not spare thirty-six for address-plus-data, so the 8086 time-multiplexes them: pins AD15–AD0 carry the address during T1 and the data during T3, and four more pins, A19/S6–A16/S3, carry the top address bits during T1 and status flags afterwards.
Go to Buses and interconnects: from a shared bus to point-to-point links →ALE
Multiplexing buys the pin count down but adds a hardware cost: the address is only valid for one quarter of the cycle, so the system must catch it — a pulse on the ALE (Address Latch Enable) pin during T1 tells external latch chips (Intel's 8282) to grab the address and hold it for the rest of the cycle while the AD pins switch to their data role.
Go to Buses and interconnects: from a shared bus to point-to-point links →wait states
T3Data travelsStatusThe READY line is sampled; if the target is not ready, extra wait states (Tw) repeat here.
Go to Buses and interconnects: from a shared bus to point-to-point links →data beat
Each transaction moves the width of the data lines, bytes, in one data beat.
Go to Buses and interconnects: from a shared bus to point-to-point links →arbitration
The bus therefore needs arbitration: a mechanism that picks exactly one master at a time.
Go to Buses and interconnects: from a shared bus to point-to-point links →Synchronous bus
The 8086's answer is the simplest: synchronous timing.
Go to Buses and interconnects: from a shared bus to point-to-point links →asynchronous bus
The alternative removes the clock from the data path entirely: an asynchronous bus synchronises each transaction with a private handshake.
Go to Buses and interconnects: from a shared bus to point-to-point links →source-synchronous
Between the poles sits source-synchronous timing: the sender launches its own strobe alongside the data, so the receiver samples with a clock that travelled the same wire as the bits it times.
Go to Buses and interconnects: from a shared bus to point-to-point links →split-transaction
A split-transaction protocol divides a transaction into two independent bus uses.
Go to Buses and interconnects: from a shared bus to point-to-point links →link
A PCIe link connects exactly two ports — no third device may join — and is built from one to thirty-two lanes; each lane is just two differential wire pairs (two wires carrying opposite voltages, the receiver reading their difference so that noise hitting both cancels), one per direction, so a link is full-duplex by construction and a "×16" slot is sixteen independent two-pair channels carrying interleaved bytes o…
Go to Buses and interconnects: from a shared bus to point-to-point links →lanes
A PCIe link connects exactly two ports — no third device may join — and is built from one to thirty-two lanes; each lane is just two differential wire pairs (two wires carrying opposite voltages, the receiver reading their difference so that noise hitting both cancels), one per direction, so a link is full-duplex by construction and a "×16" slot is sixteen independent two-pair channels carrying interleaved bytes o…
Go to Buses and interconnects: from a shared bus to point-to-point links →ports
The x86 I/O space holds individually addressable 8-bit ports, numbered 0–FFFFh (0F8h–0FFh reserved by Intel), read and written only by in/out (read a value from, write a value to, a port) — and by the string variants ins/outs (input string, output string), which move a block of values between a port and memory.
Go to I/O addressing and the physical address map →Port-mapped I/O
Port-mapped I/O (PMIO, or "isolated I/O") places device registers in a dedicated address space reachable only through special instructions.
Go to I/O addressing and the physical address map →BAR
The port space never had these hazards — nobody would cache an in — which is why PMIO survives on x86 for a few legacy devices while everything modern (every PCIe device's register block, located by its BAR — the base address register, a field in the device that the firmware sets to say where in the address map the block appears — as well as the local APIC — the Advanced Programmable Interrupt Controller on each c…
Go to I/O addressing and the physical address map →Physical address map
The table of those ranges is the machine's physical address map, and it is a design choice, not a law of nature — every machine's is different, and a famous one shaped computing for decades.
Go to I/O addressing and the physical address map →Programmed I/O
In programmed I/O the software writes a command, then polls — loops on the status register until it reports "ready" — and then copies the data itself: for each word (the unit one transfer carries, four bytes in the examples below), a load from the data register followed by a store to memory.
Go to I/O organisation: programmed I/O, interrupts, DMA and channels →Polling
In programmed I/O the software writes a command, then polls — loops on the status register until it reports "ready" — and then copies the data itself: for each word (the unit one transfer carries, four bytes in the examples below), a load from the data register followed by a store to memory.
Go to I/O organisation: programmed I/O, interrupts, DMA and channels →Interrupt-driven I/O
In interrupt-driven I/O the software arms the device — "call me when ready" — and goes on to other work.
Go to I/O organisation: programmed I/O, interrupts, DMA and channels →Direct memory access
A direct memory access engine — a DMA controller — is a small bus master (an agent that can start bus transactions on its own, as defined in buses and interconnects) with three registers: source, destination and count.
Go to I/O organisation: programmed I/O, interrupts, DMA and channels →DMA controller
A direct memory access engine — a DMA controller — is a small bus master (an agent that can start bus transactions on its own, as defined in buses and interconnects) with three registers: source, destination and count.
Go to I/O organisation: programmed I/O, interrupts, DMA and channels →Burst mode
In burst mode (block transfer) the engine takes the bus, moves the whole block and releases it — simplest to implement, and the CPU's memory traffic stalls outright for the duration.
Go to I/O organisation: programmed I/O, interrupts, DMA and channels →Cycle stealing
In cycle stealing the engine takes one bus cycle at a time, only when needed — the CPU runs nearly undisturbed, and the transfer stretches over a longer window.
Go to I/O organisation: programmed I/O, interrupts, DMA and channels →I/O processor
An I/O processor (or channel) is a bus master that executes a little instruction list — a channel program of commands like read 512 bytes from device 3 into this memory range, then the next command — stored in memory and fetched by the channel itself.
Go to I/O organisation: programmed I/O, interrupts, DMA and channels →Channel (computing)
An I/O processor (or channel) is a bus master that executes a little instruction list — a channel program of commands like read 512 bytes from device 3 into this memory range, then the next command — stored in memory and fetched by the channel itself.
Go to I/O organisation: programmed I/O, interrupts, DMA and channels →channel program
An I/O processor (or channel) is a bus master that executes a little instruction list — a channel program of commands like read 512 bytes from device 3 into this memory range, then the next command — stored in memory and fetched by the channel itself.
Go to I/O organisation: programmed I/O, interrupts, DMA and channels →Daisy chain
The daisy chain.
Go to I/O organisation: programmed I/O, interrupts, DMA and channels →Interrupt controller
The interrupt controller.
Go to I/O organisation: programmed I/O, interrupts, DMA and channels →vector
Run every device's line into a dedicated chip that latches all pending requests, applies a programmed priority and mask, and raises one line to the CPU together with a vector — a small number naming which device (or which handler) won.
Go to I/O organisation: programmed I/O, interrupts, DMA and channels →reset vector
The reset vector is the physical address at which a processor fetches its first instruction after reset — a value built into the hardware, not discovered from memory or storage.
Go to Power-on and firmware: from reset to a machine that can describe itself →bootstrap processor
The platform arbitrates the reset among the processors and picks a bootstrap processor (BSP); the rest — the application processors — are parked in a wait-for-SIPI state, holding for a startup inter-processor interrupt that the BSP will send once memory and control structures exist to describe them.
Go to Power-on and firmware: from reset to a machine that can describe itself →firmware
Ascher Opler coined firmware in 1967 for the microprograms resident in a processor's control store — code so bound to the hardware it was neither quite hardware nor software.
Go to Power-on and firmware: from reset to a machine that can describe itself →Execute in place
The firmware is held in flash — the non-volatile cell of SRAM, DRAM and ROM — soldered near the chipset and reached over an SPI (Serial Peripheral Interface) link, a simple few-wire serial connection; the chipset's fixed decode makes it answer at the top of the physical map, so the early code executes in place, each fetch a round-trip to the flash device.
Go to Power-on and firmware: from reset to a machine that can describe itself →cache-as-RAM
The first trick that unlocks a normal programming style is to borrow the cache: modern x86 firmware puts a line or a region into a mode where it is never evicted to DRAM — cache-as-RAM — and runs its early routines out of the processor's own SRAM (cache organisation explains the SRAM the cache is made of).
Go to Power-on and firmware: from reset to a machine that can describe itself →power-on self test
Its power-on routine is titled, without irony, the initial reliability tests: the power-on self test (POST).
Go to Power-on and firmware: from reset to a machine that can describe itself →IPL
Once the machine has been tested and device ROMs have run their init code — expansion cards could carry their own ROMs in the – window of the map — the BIOS reaches its real purpose: IPL, "initial program load".
Go to Handing off to an operating system: BIOS, UEFI and the boot loader →bootstrap loader
A bootstrap loader — "boot" for short — is a routine small enough to be fetched in one step, whose entire purpose is to load something bigger.
Go to Handing off to an operating system: BIOS, UEFI and the boot loader →master boot record
Hard disks added a layer: PC DOS 2.0 (March 1983, for the IBM PC XT's 10 MB hard disk) introduced partitioned disks, and the first sector of such a disk is the master boot record (MBR), whose last 66 bytes hold the partition table (which divides the disk into regions) and that signature.
Go to Handing off to an operating system: BIOS, UEFI and the boot loader →Extensible Firmware Interface
Intel began its replacement — the Extensible Firmware Interface (EFI) — in 1998 for the Itanium, a 64-bit server architecture that could not reasonably boot through 16-bit interrupt calls.
Go to Handing off to an operating system: BIOS, UEFI and the boot loader →boot services
Drivers and applications are PE32+ executables — the Windows object format — run in protected or long mode with flat addressing, against two families of C-callable services: boot services, which exist only until the OS asks for the machine (ExitBootServices ends them and frees their memory), and runtime services — time, NVRAM variables, reset — which remain callable for the OS's whole lifetime.
Go to Handing off to an operating system: BIOS, UEFI and the boot loader →runtime services
Drivers and applications are PE32+ executables — the Windows object format — run in protected or long mode with flat addressing, against two families of C-callable services: boot services, which exist only until the OS asks for the machine (ExitBootServices ends them and frees their memory), and runtime services — time, NVRAM variables, reset — which remain callable for the OS's whole lifetime.
Go to Handing off to an operating system: BIOS, UEFI and the boot loader →SEC
SEC (security phase): the entry point, no writable memory.
Go to Handing off to an operating system: BIOS, UEFI and the boot loader →PEI
PEI (pre-EFI initialization): bring up permanent memory, hand state to the next stage as a list of "hand-off blocks".
Go to Handing off to an operating system: BIOS, UEFI and the boot loader →DXE
DXE (driver execution environment): discover and run drivers for chipset and devices, produce the services the boot phase consumes.
Go to Handing off to an operating system: BIOS, UEFI and the boot loader →BDS
BDS (boot device selection): connect consoles, pick a boot target.
Go to Handing off to an operating system: BIOS, UEFI and the boot loader →EFI system partition
If nothing configured works, the fallback for removable media is not a sector but a file: an EFI system partition, a region of the disk formatted as FAT32 (a simple, widely supported file-system format), is searched for a path like \EFI\BOOT\BOOTx64.EFI, the name parameterised per architecture (BOOTAA64.EFI for 64-bit Arm, BOOTRISCV64.EFI for 64-bit RISC-V).
Go to Handing off to an operating system: BIOS, UEFI and the boot loader →Supervisor Binary Interface
RISC-V's privilege levels put the firmware in the most privileged one, machine mode, and the operating system one level down in supervisor mode, and the platform standard for what the lower level may ask of the upper is the Supervisor Binary Interface (SBI): a short list of calls the kernel makes — set a timer, send an interrupt to another core, start or stop a core, reset the machine, print a character — each mad…
Go to Handing off to an operating system: BIOS, UEFI and the boot loader →OpenSBI
OpenSBI is the open-source reference implementation of the SBI, the usual resident of the firmware slot on RISC-V boards, and the firmware QEMU's virt machine typically places at the base of DRAM.
Go to Handing off to an operating system: BIOS, UEFI and the boot loader →microcontroller
A microcontroller (MCU) is a single chip that contains a whole small computer: a processor core, non-volatile memory holding the program, a little RAM for data, and peripherals such as timers (counters driven by the clock), input/output pins, analog inputs (which measure a voltage) and serial ports (which send bits one after another), so that it can run one program with almost nothing else attached.
Go to Microcontrollers and microprocessors: an Arduino, a Raspberry Pi and where the OS fits →microprocessor
A microprocessor is a processor on a chip that relies on separate chips for its main memory and storage; with them, and usually an operating system, it forms a general-purpose computer.
Go to Microcontrollers and microprocessors: an Arduino, a Raspberry Pi and where the OS fits →system on a chip
Today's microprocessors usually come as a system on a chip (SoC): one chip holding several cores, their caches, a memory controller, a graphics processor and many device controllers — but still not the gigabytes of RAM or the storage, which stay on separate chips.
Go to Microcontrollers and microprocessors: an Arduino, a Raspberry Pi and where the OS fits →