/AVR_like_core_on_verilog

Soft core with support for the AVR8 instructions on verilog

Primary LanguageVerilogGNU General Public License v3.0GPL-3.0

Этот проект разрабатывался исключительно в развлекательных целях (да простит меня Atmel и Microchip) и спортивного интереса. Цель проекта была создать ядро AVR, которое можно программировать стандартными инструментами (AVR GCC) на языке Си, а также "заливать" прошивку без использования дополнительного оборудования (отладчиков, типа J-Link или им подобных). Также обязательным условием было использования встроенной блочной памяти без внешних Flash и Sram.

В конечном итоге (с третьей попытки) получилось родить работоспособное ядро, которое может не только помигать светодиодом, но и работать с разнообразной периферией по SPI,UART,I2C, работать с массивами в ОЗУ и всё это с использованием стандартных инструментов разработки для микроконтроллеров AVR. Само ядро, естественно, не копирует 1 в 1 оригинал такт в такт, а только исполняет его набор инструкций. Главное основное отличие - машинный цикл (исполнение одной инструкции) выполняется за два такта (в оригинале - за 1 такт), но это легко компенсируется поддержкой более высокой входной частоты (проверено до 50MHz, что в итоге эквивалентно 25MHz оригинала). Также, благодаря использованию 2-портовой RAM и ROM памяти, удалось сократить такие команды, как PUSH, ST, STD, RCALL до одного машинного цикла, POP, RET, RETI, LD, LDD и им подобные до 2 машинных циклов (в отличии от 2 и 3 соотв. у оригинала), что очень заметно особенно при обработке прерываний (где особенно часто используются команды POP и PUSH при компиляции Си кода). Из нереализованных остались команды умножения с плавающей точкой (FMUL, FMULS,FMULSU) и команды самопрограммирования SPM. Каждая команда может быть индивидуально включена или выключена из компиляции проекта с помощью макросов.

Из периферии реализовано: GPIO: три двунаправленных восьмиразрядных порта (PB,PC,PD). UART: RX и TX, только асинхронный режим 8 бит, 1 стоп, no parity. Зато baudrate может быть значительно выше оригинала. SPI: только MASTER 0-й и 2-й режимы, 8 бит, MSB-LSB. Максимальная частота равна 1/2 входной частоты. I2C: тоько мастер, не реализованы статусы TWSR, добавлена возможность отслеживать ACK/NACK от слейва по дополнительному флагу. TIMER0: функционально идентичен оригиналу. Карта регистров совпадает с Atmega8, но есть некоторые отличия по инициализации модулей (кроме GPIO, там всё идентично оригиналу). Отличия в основном по настройке делителей частоты (UART: F_CPU / BAUD, UBRR - 15 бит; I2C: F_CPU / F_SCL / 8, TWBR - 8 бит). В любом случае, периферию можно реализовать свою, на любой вкус. Также реализована система прерываний: добавлена и проверена обработка четырёх векторов (INT0, INT1, TIMER0 Overflow, USART RXC). На остальных векторах стоят заглушки, но их несложно добавить по аналогии с уже реализованными.

Работа ядра была опробована на Altera CycloneIV и Xilinx Spartan6. Также на начальных этапах проверялось на GOWIN, но ближе к финальной версии на данной платформе ядро просто перестало работать, долго не стал разбираться, почему. Так как другие мои крупные проекты, отлаженные на Альтере, также некорректно работали на Gowin. Видимо, эта китайская платформа ещё сыровата. В общем, весь проект, включая ядро, периферию, прерывания, а также дополнительно динамическую индикацию для отладки занял после всех возможных (для меня) оптимизаций - 5740 ячеек LUT4 (Altera), 2920 ячеек LUT5 (Xilinx, не уверен, что это корректная цифра). Не знаю, много это или нет?

Структура проекта очень проста: soft_avr_top.v - модуль верхнего уровня, в нём в основном расположены все соединения между модулями, внешне порты, а также делитель входной частоты. core.sv - основной модуль, где распололожено само ядро. io.sv - модуль, где расположена вся периферия, общается с модулем core с помощью шин данных, адреса и линий чтения/записи IODIN,IODOUT,IOCNT,IOW,IOR. params.sv - файл с основными настройками ядра (размер ROM,RAM, включение/выключение компиляции команд и прерываний для экономии ресурсов ПЛИС). din7seg.v - дополнительный модуль для подключения динамической индикации к линиям ядра для отладки (отключается макросом в TOP модуле).

