АЛУ: Сердце вашего процессора
До этого момента мы занимались, в общем-то, простыми вещами: учились складывать двоичные числа. Сначала один бит (HalfAdder), потом три (FullAdder), потом цепочку из восьми (Ripple Carry Adder). Всё это время мы шли к одной цели: научиться делать a + b в железе.
Но процессор — это не только сложение. Ему нужно вычитать, сравнивать, выполнять логические операции (AND, OR, XOR) и многое другое. Вопрос: как уместить всю эту математику в один компактный блок, который понимает разные команды?
Ответ называется АЛУ — Арифметико-Логическое Устройство (ALU, Arithmetic Logic Unit).
АЛУ — это не один калькулятор, а много калькуляторов внутри одного
Здесь кроется главный инсайт, который многие программисты понимают неправильно. Когда ты пишешь в коде if (a > b) ..., процессор не «думает»: «сейчас мне нужно сравнить a с b, давай-ка включу блок сравнения». Нет. Всё работает совсем не так.
Внутри АЛУ все операции выполняются одновременно. Всегда. Каждый такт. Без исключений.
Представь заводской конвейер, на котором параллельно работают 8 станков. Станок №1 штампует деталь, станок №2 сверлит в ней отверстие, станок №3 нарезает резьбу. Все станки работают одновременно. В конце конвейера стоит сортировщик, который берёт только нужную деталь, а остальные выбрасывает.
АЛУ устроено так же. Внутри него параллельно работают сумматор (считает a + b), блок вычитания (через дополнительный код), блок AND, OR, XOR, NOT. Все они одновременно получают одни и те же входные числа a и b. Все они одновременно вычисляют свои результаты. А потом мультиплексор, управляемый кодом операции (op_code), выбирает один из этих результатов и отправляет на выход.
Код операции: дирижёр оркестра
Как АЛУ узнаёт, что именно нужно сделать на этом такте? Ему нужен управляющий сигнал — код операции (operation code, op_code). Это просто число (обычно 3-4 бита), которое говорит мультиплексору: «выбери выход номер N».
000 → a + b (сложение)
001 → a - b (вычитание)
010 → a & b (AND)
011 → a | b (OR)
100 → a ^ b (XOR)
101 → ~a (NOT)
110 → a (передача A)
111 → b (передача B)
Анатомия 8-битного АЛУ
Сумматор, AND, OR, XOR, NOT — все работают параллельно. Мультиплексор 8:1 выбирает, какой результат пойдёт на выход.
Обрати внимание: в схеме нет логики «если op_code = 000, то включи сумматор, а если 010 — включи AND». Все блоки включены всегда. Просто из восьми результатов только один доходит до выхода. Остальные семь «висят в воздухе» — они есть, но их никто не читает.
Сложение и вычитание: два в одном
Здесь мы используем трюк из двоичной арифметики: a - b = a + (~b + 1). Называется дополнительный код (two's complement).
Чтобы получить отрицательное число в двоичной системе, нужно инвертировать все биты числа и прибавить 1.
Проверка: 5 - 3 = 2. В дополнительном коде: 5 + (~3 + 1) = 5 + (11111100 + 1) = 5 + 11111101 = 1 00000010 = 2 (с отбрасыванием старшего переноса).
Пишем Verilog
Для АЛУ есть лаконичный способ — описать поведение в стиле always с case:
module ALU8(
input [7:0] a,
input [7:0] b,
input [2:0] op_code,
output reg [7:0] result,
output zero
);
always @(*) begin
case (op_code)
3'b000: result = a + b;
3'b001: result = a - b;
3'b010: result = a & b;
3'b011: result = a | b;
3'b100: result = a ^ b;
3'b101: result = ~a;
3'b110: result = a;
3'b111: result = b;
endcase
end
assign zero = (result == 8'b0);
endmodule
Разберём по строчкам:
output reg [7:0] result—regозначает, что переменная будет записываться внутриalways-блока.always @(*)— «выполнять этот блок каждый раз, когда меняется хотя бы один сигнал на входе».case (op_code) ... endcase— аналог switch/case из C.assign zero = (result == 8'b0)—zeroбудет 1, если результат равен нулю.
Этот код лаконичен, но под капотом синтезатор превратит его ровно в ту же схему, которую мы описывали: параллельные вычислительные блоки + мультиплексор.
Параллелизм в действии: почему это быстро
АЛУ получает op_code = 000, и результат сложения появляется на выходе мультиплексора. Всё это занимает один такт (плюс задержка распространения сигнала).
А теперь представь, что АЛУ спроектировали «экономно» — как в софте: проверяет op_code, и только потом включает нужный блок. Это потребовало бы дополнительных тактов на переключение. Именно этого не происходит. Всё считается сразу, а мультиплексор просто «отфильтровывает» ненужное.
Резюме
1. АЛУ — это набор параллельно работающих вычислительных блоков (сумматор, AND, OR, XOR и т.д.), результат которых выбирается мультиплексором по сигналу op_code.
2. В Verilog АЛУ можно описать поведенчески через always @(*) case, и синтезатор сам построит нужную структуру.
3. Параллелизм — это не оптимизация, а фундаментальное свойство кремния. Все операции считаются одновременно, всегда.
На уровне 36 тебе предстоит собрать 8-битное АЛУ, поддерживающее две операции: сложение (ADD) и побитовое И (AND). Используй модуль Adder8 из прошлого уровня, мультиплексоры и вентили AND. Это финальный вызов цикла Verilog — после него ты сможешь сказать, что своими руками спроектировал сердце процессора.