Лисья Нора

Оглавление


§ Базовые функции

Итак, поскольку получилось реализовать не только декодер, но даже (частично) довольно важную группу инструкции, то пришло время поговорить и о них, так как именно модуль АЛУ (арифметическо-логического устройство), а также модуль сдвигов являются очень важными, и даже центральными компонентами всей этой сложной системы.
Всего существует 8 базовых функции АЛУ.
#КОДОПИСАНИЕ
0ADDСложить A + B
1ORЛогическая "ИЛИ"
2ADCСложить A + B + CF (флаг переноса)
3SBBВычесть A – B – CF
4ANDЛогическая "И"
5SUBВычесть A – B
6XORЛогическое "Исключающее ИЛИ"
7CMPВычесть A – B, писать только флаги
Эти функции сами по себе реализуются достаточно просто:
Есть много разных способов сделать АЛУ, и это – лишь один из способов, который легко реализуется в верилоге, и поскольку матрицы ПЛИС как раз очень хорошо "заточены" под мультиплексирование, то такие схемы там достаточно распространены, если не сказать, что повсеместно.

§ Вычисление результата

wire [16:0] ar =
alu == ADD ? op1 + op2 :
alu == ADC ? op1 + op2 + flags[CF] :
alu == SBB ? op1 - op2 - flags[CF] :
alu == AND ? op1 & op2 :
alu == XOR ? op1 ^ op2 :
alu == OR ? op1 | op2 : op1 - op2;
Создадим мультиплексор, но операции CMP и SUB объединяются в одну из-за того что они выполняются совершенно одинаково. Разница заключается лишь только в том, что CMP записывает результат только во флаги, а SUB – обратно в регистр или память, в том числе и во флаги тоже.
Интересная особенность в том, что вычисления всегда производятся с 16-битными операндами. В этом нет ошибки. Главное только в том, чтобы при работе с 8-битными значениями в старших 8 битах операндов op1 и op2 были именно строгие нули (0), иначе тогда ничего не будет работать корректно.
В зависимости от размера операндов выбирается бит, который будет находится в самой вершине (номер старшего бита):
wire [3:0] top = size ? 15 : 7;
Номер бита будет либо 7, если работаем с 8-битными значениями (от 0 до 7), либо же 15 с 16-битными операндами (от 0 до 15).
wire isa = alu == ADD || alu == ADC;
wire isl = alu != AND && alu != OR && alu != XOR;
В данных двух условиях происходит следующий анализ:
Рассмотрим же теперь сами вычисления флагов. Флаги расположил по порядку от старшего бита к младшему.
wire new_o = (op1[top] ^ op2[top] ^ isa) & (op1[top] ^ ar[top]);
wire new_s = ar[top];
wire new_z = 0 == (size ? ar[15:0] : ar[7:0]);
wire new_a = op1[4] ^ op2[4] ^ ar[4];
wire new_p = ~^ar[7:0];
wire new_c = ar[top + 1];
Самый, пожалуй, сложный тут будет флаг OF. Возможны две ситуации:
Здесь top это либо 7, либо 15, то есть, это старший бит операндов и результата, и, как можно отметить, вместо 1 или 0 установлено значение isa, который равно 1, если ADD или ADC, и 0 в других случаях.
Что делают остальные флаги:
А теперь последнее – вычисление флагов.
wire [11:0] af = {
new_o & isl, // 11 OF
flags[10:8], // 10-8
new_s, // 7 SF
new_z, // 6 ZF
1'b0, // 5
new_a & isl, // 4 AF
1'b0, // 3
new_p, // 2 PF
1'b1, // 1
new_c & isl // 0 CF
};
Всё это кажется сложным, но это не так. Для логических функции там где & isl флаги OF, AF, CF просто обнуляются. В некоторых битах (5,3,1) значения битов строго заданы и никогда не изменяются. Для битов 8..10 значения просто копируются из предыдущих флагов, так как эти биты никак не меняются для АЛУ-операции.
В этой статье я рассмотрел только эти базовые функции, не переходя к вычислению сдвигов, поскольку для них будет написана отдельная статья.