Ретро-компьютер уровня «Радио-86РК» с RISC-V процессором на плате OMDAZZ
RISC-V компьютер
Введение
Основным инструментом «Школы дизайна цифровых схем» является недорогая отладочная плата OMDAZZ c ПЛИС CycloneIV EP4CE6E22C8N. Эта ПЛИС содержит всего 6000 логических элементов и 270 КБит RAM. Такая ограниченность в ресурсах и сформировала требования к параметрам проекта: 16 КБайт ОЗУ и текстовый графический адаптер. Идея проекта навеяна похожими параметрами радиолюбительского компьютера «Радио-86РК» опубликованного в начале 80-х годов прошлого века в журнале «Радио». Этот компьютер был достаточно распространен, а по ресурсам как-раз соответствует возможностям нашей версии платы, что позволило предполагать, что в результате можно будет запустить нечто более сложное чем «Hello world». Используемая отладочная плата была уже представлена в статьях Юрия Панчула. Информация и примеры использования платы доступны на GitHub.
Для работы с проектом необходим установленный Intel Quartus последней версии. Заливка проекта происходит при помощи скриптов Plus/boards/omdazz.
$ ./01_clean.bash - Очиста проекта
$ ./05_synthesize_for_fpga.bash - Синтез в Quartus и заливка проекта в FPGA
$ ./06_configure_fpga.bash - Заливка проекта в FPGA
$ ./07_upload_soft_to_fpga.bash - Загрузка программы через последовательный порт
Аппаратное обеспечение
Центральный процессор
Основой проекта является RISC-V процессор YRV опубликованный в книге одного из разработчиков процессоров семейства Z80 Монте Далримпла «Inside an Open-Source Processor» и адаптированный для ПЛИС Altera Юрием Панчулом. YRV — это ядро микроконтроллерного класса, в нем отсутствует кэш, виртуальная память, внеочередное выполнения инструкций, но есть статический конвейер с шестью стадиями и байпасами. В тоже время ядро поддерживает не только стандарт RV32I, но и двухбайтовые команды стандарта RV32C, и стандарт на атомарные операции. Особенностью процессора является возможность использования узкой шины данных (16 бит), что позволяет эффективно использовать процессор с реальными микросхемами памяти. Все это делает данное ядро интересным с точки зрения получения реального опыта применения микроконтроллерных RISC-V ядер. Адаптированная Юрием Панчулом версия YRV-Plus для ПЛИС Cyclone IV находится в репозитории. Эта версия ядра поддерживает тактовую частоту 50МГц.
Шина данных и порты
В качестве внешней шины использует подмножество шины AHB-Lite, основное различие заключается в том, что AHB-Lite поддерживает пакетную передачу и передачу более 32 бит, что в данном проекте не используется. Процессор использует сильно связанный интерфейс шины, так как это гарантирует предсказуемое время выполнения инструкций, что является важным фактором для микроконтроллеров, а также упрощает дизайн. Жесткая связь означает, что когда на внешней шине появляется состояние ожидания, то весь конвейер останавливается. И хотя в RISC-V Instruction Set Manual утверждается, что для embedded приложений необходим меньший регистровый файл, ширина шины данных часто является гораздо более определяющим фактором. Проект включает в себя опцию для 16-битной шины данных.
Проект поддерживает 7 memory-mapped 16-битных портов, один из которых используются для работы с модулем последовательного порта.
Знакогенератор
Видеоадаптер
Первоначально планировалось использовать QVGA адаптер подобно VGA mode 13h для IBM PC. Этот вариант хорошо работает на плате DE0-CV (любезно предоставленной FPGA-Systems), но из-за небольшого количества ресурсов в проекте пришлось ограничится монохромным текстовым видеоадаптером в режиме 640×480 с размером знака 8×8. Управление лучом производится модулем из состава лабораторных работ DDVCA проходивших в Бишкеке в 2022 году. За основу шрифта взят шрифт ZX-Spectrum. Создание шрифта производилось в программе 8×8 Pixel ROM Font Editor.
Char_065 db 0x7C, 0x82, 0x82, 0xFE, 0x82, 0x82, 0x82, 0x00 ; (A)
Char_066 db 0xFC, 0x82, 0x82, 0xFC, 0x82, 0x82, 0xFC, 0x00 ; (B)
Программа позволяет экспортировать шрифт в ассемблерный файл с операторами db, представляющими символы в шестнадцатеричном виде, который затем конвертируется при помощи любого текстового редактора в HEX файл для дальнейшей загрузки в ПЛИС при помощи функции $readmemh.
reg [7:0] char[0:2047];
initial $readmemh("char.mem8", char);
Это позволяет создавать любые свои символы в случае необходимости. Так как плата OMDAZZ не содержит ЦАП для VGA, то используется черно-белый режим, который может быть без труда доработан для любой двухцветной комбинации.
Клавиатура
В проекте используется PS/2 клавиатура, которая взаимодействует через модуль ps2scan из штатных примеров платы OMDAZZ с небольшой доработкой.
Модуль в составе проекта возвращает не последнее нажатое значение, а текущую нажатую клавишу, что позволяет более просто реализовать функцию getchar (). Взаимодействие осуществляется через порт микроконтроллера.
Модуль возвращает только ASCII код латинских прописных символов. Также, через порт port5_in подключается счетчик, который работает и как таймер и как генератор случайных чисел…
Внешняя память
Традиционно, для загрузки программ в домашних компьютерах использовался магнитофон, и для загрузки программы было необходимо выполнить определенные действия: найти кассету, при помощи счетчика отмотать на нужное место и после нажатия определенных клавиш запустить проигрывание на магнитофоне.
$ ./07_upload_soft_to_fpga.bash
В данном проекте используется сходная идея, но загрузка осуществляется через последовательный порт при помощи скрипта. Загрузка производится в оперативную память, при помощи модулей разработанных Юрием Панчулом. Формат загружаемых программ соответствует HEX формату используемому в функции $readmemh. В отличие от функции $readmemh, в данном методе загрузка происходит через шину AHB-Lite, что позволяет загружать программы не только в BRAM ПЛИС, но и в ОЗУ основанное на реальных микросхемах.
ОЗУ
Базовое ОЗУ проекта основано на узком регистровом файле, и в отличии от [31:0] mem[…] который предлагается в Харрис и Харрис, используется регистровый файл вида [7:0] mem[…]. Как было сказано выше, ядро разработано для использования узкой 16 битной шины данных (и без большого труда может быть доработано для использования 8 битной шины данных). Узкая шина позволяет взаимодействовать с реальными микросхемами памяти, с шиной 8 и 16 бит. Одной из задач проекта является проверка возможности работы с внешним SRAM ОЗУ, скорость доступа которым составляет 55–70нс, поэтому тактовая частота ядра уменьшена до 12 МГц.
Книга которую следует прочитать
Процессор YRV
Поддерживаемые стандарты
Базовый набор инструкций RV32I
Инструкции стандарта RV32C (кроме c.mul, опционально)
Атомарные операции с памятью RV32A: amoadd.w, aamoand.w, amoor.w, amoswap.w and amoxor.w
Операции с несогласованной атомарной памятью (Zam)
Операции манипуляции с битами (Zbb, Zbs, Zbkb) , кроме clz, cpop, ctz, max, maxu, min, minu and orc.b zip and unzip
Расширение для счетчиков и таймеров (Zicntr, опционально)
Расширение для CSR (включая внешний интерфейс)
Инструкция, обеспечивающие явную синхронизацию операций чтения и записи в память (Zifencei)
Независимое от внешних данных время выполнения (Zkt)
Интерфейс отладки Sdext
Прерывания, CSR, отладка
Ядро поддерживает четыре типа прерываний: внешние, немаскируемое, программное и таймерное. Кроме того, поддерживаются шестнадцать пользовательских локальных прерываний. Немаскируемое прерывание имеет наибольший приоритет, за которым следуют локальные прерывания, а затем внешние прерывания. Шестнадцать локальных прерываний с меньшим номером, имеющим более высокий приоритет.
Внешний интерфейс CSR аналогичен сигналам и протоколу APB. Все обращения к регистрам отображаются на шине.
Ядро YRV не включает модуль отладки, но включает основные функции, необходимые для поддержки модуля отладки реализующим стандарт.
Программное обеспечение
Toolchain
В проекте используется стандартный компилятор GCC версии 12.1.0 и выше. Для удобства используется готовый toolchain для NEORV32 RISC-V Processor. Так как процессор поддерживает различные виды прерываний и стартовая точка размещена по адресу 0×200, то набор стартовых подпрограмм crt0 формируется из нескольких ассемблерных файлов которые линкуются при помощи ld скрипта. Настройки компилятора и алгоритм создания ld скрипта аналогичен скриптам процессоров Cortex-M, поэтому все учебные материалы по данной тематики для процессоров ARM будут актуальны и для процессора YRV. Так же актуальны и материалы для процессоров SiFive. Преобразование бинарного файла в HEX для дальнейшей загрузки в оперативную память осуществляется при помощи утилиты elf2hex компании SiFive.
Целочисленная математика
Код для целочисленной математики: деления и умножения взяты из стандартного исходного кода для GCC для Lattice Mico32, так как эта реализация сделана на языке С. Исходный код для целочисленной математики находится в директории static каждого из проектов.
Стандартная библиотека
Набор необходимых C функций таких как strlen был сформирован опытным путем во время портирования теста CoreMark для более раннего проекта. Из-за ограничений памяти и нацеленности проекта на изучение языка ассемблера RISC-V, для сборки не используются стандартные библиотеки типа newlib и picolib, функции добавляются по мере необходимости. Для вывода на экран используется функция ee_printf (…) из состава CoreMark, для чтения клавиатуры getchar ().
Изначально планировался QVGA режим 320×240 аналогичный int 13h, поэтому видеопамять размещена по адресу 0xA0000000. Функция вывода символа на экран выглядит следующим образом:
static char *VGA=(char *)0xA0000000L;
void putc(int x, int y, char c) {
VGA[80*x + y] = c;
}
Динамическое управление памятью
На данный момент динамическое распределение памяти и реализации функций malloc и free в проекте отсутствуют.
Взаимодействие с портами
Имена портов определены в заголовочном файле memory_mapped_registers.h. Необходимо помнить, что тип переменной char, short или int определяет какая процессорная инструкция lb, lh или lw будет использоваться.
#define IO_PORT54_ADDR 0xFFFF0008
#define port4 (* (volatile unsigned short*) IO_PORT54_ADDR )
....
char getchar() {
return (char) port4;
}
Будьте внимательны, при использовании типа int вы можете неявно передавать 0 в соседний порт, так как компилятор будет использовать функцию lw, а не lh.
Заключение
Так как ресурсы платы достаточно малы по сравнению с платами типа DE10, то исходный код проекта был перенесен на GitHub в отдельный репозиторий.
Полученный результат конечно не может сравниться с контроллерами от SiFive, но будет вполне достаточным для тех, кто дошел до 8 главы Харрис и Харрис и хочет поморгать светодиодами на RISC-V. Изначально проект предназначался как платформа изучения ассемблера, а портирование тулчейна — всего лишь доказательство того, что процессор работоспособен. Но по иронии судьбы, в репозитории я разместил математику реализованную на C, не смотря на то, что в проектах есть реализация на ассемблере RV32C.
Автор выражает благодарность @YuriPanchul, а также огромную благодарность @KeisN13, за все его добрые дела, без которых этот проект не состоялся бы.