Для успешной компиляции проекта необходимо создать модули памяти DUAL PORT ROM с разрядностью шины данных 16 бит и соответствующим количеством ячеек памяти (4096 для Atmega8), DUAL PORT RAM с разрядностью шины данных 8 бит и соответствующим количеством ячеек памяти (1024 для Atmega8). Особенность для Altera: при создании обоих модулей памяти выбирать раздельный CLOCK на каналы A и B, убрать защёлки с выходных шин данных. Особенности для Xilinx: при создании модуля RAM выбирать TRUE DUAL PORT RAM.

Прошивка микроконтроллера зашивается на стадии компиляции проекта при создании модулей DUAL PORT ROM (указывается файл инициализации памяти: *.mif для Altera, *.coe для Xilinx). Для создания файлов инициализации, я написал свой конвертер (расположен в папке software): для создания файла выбираем HEX прошивки, устанаыливаем галочку на соответствующий вид ПЛИС, выбираем ширину шины 16 бит и соответствующий размер прошивки (в 16 битных словах, т.е. 4096 для Atmega8) и нажимаем кнопку "Конвертировать", сохраняем полученный файл с правильным расширением.

У меня была мысль и даже попытка создать модуль загрузки прошивки "на лету", чтобы постоянно не перекомпилировать весь проект из-за замены прошивки памяти, но пока безрезультатно. Буду продолжать работать в этом направлении.

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

Если Вам понравился мой проект, не поленитесь поддержать его звёздочкой (лайком).

22.09.23 Исправил модуль UART RX (точнее, скопировал из примера https://nandland.com/uart-serial-port-module/, да простит меня автор за плагиат), так как моя реализация работала нестабильно и отловить ошибку я так и не смог.

Добавил поддержку "прошивки" HEX файла в ROM "на лету" без перекомпиляции всего проекта. Для прошивки необходимо: 1 - заменить в проекте модуль DUAL PORT ROM на DUAL PORT RAM, аналогичного объёма и ширины шины данных, можно также его инициализировать какой-либо прошивкой. 2 - подключить ПЛИС через USB-UART переходник к сигналу uart_rx (uart_tx не обязательно), запустить программу "SoftAVRLoader" (в папке software), выбрать скорость UART (11520 по умолчанию), COM порт, нажать кнопку "Connect", выбрать HEX файл, зажать кнопку rst на своей плате и, удерживая rst, нажать кнопку "Write", дождаться окончания прошивки и отпустить rst. Файлы проекта обновлены только в папке Altera, на Xilinx я пока ещё не проверял, но, думаю, должно работать. Достаточно изменить файлы проекта, аналогично альтеровским, не забывая про положительную полярность сигнала clock блочной памяти Xilinx. Теперь для отладки прошивки микроконтроллера нет необходимости перекомпилировать проект целиком с заменой файла инициализации ROM.

23.09.23 Исправил обнаруженную ошибку с коммандой ASR. Наконец-то добился поставленной задачи - FPGA работает с FatFS на Си!

24.09.23 Добавил и проверил TIMER2 (кроме PWM Phase Correct) на реальном проекте (WAV плеер на SD карте, где используются: TIMER0 с прерыванием по переполнению для временных интервалов, TIMER2 для генерации ШИМ, SPI для общения с SD картой и UART для отладки). Также добавил возможность компилировать проект под Atmega16/32 под уже имеющуюся периферию (макрос в модуле "io"). Добавлен PORTA для совместимости. Карта регистров Atmega8 и Atmega16/32 мало чем отличается, зато у последней аж 2Кб ОЗУ! Векторы прерываний также перемещены по нужным адресам. Для правильной компиляции проекта под Atmega16/32 необходимо: увеличить объём ROM и RAM, в модуле "params" изменить соответствующие параметры (FSIZE,RSIZE), обязательно включить компиляцию комманд CALL,JMP, в модуле "io" раскомментировать макрос define ATMEGA16_32 и закомментировать define ATMEGA8. Изменения внесены и проверены на обеих платформах.

3.10.23 Выявленные глюки, видимо, появлялись из-за адресов ОЗУ, которые генерировал GCC AVR с выставленной оптимизацией Og или O2 (они значительно превышают объём ОЗУ микроконтроллера), при уровне оптимизации О1 программа работает корректно. Я думаю, что проблема из-за раздельных адресных шин ОЗУ и регистров IO, в настоящих AVR шина общая, а у меня раздельная из-за особенностей работы блочной памяти ПЛИС. Хотя на настоящей атмеге программа работает и с оптимизацией O2.