Understanding priority levels of ISR and FreeRTOS APIs

  • So NVIC interrupt priority values are down to up: the lower it is, the higher the priority, yes? Whereas for RTOS tasks, the higher the priority number, the higher its priority.
    Considering that, why does the ISR using a FreeRTOS API have to be given the priority lower or numerically higher than that of configMAX_SYSCALL_INTERRUPT_PRIORITY? So the ISR could be preempted by a FreeRTOS API?

  • If you’re calling xTaskNotifyFromISR() within an ISR, what becomes pxTCB (notified task) and pxCurrentTCB (current task)? is ISR considered a task?!

  • I have an ISR for a push button. Inside its ISR, I’m calling xTaskNotifyFromISR(), but right after the program gets stuck on the following condition:

configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );

ucMaxSysCallPriority is 80, and for some reason, ucCurrentPriority has a value of 0 even though I tried different priority values.

// Configure GPIO pin : PC13 [PUSH BUTTON]
	gpio.Pin = GPIO_PIN_13;
	gpio.Mode = GPIO_MODE_IT_FALLING;
	gpio.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(GPIOC, &gpio);
	NVIC_SetPriority(EXTI15_10_IRQn, 85);
	HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

The purpose of the restriction is that there are spots in the code where FreeRTOS needs a short critical section to adjust system variables when it doesn’t want some other task or ISR to get in the way and be fiddling with them too. So it disable interrupts up to configMAX_SYSCALL_INTERRUPT_PRIORITY, but leaves higher priority interrupts enabled. This allows those higher priority interrupts, that don’t (directly) use FreeRTOS to not be delayed.

One confusing things with Cortex M interrupts, is that there priority can be talked about two different ways. One is at the ‘hardware’ level as an 8 bit unsigned number. But the hardware doesn’t use all those bits, and many of the software routines take the priorities with the unused bits removed, often as number in the range of 0-7 or 0-15. Giving the SetPriority routine a number scaled wrong will cause it to load the wrong value, or maybe even not load a value at all.

Correct - that is not the same on all chips though - and FreeRTOS runs on some 40+ architectures.

Have you seen this page RTOS for ARM Cortex-M?

On the Cortex-M FreeRTOS creates critical sections by masking interrupts up to the application writer defined maximum syscall interrupt priority. API functions contain critical sections so if you call an API function from an interrupt that has a LOGICAL priority above the maximum system call interrupt priority then critical sections will not work and corruption can occur. Interrupts above that logical priority are always enabled so are not impacted by the kernel - this is important if you are going something with critical timing such as motor commutation - but you cannot call FreeRTOS API functions from those priorities.

No, ISRs and tasks are very different things. ISRs are under the control of the hardware - the hardware knows about them and their priority. A FreeRTOS application on the other hand is just a C program that is executed by the hardware - the hardware has no knowledge of tasks or task priorities. Assuming interrupts are not masked an interrupt will always interrupt a task - but a task can never interrupt an ISR.

This is why we have so many configASSERTS() to catch configurations - getting Cortex-M interrupt priorities right is anything but trivial - as can be seen from the page I linked to above.

