Микропроцессор своими руками. Часть 1.2.

№ 7’2002
При разработке и создании все более сложных и объемных цифровых схем появляется проблема повышения эффективности работы разработчиков и гибкости проекта, а основным требуемым критерием эффективности разработки является время, затраченное на разработку.

Все статьи цикла:

Начинаем проект в FPGA

При разработке и создании все более сложных и объемных цифровых схем появляется проблема повышения эффективности работы разработчиков и гибкости проекта, а основным требуемым критерием эффективности разработки является время, затраченное на разработку, или, иначе говоря, «time to market» — время от начала реализации проекта до выхода готового продукта на рынок. Поэтому, проект строится в виде иерархической пирамиды файлов. При таком подходе каждый «кирпич» пирамиды проектируется и отлаживается отдельно, что позволяет в дальнейшем пользоваться набором проверенных блоков, называемых библиотечными. Проект будем выполнять на языке AlteraHDL, так как он достаточно широко распространен и описан. Кроме того, для начинающих, он гораздо более доступен, чем другие языки группы VHDL. Так же более доступны и студенческие версии ПО.

Проект будет выполнен в виде текстовых файлов. Это позволит применить параметрическое выполнение описаний, что даст возможность применить файлы и в других проектах. В пакете ПО MaxPlus фирма Altera дает библиотеку параметризуемых мегафункций, позволяющих легко встраивать в проект счетчики, дешифраторы и пр. Описание этих мегафункций приведено в Help-файлах ПО.

Соглашение о названии сигналов (о наименовании цепей)

Для однообразия примем, что активное состояние сигналов будет равно 1. Если активное состояние сигнала будет равно 0, то в название сигнала на конце будет добавлена буква «n».

В файлах описаний применим следующие названия сигналов:

сlk — частота, синхрочастота,

_еna — сигнал разрешения записи, в большинстве случаев его длительность будет равна длительности 1-го периода синхрочастоты,

reset — сигнал системного сброса.

_load — сигнал загрузки,

_node — название сигнала относится к внутреннему узлу схемы,

_rg — название сигнала относится к внутреннему регистру

_cnt — название сигнала относится к внутреннему счетчику.

Все сигналы внутри FPGA Altera могут быть только однонаправленными, поэтому у каждого блока обычно есть входы и выходы данных, поэтому d[], data_a[], data_b[] — эти и другие подобные сигналы будут являться входными шинами данных для разрабатываемых блоков, q[] — эти и другие подобные сигналы будут являться выходными шинами данных для разрабатываемых блоков (если это не оговорено отдельно).

Начинаем с разработки вспомогательных файлов.

Произведем разработку вспомогательных файлов. Начнем с регистра из D-триггеров.

Далее приведен текст файла Dffex_rg.tdf. Это регистр со входами сброса, установки и разрешения записи. Разрядность регистра задается в параметрах и, по умолчанию, принята равной 8 бит.

Выберем в качестве устройства для проекта микросхемы серии ACEX или FLEX10, а далее выбор конкретного устройства установим в режим AUTO.

Создайте рабочую папку, например My_cpu, запишите файл в эту папку, затем выполните компиляцию, чтобы убедиться в отсутствии ошибок, и создайте инклюдный файл Dffex_rg.inc. Так же будем поступать и со всеми другими файлами проекта о которых будет говориться здесь.

Далее создедим еще один вспомогательный файл — mdffex_rg.tdf. Это регистр, аналогичный предыдущему, со входами сброса, установки и разрешения записи. Здесь уже используется предыдущий файл, как основа для построения данного файла. От предыдущего этот регистр отличается тем, что имеет на входе мультиплексор данных. Сигнал sel выбирает какую шину data_a[DATA_WIDTH-1..0] или data_b[DATA_WIDTH-1..0] подключить ко входу регистра. Разрядность регистра задается в параметрах и, по умолчанию, принята равной 16 бит.

