Bus -- the "highway system" inside a computer
The various components inside a computer need to constantly exchange information. The CPU has to fetch instructions from memory and read/write data. How is this information transmitted? The answer is —Bus。
A bus is a set of shared communication lines connecting the CPU, memory, hard drive, graphics card, and other devices, just like a city's road system — with main roads and branch roads, each with a clear purpose.
This lecture will help you understand the division of labor among the three types of buses, and use Python to simulate a complete memory read operation.
Highway system analogy: three kinds of roads, three kinds of uses
Imagine you're delivering packages in a big city. The courier needs to know three things:where to deliver、What to send?、Does the recipient need to sign?。
Inside the computer, there are also three exactly analogous "highways":
| Bus type | Features | Transfer direction | life analogy |
|---|---|---|---|
| Address Bus | The memory address to be accessed for transmission | CPU → Memory (one-way) | The "house number" on the delivery slip — telling the recipient "I'm looking for this address" |
| Data Bus | Transmit the actual data content | bidirectional | The "package" in the courier's hands — the item itself, which can be sent out or received |
| Control Bus | Transmit control signals such as read/write and interrupts | CPU ↔ Components (bidirectional) | The "receipt" — telling the other party whether it is currently a pickup or a delivery, and whether the transfer is complete |
The width of the address bus determines how much memory the CPU can access. A 32-bit address bus can access 2^32 = 4GB of memory space, and a 64-bit address bus can theoretically access 2^64 bytes (about 16EB). This is why a 32-bit operating system can use at most 4GB of memory.
Complete flow of a memory read operation
Suppose the CPU needs to read a piece of data from memory address 100. The entire process is divided into 4 steps, with the three buses working together:
Step breakdown
- CPU places address: The CPU places the address value 100 onto theAddress buson. The memory has now received the address.
- CPU sends signal: CPU atControl busand issues a "read" signal (READ) on it. The memory knows the other party wants to fetch something.
- Store data in memory: After the memory receives the address and the read signal, it finds the data stored at address 100 and places it onto theData buson.
- CPU receives data: The CPU reads the data from the data bus and stores it into an internal register. The transfer is complete.
sequence diagram
The following ASCII timing diagram shows the bus state changes during the execution of this instruction:
时间 | 地址总线 控制总线 数据总线 ------|------------------------------------------ T0 | 100 IDLE XXXX ← CPU 放上地址 T1 | 100 READ XXXX ← CPU 发出读信号 T2 | 100 READ 0x2A ← 内存放上数据 T3 | XXXX IDLE 0x2A ← CPU 取走数据 ------|------------------------------------------ 结果: CPU 从地址 100 读到了数据 0x2A
Code demonstration: Simulating the bus interaction process with Python
The following Python code fully simulates the collaboration process of the address bus, data bus, and control bus during a memory read operation.
Example
Bus Interaction Simulation - A Complete Memory Read Operation (Example Demo)
This program simulates the complete process of the CPU reading data from memory through three buses:
1. Address bus: CPU issues the memory address to access
2. Control bus: CPU issues read/write control signals
3. Data bus: memory returns/receives data
Each bus state change prints a detailed log, making it easy to observe the collaborative work of the three buses.
"""
import time
class AddressBus:
"""Address bus - unidirectional: CPU sends address to memory"""
def __init__(self):
self.value = None # Current address value on the bus
self.width = 16 # 16-bit address bus, addressable 64KB
def set_address(self, addr):
"""CPU puts the address on the address bus"""
self.value = addr
print(f" [Address Bus] CPU puts address: {addr} (0x{addr:04X})")
return addr
def clear(self):
"""Clear bus"""
self.value = None
print(f[Address bus] Bus cleared, entering idle state)
class DataBus:
"""Data bus - Bidirectional: both CPU and memory can send and receive data"""
def __init__(self):
self.value = None # Current data value on the bus
self.width = 8 # 8-bit data bus, transfers one byte at a time
self.direction = None # 'TO_CPU' or 'TO_MEMORY'
def send_from_cpu(self, data):
"""CPU sends data to the bus"""
self.value = data
self.direction = 'TO_MEMORY'
print(f" [Data Bus] CPU → Bus, Data: {data} (0x{data:02X})")
return data
def send_from_memory(self, data):
Memory sends data to the bus
self.value = data
self.direction = 'TO_CPU'
print(f[Data Bus] Memory → Bus, data: {data} (0x{data:02X}))
return data
def read_to_cpu(self):
"""CPU reads data from the data bus"""
data = self.value
print(f[Data Bus] CPU reads data from the bus: {data} (0x{data:02X}))
self.clear()
return data
def read_to_memory(self):
"""Memory reads data from the data bus (used during write operations)"""
data = self.value
print(f[Data Bus] Memory reads data from the bus: {data} (0x{data:02X}))
self.clear()
return data
def clear(self):
"""Clear bus"""
self.value = None
self.direction = None
print(f[Data bus] Bus cleared, entering idle state)
class ControlBus:
"""Control bus - bidirectional: transmits read/write signals, interrupt signals, etc."""
# Control signal type
READ = 'MEM_READ' # Memory Read Signal
WRITE = 'MEM_WRITE' # Memory Write Signal
IDLE = 'IDLE' # Idle
def __init__(self):
self.signal = self.IDLE
def set_signal(self, signal):
"""Set control signals"""
self.signal = signal
signal_name = {
self.READ: 'Memory Read (MEM_READ)',
self.WRITE: 'Memory write (MEM_WRITE)',
self.IDLE: 'Idle (IDLE)'
}
print(f[Control Bus] CPU sends signal: {signal_name.get(signal, signal)})
def clear(self):
"""Clear signal"""
self.signal = self.IDLE
print(f[Control bus] Signals cleared, entering idle state)
class SimpleMemory:
"""Simplified memory module - 256 bytes of storage space"""
def __init__(self, size=256):
self.size = size
self.data = {}
# Initialize some demo data
for i in range(size):
self.data[i] = i % 256 # Address i stores value i % 256
# Place some special values at specific addresses for demonstration
self.data[100] = 42 # Address 100: store 42 (0x2A)
self.data[200] = 0xAB # Address 200: store 0xAB (171)
self.data[50] = ord('R') # Address 50: stores the ASCII code of letter 'R'
self.data[51] = ord('U')
self.data[52] = ord('N')
self.data[53] = ord('O')
self.data[54] = ord('O')
self.data[55] = ord('B')
def read(self, addr):
Read data from the specified address
if 0 <= addr < self.size:
return self.data.get(addr, 0)
else:
raise ValueError(fAddress {addr} exceeds memory range (0-{self.size-1}))
def write(self, addr, data):
Write data to the specified address
if 0 <= addr < self.size:
self.data[addr] = data
else:
raise ValueError(fAddress {addr} exceeds memory range (0-{self.size-1}))
class CPU:
"""Simplified CPU - interacts with memory through the bus"""
def __init__(self, addr_bus, data_bus, ctrl_bus, memory):
self.addr_bus = addr_bus # Address bus
self.data_bus = data_bus # Data bus
self.ctrl_bus = ctrl_bus # Control bus
self.memory = memory # Memory
# CPU internal registers
self.mdr = 0 # Data register (Memory Data Register)
self.mar = 0 # Address Register (Memory Address Register)
def memory_read(self, addr):
"""
Execute a complete memory read operation
Process:
T0: MAR ← addr (internal preparation)
T1: address bus ← addr, control bus ← READ
T2: Memory reads data, data bus ← data
T3: MDR ← data bus, clear all buses
Return: the read data value
"""
print(f"\n{'='*55}")
print(fOperation start: CPU reads data from memory address {addr})
print(f"{'='*55}")
# T0: CPU internal preparation — load the address into MAR
self.mar = addr
print(f"\n[T0 - CPU internal preparation]")
print(f[CPU Internal] MAR (Memory Address Register) ← {addr})
# T1: CPU places the address on the address bus and simultaneously issues a read signal on the control bus.
print(f"\n[T1 - Issue address and read signal]")
self.addr_bus.set_address(addr)
self.ctrl_bus.set_signal(ControlBus.READ)
# Simulate signal transmission delay
time.sleep(0.001)
# T2: Memory response — based on the address on the address bus, find the data and place it on the data bus
print(f"\n[T2 - Memory Response]")
data = self.memory.read(addr)
self.data_bus.send_from_memory(data)
# Simulate signal transmission delay
time.sleep(0.001)
# T3: CPU reads data from the data bus into MDR. Buses are cleared.
print(f"\n[T3 - CPU receives data]")
self.mdr = self.data_bus.read_to_cpu()
self.ctrl_bus.clear()
self.addr_bus.clear()
# Output the result
print(f"\n{'='*55}")
print(f"Operation完Cheng: fromAddress {addr} (0x{addr:04X}) read取toData {self.mdr} (0x{self.mdr:02X})")
print(f"{'='*55}")
return self.mdr
def memory_write(self, addr, data):
"""
Execute a complete memory write operation
Process:
T0: MAR ← addr, MDR ← data
T1: Address Bus ← addr, Data Bus ← data, Control Bus ← WRITE
T2: Memory takes data from the data bus and writes to address addr
T3: Clear all buses
"""
print(f"\n{'='*55}")
print(fOperation start: CPU writes data {data} to memory address {addr})
print(f"{'='*55}")
# T0: CPU internal preparation
self.mar = addr
self.mdr = data
print(f"\n[T0 - CPU internal preparation]")
print(f" [CPU internal] MAR ← {addr}, MDR ← {data}")
# T1: CPU puts the address and data on the bus and issues a write signal
print(f"\n[T1 - issue address, data, and write signal]")
self.addr_bus.set_address(addr)
self.data_bus.send_from_cpu(data)
self.ctrl_bus.set_signal(ControlBus.WRITE)
time.sleep(0.001)
# T2: Memory takes data from the data bus and writes it
print(f"\n[T2 - Memory Write]")
received = self.data_bus.read_to_memory()
self.memory.write(addr, received)
print(f" [Memoryinside部] Address {addr} (0x{addr:04X}) ← {received} (0x{received:02X})")
time.sleep(0.001)
# T3: Clear all buses
print(f"\n[T3 - Clear Bus]")
self.ctrl_bus.clear()
self.addr_bus.clear()
print(f"\n{'='*55}")
print(f"Operation complete: {data} has been written to address {addr}")
print(f"{'='*55}")
# ===== Main program: run demo =====
if __name__ == "__main__":
print("=" * 60)
print(Bus Interaction Simulation — EXAMPLE Computer Organization Principles Demo)
print("=" * 60)
print()
# 1. Initialize bus
print("[System Initialization] Created address bus, data bus, control bus, memory module")
addr_bus = AddressBus()
data_bus = DataBus()
ctrl_bus = ControlBus()
memory = SimpleMemory()
print()
# 2. View the initial state of memory
print("[Memory Initial State] Data preview at key addresses:")
for addr in [50, 51, 52, 53, 54, 55, 100, 200]:
ch = chr(memory.data[addr]) if 32 <= memory.data[addr] < 127 else '?'
print(f" Address {addr} (0x{addr:04X}): {memory.data[addr]:3d} (0x{memory.data[addr]:02X}) '{ch}'")
print()
# 3. Initialize CPU
cpu = CPU(addr_bus, data_bus, ctrl_bus, memory)
# 4. Demo: Reading data from memory
print("\n" + "▼" * 60)
print(Demo 1: Read data from memory address 100)
print("▼" * 60)
val1 = cpu.memory_read(100)
print("\n" + "▼" * 60)
print(Demo 2: Read data from memory address 200)
print("▼" * 60)
val2 = cpu.memory_read(200)
print("\n" + "▼" * 60)
print(Demo 3: Read sequentially from memory addresses 50-55 to assemble the characters (EXAMPLE))
print("▼" * 60)
chars = []
for addr in range(50, 56):
val = cpu.memory_read(addr)
chars.append(chr(val))
print(f"\n"Assembled string: {''.join(chars)}")
# 5. Demo: Writing Data to Memory
print("\n" + "▼" * 60)
print("Demo 4: Write data 77 to memory address 10")
print("▼" * 60)
cpu.memory_write(10, 77)
# 6. Verify write result
print("\n" + "▼" * 60)
print("Demo 5: Verify write — read data back from address 10")
print("▼" * 60)
val3 = cpu.memory_read(10)
print(f"\nVerification: write 77, read {val3}, {'success' if val3 == 77 else 'failure'})
print("\n" + "=" * 60)
print(" All demonstrations complete!")
print("=" * 60)
Bus width and actual performance
The width of the address bus determines "how much memory it can manage"
Each line of the address bus transmits one binary bit. N address lines can represent 2^N different addresses.
| Address bus width | Addressable space | Typical CPU |
|---|---|---|
| 16-bit | 2^16 = 64 KB | Intel 8086 (1978) |
| 20-bit | 2^20 = 1 MB | Intel 8088 (IBM PC, 1981) |
| 24-bit | 2^24 = 16 MB | Intel 80286 (1982) |
| 32-bit | 2^32 = 4 GB | Intel 80386 (1985) |
| 64-bit | 2^64 ≈ 16 EB | Modern x86-64 processors |
Note: Modern 64-bit CPUs typically have fewer than 64 actual address lines (e.g., 48), because 2^64 bytes far exceeds the actual physical memory requirements today. 48 address lines can already address 256 TB, which is sufficient for current use.
The width of the data bus determines "how much data can be transferred at one time"
An 8-bit data bus transfers 1 byte at a time, while a 64-bit data bus transfers 8 bytes at a time. This is why, for the same task, 64-bit systems are often faster than 32-bit systems — the amount of data transferred per single operation is doubled.
Signal types of the control bus
The control bus is not as uniform as the address and data buses. It is a collection of many types of signal lines, including:
- MEM_READ / MEM_WRITE: Memory read/write enable signals
- IO_READ / IO_WRITE: I/O port read/write signals
- INTERRUPT: Interrupt request signal — the way external devices notify the CPU
- CLOCK: Clock signal — the "heartbeat" that drives the entire system to work synchronously
- RESET: Reset signal — returning the CPU to its initial state
Summary and verification
One-sentence summary:Address busnegative责「go哪里」,Data busnegative责「Fortune什么」,Control busnegative责「做什么」——three条buseach司its职,cooperatesame完ChengeveryonetimesDatatransmission。
self-test questions
- When the CPU wants to read a piece of data from memory, who places the information on the address bus? And who places the information on the data bus?
- If a computer's address bus has only 16 lines, what is the maximum amount of memory it can manage?
- What is the biggest difference between the control bus and the address bus and data bus? (Hint: think about the "composition" of the control bus)
Reference answer: 1. The address is placed by the CPU, and the data is placed by the memory. 2. 2^16 = 65536 bytes = 64KB. 3. The address and data buses transmit uniform digital signals (address values/data values), while the control bus is a set of distinct discrete signal lines, each with its own independent function.
other extensions