Внутри процессора: регистры, АЛУ, УУ, ПК
Урок проводит экскурсию по внутренним блокам процессора и показывает, как они связаны в тракт данных.
Тракт данных (datapath) — совокупность блоков (регистры, АЛУ, шины, мультиплексоры), по которым движутся данные. Устройство управления дирижирует трактом, выдавая управляющие сигналы.
Зачем знать узлы процессора
Мы собрали кирпичики: регистры (память), АЛУ (вычисления), мультиплексоры (выбор), автоматы (управление). Теперь соберём из них процессор. Понимание этих узлов объясняет, почему ассемблер выглядит именно так, откуда берутся регистры в коде и что физически происходит при ADD R1, R2.
Главные узлы
| Узел | Назначение |
| Регистровый файл | набор быстрых регистров для операндов и результатов |
| АЛУ | арифметика и логика над операндами |
| Счётчик команд (PC) | адрес следующей команды |
| Регистр команд (IR) | хранит текущую выбранную команду |
| Устройство управления (УУ) | декодирует команду, выдаёт управляющие сигналы |
| Регистр флагов | хранит Z/N/C/V после операций АЛУ |
Тракт данных (упрощённо)
┌─────┐ ┌───────────────┐
│ PC │───→│ ПАМЯТЬ │
└──┬──┘ │ команд │
│ +1 └──────┬────────┘
└───────────┐ │ команда
│ ▼
┌───┴──────┐
│ IR │ (регистр команд)
└────┬─────┘
│ декод
┌────▼─────┐ ┌──────────────┐
│ УУ │───→│ упр. сигналы │
└──────────┘ └──────┬───────┘
▼
┌────────────┐ операнды ┌────────┐
│ Регистровый│─────────────→│ АЛУ │──→ результат
│ файл │←─────────────└────────┘ (обратно в регистр)
└────────────┘
Как работает под капотом: счётчик команд
Счётчик команд (Program Counter, PC) — крошечный, но важнейший регистр: он хранит адрес следующей команды. После выборки команды PC увеличивается (на размер команды), указывая на следующую. Команда перехода (jump) — это просто запись нового значения в PC. Промоделируем регистровый файл и PC:
class CPU:
def __init__(self, nregs=4):
self.regs = [0] * nregs # регистровый файл
self.pc = 0 # счётчик команд
self.flags = {"Z": 0, "N": 0}
def execute(self, op, dst, a, b):
if op == "ADD":
self.regs[dst] = self.regs[a] + self.regs[b]
elif op == "SUB":
self.regs[dst] = self.regs[a] - self.regs[b]
self.flags["Z"] = int(self.regs[dst] == 0)
self.flags["N"] = int(self.regs[dst] < 0)
self.pc += 1 # переход к следующей команде
cpu = CPU()
cpu.regs[1] = 12
cpu.regs[2] = 5
cpu.execute("ADD", 0, 1, 2) # R0 = R1 + R2
print("R0 =", cpu.regs[0], "| PC =", cpu.pc, "| флаги", cpu.flags)
cpu.execute("SUB", 3, 2, 1) # R3 = R2 - R1
print("R3 =", cpu.regs[3], "| PC =", cpu.pc, "| флаги", cpu.flags)Вывод:
R0 = 17 | PC = 1 | флаги {'Z': 0, 'N': 0}
R3 = -7 | PC = 2 | флаги {'Z': 0, 'N': 1}
Почему регистров мало
Регистры — самая быстрая, но и самая дорогая память: они физически рядом с АЛУ. Поэтому их немного (обычно 16–32 у архитектуры). Их адресуют несколькими битами прямо в команде (например, 5 бит = 32 регистра). Чем больше регистров, тем длиннее код команды и тем больше площадь кристалла — снова компромисс.
Глубже в тему
Здесь полезно осознать, что мы наконец собираем процессор из ранее изученных кирпичиков, и каждый узел — это знакомый блок. Регистровый файл — это набор регистров, то есть триггеров из раздела про память. АЛУ — это сумматор и логика, выбираемые мультиплексором, из раздела про комбинационные схемы. Устройство управления — это конечный автомат. А мультиплексоры пронизывают весь тракт данных, решая, что куда направить. Тракт данных (datapath) — это и есть «дороги», по которым движутся числа: регистры, шины, MUX и АЛУ. Устройство управления при этом само не считает — оно лишь дирижирует, выдавая в нужный такт управляющие сигналы: «прочитать такой-то регистр», «АЛУ, выполни сложение», «защёлкни результат вот сюда». Понимание этого разделения объясняет, почему ассемблер выглядит именно так, откуда в коде берутся регистры и что физически стоит за командой ADD R1, R2.
Связку «PC хранит адрес, IR хранит команду» стоит закрепить намертво, потому что их путают чаще всего. Счётчик команд (PC) — крошечный регистр, в котором лежит адрес следующей команды; регистр команд (IR) держит саму выбранную команду, пока устройство управления её декодирует. Это два совершенно разных объекта: один указывает, откуда брать, другой хранит то, что взяли. Команда перехода (jump) тогда перестаёт быть чем-то магическим — это просто запись нового значения в PC. Цикл, условие, вызов функции — всё это в конечном счёте сводится к управляемым изменениям одного маленького регистра PC.
Почему регистров мало — отличный пример инженерного компромисса, пронизывающего всю архитектуру. Регистры — самая быстрая память, потому что лежат вплотную к АЛУ, но именно поэтому и самая дорогая по площади кристалла. Вдобавок номер регистра кодируется битами прямо внутри команды: чтобы адресовать 32 регистра, нужно 5 бит в каждой команде, обращающейся к регистрам, а 64 регистра потребовали бы уже 6 бит — и команды распухнут, а код станет длиннее. Поэтому типичная архитектура останавливается на 16–32 регистрах: это баланс между скоростью, площадью и компактностью кодирования команд. Здесь видно, как, казалось бы, низкоуровневое решение о числе регистров напрямую отражается на формате машинных команд.
Наконец, важно не считать регистры «переменными программы» — это распространённое заблуждение. Переменные вашей программы живут в оперативной памяти, а регистров физически мало и они общие на весь процессор. Компилятор лишь временно загружает значения переменных в регистры, проводит над ними вычисления и выгружает результаты обратно в память, освобождая регистры для следующих операций. Эта постоянная циркуляция «загрузил — посчитал — выгрузил» — суть работы тракта данных, и именно она задаёт ритм цикла выборки-декодирования-исполнения, который мы разберём в следующем уроке.
Частые ошибки
- Путать PC и регистр команд (IR). PC хранит адрес следующей команды, IR — саму текущую команду.
- Думать, что переход «прыгает» магически. Переход — это просто запись нового адреса в PC.
- Считать регистры «переменными». Их физически мало и они фиксированы; переменные программы живут в памяти, а в регистры лишь временно загружаются.
Итог
- Процессор = регистровый файл + АЛУ + УУ + PC + IR + регистр флагов.
- PC хранит адрес следующей команды; переход = запись нового адреса в PC.
- Регистров мало (дорогая быстрая память), их номера кодируются прямо в команде.