Вот, скажем, Program Counter - PC

May 22, 2018 17:07


Основа основ, указующий перст, краеугольный камень в основании всей шаткой конструкции. Он указывает на адрес в памяти программ (не важно, "фон Нейман" или "Гарвард"). Чтобы взять следующую команду, в случае линейного исполнения, к текущему значению PC надо прибавить некое смещение, равное размеру только что выполненной инструкции. В самом простом случае, когда все команды имеют одинаковую длину (допустим, ровно 1 байт всегда) и в программе нет никаких ветвлений, PC может быть просто обычным инкрементирующим счётчиком, прибавляющим +1 на каждом такте.

Если же есть ветвления, то когда условие перехода выполнено (или если переход безусловный), то счётчик нужно перегрузить новым значением, с которого и продолжится счёт. Если мы хотим иметь возможность вернуться обратно, тогда значение "PC+1" нужно ещё где-то сохранить, это будет адрес возврата на СЛЕДУЮЩУЮ ПОСЛЕ ветвления команду. Т.е. прыгнули, что-то сделали, вернулись.

Если может быть так, что прыжки будут вложенными, то адресов возврата тоже будет много, отсюда нужен какой-то стек, наверху которого будет лежать самый недавний переход, а в самом низу - самый первый. Чтобы не засорять код самоочевидными командами "сохранить PC+1 в стек", это действие лучше делать автоматически, где-то один раз указав адрес расположения этого стека и, возможно, его объём (чтобы вызывать срабатывание аварийной системы, если стек переполнен). Но это уже другой вопрос, я подумаю над ним потом.

Теперь, собсно, как посчитать адрес перехода.

Допустим, у нас есть команда с условной мнемоникой:
BLE X1, X2, +100500
то есть Branch If Less, переход при условии "если X1 меньше X2", при этом тут несколько возможных вариантов реализации.
1) в PC сразу загружается абсолютный адрес 100500, с которого и продолжится исполнение. Это вполне годно для систем, в которых работает одна цельнобетонная программа, все адреса переходов внутри которой известны уже во время компиляции-сборки. Но если мы хотим иметь систему, которая может загружать-выгружать программы в условно-произвольное место в памяти, тогда надо либо прямо в момент загрузки программы заново пересчитывать все эти точки, либо грузить всегда по известным адресам, либо использовать относительные переходы, то есть...

2) в PC загружается результат арифметического сложения текущего значения PC и заданного командой смещения +100500 (оно может быть и отрицательным, если прыгаем "назад" - ибо почему нет, собсно?). Тогда не надо ничего пересчитывать в момент загрузки программы, фактические адреса переходов будут каждый раз вычисляться процессором самостоятельно.

Второй метод явно более прогрессивен.

Итак, что из железа нужно для работы переходов?

Во-первых, нужно некое решающее устройство, которое будет формировать сигнал "условие выполнено", анализируя исходные данные: содержимое двух регистров. Это может быть и АЛУ (сравнивать два числа они умеют), это может быть отдельный двоичный компаратор. Например, есть чипы 7485 и их КМОП-потомки: сравнивают два четырёхбитных числа, учитывая так же начальное условие, и выдают вердикт: больше, равно, меньше. Есть ещё чипы: 74hc682, *521, *688. Они поширше, 8 бит, но само сравнение попроще: либо только на равенство, либо на равенство и меньшесть (впрочем, этого вполне достаточно, ибо из "РАВНО" и "МЕНЬШЕ" можно вывести прочие соотношения). И ещё их купить сложнее.

Из 11 штук микросхем 7485 (можно воспользоваться одной неочевидной хитростью и сократить количество микросхем до, если я правильно посчитал, девяти -- но это повлияет только на стоимость деталек, ибо задержка практически никак не уменьшиться) можно собрать компаратор, побитно сравнивающий два 32-разрядных беззнаковых (в доп-2 коде) числа. Со знаковыми числами выходит заминка, ибо с т.з. такого компаратора 111...111 безусловно больше, чем 000...000, хотя мы-то понимаем, что отрицательные числа всегда меньше положительных.

Поэтому, чтобы можно было сравнивать и знаковые, и беззнаковые числа, нам потребуется
1) несколько разных команд: BLE и BLEU для знаковых и беззнаковых; BGE, BGEU (аналогично); BEQ, BNE (знаковые и беззнаковые на равенство сравниваются тупо побитово)
2) небольшое усложнение схемотехники: в случае чисел со знаком надо будет сначала смотреть на старший разряд и ежели там разные знаки, тогда принудительно считать, что "0" больше "1" вне зависимости от значения остальных битов; если же знаки одинаковы, тогда сравнивать как если бы это были обычные беззнаковые числа.
3) некий мультиплексор, который будет выбирать из возможных вариантов ответа ("БОЛЬШЕ", "МЕНЬШЕ", "РАВНО", "НЕ РАВНО", "БОЛЬШЕ ИЛИ РАВНО" и т.п.) тот, который нам надо. На выходе мультиплексора - один-единственный бит: "УСЛОВИЕ ВЫПОЛНЕНО".

Теперь нам нужно будет скормить этот бит некоему блоку, который ... и снова варианты, как минимум два немного разных:
1) если УСЛОВИЕ ВЫПОЛНЕНО, то прибавит к PC указанное смещение, иначе прибавит +1. В любом случае, полученное значение запишется обратно в PC. Т.е. нужен сумматор, на один вход которого подавать PC, на другой - выход мультиплексора, по УСЛОВИЮ выбирающего либо смещение, либо +1. Это проще с т.з. логики и будет работать быстрее (тупой сумматор всегда быстрее АЛУ), но довольно громоздко схемотехнически
2) если УСЛОВИЕ ВЫПОЛНЕНО, то исполнит команду Arithmetic Sum "PC + указанное смещение" с занесением результата в PC, иначе исполнит команду No Operation, результат которой .. его нет, поэтому делать с ним ничего не надо. Тут явно напрашивается использование штатного АЛУ. Несколько печально от того, что у 74181 есть команда "A minus 1", но нет "A plus 1" (зато есть "A plus B")

Итого, вырисовывается примерно такая структура:

Многоразрядный (пусть будет 32-битный) синхронный счётчик, на каждом такте ЛИБО прибавляющий к самому себе +1, ЛИБО загружающий новое значение от выхода Y АЛУ. Вход управления "счёт/загрузка" - это "УСЛОВИЕ ВЫПОЛНЕНО" от компаратора.
Мультиплексоры на входах АЛУ:
- на входе A выбор между PC для команд перехода и выходом A регистровой памяти для прочих команд
- на входе B выбор между Immediate либо константой +1 (управляется через "УСЛОВИЕ ВЫПОЛНЕНО") для команд перехода и выходом B регистровой памяти для прочих команд

При выполнении переходов (особенно безусловных) полезно иметь опциональную возможность сохранения либо текущего PC, либо сразу PC+1

Как-то так, очень условно.

P.S. прикинул задержку распространения на таком компараторе: если взять быструю логику (74AC или даже 74LVC в overvoltage; к сожалению, в этих сериях нет компараторов), то при комнатной температуре будет около 75нс без учёта задержек в проводниках. При полном нагреве будет около 180 нс. На -40Ц не считал :)
Previous post Next post
Up