Портирование приложений FreeRTOS на процессоры RISC-V
В этой лекции описывается, как FreeRTOS может быть портирована на процессор RISC-V. Это пошаговое справочное руководство.
Это руководство начинается с объяснения того, как получить необходимые файлы для портирования FreeRTOS и какие файлы нужно отредактировать для соответствия требованиям процессора RISC-V. Затем рассматривается, как эти файлы интегрируются в проект. В завершение главы демонстрируется, как скомпилировать и запустить портированное приложение на процессоре RISC-V в режиме эмуляции.
К концу этой лекции вы должны уметь:
- Перечислять шаги, связанные с получением необходимых файлов для порта FreeRTOS.
- Перечислять шаги, связанные с редактированием необходимых файлов для процессора RISC-V.
- Интегрировать файлы в требуемый проект.
- Компилировать и запускать портированную версию на процессоре RISC-V.
- Использовать встроенный тест согласованности (проверки работоспособности, sanity test) FreeRTOS.
Требования для переноса FreeRTOS на процессоры RISC-V
Особенности портированной версии для RISC-V
FreeRTOS уже портирована на различные платформы RISC-V. Ниже приведены функции портированной на RISC-V версии, которые были реализованы.
- Включает порты для компиляторов GCC и IAR.
- Поддерживает целочисленное выполнение в 32- и 64-битовом машинном режиме.
- Реализует отдельный стек прерываний для уменьшения размера стека оперативной памяти.
- Служит базовым портом, который можно использовать для переноса любых расширений архитектуры, специфичных для реализации RISC-V.
Требования к исходным файлам FreeRTOS
Необходимые файлы RTOS - это файлы, перечисленные в предыдущей лекции. Либо выберите необходимые исходные файлы, либо выберите проект примера RISC-V и используйте его в качестве отправной точки.
Для всех портов RISC-V, требующих расширения архитектуры (что характерно для большинства чипов RISC-V), требуется один дополнительный файл. Этот дополнительный заголовочный файл называется freertos_risc_v_chip_specific_extensions.h. Существует одна реализация этого заголовочного файла для каждого поддерживаемого расширения архитектуры, причем все реализации расположены в подкаталогах каталога /FreeRTOS/Source/Portable/[compiler]/RISC-V/chip_specific_extensions, где [compiler] - ?используемый компилятор.
Чтобы включить файл для вашего процессора, включите путь к нему в путь включения ассемблера. Например:
- Если ваш чип реализует базовую архитектуру RV32I или RV64I, включает локальный прерыватель Core Local Interrupter (CLINT), но не имеет других регистровых расширений, то добавьте /FreeRTOS/Source/Portable/[compiler]/RISC-V/chip_specific_extensions/RV32I_CLINT_no_extensions в путь include ассемблера.
- Если ваш чип использует ядро PULP RI5KY, реализованное на плате RV32M1RM Vega, которое включает шесть дополнительных регистров и не включает CLINT, то добавьте /FreeRTOS/Source/Portable/[compiler]/RISC-V/chip_specific_extensions/Pulpino_Vega_RV32M1RM в путь include ассемблера.
Если ни одно из этих расширений не подходит для вашей реализации и у вас есть свои собственные расширения, то выполните действия, перечисленные в следующей статье: "Перенос на новые 32-битовые или 64-битовые реализации RISC-V". Это позволит вам создать файл freertos_risc_v_chip_specific_extensions.h, который будет соответствовать вашей конкретной архитектуре процессора. В приведенной выше ссылке описывается список параметров, которые необходимо определить для процессорных расширений, чтобы FreeRTOS могла обрабатывать эти расширения соответствующим образом.
Настройки FreeRTOSConfig.h
Обратите внимание, что информация в этом разделе взята с сайта FreeRTOS.
Параметры configMTIME_BASE_ADDRESS и configMTIMECMP_BASE_ADDRESS должны быть определены в FreeRTOSConfig.h. Если целевой RISC-V чип включает машинный таймер (MTIME), то установите configMTIME_BASE_ADDRESS в базовый адрес MTIME, а configMTIMECMP_BASE_ADDRESS в адрес регистра сравнения MTIME (MTIMECMP). В противном случае установите оба значения в 0.
Например, если базовый адрес MTIME равен 0x2000BFF8, а адрес MTIMECMP равен 0x20004000, то добавьте следующие строки в FreeRTOSConfig.h:
#define configMTIME_BASE_ADDRESS ( 0x2000BFF8UL ) #define configMTIMECMP_BASE_ADDRESS ( 0x20004000UL )
Если MTIME нет, то добавьте следующие строки в FreeRTOSConfig.h:
#define configMTIME_BASE_ADDRESS ( 0 ) #define configMTIMECMP_BASE_ADDRESS ( 0 )
Настройка стека прерываний
Порт FreeRTOS RISC-V использует выделенный стек прерываний перед вызовом любых функций C из процедуры обслуживания прерываний (interrupt service routine, ISR).
Память для использования в качестве стека прерываний может быть определена в скрипте компоновщика или объявлена на уровне портов FreeRTOS как статически выделенный массив.
Первый метод предпочтительнее для микроконтроллеров (MCU) с ограниченным объемом памяти, поскольку он позволяет использовать стек планировщика, используемый main() до запуска планировщика, в качестве стека прерываний, когда он больше не используется для первоначальной цели после запуска планировщика.
Чтобы использовать статически выделенный массив в качестве стека прерываний, определите configISR_STACK_SIZE_WORDS в FreeRTOSConfig.h как размер выделяемого стека прерываний. Обратите внимание, что размер определяется в словах, а не в байтах. Например, чтобы использовать статически выделяемый стек прерываний размером 500 слов (2000 байт на RV32, где каждое слово равно 4 байтам), добавьте следующее в FreeRTOSConfig.h:
#define configISR_STACK_SIZE_WORDS ( 500 )
Чтобы определить стек прерываний в сценарии компоновщика (обратите внимание, что на момент написания этой статьи, июнь 2022 года, этот метод поддерживается только в порте GCC), объявите переменную компоновщика под названием __freertos_irq_stack_top, которая будет содержать самый высокий адрес стека прерываний, а также убедитесь, что configISR_STACK_SIZE_WORDS не определена.
Использование этого метода требует редактирования скрипта компоновщика. Если вы не знакомы со скриптами компоновщика, то важно знать (по крайней мере, при использовании GCC), что ". " - это так называемый счетчик расположения, который содержит значение адреса памяти в данный момент в скрипте компоновщика. Пример управления местоположением показан ниже.
/* Define the start address and size of the two RAM regions not used by the linker. */ #define ( ( uint8_t * ) RAM2_START_ADDRESS 0x00020000 ) #define RAM2_SIZE ( 32 * 1024 ) #define ( ( uint8_t * ) RAM3_START_ADDRESS 0x00030000 ) #define RAM3_SIZE ( 32 * 1024 ) /* Declare an array that will be part of the heap used by heap_5. The array will be placed in RAM1 by the linker. */ #define RAM1_HEAP_SIZE ( 30 * 1024 ) static uint8_t ucHeap[ RAM1_HEAP_SIZE ]; /* Create an array of HeapRegion_t definitions. The HeapRegion_t structures must appear in start address order, with the structure that contains the lowest start address appearing first. */ const HeapRegion_t xHeapRegions[] = { { ucHeap, RAM1_HEAP_SIZE }, { RAM2_START_ADDRESS, RAM2_SIZE }, { RAM3_START_ADDRESS, RAM3_SIZE }, { NULL, 0 } /* Marks the end of the array. */ };
Необходимые параметры компилятора и ассемблера
В этом разделе подробно описаны опции компилятора и ассемблера, которые должны быть установлены в командной строке перед выполнением. Это необходимо, поскольку различные реализации RISC-V предоставляют различные обработчики прерываний для своих внешних прерываний. Установка опций, соответствующих данной реализации, указывает ядру FreeRTOS, какой обработчик внешних прерываний ему нужно вызвать.
Чтобы задать обработчик внешнего прерывания, необходимо определить следующие параметры:
- Найдите имя обработчика внешнего прерывания, предоставляемого в рамках программы выполнения RISC-V поставщиком микросхемы. Обработчик прерывания должен иметь один параметр, которым является значение регистра причины RISC-V в момент возникновения прерывания. Прототип обработчика прерывания должен иметь вид: void ext_int_handler (uint32_t cause);
- Определите макрос ассемблера (обратите внимание, что это макрос ассемблера, а не макрос компилятора) под названием portasmHANDLE_INTERRUPT, равный имени обработчика прерываний. Если вы используете GCC, этого можно добиться, добавив в командную строку ассемблера следующее, предполагая, что обработчик прерываний называется ext_int_handler: -DportasmHANDLE_INTERRUPT=ext_int_handler.
Также не забудьте добавить заголовочный файл, специфичный для реализации процессора RISC-V, в путь включения ассемблера.
Установка обработчика ловушек FreeRTOS
Последний необходимый шаг - установка обработчика прерываний FreeRTOS, freertos_risc_v_trap_handler(). Это центральная точка входа для всех прерываний и исключений. Обработчик ловушек FreeRTOS вызывает обработчик внешних прерываний, когда источником ловушки является внешнее прерывание.
Чтобы установить обработчик ловушек:
- Если используемое ядро RISC-V включает CLINT, то portasmHAS_SIFIVE_CLINT должен быть установлен в 1 в freertos_risc_v_chip_specific_extensions.h, что приводит к автоматической установке freertos_risc_v_trap_handler(). В этом случае никаких дополнительных действий не требуется.
- Во всех остальных случаях необходимо установить freertos_risc_v_trap_handler() вручную. Это можно сделать, отредактировав код запуска, предоставленный вашим поставщиком микросхем.
Если чип RISC-V использует контроллер векторных прерываний, то установите freertos_risc_v_trap_handler() в качестве обработчика для каждого вектора.