Как наилучшим образом преобразовать устаревшую архитектуру встроенного программного обеспечения для опросов в управляемую событиями архитектуру? - PullRequest
0 голосов
/ 20 ноября 2018

У меня есть семейство встраиваемых продуктов, работающих под управлением типичной прошивки на основе основного цикла с 150 + k строками кода.Нагрузка сложных критических функций синхронизации реализуется с помощью комбинации аппаратных обработчиков прерываний, опроса таймера и протопотоков (подумайте о совместных процедурах).На самом деле протопотоки хорошо опрашиваются и «только» являются синтаксическим сахаром для имитации псевдопараллельного планирования нескольких потоков (бесконечные циклы).Я постоянно добавляю исправления и расширения в прошивку.На местах имеется около 30 тыс. Устройств с примерно 7 слегка различающимися типами и версиями оборудования.

Для нового члена семейства продуктов мне нужно интегрировать внешний проект на основе FreeRTOS в новый продукт только тогда, когда все более старыепродукты должны получить дополнительные функции и улучшения.

Чтобы не пришлось портировать всю сложную унаследованную прошивку на FreeRTOS со всем риском поломки совершенно прекрасных продуктов, я планирую позволить старой прошивке работать внутри задачи FreeRTOS,На старых продуктах прошивка по-прежнему не запускается без FreeRTOS.Внутри задачи FreeRTOS унаследованная микропрограмма потребляет все доступное процессорное время, поскольку ее базовая схема реализации основана на опросе.Из-за того, что он использует протезы (таймер и аппаратный опрос, основанный на кулисах) и опросы в автономном регистре счетчика процессора, я надеюсь, что смогу преобразовать опрос в поведение, управляемое событиями.

Вотдва примера:


// first example: do something every 100 ms
if (GET_TICK_COUNT() - start > MS(100))
{
     start = GET_TICK_COUNT();
     // do something every 100 ms
}

// second example: wait for hardware event
setup_hardware();
PT_WAIT_UNTIL(hardware_ready(), pt);

// hardware is ready, do something else

Таким образом, у меня сложилось впечатление, что я могу преобразовать эти 2 паттерна программирования (например, с помощью магии макросов и функциональности FreeRTOS) в основное событиеоснованная схема.

Итак, теперь мой вопрос: кто-нибудь уже сделал такое?Есть ли образцы и / или лучшие практики для подражания?

[Обновление]

Спасибо за подробные ответы.Позвольте мне прокомментировать некоторые детали: мне нужно объединить «устаревшую прошивку на основе симуляции многопоточности (с использованием прототипов реализации coo-рутин») и проект на основе FreeRTOS, состоящий из пары взаимодействующих задач FreeRTOS.Идея состоит в том, чтобы позволить полной старой прошивке работать в своей собственной задаче RTOS помимо других новых задач.Мне известны принципы и шаблоны RTOS (упреждение, совместное использование ресурсов, операции блокировки, сигналы, семафоры, мьютексы, почтовые ящики, приоритеты задач и т. Д.).Я планировал основывать взаимодействие старых и новых деталей именно на этих механизмах.То, о чем я прошу, это: 1) Идеи, как преобразовать устаревшую прошивку (150k + LOC) полуавтоматическим способом так, чтобы схемы ожидания / опроса занятости, которые я представил выше, использовали любой из новых механизмов при запуске внутри задач RTOSили просто работайте по-старому при сборке и запуске как текущая прошивка типа основного цикла.Полный перезаписать / полный порт унаследованного кода не вариант.2) Больше идей о том, как научить старую реализацию прошивки, которая используется для того, чтобы иметь в своем распоряжении полные ресурсы ЦП, хорошо вести себя в новой тюрьме задачи RTOS, а не просто использовать все доступные циклы ЦП (если им присвоен наивысший приоритет) или производитьновые большие задержки в реальном времени, когда они запускаются не с самым высоким приоритетом RTOS.

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

Ответы [ 2 ]

0 голосов
/ 21 ноября 2018