Are you sure that is how NVIC-SetPriority() wants you to define interrupt priorities? As described on the page linked above some library functions want the interrupt priority defined without it being shifted into a machine understandable bit position - while others want it without the shif. What priority are you trying to set it to? Try just entering that number (or look at how NVIC_SetPriority() is implemented, if it is any way comprehensible, to see if that is doing the shift for you.

API functions contain critical sections so if you call an API function from an interrupt that has a LOGICAL priority above the maximum system call interrupt priority then critical sections will not work and corruption can occur.

sorry but how does having an interrupt of a logically higher priority than configMAX_SYSCALL_INTERRUPT_PRIORITY corrupt the critical sections? Is the logic that you don’t want to interrupt the processor while a critical section is being executed hence why the kernel disables the interrupts? Also, doesn’t interrupt priority <= configMAX_SYSCALL_INTERRUPT_PRIORITY means logically higher priority given the NVIC priority levels?

What priority are you trying to set it to?

just anything configMAX_SYSCALL_INTERRUPT_PRIORITY such that it doesn’t complain. According to the docs, I should be setting it to <= configMAX_SYSCALL_INTERRUPT_PRIORITY. And I see NVIC_SetPriority() does some bit shift but not sure why ucCurrentPriority is even 0

As per:

That means an interrupt cannot itself be interrupted by another interrupt that has a logical priority below the maximum syscall interrupt priority - and can therefore access whatever is being protected by the critical section.

Yes.

The priority registers is only 8-bits and will only implement a few bits of the 8-bit register, so presumably when the value gets shifted you are not ending up with any bits set in any of the bits implemented by the hardware, so the value will appear to the hardware as 0. However you have the code there so you can determine exactly what it is doing.

When FreeRTOS is manipulation its internal structures, like a ready list, it needs to make sure that no one else is going to also try to manipulate the structure. It does this by disabling the interrupts at and below configMAX_SYSCALL_INTERRUPT_PRIORITY. If an interrupt has a higher priority (lower value) than this and does call a FreeRTOS function (and the assert is present to catch it), and that function manipulates the same structure, that can result in a corrupted data structure, things like a task being lost from the ready queue is a very real possibility.

1 Like

It does this by disabling the interrupts at and below configMAX_SYSCALL_INTERRUPT_PRIORITY

If an interrupt has a higher priority (lower value) than this

i’m getting confused by these two lines. ISRs with interrupt priority <= configMAX_SYSCALL_INTERRUPT_PRIORITY mean they’re of a higher priority. If the kernel disables “high” priority interrupts, then why would the interrupt occur?

In essence, we want to prevent any ISR of priority >= configMAX_SYSCALL_INTERRUPT_PRIORITY from triggering while critical section is being executed, yeah?

Kind of. configMAX_SYSCALL_INTERRUPT_PRIORITY sets the maximum LOGICAL priority from which FreeRTOS API calls can be made, so we want to block interrupts with a priority greater than configMAX_SYSCALL_INTERRUPT_PRIORITY, not greater than or equal to configMAX_SYSCALL_INTERRUPT_PRIORITY.

that’s fine. I’m trying to distinguish the priority levels of ISRs from that of FreeRTOS’.
So when we say we want to block the interrupts of a priority > configMAX_SYSCALL_INTERRUPT_PRIORITY, we actually mean high or low priorities cause the higher numbers represent lower priorities?

Yes, FreeRTOS sets a mask register that controls what interrupt priorities can trigger to configMAX_SYSCALL_INTERRUPT_PRIORITY, which means interrupts of that priority or greater-value lower-priority are blocked, and lesser-value higher-priority can still happen. The ordering of interrupt priorities is the reverse of the value, so higher priority is lesser value. (and I try to always use higher or lower, never greater or lesser (as well as the mathematical symbols) as those tend to refer to the NUMBERS (as in priority value), not the priority order of the interrupts

So it disable interrupts up to configMAX_SYSCALL_INTERRUPT_PRIORITY, but leaves higher priority interrupts enabled.

I feel in your previous message, you probably mistyped. The interrupts < configMAX_SYSCALL_INTERRUPT_PRIORITY are of higher priority so if it’s disabling interrupts upto that, it’s disabling high priority interrupts and leaving low priority (>= configMAX_SYSCALL_INTERRUPT_PRIORITY) enabled, no?

Also, if the high priority interrupts are disabled while critical section is being executed, why the fear of them triggering and corrupting the critical section?

Sorry if I’m being a nuisance right now.

Be VERY careful how you interpret things, by “disable interrupts up to configMAX_SYSCALL_IN TERRUPT_PRIORITY”, it is meant to disable any interrupt with a priority value >= configMAX_SYSCALL_INTERRUPT_PRIORITY. I try to use very distinct here, ‘higher’ and ‘lower’ are logical priorities, higher priority interrupts have numerically smaller values.

The interrupt hardware doesn’t have an option to turn off just higher priority interrupts, and leave lower priorities enabled, largely because it doesn’t make sense. The hardware has a register for the current mask level, and interrupt of that level and lower (bigger values) are masked off and won’t generate an interrupt still the mask is removed of set to a lower priority (higher value) than the interrupt in question.

Yes I believe I’m interpreting the right way: disabling interrupts of a higher priority upto configMAX_SYSCALL_INTERRUPT_PRIORITY - in other words, if configMAX_SYSCALL_INTERRUPT_PRIORITY == 4, priority values of 1,2,3,4 are disabled, yes?

Also from the doc:

ISR’s running above the maximum syscall priority are never masked out by the RTOS kernel itself

I reckon they’re referring to the numerical interrupts and not logical.

Then what do we mean by disabling interrupts if it isn’t for “disabling interrupts up to configMAX_SYSCALL_INTERRUPT_PRIORITY” i.e disabling only high priorities?

Just think in LOGICAL priorities where higher is above lower and lower is below [edit!] higher - because you are turning everything in knots. Ignore the actual numerical values - as I said originally - ARM MCUs use low NUMERICAL values to represent high LOGICAL priorities - but nearly every other MCU (and FreeRTOS runs on 40+ architectures) do it the other way around. Therefore the website cannot make an assumption as to which way around a particular MCU happens to number interrupt priorities - but no matter logically higher interrupt priroities are always higher in priority than logically lower interrupt priorities - that is why they are called priorities. If you mask up to a logical priority level then only interrupts that have a logically higher priority can execute.

Therefore configMAX_SYSCALL_INTERRUPT_PRIORITY is the maximum LOGICAL priority from which a FreeRTOS API function can be called.

When the MCU masks interrupts up to a specified LOGICALLY priority value, all interrupt priorities LOGICALLY lower than it (the ones that the MCU sees as having lower priority, doesn’t matter what its actual number is) are masked and interrupt priorities LOGICALLY higher than it (the ones that can still interrupt because they are not masked) can still execute.

1 Like

NO!, it means priorities 4,5,6,… get disabled. ‘up to’ is talking about the LOGICAL priority order not numerical. As I said, the hardware doesn’t have a mechanism to disable high priority (small numerical value) interrupts but leave lower priority (larger numerical value) still enabled

Again, you have it backwards, it says that interrupts with a higher priority, i.e a SMALLER value, than configMAX_SYSCALL_INTERRUPT_PRIORITY are never disabled by FreeRTOS.

If you are ever thinking that you have a case where an interrupt of priority value 1 is disabled, but an interrupt of priority 6 is still enabled, you have it wrong. The only way to do something like that it go to the specific interrupt (by number, not interrupt priority) and disable it.

The FreeRTOS documentation will almost always be referring to the Logical interrupt priority (lower meaning higher value and higher being lower values, on the Cortex M series) because it is describing in a hardware independent way what is happening, and on other machines, priority 0 may be the lowest priority. One in the chapter specifically for the Cortex M processors will it talk specifically about interrupt priority values (Thats the page Richard Barry pointed out near the beginning of the topic).

A pictorial representation, if that helps:


            Logical      Numerical
           Priorities    Priorities
           +--------+    +-------+             ^
           |        |    |       |             |
           |        |    |       |             |
           |        |    |       |             | Not masked by FreeRTOS kernel
           | Higher |    | Lower |             | ( FreeRTOS APIs can not be
           |        |    |       |             |    called from these ISRs. )
           |        |    |       |             |
           |        |    |       |             v
           +--------+    +-------+ <---------------- configMAX_SYSCALL_INTERRUPT_PRIORITY
           |        |    |       |             ^
           |        |    |       |             |
           |        |    |       |             |
           |        |    |       |             |
           |        |    |       |             |
           |        |    |       |             |
           |        |    |       |             |
           | Lower  |    |Higher |             | Masked by FreeRTOS Kernel
           |        |    |       |             | ( FreeRTOS APIs can be
           |        |    |       |             |    called from these ISRs. ) 
           |        |    |       |             |
           |        |    |       |             |
           |        |    |       |             |
           |        |    |       |             |
           |        |    |       |             |
           |        |    |       |             |
           +--------+    +-------+             v

So in terms of NUMERICAL PRIORITIES, if configMAX_SYSCALL_INTERRUPT_PRIORITY == 4, then 4, 5, 6 and above are masked during critical sections.

Thanks.

4 Likes

Right. So the kernel disables the low priority interrupts to prevent any other low priority interrupts using fromISR APIs from accessing the critical region while it’s being accessed by another ISR already and prevents any high priority interrupt from fiddling with the critical section by asserting out hence not suggested to not fromISR in high priority ISRs.

To clarify: a lower priority interrupt has acquired a lock, and a high priority interrupt occurs. The higher priority interrupt will rightaway be executed but what happens to the lock? technically if the lock remains intact then we shouldn’t worry about higher priority interrupt fiddling with a critical section but that’s what I’m trying to clarify.

Yep, thanks for it. But what causes the confusion is in some examples, they have referred higher numerical priority to a higher priority.

Refer to the table captioned Example interrupt priority configuration

When I low priority interrupt enters a critical section, the interrupt mask level is temporarily bumped to configMAX_SYSCALL_INTERRUPT_PRIORITY, so the higher level interrupt will be masked off for the duration of the critical section. Interrupts with higher priority (lower value on Cortex M) than configMAX_SYSCALL_INTERRUPT_PRIORITY, which aren’t allowed interact with FreeRTOS, can still occur, but they won’t mess with the data protected by the critical section.

You are misunderstanding that page. The ONLY spot it actually refers to a numeric physical priority (as opposed to a logical number where 0 is lowest priority) is in the section title “Special Note for ARM Cortex-M3 and Cortex-M4 users”. Everywhere else, you should be reading the “number” used as a priority NOT as the value loaded into the priority register, but as a logical priority value where lower number is lower priority. Except for that note, the whole page is designed to talk about a generic architecture, and assumes the “Low Priority” means it is a Low Priority (which turns out on the Cortex-M to be a big number).

1 Like