Статья описывает этапы адаптации операционной системы Azure RTOS к микроконтроллеру платы управления резервным питанием BACKPMAN v1.0.
Все статьи по проекту
Cсылка на открытый проект: https://github.com/Indemsys/Backup-controller_BACKPMAN-v1.0
Поскольку опыт применения Azure RTOS в прошлом проекте был более чем успешным было решено применять ее везде.
Нельзя сказать что ядро этой RTOS чем-то уникально. Тут имеют значение другие факторы:
подтверждённая временем и сертификатами надёжность.
богатое программное обеспечение промежуточного уровня включающее: файловую систему, TCP стек, GUI, IoT протоколы, USB классы и т.д.
аскетичность и сбалансированность API ядра. Отсюда вытекает простота портирования и освоения.
наличие плагинов для отладки сервисов ядра RTOS в среде разработки IAR Embedded Workbench.
доступная online документация и книги об этой RTOS.
активное развитие. Обновления и исправления идут каждую неделю.
поддержка крупными производителей микроконтроллеров: ST, NXP, Cypress, Infineon, Renesas... Они включают эту RTOS в свои SDK.
наличие эмуляции API FreeRTOS, OSEK, POSIX.
поддержка технологии Symmetric Multi-Processing (SMP) и динамической загрузки и исполнения программных модулей.
А надежность от RTOS в этом проекте требуется исключительная. Поскольку схемотехника платы требует четкой работы программного обеспечения. Сбои могут привести к выходу компонентов из строя, деградации аккумуляторов, возгораниям или даже к повреждениям внешних систем.
В данном проекте из всего репозитория будет использовано только ядро RTOS. Ядро называется Azure RTOS ThreadX. Остальные части RTOS в данном случае либо пока не нужны, либо для них не хватает ресурсов (но они пригодятся в следующей ревизии платы).
Старт с hello word без RTOS
Напомню что портировать будем на плату с микроконтроллером MKE18F512VLL16 ( 32-Bit 168MHz ARM Cortex-M4F core, 512KB (512K x 8) FLASH, 64 KB SRAM).
Готовых примеров портирования ThreadX на эти чипы в репозиторий и на сайте NXP нет, но есть директория с портом для архитектуры Cortex-M4F под IAR.
В этом случае все что надо сделать - это сделать для чипов MKE18F простенький проект с пустой функцией main и правильной инициализацией всех тактирующих узлов, подключить к проекту директорию исходников ThreadX, немного откорректировать несколько файлов ThreadX, настроить приоритеты прерываний ядра и все!
Гораздо сложнее, как всегда, будет адаптировать слой абстракции периферии к RTOS. Но эта работа может быть разделена на этапы и растянута вплоть до завершения разработки окончательной функциональности.
Правильную инициализацию тактирования легко сделать с помощью утилиты MCUXpresso Config Tools и SDK с сайта NXP.
Несмотря на то что чип может работать на частоте 168 МГц, в конфигураторе выбираем частоту 120 МГц для ядра, поскольку на более высокой частоте будет затруднительно работать с встроенной EEPROM чипа. Встроенная EEPROM может записываться только на системной частоте ниже или равной 120 МГц.
Пины не конфигурируем, только может быть настроим 4-е пина UART0 чтобы передать информацию по UART. UART по умолчанию будет задействован при генерации приложения конфигуратором. Но использовать UART не обязательно. На начальном этапе все потребности в отладке покрывает SWD/JTAG адаптер J-Link.
Выбираем генерацию приложения hello_word и получаем проект с такой структурой директорий:
И среди прочего в директории source будет файл hello_world.c с такой функцией main:
int main(void)
{
char ch;
/* Init board hardware. */
BOARD_InitPins();
BOARD_InitBootClocks();
BOARD_InitDebugConsole();
PRINTF("hello world.\r\n");
while (1)
{
ch = GETCHAR();
PUTCHAR(ch);
}
}
Перенос исходников ThreadX в свой проект
Теперь из репозитария threadx переносим в наш проект содержимое директорий common, threadx/ports/cortex_m4/iar/inc, threadx/ports/cortex_m4/iar/src/ и файл tx_initialize_low_level.s. Исключаем из проекта файл tx_misra.s поскольку он вызовет ошибку повторного объявления. Дописываем во вкладке препроцессора компилятора С в IDE IAR пути к добавленным директориям. Перекомпилируем проект. Все должно пойти без ошибок.
Редактирование исходников ThreadX и конфигурирование
Во-первых, надо сделать исправления в файле tx_initialize_low_level.s. Изменим объявление константы:
SYSTICK_CYCLES EQU ((SYSTEM_CLOCK / 100) -1)
Эта константа инициализирует генератор системных тиков RTOS.
Мы записываем:
SYSTICK_CYCLES EQU ((SYSTEM_CLOCK / TX_TIMER_TICKS_PER_SECOND) -1)
В этом же файле добавляем в начале строку
#include "tx_user.h"
Затем файл tx_user_sample.h переименовываем в tx_user.h
В IDE IAR в закладке препроцессора компилятора добавляем запись: TX_INCLUDE_USER_DEFINE_FILE
Теперь при компиляции будет учитываться файл tx_user.h
В этом файле находятся все важнейшие опции RTOS.
Их надо соответствующим образом выбрать.
В начале файла делаем объявления использованных выше констант:
#define SYSTEM_CLOCK 120000000
#define TX_TIMER_TICKS_PER_SECOND 1000
Системный тик RTOS выбираем равным 1 мс. Это будет удобно при при манипуляциях со временем в автоматах состояний.
Список макросов активизированных в файле конфигурации:
#define TX_MAX_PRIORITIES 32
#define TX_DISABLE_PREEMPTION_THRESHOLD
#define TX_DISABLE_REDUNDANT_CLEARING
#define TX_DISABLE_NOTIFY_CALLBACKS
#define TX_NO_FILEX_POINTER
#define TX_TIMER_PROCESS_IN_ISR
Такой список обеспечивает минимальные размеры RTOS.
Теперь остается только модифицировать наш файл hello_world.c. Полностью он будет выглядеть так:
#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#include "board.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "tx_API.h"
#define THREAD_MAIN_STACK_SIZE 1024 // Размер стека выделяемый задаче
#define THREAD_MAIN_PRIORITY 1 // Приоритет первой задачи
TX_THREAD main_thread;
#pragma data_alignment=8
uint8_t thread_main_stack[THREAD_MAIN_STACK_SIZE];
static void Thread_main(ULONG initial_input);
/*-----------------------------------------------------------------------------------------------------
Модифицированная функция main из SDK
\param void
\return int
-----------------------------------------------------------------------------------------------------*/
int main(void)
{
/* Init board hardware. */
BOARD_InitPins();
BOARD_InitBootClocks();
BOARD_InitDebugConsole();
tx_kernel_enter();
}
/*-----------------------------------------------------------------------------------------------------
Вспомогательная функция инициализирующая и запускающая первую задачу
\param first_unused_memory
-----------------------------------------------------------------------------------------------------*/
void tx_application_define(void *first_unused_memory)
{
tx_thread_create(&main_thread, "Main", Thread_main,
0,
(void *)thread_main_stack, // stack_start
THREAD_MAIN_STACK_SIZE, // stack_size
THREAD_MAIN_PRIORITY, // priority.
// Numerical priority of thread.
// Legal values range from 0 through (TX_MAX_PRIORITES-1), where a value of 0 represents the highest priority.
THREAD_MAIN_PRIORITY, // preempt_threshold.
// Highest priority level (0 through (TX_MAX_PRIORITIES-1)) of disabled preemption.
// Only priorities higher than this level are allowed to preempt this thread.
// This value must be less than or equal to the specified priority.
// A value equal to the thread priority disables preemption-threshold.
TX_NO_TIME_SLICE,
TX_AUTO_START);
}
/*-----------------------------------------------------------------------------------------------------
Первая задача
\param initial_input
-----------------------------------------------------------------------------------------------------*/
static void Thread_main(ULONG initial_input)
{
char ch;
PRINTF("hello world.\r\n");
while (1)
{
ch = GETCHAR();
PUTCHAR(ch);
}
}
И все! Процесс установки RTOS закончен. Осталось проверить работу. Компилируем, загружаем в контроллер и проверяем вывод в терминал.
Для более глубокой проверки заходим в отладчик IAR в котором убеждаемся что тики действительно идут каждую миллисекунду, а задача выполняется и размера стека ей достаточно. При этом сам обработчик системного прерывания длиться не более 0.52 мкс.
Из map файла сгенерированного после компиляции видим что вся RTOS заняла 2506 байт памяти программ. Весь размер программы 10198 байт.
Особенности порта Azure RTOS
Надо сказать что эффективность RTOS сильно зависит от того как реализован ее порт (т.е. адаптация к конкретной архитектуре микроконтроллера и компилятора).
Порт может быть перегружен хуками и отладочными вставками, усложнен фичами планировщика, неэффективно использовать контроллер прерываний и не учитывать особенности компилятора. Т.е. у в принципе хорошей RTOS плохой порт может все испортить. В ThreadX с этим все в порядке, но все же вот несколько особенностей которые надо знать:
1. По дефолту не все функции стандартной библиотеки С могут быть безопасно использованы в вытесняющей RTOS . Объявление макроса TX_ENABLE_IAR_LIBRARY_SUPPORT в файле tx_user.h позволяет сделать стандартные библиотеки С в среде IAR мультипоточными. Для этого в ThreadX есть файл tx_iar.c
2. Выделяя массив под стек для задачи следует адрес его начала выравнивать по границе 8. Иначе операции с плавающей точкой в задачах могут приводить к ошибкам.
3. В ThreadX используется так называемая "ленивое" сохранение стека . Это когда регистры сопроцессора операций с плавающей точкой не сохраняются если такие операции не выполнялись в задаче перед переключением контекста. Отсюда следует что переключение контекста может замедлиться на время сохранения 33-х регистров FPU если задача использует операции с плавающей точкой. Но операции с сопроцессором FPU могут использоваться даже когда нет явного использования типов float или double. Это свойство оптимизирующего компилятора IAR. Поэтому для полной уверенности в быстроте ISR всегда следует просматривать ассемблерный код.
4. Общепринято защищать критические секции мьютексами или другими стандартными сервисами RTOS. Но выполнение таких сервисов может занимать сотни тактов. На некоторых этапах выполнения у них запрещаются любые прерывания. Это не хорошо если в системе присутствуют критические к времени отклика прерывания. Например прерывания по сигналам перенапряжения или перегрузки по току, которые должны что-то сделать в течении долей микросекунд от момента появления сигнала. Проблему можно решить используя вместо мьютексов простое поднятие приоритета выше приоритета прерывания системного тика и прерывания по вектору PendSV. Но для этого надо в файле tx_initialize_low_level.s поменять приоритеты этих прерываний как показано ниже:
LDR r1, =0xF0000000 ; SVCl, Rsrv, Rsrv, Rsrv
STR r1, [r0, #0xD1C] ; Setup System Handlers 8-11 Priority Registers
; Note: SVC must be lowest priority, which is 0xFF
LDR r1, =0xE0F00000 ; SysT, PnSV, Rsrv, DbgM
STR r1, [r0, #0xD20] ; Setup System Handlers 12-15 Priority Registers
; Note: PnSV must be lowest priority, which is 0xFF
Здесь системному тику присвоен приоритет 14, а PendSV приоритет 15. Если поднять приоритет выше 14 при входе в критическую секцию, то никакие задачи и сервисы RTOS не смогут прервать ее выполнение.
Код ниже реализует макросы для обеспечения такой функциональности.
#define DISABLE_OS_PRI_LEV (14 << (8 - __NVIC_PRIO_BITS)) // Маска приоритета в регистре BASEPRI запрещающая прерывания PendSV и SysTick
#define ENABLE_OS_PRI_LEV 0 // Маска приоритета в регистре BASEPRI разрешающая прерывания с любыми приоритетами
#define DISABLE_OS_INTERRUPTS __set_BASEPRI(DISABLE_OS_PRI_LEV)
#define ENABLE_OS_INTERRUPTS __set_BASEPRI(ENABLE_OS_PRI_LEV)
К слову сказать пока писал эту статью решил посмотреть что произошло за это время в репозитарии Azure RTOS. Оказалось что совсем недавно в версии 6.1.7 ребята пришли к аналогичной мысли и ввели макрос TX_PORT_USE_BASEPRI, а с ним и константу TX_PORT_BASEPRI. Если их объявить, то можно пользоваться стандартными макросами ThreadX для управления прерываниями TX_DISABLE и TX_RESTORE и они будут не запрещать прерывания, а только повышать приоритет до уровня TX_PORT_USE_BASEPRI . При этом в процедурах использующих эти макросы нужно в начале вставлять запись TX_INTERRUPT_SAVE_AREA.
Константа TX_PORT_BASEPRI имеет тот же смысл что и константа DISABLE_OS_PRI_LEV в примере выше.
Однако надо помнить что нельзя делать вызовы сервисов RTOS из прерываний с приоритетом выше TX_PORT_BASEPRI если мы объявили TX_PORT_USE_BASEPRI. Иначе это разрушит работу ядра из-за нарушения защиты критических секций в функциях сервисов.
Проект демонстрационной программы hellow_word находится здесь.
Включение профилирования RTOS
Профилирование дает возможность в среде отладчика IAR видеть статистику о ресурсах процессорного времени занимаемых задачами.
Для этого надо подключить к проекту два файла из репозитария: tx_execution_profile.c и tx_execution_profile.h. После этого в файл tx_user.h добавить объявление:
#define TX_EXECUTION_PROFILE_ENABLE
Целиком файл tx_user.h будет выглядеть так:
#define TX_MAX_PRIORITIES 32
#define TX_DISABLE_PREEMPTION_THRESHOLD
#define TX_DISABLE_REDUNDANT_CLEARING
#define TX_DISABLE_NOTIFY_CALLBACKS
#define TX_NO_FILEX_POINTER
#define TX_TIMER_PROCESS_IN_ISR
#define TX_ENABLE_IAR_LIBRARY_SUPPORT
#define TX_ENABLE_STACK_CHECKING
#define TX_EXECUTION_PROFILE_ENABLE
#define TX_BLOCK_POOL_ENABLE_PERFORMANCE_INFO
#define TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
#define TX_EVENT_FLAGS_ENABLE_PERFORMANCE_INFO
#define TX_MUTEX_ENABLE_PERFORMANCE_INFO
#define TX_QUEUE_ENABLE_PERFORMANCE_INFO
#define TX_SEMAPHORE_ENABLE_PERFORMANCE_INFO
#define TX_THREAD_ENABLE_PERFORMANCE_INFO
#define TX_TIMER_ENABLE_PERFORMANCE_INFO
Сюда еще добавлены макросы для активизации инструментов измерения производительности сервисов RTOS.
Также надо добавить подключение #include "tx_user.h" в файл tx_thread_schedule.s
Убираем файл hello_world.c и вставляем в проект файл demo_threadx.c из репозитория ThreadX. Немного корректируем функцию main, вставив туда вызовы инициализации чипа. Этот демонстрационный файл создает 8 задач и по одной очереди, семафору, событию, пулу байтов, пулу блоков. Это дает возможность наблюдать за работой всех основных сервисов.
После запуска на плате под отладчиком IAR получаем очень подробную статистику по функционированию задач и сервисов RTOS:
Здесь видим сколько задач запущено, сколько каждая задача потребляет процессорного времени, сколько времени занимают прерывания, сколько стека использовано задачами и много другое. Видно что прерывание переключателя контекста PendSV длиться не дольше 1.7 мкс, а прерывание тика системы не дольше 8 мкс. Если период тиков 1 мс то эти все прерывания для 8-и задач занимают всего около 2% процессорного времени. Это и будут накладные на использование RTOS.
Проект демонстрационной программы RTOS_profiling находится здесь.
Итак, первичное исследование на плате контроллера резервного питания подтвердило возможность использования ThreadX из репозитария Azure RTOS в качестве основы для реализации управления с жестким реальным временем.
Ссылка на открытый проект: https://github.com/Indemsys/Backup-controller_BACKPMAN-v1.0
Комментарии (30)
predator86
22.08.2021 14:40прерывание тика системы не дольше 8 мкс
С чем связано такое долгое прерывание тика?Indemsys Автор
22.08.2021 15:25Потому что так выбрана опция RTOS.
Все пользовательские таймеры отрабатываются в этом прерывании.
Если выставить опцию обработки таймеров в отдельной задаче, то прерывание тика длиться 0.52 мкс.predator86
22.08.2021 15:32+1пользовательские таймеры
Что они делают?lamerok
22.08.2021 15:36Полагаю это программные таймера. Например, во FreeRtos пользовательские таймера обрабатываются в отдельной отдельной задаче. Обычно они посылают события, всякие разные, например, раз в 1 секунду моргать светодиодом. Можно конечно и аппаратные таймера задействовать, но их бывает не хватает.
predator86
22.08.2021 15:43Просто 8us это приличное время и у этих таймеров должно быть конкретное оправдание.
Indemsys Автор
22.08.2021 16:05Таймера организуются в демо приложении. Я не смотрел сколько точно их там.
Это не мое приложение и оно специально не оптимизировано, его цель только демонстрация работы сервисов.
К тому же включен профайлинг и сбор статистики которые тоже потребляют сотни тактов.
И наконец 8 мкс это худшее время, и оно появляется только когда истекает какой либо из таймеров. А так прерывание тика длится все так же в районе 0.6 мкс.
Кстати таймера требуются и сервисам с таймаутами. Так что там хватает таймеров.
predator86
22.08.2021 14:43для 8-и задач занимают всего около 2% процессорного времени
Как Вы это посчитали?Indemsys Автор
22.08.2021 15:27Сложил длительность всех прерываний ядра за один тик. Не совсем точно, но дает представление о масштабах.
predator86
22.08.2021 15:35Я посчитал так ((8+1.7)/1000)*100=0,97%. А как Вы считали?
Indemsys Автор
22.08.2021 16:07Восемь задач. И восемь прерываний от каждой в каждом тике в худшем случае.
Я считаю всегда худшие случаи.predator86
22.08.2021 18:06Тогда если судить по графику Timeline то накладные расходы в худшем случае примерно 25%.
Indemsys Автор
22.08.2021 23:26Тогда опишите этот случай. Я его не вижу.
predator86
23.08.2021 00:36Между переключениями контекста проходит 6.5us из которых само переключение занимает 1.62us, следовательно на работу программ остаётся 4.88us.
Indemsys Автор
23.08.2021 10:37Пауза между прерываниями это тоже работа сервисов RTOS.
Поэтому накладные будут 100% в этом случае.
Да, с такими накладными будет работать программа с багом, например с deadlock или некорректным ISR ядра.
RTOS - не средство от багов, а скорее катализатор багов.
Но статья рассчитана на тех кто уже знает зачем им RTOS.predator86
23.08.2021 15:10Пауза между прерываниями это тоже работа сервисов RTOS.
Здесь я не понял. А программы когда исполняются?с такими накладными будет работать программа с багом, например с deadlock
Разве deadlock может как то поднять накладные расходы? Я думал что просто все участники deadlock'а заснут, а остальные продолжат свою работу.RTOS — не средство от багов, а скорее катализатор багов.
Т.е. ОС не упрощает, а усложняет жизнь программистам? Из этого следует вывод, что из более громоздких и сложных проектов необходимо исключить ОС для уменьшения накладных расходов и меньшего возникновения ошибок. Правильно я Вас понял?
Просто хочу разобраться.Indemsys Автор
23.08.2021 15:43Здесь я не понял. А программы когда исполняются?
Сначала это я не понял.
Не понял как вы по маленьком фрагменту таймлайна определили полный процент накладных.
Я решил что вы начали рассматривать гипотетические случаи.
В таком разе я вам привел гипотетический случай с двумя залочеными задачами. И в этом случае никакой полезный код не будет выполняться и накладные 100%Что делать разработчикам громоздких и сложных проектов я советовать не буду. На то они и разработчики сложных проектов. Полагаю они знают как бороться со сложность раз берутся за такие проекты.
Аргументы за Azure RTOS приведены в начале статьи. И там нет аргумента по поводу уменьшение ошибок.
Как я отлаживаю свой проект планирую написать в следующей статье.Indemsys Автор
23.08.2021 16:13Это на ваше усмотрение. Как сделаете так и будет.
Задачи могут переключаться по команде из других задач явно или неявно (вызовом мьютексов, флагов и проч.), по внешним прерываниям , по тику RTOS (тоже прерывание) который может прервать затянувшуюся задачу и передать управление другой.
Но это все конфигурируется.
Вы свободны что-то из этого запрещать, что-то разрешать.
predator86
23.08.2021 16:18Спасибо за разъяснения, но это всё как-то сложновато. Хотелось бы чтобы оно всё работало без заморочек.
Indemsys Автор
23.08.2021 16:27Эх, сложновато....
Сложность еще и не началась.
Портирование RTOS - самый простой этап в проекте.
predator86
23.08.2021 16:32Я вот просмотрел Ваши предыдущие статьи и заметил что Вы часто используете в них разные RTOS. С чем это связано?
Indemsys Автор
23.08.2021 17:11Нет, одновременно я не использую разные, просто они у меня меняются с течением времени.
Сами производители чипов заставляют менять.
tzlom
А вы лицензию на это поделие читали? Вот вам выжимка:
1- данная ОС не лицензирована на тот чип который вы используете, фактически ваша статья является доказательством нарушения лицензии. Можете связяться с микрософтом чтобы узнать что вам за это будет.
2- вы должны соблюдать экспортные ограничения США, т.к. это требование лицензии то отмазаться по 25% не получится. Сейчас из Ирана ваш репо склонируют и вы потенциально влетите на проблемы. Я правда не знаю с кем- с М$ за нарушение лицензии или с правительством США за нарушение экспортных ограничений, но я думаю вам без разницы.
Так что уберите тег Open Source, это не он.
Indemsys Автор
Немного не так.
Читатели этой статьи только лишь ограничены в “Distribution and Production Use” rights на изделия с этой RTOS на этом чипе.
Я же не ограничен портировать и писать статьи про это.
Но я не произвожу и не продаю. Это исследовательский проект.
tzlom
Indemsys Автор
evaluation purposes
tzlom
https://github.com/Indemsys/Backup-controller_v2.0/tree/main/Software/AzureRTOS_profiling
distribution
Indemsys Автор
Definition of 'Distribution'
tzlom
Угу, согласно этому определению бесплатное распространение вообще не существует т.к. его никто не покупает. Хотите апеллировать к значению слов давайте ссылку на словарь.
https://www.merriam-webster.com/dictionary/distribution
https://www.merriam-webster.com/dictionary/distribute