I am using FreeRTOS on a Cortex-M33 where I use Trustzone-M to communicate
with a DSP. While the DSP is doing its work I want to switch the M33 to
another runable task if present. I am trying to realize this with
callbacks to non-secure world and a semaphore in non-secure world.
When the DSP reports that it is finished the interrupt handler performs
a callback to non-secure world and in callback functions I execute:
xResult = xSemaphoreGiveFromISR(semaphore, &xHigherPriorityTaskWoken);
The code in secure world waits on the DSP by also sending a callback
to non-secure world which executes:
xResult = xSemaphoreTake(semaphore, portMAX_DELAY);
The semaphore is created with xSemaphoreCreateCounting().
At first sight the code seems to work. However when I run it for a longer
time then xSemaphoreGiveFromISR does no longer unblock the
xSemaphoreTake. Sometimes after a few interactions with the DSP. Sometimes
after many thousands. What could be wrong? In my debug tracing, I see
that xSemaphoreGiveFromISR returns but it does not unblock xSemaphoreTake.
There are not other tasks in the test code.
Can you please give a little clarification as to what using Truszone-M to communicate with a DSP means. I’m assuming you have a dual core system with a Cortex-M33 and a DSP as two separate cores (or two separate chips even) and you are communicating with the DSP from the secure side of the Cortex-M33. Is that correct?
Also can you elaborate on the what it means for the code in the secure world waiting on the DSP - is the code that is waiting a FreeRTOS task?
I am using Trustzone because the usage of the DSP needs security. To use the DSP from the non-secure world, the SW calls a non-secure callable function in secure world that sends a command to the DSP. Then in secure world we call a callback function in non-secure world that will do a ‘semaphore take’ so that it blocks on a samaphore until the DSP finishes the operation. When the DSP finishes the operation it will send an interrupt to the M33 that arrives in the secure-world. The secure world will then again call a callback function in non-secure world that does a ‘semaphore give’ operation. This unblocks the task that was waiting on the result of the DSP.
In an implementation with a spin-loop to wait on the DSP result everything seems to work fine. But that is a waste of cpu cycles and power. The semaphore implementation is sort of working. It can work for a few minutes or a few hours but then it deadlocks. The ‘give’ for the ISR does not unblock the task that is blocked by the ‘take’.
When the DSP finishes the operation it will send an interrupt to the M33 that arrives in the secure-world. The secure world will then again call a callback function in non-secure world that does a ‘semaphore give’ operation.
Are you calling the callback function in non-secure world from the secure ISR? If so, are you using the “FromISR” function for the ‘semaphore give’ operation?
Yes and Yes. I also do portEND_SWITCHING_ISR(xHigherPriorityTaskWoken).
And I verify with an assert that the semaphore operations return pdTRUE. The asserts are never failing.
I think this is what is happening: FreeRTOS enables secure exception priority boosting to ensure that the secure interrupts are not masked by FreeRTOS critical sections. As a result, a secure ISR cannot call FreeRTOS API functions because it can interrupt FreeRTOS critical section and therefore, can potentially corrupt FreeRTOS internal data structures.
Instead of calling the semaphore give API from the secure ISR, would you please pend a non-secure interrupt using NVIC_ISPRn_NS register and call the semaphore give API from the non-secure ISR. If you are using CMSIS, you can use the
Note that you will need to ensure that the non-secure interrupt that you pend has a logical priority less than (i.e. numerically higher priority) condigMAX_SYSCALL_INTERRUPT_PRIORITY. If you have configASSERT defined, it will be triggered if the priority configuration is not correct.
Thanks a lot! Your thinking makes sense and I assume that this is the problem. It is now running for a couple of hours without problems.
Instead of posting the interrupt from secure world, I still do a callback to non-secure world and in the non-secure world I post the interrupt.