Ретро-компьютер уровня «Радио-86РК» с RISC-V процессором на плате OMDAZZ

RISC-V компьютер

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, за все его добрые дела, без которых этот проект не состоялся бы.

© Habrahabr.ru