В RTOS вы создаете и запускаете задачи.Если вы не выполняете более одной задачи, то использование RTOS не дает большого преимущества.

Я не использую FreeRTOS (но уже выполнил), но следующее относится к любой RTOS и является псевдо-код, а не специфичный для FreeRTOS API - многие детали, такие как приоритеты задач и распределение стека, намеренно отсутствуют.

Первая в самой простой ОСРВ, включая FreeRTOS, main() используется для инициализации оборудования, создания задач и запуска планировщика:

int main( void )
{
    // Necessary h/w & kernel initialisation
    initHardware() ;
    initKernel() ;

    // Create tasks
    createTask( task1 ) ;
    createTask( task2 ) ;

    // Start scheduling
    schedulerStart() ;

    // schedulerStart should not normally return
    return 0 ;
}

Теперь давайте предположим, что ваш первый пример реализованв task1.Типичная ОСРВ будет иметь функции таймера и задержки.Самым простым в использовании является задержка, и она подходит, когда периодическая обработка гарантированно займет менее одного такта ОС:

void task1()
{
    // do something every 100 ms
    for(;;)
    {
        delay( 100 ) ; // assuming 1ms tick period

        // something
        ...
    }
}

Если something в этом случае занимает более 1 мс, этобудет выполняться не каждые 100 мс, а через 100 мс плюс время выполнения something, которое само по себе может быть переменным или недетерминированным, что приводит к нежелательному дрожанию синхронизации.В этом случае вы должны использовать таймер:

void task1()
{
    // do something every 100 ms
    TIMER timer = createTimer( 100 ) ; // assuming 1ms tick period
    for(;;)
    {
        timerWait() ;     

        // something
        ...
    }
}

Таким образом something может занять до 100 мс и все равно будет выполняться точно и детерминистически каждые 100 мс.

Теперь к вашему второму примеру;это немного сложнее.Если ничего не произойдет до тех пор, пока не будет инициализировано оборудование, вы можете использовать существующий шаблон в main() перед запуском планировщика.Однако, как обобщение, ожидание чего-либо в другом контексте (задача или прерывание) происходит с использованием примитива синхронизации, такого как семафор или флаг события задачи (не во всей ОСРВ есть флаги событий задачи).Таким образом, в простом случае в main() вы можете создать семафор:

createSemaphore( hardware_ready ) ;

Затем в контексте, выполняющем процесс, который должен завершиться:

// Init hardware
...

// Tell waiting task hardware ready
semaphoreGive( hardware_ready ) ;

Затем в некоторой задаче, которая будетдождитесь готовности оборудования:

void task2()
{
    // wait for hardware ready
    semaphoreTake( hardware_ready ) ;

    // do something else
    for(;;)
    {
        // This loop must block is any lower-priority task
        // will run.  Equal priority tasks may run is round-robin
        // scheduling is implemented. 
        ...
    }
}
0 голосов
/ 20 ноября 2018

Вы столкнулись с двумя большими ошибками ...

  1. Поскольку старый код реализован с использованием протопотоков (сопрограмм), между ними никогда не возникает асинхронный конфликт ресурсов.Если вы разделите их на задачи FreeRTOS, там будут приоритетные переключатели планирования;эти переключения могут происходить в тех местах, где прототипы не ожидали, оставляя данные или другие ресурсы в несогласованном состоянии.
  2. Если вы преобразуете какие-либо вызовы PT_WAIT ваших прототипов в реальные ожидания в FreeRTOS, вызов действительно заблокируется,Но протопотоки предполагают, что другие протопотоки продолжаются, пока они заблокированы.

Таким образом, # 1 подразумевает, что вы не можете просто преобразовать протопотоки в задачи, а # 2 подразумевает, что вы должны конвертировать протопотоки в задачи (если вы используетеПримитивы, блокирующие FreeRTOS, такие как xEventGroupWaitBits ().

Самый простой подход - поместить все ваши протопотоки в одну задачу и продолжить опрос внутри этой задачи.

...