Теперь создадим файл, описывающий стек.

Стек будет представлять набор регистров с мультиплексорами на входах. Регистры будут соединены каскадно. При этом, на входы регистра, через мультиплексор, начиная с 1-го, подаются выходы от регистра с меньшим номером или от регистра с большим номером. На вход 0-го регистра подается входная шина данных, а его выходы соединены с выходной шиной данных стека. Если на входах управления режимом работы блока — push и pop нет активного сигнала, то сохраняется предыдущее состояние. Если активен сигнал push, то в 0-й регистр записываются данные со входной шины данных, в остальные регистры записываются данные от «соседа» с меньшим номером и, следовательно, данные «проталкиваются» в стек, а если активен сигнал pop, то в регистр записываются данные от «соседа» с большим номером и данные извлекаются из стека.

Наш стек будет иметь параметры как по разрядности шины данных, так и по глубине. Такое выполнение файла позволит, при необходимости, легко модернизировать микропроцессор.

Основа стека — это двухмерный массив регистров с мультиплексорами s[DATA_WIDTH-1..0][STACK_WIDTH-1..0], обозначенный в файле как узел — NODE. Массив имеет ширину — DATA_WIDTH и глубину — STACK_WIDTH.

Для проверки работы стека был создан файл, аналогичный приведенному файлу stack, но для удобства проверки работы, выходы регистров, образующих стек были выведены как выходные шины — q0_[] …q3_[].

Симуляция работы блока stack приведена на рис. 2.

Сначала выполняется команда push, данные помещаются в стек, затем подается команда pop и данные извлекаются из стека.

Создадим файл, описывающий счетчик команд.

Нам нужен счетчик, который мог бы загружаться при выполнении команд JMP, CALL, RET. Значение адреса, которым будет загружаться счетчик, подадим по входной шине данных. Счетчик должен считать «вверх». Счетчик также должен иметь входной сигнал разрешения счета, так как для некоторых операций счет будет запрещен. Для счетчика команд применим библиотечный элемент — lpm_counter. Зададим параметр по разрядности шины адресов памяти команд — PS_WIDTHAD.

Счетчик имеет входную шину данных, входы синхронной загрузки и разрешения счета. Выходы счетчика подключены к выходной шине блока. По умолчанию такой счетчик будет считать «вверх». Итак, счетчик адресов удовлетворяет поставленным требованиям.

Создадим файл, описывающий ALU — основу нашего микропроцессора.

Прежде чем приступить к описанию ALU, позволю себе небольшое отступление. Когда в известной басне слепых спрашивали, какой им представляется слон, то каждый из них давал свое описание. Так же происходит и с микропроцессорами. На вопрос, как и почему микропроцессоры работают, дилер из коммерческой фирмы не задумываясь ответит: «Потому что они импортные!». Программист начнет рассказывать об операционных системах, языках программирования, оптимизирующих компиляторах и т.д. Если же сейчас спросить нас с Вами в этой части статьи, то мы должны ответить: «Микропроцессор работает только потому, что есть генератор тактовой частоты, счетчик адресов памяти, сама память, кое-какие регистры, называемые «порты», а все остальное — дешифратор. Да и память, собственно, это тоже большой дешифратор». Так вот — лучшая часть этого дешифратора и называется ALU!

Наше ALU будет состоять из двух «логических» частей: первая будет вырабатывать сигналы управления для всего, что есть в данном проекте, а вторая будет коммутировать выходные шины блоков на общую внутреннюю шину микропроцессора.

Самый верхний приоритет в исполнении команд имеет вход запроса прерывания, и, если есть состояние IRQ=1, то необходимо выполнить переход по адресу вектора и сохранить адрес возврата в стеке. Если данную команду представить в виде элементарных команд, то она эквивалентна двум командам JMP <addr> и PUSH.

