Введение
Основным инструментом “Школы дизайна цифровых схем” является недорогая отладочная плата 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 с размером знака 8x8. Управление лучом производится модулем из состава лабораторных работ DDVCA проходивших в Бишкеке в 2022 году. За основу шрифта взят шрифт ZX-Spectrum. Создание шрифта производилось в программе 8x8 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. Так как процессор поддерживает различные виды прерываний и стартовая точка размещена по адресу 0x200, то набор стартовых подпрограмм 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 , за все его добрые дела, без которых этот проект не состоялся бы.
Автор: Дмитрий