why taskENTER_CRITICAL/taskEXIT_CRITICAL can not be used in ISR ?

jiangtianyu007 wrote on Tuesday, July 14, 2015:

For cortex M3/M4, taskENTER_CRITICAL/taskEXIT_CRITICAL mantain a global variable uxCriticalNesting to enable nest, and portSETINTERRUPTMASKFROMISR() - which returns the existing interrupt mask before setting the mask up, and portCLEARINTERRUPTMASKFROMISR() which sets the mask back to its previous value.

  1. I have known that we need to use taskENTER_CRITICAL/taskEXIT_CRITICAL in tasks, and use portSETINTERRUPTMASKFROMISR/portCLEARINTERRUPTMASKFROMISR in ISRs. But why? why I can’t use taskENTER_CRITICAL/taskEXIT_CRITICAL in ISR? If I do, what will be happened?

  2. I have mentioned that because of a combination of the way the port works and the intelligence built into the NVIC it is probably ok to use taskENTER_CRITICAL/taskEXIT_CRITICAL in ISRs. It means specifically only to the Cortex-M3/4 ports, it will be ok, but I don’t know why other core architecture will be not allowed.

rtel wrote on Tuesday, July 14, 2015:

I think on the Cortex-M you actually can use taskENTER_CRITICAL() and taskEXIT_CRITICAL() in an ISR - but that is one of the very few of the 35+ ports where this is the case. The reason being that on that port you cannot enter an interrupt unless the critical nesting count is 0. In other ports, although it sounds odd, it is legitimate for the FreeRTOS code (not application code!) to context switch when the critical nesting count is non-zero, and in that case using taskENTER_CRITICAL() and taskEXIT_CRITICAL() will be using the nesting count of a task (corrupting the context of a task).

There are several ‘rules of thumb’ that are stated as blanket rules, although they may not hold in all cases, because documenting each deviation is just too big a task and too confusing for end users. For example, there are rules about when an API function can and cannot be used which are stated as blanket rules that are safe in all cases, whereas the intricacies of the design means that sometimes the rules can be broken with no detrimental effect. Documenting each use case for each function is way too complex for end users, so its easier and safer to generalise rules to ensure safety in all cases.

Regards.

jiangtianyu007 wrote on Wednesday, July 15, 2015:

Thanks for your reply.

But actually I am a little confused.

According to your reply, some issues need to be aligned with you.

Firstly, the reason why I can use taskENTER_CRITICAL() and taskEXIT_CRITICAL() in an ISR for Cortex-M is that interrupt below configMAX_SYSCALL_INTERRUPT_PRIORITY will not happen unless the critical nesting count is zero. Is it right?

Secondly, for some other cpu architecture (can you tell me the cpu architecture, so I can look the related port code), context switch can happen even the critical nesting count is non-zero. Because in my mind, context switch need a interrupt to trigger, so does it means that even in critical section, interrupt also can happen?

Lastly, you mentioned that “in some case using taskENTER_CRITICAL() and taskEXIT_CRITICAL() will be using the nesting count of a task (corrupting the context of a task)”. Dose “using taskENTER_CRITICAL() and taskEXIT_CRITICAL()” means using taskENTER_CRITICAL() and taskEXIT_CRITICAL() in ISR? and Why it will lead to context corrupt? Can you give me some example or code snippet to explain?

Thanks in advance.

rtel wrote on Wednesday, July 15, 2015:

The predecessor to the Cortex-M, the ARM7, is an example of an architecture port that would corrupt a task if it were to use taskENTER/EXIT_CRITICAL() in an interrupt - and yes I am only talking about in an interrupt. As per my previous email, it will corrupt the context of a task because it will corrupt the critical nesting count, which on architectures such as the ARM7 is part of the task’s context.

jiangtianyu007 wrote on Wednesday, July 15, 2015:

Thanks very much for your patient answers.

In FreeRTOSV8.2.1\FreeRTOS\Source\portable\GCC\ARM7_LPC2000\portISR.c, I review the vPortEnterCritical/vPortExitCritical code. I don’t konw much about the ARM7 architecture, but I believe the mechanism of taskENTER_CRITICAL() and taskEXIT_CRITICAL() is same as Cortex-M architecture.

The critical nesting count is a global variable, so why you means it is part of the task’s context for ARM7 architecture, and how can it corrupt? Can you give me some scenario to explain it.

Looking forward for your reply.
Thanks in advance.

rtel wrote on Wednesday, July 15, 2015:

The critical nesting method is the same between the ARM7 and Cortex-M,
but the context switching mechanism is completely different.

It is because the critical nesting count is just a variable that it has
to be saved and restored as part of the task context on the ARM7.

On the ARM7, when a task is switched out the variable is saved as part
of the task context, and when a task is switch in the critical nesting
count as that task expects to find it is restored again. This is just
like the registers on the CPU - which are also used by all tasks - and
so saved and restored as part of the task context. If an interrupt then
uses the critical nesting count, it is using the count of a specific
task - if the task context then gets saved before the interrupt exits
the critical section, then the wrong value for that task will be saved,
hence it is corrupt.

The way the Cortex-M is implemented, a context switch cannot happen
unless the critical nesting count is 0, so there is no need to save it
as part of the task’s context.

jiangtianyu007 wrote on Tuesday, July 21, 2015:

Thanks a lot for your detailed reply.

In my opinion, portSETINTERRUPTMASKFROMISR/portCLEARINTERRUPTMASKFROMI can be used in both task thread and ISR because interrupt mask status is stored in local variable other than gloabl variable(uxCriticalNesting). So why FreeRTOS use the two different method of entering critical section?

Why don’t we only use portSETINTERRUPTMASKFROMISR/portCLEARINTERRUPTMASKFROMI to enter critical section both in task thread and ISR ?

Thanks in advance.

rtel wrote on Tuesday, July 21, 2015:

…because the method used within interrupts takes stack space, whereas
the method used outside of interrupts does not. That is not so
important these days but was when FreeRTOS was frequently used on tiny
MCUs where every byte of RAM counted.

…also because ports to small MCUs don’t support interrupt nesting, so
only have the task version.

Regards.

jiangtianyu007 wrote on Wednesday, July 22, 2015:

Thanks very much.