Если нет запроса прерывания, т.е. IRQ=0, то необходимо продешифрировать слово команды, поступившее с выходов блока PS. Биты [23..20] дадут поле кода операции, биты [19..16] — укажут на регистр — приемник информации, биты [3..0] — укажут на регистр — источник информации, биты [15..0] — укажут на адрес поля Mem.

Создадим файл программы, которую будет выполнять микропроцессор.

Для описания памяти команд применим библиотечную функцию lpm_rom, и для ее инициализации применим файл step1.mif. Файл step1 будет содержать коды команд микропроцессора, требуемые для отладки проекта.

Чтобы нам было легче ориентироваться в диаграмме временной симуляции проекта, создадим несколько файлов инициализации памяти команд, причем каждый файл будет проверять работу не более чем одной или двух команд микропроцессора.

Файл step1 будет проверять работу команд NOP и JMP. Коды команд и их мнемоника приведены в таблице 1. По этой программе микропроцессор должен выполнить два шага и вернуться в нулевой адрес. Далее процесс доолжен повторяться циклически.

Таблица1. Коды, выполняемые микропроцессором, по программе step1

Для проверки микропроцессора по программе Step1, создадим файл инициализации памяти программ микропроцессора step1.mif

Остальные команды микропроцессора могут быть проверены аналогично. Подробное описание работы с этой программой будет приведено в разделе, описывающем этап симуляции работы микропроцессора.

Создадим файл, описывающий микропроцессора. Верхний файл проекта.

Теперь мы можем собрать верхний файл проекта — непосредственно микропроцессор. В микропроцессор входят файлы:

  1. dffex_rg — этот файл будет выполнять все функции, где необходимы регистры: порт ввода-вывода, DS и др.,
  2. stack — этот файл будет выполнять функцию стека,
  3. ps_cnt — этот файл будет выполнять функцию счетчика адресов памяти команд,
  4. alu — этот файл будет выполнять функцию АЛУ,
  5. для описания памяти команд применим библиотечную функцию lpm_rom, и для ее инициализации применим файл step1.mif.

Далее приведен текст файла описания микропроцессора uP.tdf

В качестве DEVICE для проекта — выберем серию ACEX, а для выбора микросхемы установим режми AUTO. Произведем компиляцию проекта. Далее, создадим файл симуляции проекта микропроцессора с файлом команд step1, такой как на рис. 3. И произведем симуляцию проекта.

Синхрочастота, поступающая на тактовый вход микропроцессора, имеет длительность импульса 20 нсек и каждый положительный фронт синхрочастоты пронумерован. Все описание работы будет опираться на соответствующий фронт синхрочастоты. Будем называть фронты так: фронт1, фронт2 и т. д.

При действии фронта1 микропроцессор находится в состоянии сброса. Счетчик адреса обнуляется и из PS извлекается первая команда NOP, и никаких изменений не происходит. Под фронт2 и фронт3 из PS так же извлекаеютя команды — NOP. Таким образом выполняются строки 0..1 из файла step1. Когда счетчик адреса памяти команд находится в состоянии 2, из памяти команд извлекается команда, имеющая код 100000. Это команда — JMP <000000>. Получив код этой команды, АЛУ производит дешифрацию кода операции, и выставляет сигнал на запись нового адреса в счетчик команд. Код адреса из АЛУ подается на счетчик команд. Под фронт4 производится запись в счетчик команд нового адреса. Далее на шинах адреса устанавливается новый адрес. По этому адресу извлекается новый код команды. Поскольку мы выполняли команду JMP <000000>, то микропроцессор перешел в адрес 0000 и на этом программа зацикливается в соответствии с файлом step1. Результат симуляции показывает, что наш микропроцессор успешно выполнил команды NOP и JMP.

Далее читатели могут самостоятельно выполнить подобные действия для проверки работы микропроцессора с другими командами.

Для полной проверки микропроцессора, создадим файл инициализации памяти программ микропроцессора step2.mif. В этом файле будут проверяться все команды микропроцессора и так же будет проверяться работа по прерываниям. Произведем компиляцию проекта с данным файлом инициализации памяти.

