Привет Мир! В железе!
Предлагаю вам перевод цикла The EightThirtyTwo ISA Part 8: Hello, World! In hardware! автора Alastair M. Robinson.
В течение последних нескольких недель я работал над реализацией VHDL ISA EightThirtyTwo, используя отличную комбинацию GHDL и GtkWave для разработки, отладки, rip up, запуска и, в конечном счете, создания чего-то, способного отправлять “Hello, World!” в UART.
Это была крутая кривая обучения, так как я раньше не возился с симуляцией на этом уровне (ранее я всегда использовал SignalTap для отладки), и я также не работал над конвейерным процессором раньше.
Дизайн, который я создал, довольно хорошо соответствует моим планируемым расходам на логику – примерно 1500 логических элементов при сборке для Cyclone III. Хотя было бы неплохо немного уменьшить размер, следует отметить, что регистровый файл реализован как логика – поэтому нет использования блочной оперативной памяти – и у нас также есть полное выравнивание загрузки/сохранения.
Дизайн разделен на следующие блоки:
- Decoder-это часть комбинационной логики, которая принимает исходный код операции и выводит сигналы, определяющие два источника регистров для ALU, операцию ALU и битовую карту сигналов включения для других частей процессора. С прошлого раза я значительно скорректировал кодирование команд, пытаясь минимизировать объем ресурсов этого декодера; плохой выбор кодировки может легко удвоить его размер.
- Синхронная логика в Decode stage записывает декодированную операцию ALU и содержимое соответствующих регистров на входы ALU.
- ALU имеет два 32-битных регистровых входа, 6-битный непосредственный вход (используется только для инструкции li) и имеет два 32-битных выхода. ALU отвечает за арифметические, побитовые и сдвиговые инструкции, но также обрабатывает сборку многобайтовых непосредственных значений, а также постинкрементные и прединкрементные адреса. Первый вывод ALU может быть записан либо в регистр, либо в tmp. В большинстве случаев второй из двух выходов просто проходит через соответствующий вход, который может быть записан в tmp или в блок загрузки/хранения – это полезно для “swap”, для инструкций хранения и для специального случая “add r7”, который помещает старое значение r7 в temp. С другой стороны, инструкция multiply дает 64-битный результат, поэтому она помещает верхнюю половину на второй вывод, откуда она записывается в temp.
- Execute stage отвечает за изменения счетчика программ и определение того, когда это безопасно сделать. Я еще не пытался смягчить опасности вообще – мы просто останавливаем PC и вставляем пузыри в конвейер, но в долгосрочной перспективе здесь можно было бы сделать некоторые рудиментарные инструкции, сбрив пару циклов с таких конструкций, как “li 0; mr r0”.
- Memory stage запускает операции загрузки/хранения, а также записывает результаты из ALU обратно в файл регистра.
- Наконец, что было бы Writeback stage в классическом дизайне, но на самом деле его работа в значительной степени выполняется этапом памяти. Все, что мы здесь делаем, это ждем завершения операций загрузки/хранения, а для операций загрузки записываем результат в temp.
Я также внес еще несколько изменений в ISA: во-первых, хотя у меня еще нет поддержки прерываний, я обдумывал, как добавить это. Не имея никаких инструкций, способных сохранять или восстанавливать флаги процессора, я решил просто сопоставить их с четырьмя верхними битами счетчика программ. Это уменьшает доступное адресное пространство программы с 4 ГБ до 256 МБ – я могу жить с этим, и это означает, что сохранение счетчика программы в начале прерывания или подпрограммы автоматически сохраняет флаги. Хотя это означает, что подпрограмма не может вернуть результат вызывающему с помощью флагов состояния, это было бы трудно в любом случае, потому что вытягивание обратного адреса из стека повлияет на нулевой флаг.
Другим существенным изменением ISA является то, что я решил не реализовывать инструкцию ltmpinc, которая вызвала бы операцию загрузки, используя содержимое temp в качестве адреса, увеличивая temp и сохраняя результат в назначенном регистре. Это была бы единственная инструкция загрузки, которая должна была записывать в регистровый файл; все остальные записи в файл регистров поступают непосредственно из ALU, поэтому это значительно увеличило бы сложность. Вместо этого, поскольку это по существу свободно реализовать, я добавил инструкцию “ldidx”, которая использует сумму tmp и выбранный регистр в качестве адреса загрузки, считывает из памяти и записывает результат в tmp.
Также, я создал second repository, аналогично моему предыдущему ZPUDemos, содержащему тестовые проекты для этого процессора. Сам процессор является подпроектом.
Адрес для контактов : imax9@narod.ru
Если вам понравились мои работы и вы желаете поддержать сайт - сделайте дотацию.
При копировании статьи – обязательна ссылка на авторство и источник. Без разрешения автора копирование запрещено.
© Максим Ильин 2022г.