I have a setup that in case of high cpu load fails to schedule/context switch from ISR to a task with high priority after xTaskNotifyFromISR is called.
When the error occurs, a low priority task (Disk Manager) is running for several milli seconds. Every 1 ms the USB host ISR rutine runs and calls xTaskNotifyFromISR followed by portYIELD_FROM_ISR. The notification is passed to a task (usb ehci host task) with higher priority than the Disk Manager task. The ISR looks like below and because it includes portYIELD_FROM_ISR, I would expect the context switch to happen immediately after the ISR returns.
if ((interruptStatus & USBHS_USBSTS_UI_MASK) ||
(interruptStatus & USBHS_USBSTS_UEI_MASK)) /* USB interrupt or USB error interrupt */
xTaskNotifyFromISR(ehci_task_handle, EHCI_TASK_EVENT_TRANSACTION_DONE, eSetBits, &xHigherPriorityTaskWoken);
In most cases that is also true, however some times the context switching is not occurring or delayed.
I have traced the sequence so it can be viewed in SEGGER SystemView 3.12
I have encircled the point at which I would have expected the context switch to the usb ehci host task which has higher priority than the running Disk Manager task. In fact you can see several xTaskGenericNotifyFromISR occurring every 1 ms but none trigger the context switch. It is not until after 15 ms when the Disk Manager task blocks, the context is switched to the usb ehci host task (cannot be seen from the screenshot above).
Can anyone explain the above behavior? Is it a bug in FreeRTOS?
I’m pretty sure there is no such fatal (scheduling) bug in FreeRTOS.
Almost always ‘strange’ FreeRTOS behavior is caused by application code issues or misunderstandings.
Is it possible that there is a (too) long critical section in the DiskManager task ?
A critical section at task level is usually done by suspend/resume the scheduler using vTaskSuspendAll/xTaskResumeAll.
I guess you’ve configured FreeRTOS with preemption and probably time slicing enabled.
Yes, FreeRTOS is configured with preemption and time slicing enabled.
I do not have and source that calls vTaskSuspendAll/xTaskResumeAll directly and there are no critical sections in Disk Manager so I have a hard time understanding why it is not suspended when the usb ehci host task is ready to run.
Any hints on how to debug the situation or get closer to why the usb ehci host task is not scheduled.
You could debug into/set breakpoints in
xTaskGenericNotifyFromISR() to see what’s going on.
There are checks of
uxSchedulerSuspended and also the setting of the given
xHigherPriorityTaskWoken which could help to find the problem.
For the case you described that the highest prio usb ehci host task is blocked i.e. waiting for it’s ISR notification the code path setting
*pxHigherPriorityTaskWoken = pdTRUE; should be taken and
uxSchedulerSuspended should be
I’d instrument the
xTaskGenericNotifyFromISR code e.g. using a global flag variable set / reset in the USB host ISR to set 'conditional
if ( inUSBISR )
asm volatile("nop"); // set breakpoint here
// or let the Cortex-M MCU always break here
// asm volatile("bkpt 0");
You might want to try our tool Tracealyzer. It offers more analysis capabilities than Segger SystemView. It supports J-Link as well. If you could send me a trace, I can take a look and see what I can find. You reach me at firstname.lastname@example.org.
Thank you both for your input and help.
By thoroughly analyzing the trace from Segger SystemView I found a point in time shortly before the error occurred where the Disk Manager inherited the priority of a another high priority task also waiting to access the disk. In other words, the Disk Manager task got higher priority than the usb ehci host task, hence starving it.
By sorting out the different task priorities, I believe I have solved the problem.