Создадим новый файл симуляции проекта.

Временная диаграмма симуляции работы микропроцессора с файлом команд step2 приведена на рис. 4.

Синхрочастота, поступающая на тактовый вход микропроцессора, также как и в предыдущем случае имеет длительность импульса 20 нсек и каждый положительный фронт синхрочастоты пронумерован. При действии фронта1 микропроцессор находится в состоянии сброса. Счетчик адреса обнуляется и из PS извлекается первая команда NOP и никаких изменений не происходит. Под фронт2, фронт3 и фронт4 из PS так же извлекаеютя команды — NOP. Таким образом выполняются строки 0..2 из файла step2. Счетчик команд находится в состоянии 3 и из этого адреса извлекается код команды 200010, что соответствует команде CALL <0010>. Под фронт5 АЛУ формирует импульс записи нового адреса в счетчик команд и следующий адрес будет 10. Так же под фронт5 производится запись в стек адреса возврата, на выходной шине стека появляется значение 3. Под фронт6 начинает выполняться подпрограмма, см. файл step2, с адреса 10. Здесь выполняется команда LDI Reg0, <55AA>. АЛУ формирует строб записи в регистр 0. Данные на вход регистра поступают по внутренней шине микропроцессора. Аналогично под фронт7 выполняется команда LDI Reg1, <33CC>. Под фронт8 выполняется команда MOV Reg1, Reg0. Под фронт9 выполняется команда MOV [Mem], Reg1. Под фронт10 выполняется команда LDI Reg0, <1111>. Эта команда нужна нам для того, чтобы показать, что следующая за ней команда MOV Reg0, [Mem], выполняемая под фронт11 снова изменит содержимое этого регистра. Под фронт12 выполняется команда NOP. Под фронт13 из памяти команд выбрана команда RET и выполняются следующие действия: из стека извлекается адрес возврата и заносится в счетчик команд. Стек выполняет команду POP и на его выходе значение с 3 меняется на 0, т. е. стек приходит в то состояние, которое у него было до вызова подпрограммы, см фронт0..4. Далее происходят следующие действия: под фронт14 микропроцессор вернулся в адрес 3 и снова начал выполнять вызов подпрограммы по адресу 10. Но под фронт16 на входе АЛУ появился сигнал запроса прерывания irq. По этому сигналу производятся действия, аналогичные действиям по команде CALL <0000>. АЛУ формирует импульс записи нового адреса — адреса вектора прерывания в счетчик команд и следующий адрес будет 0. Так же под этот фронт производится запись в стек адреса возврата, на выходной шине стека появляется значение 11, а предыдущее значение стека—3 будет «протолкнуто» внутрь стека. Далее микропроцессор выполняет команды, которые будут извлекаться из PS из адресов, по которым выполнен переход, по адресу вектора прерывания. Таким образом, мы видим, что микропроцессор выполняет все команды в соответствии с заданием.

Несколько слов об отладке программ во встроенных микропроцессорах.

Во-первых, используемые микросхемы FPGA — перепрограммируемые, то есть в случае обнаружения ошибки всегда есть возможность исправить эту ошибку без переделки всего устройства.

Во-вторых, при отладке программы во встроенных микропроцессорах есть много дополнительных аппаратных возможностей, используя которые, можно достичь быстрого положительного результата.

Поскольку весь проект «самодельного» микропроцессора для нас открыт, и мы знаем его устройство, то мы можем облегчить отладку программ, при помощи дополнительных аппаратных ресурсов, так как в процессе отладки можно использовать FPGA с большим количеством ячеек, чем нужно для функционирования проекта. Это позволит выделять отладочные ресурсы. К таким ресурсам можно отнести:

  1. дополнительные поля памяти для вызова тестовых и мониторных программ,
  2. тестовые входы и выходы для микропроцессора, позволяющие снимать состояния сигналов и задавать воздействия,
  3. дополнительные поля памяти могут использоваться для включения в систему встроенных логических анализаторов см. Л15, что является очень мощным средством отладки,
  4. дополнительные счетчики, тестовые триггеры и т. д.

