I’m having problems notifying multiple Tasks in an ISR via direct to task notifications. The Firmware freezes after running for a while without even the tick interrupt coming through. And this seems to happen only when compiler optimization is turned on (O2)
The setup looks like this:
– C++14
– Nios2 Board running with auto generated BSP (manually adapted to use FreeRTOS Kernel).
– FreeRTOS Kernel Release V10.5.1
I wrote a C++ wrapper for the API, and the notification looks like this:
#include "FreeRTOS.h"
#include "task.h"
#include <cstdint>
#include <chrono>
#include "is_isr.h"
namespace osal
{
namespace this_thread
{
std::uint32_t sleepUntilNotify(
std::chrono::milliseconds maxWaitTime = std::chrono::milliseconds(portMAX_DELAY)) noexcept
{
return ulTaskNotifyTake(false, pdMS_TO_TICKS(maxWatiTime.count()));}
}
template <typename... Args>
class Thread<void(Args...)> final
{
public:
...
void notify() const noexcept
{
// a variable is modified in the assembly when entering/exiting an ISR, which gives us the knowledge whether we are in an ISR.
if (isISR())
{
BaseType_t higherPrioTaskWoken = false;
vTaskNotifyGiveFromISR(m_handle, &higherPrioTaskWoken);
if (higherPrioTaskWoken)
{
vTaskSwitchContext();
}
return;
}
xTaskNotifyGive(m_handle);
}
...
private:
...
TaskHandle_t m_handle;
...
};
}
in the ISR, 2 Tasks are notified directly
void irqHandler(void* context)
{
...
// let's suppose thread1&2 are just static variables that are accessible in the ISR.
thread1.notify();
thread2.notify();
}
I did try and debug the optimized build. Not sure it renders anything reliable. Often it showed that the FW is stuck in the while loop in task.c in xTaskResumeAll()
BaseType_t xTaskResumeAll( void )
{
...
taskENTER_CRITICAL();
{
--uxSchedulerSuspended;
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )
{
/* Move any readied tasks from the pending list into the
* appropriate ready list. */
while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )
{
pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */
listREMOVE_ITEM( &( pxTCB->xEventListItem ) );
portMEMORY_BARRIER();
listREMOVE_ITEM( &( pxTCB->xStateListItem ) );
prvAddTaskToReadyList( pxTCB );
/* If the moved task has a priority higher than or equal to
* the current task then a yield must be performed. */
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
xYieldPending = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
...
}
I do not think you can call vTaskSwitchContext() (whatever that does) twice in one isr. The result necessarily will be undefined. It must confuse the scheduler.
Just tried out with only one vTaskSwitchContext(), could still reproduce. Thanks for the input though.
I trimmed the FW to narrow down the problem. It seems to only occur in the constellation of 2+ tasks that are notified in the ISR and a task for the EtherCat Slave communication, which runs auto generated C code from Beckhoff (SSC Tool). So don’t look too hard in to the Kernel Code…