АЛУ: Сердце вашего процессора

До этого момента мы занимались, в общем-то, простыми вещами: учились складывать двоичные числа. Сначала один бит (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

Разберём по строчкам:

Этот код лаконичен, но под капотом синтезатор превратит его ровно в ту же схему, которую мы описывали: параллельные вычислительные блоки + мультиплексор.

Параллелизм в действии: почему это быстро

АЛУ получает op_code = 000, и результат сложения появляется на выходе мультиплексора. Всё это занимает один такт (плюс задержка распространения сигнала).

А теперь представь, что АЛУ спроектировали «экономно» — как в софте: проверяет op_code, и только потом включает нужный блок. Это потребовало бы дополнительных тактов на переключение. Именно этого не происходит. Всё считается сразу, а мультиплексор просто «отфильтровывает» ненужное.

Резюме

1. АЛУ — это набор параллельно работающих вычислительных блоков (сумматор, AND, OR, XOR и т.д.), результат которых выбирается мультиплексором по сигналу op_code.

2. В Verilog АЛУ можно описать поведенчески через always @(*) case, и синтезатор сам построит нужную структуру.

3. Параллелизм — это не оптимизация, а фундаментальное свойство кремния. Все операции считаются одновременно, всегда.

На уровне 36 тебе предстоит собрать 8-битное АЛУ, поддерживающее две операции: сложение (ADD) и побитовое И (AND). Используй модуль Adder8 из прошлого уровня, мультиплексоры и вентили AND. Это финальный вызов цикла Verilog — после него ты сможешь сказать, что своими руками спроектировал сердце процессора.