Далее, ко встроенным микропроцессорам может быть «пристроен» блок, выполняющий функции внутрисхемного эмулятора. С его помощью пользователю становятся доступны внутренние регистры процессора, осуществляется останов по адресу, шаговый режим и пр.

Все эти возможности позволяют проводить успешную отладку программы не только в программном симуляторе, но и на «живом железе».

Что мы получили.

По итогам компиляции проекта, из файла рапорта, получаем следующий результат:

Для реализации проекта потребовалось всего 182 ячейки, при том, что проект содержит три 16-битных регистра.

Сравнительная характеристика.

Для сравнения можно сказать, что широко известные микропроцессоры, такие как MCS51 выполняют одну команду за 12 тактов, а их более «продвинутые» версии — за 4 такта, и только новейшие CYGNAL за 1 такт. Чтобы сравнить производительности различных микропроцессоров с тем, что мы разработали, воспользуемся диаграммой, приведенной в Л16, и добавим к ней данные по нашему проекту (см. рис. 5).

Заключение

Мы получили 16-битный RISC-микропроцессор, работающий на частоте 50 МГц, и выполняющий все команды за один такт. И, следовательно, спроектированный микропроцессор имеет производительность 50 MIPS. Данный проект может послужить основой для разработки микропроцессора с требуемым набором команд и, с требуемыми для вычислений, ресурсами. На основе такого микропроцессора может быть разработан микроконтроллер, путем добавления к проекту блоков периферийных устройств, из имеющихся библиотечных мегафункций. Проект имеет все возможности, чтобы его можно было легко доработать до реальной конструкции. Конечно, при значительном увеличении числа команд сложность АЛУ тоже возрастет, а быстродействие несколько уменьшится. Однако применение конвейера команд позволяет преодолеть эти трудности. Но конвейер команд, тайм-слоты задерки переходов и прочие профессиональные «хитрости» выходят за рамки данной статьи.

Литература

  1. Дмитрий Фомин «Кремний на бумаге» или fabless? Живая электpоника pоссии, 2001, том 1 http://www.elcp.ru
  2. А.П. Антонов Язык Описания цифровых устройств AlteraHDL. М. РадиоСофт, 2002.
  3. И.Кривченко. Системы на кристалле: общее представление и тенденции развития. Компоненты и технологии, №6 2001.
  4. Processors drive (or dive) into programmablelogic devices. By Markus Levy. EDN От 07.20.2000
  5. Каршенбойм И.Г. Микроконтроллер для встроенного применения — NIOS. Конфигурация шины и периферии. «Компоненты и технологии» №2, 3, 4, 5 2002
  6. Altera™. Nios Soft Core Embedded Processor, data sheet June 2000, ver.1
  7. Altera™. Nios_2_0_CPU_datasheet
  8. Altera™. Nios 32-Bit Programmer’s Reference Manual January 2002 Version 2.0
  9. Altera™. Nios 16-Bit Programmer’s Reference Manual January 2002 Version 2.0
  10. Altera™. ARM-Based Embedded Processor Device Overview, data sheet February 2001, ver.1.2
  11. Altera™. Nios_tutorial.pdf
  12. Altera™. Nios_an188_Custom_Instructions
  13. Altera™. Simultaneous Multi-Mastering with the Avalon Bus
  14. И. Каршенбойм, Н. Семенов Микропрограммые автоматы на базе специализированных ИС. — Chip News, №7, 2000.
  15. Каршенбойм И.Г. «Встроенный» логический анализатор — инструмент разработчика «встроенных» систем. Схемотехника №12, 2001.
  16. 8051CYGNAL.pdf

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *