Nested Interrupts, enable state after context switch

Hello,

i implement a port for the intel (aka altera) nios 2 processor and have a question regarding nested interrupst ( configMAX_SYSCALL_INTERRUPT_PRIORITY):

Inside the context switch i have to disable (and re-enable) interrupts below (or even) the configMAX_SYSCALL_INTERRUPT_PRIORITY.
Therefore I store the ISR enable state of each interrupt on the stack (form “INT_ENABLE” register) and disable all ISRs below (or even) configMAX_SYSCALL_INTERRUPT_PRIORITY.
When I restore the next context, I read the saved enable state from stack, and write it back to the “INT_ENABLE” register…

This means, that when I enable a interrupt from a task (after the schedular is startet), this interrupt will be disabled, when a context switch occures to another task…

Is this the correct behavior (how it should be)?! So if i want an interrupt to be enabled at all time, i have to enable it, before starting the scheduler?!

Can somebody help me to understand, what the enable status of each interrupt should be after a context switch?!

Regards, Andy

The enable state is part of the task context so should be restored to whatever it was when it was saved.

Are you using our official (if rather old) port? Sounds like you are writing your own.

Yes i wrote a port for the NIOS processor with vectored interrupt controller last year (LINK)…

There were some issues with it, and now i try to fix it… The first thing was the bad nested interrupt implementation :roll_eyes:

Now i think it works like expected… The IRQ state will be restored in every task. I think i also fixed the “portDisable” and “portEnable” Interrupts implementiation, by saving the last enable state (for every task) on the stack (while context is swtiched) und write this state to the ISR_ENABLE register, as soon “portEnable” will be called…
Nesting should also work because of the use of the “vTaskEnterCritical” and “vTaskExitCritical” implementation…

Thank you for your help!
Andy

Hello,

i also implemented the portSET_INTERRUPT_MASK_FROM_ISR and portCLEAR_INTERRUPT_MASK_FROM_ISR makros…

Can somebody tell me the diffrence to the portDISABLE_INTERRUPTS and portENABLE_INTERRUPTS implementation?!

Both makros should disable and (re)enable all interrupts below or equal to configMAX_SYSCALL_INTERRUPT_PRIORITY?!

Regards,
Andy

The FROM_ISR versions allow the caller to save/restore the previous interrupt mask. That’s important in an ISR because you don’t want to enable interrupt priorities lower than the ISR you’re currently executing.

No, portCLEAR_INTERRUPT_MASK_FROM_ISR() doesn’t enable all interrupts below configMAX_SYSCALL_INTERRUPT_PRIORITY. It enables only those above the parameter passed to it. As noted above, the purpose of that parameter is to let the caller restore the original interrupt mask level.

Thanks for the quick response!

That’s important in an ISR because you don’t want to enable interrupt priorities lower than the ISR you’re currently executing.

Why I dont want to enable interrupts below the current executing?

There are a few reasons. Consider a task running inside a critical section that masks interrupts up to priority 3. Then consider an interrupt with priority 4 that interrupts the task’s critical section. Let’s say the ISR masks all interrupts for its own critical section. When the ISR’s critical section ends, it should restore the mask to level 4, not all the way down to level 0. If it restores the mask down to 0, it will break the task’s critical section.

Another reason is simply priority. The developer decides interrupt priority to make sure a higher priority ISR gets to finish its work before any lower-priority ISR can execute.

Another reason is ISR reentrancy. One some architectures, a peripheral can request a new interrupt in the middle of its own ISR. If the ISR has lowered the interrupt mask, you could have an ISR execute reentrantly. Most developers don’t write ISRs to be reentrant.

Some architectures (Cortex M for example) actually enforce this principle in hardware. No matter how the software sets the interrupt mask, the hardware simply won’t allow lower priority interrupts than the one currently being serviced.

Thanks again for your explanation!

1.)

These topics are already handle through hardware. I use the NIOS2 processor for Intel (aka Altera) FPGAs with external interrupt controller (EIC) with shadow register sets.
So in my configuration, every interrupt has its own unique priority and register set.
Higher IRQ will preempt lower IRQ. Reentrance is not allowed inside the same register set, unless you dont explicitly re-enable the RSIE flag (which will be cleared by hardware, when interrupt is taken).


I dont use non maskable interrupts (RNMI).

2.)

Ok i think i missunderstood, how to implement the portDISABLE_INTERRUPTS and portSET_INTERRUPT_MASK_FROM_ISR if i want to implement nesting.

What i am doing at the moment is:
1.)
Context Switch (SysTick, lowest ISR prio):
At the beginning i use the CPU IL (interrupt field) to disable all ISRs below or equal configMAX_SYSCALL_INTERRUPT_PRIORITY. Then i set PIE (interrupt enable flag) to allow higher interrupts (above configMAX_SYSCALL_INTERRUPT_PRIORITY) to execute.
after contxt switch and restoring registers, i set IL field again to 0 (enable all interrupts).

2.)
portDISABLE_INTERRUPTS():
disables all irqs below or equal configMAX_SYSCALL_INTERRUPT_PRIORITY with IL fiel (set to configMAX_SYSCALL_INTERRUPT_PRIORITY)
portENABLE_INTERRUPTS():
enables all irqs with IL field (set to 0)

3.)
portSET_INTERRUPT_MASK_FROM_ISR():
Store the interrupt enable register (INT_ENABLE) from the external interrupt controler. Then disable all ISR below or equal configMAX_SYSCALL_INTERRUPT_PRIORITY by clearing the corresponding bits inside INT_ENABLE register.
portCLEAR_INTERRUPT_MASK_FROM_ISR():
restore the INT_ENABLE register with “uxSavedInterruptStatus” parameter.

I think with this implementation I dont support real nesting (is this correct?!)
So if this is true, there are some new question :slight_smile:
1.) I am correct in assuming that it is correct to disable all interrupt below or equal configMAX_SYSCALL_INTERRUPT_PRIORITY for context switching (e.g. SysTick handler or yield)
2.)

This wouldnt happen in the actual implementation (what is not correct). If the system behaves like you said, then what happens if th ISR with prio 4 calls a FreeRTOS API funktion (like xQueueSendFromISR ) which unblocks the interrupted task with prio 3?! Is it allowed to use a API in higher ISRs which potentially interrupted a critical section of a lower task?

Again thank you very much for your support!
Regards, Andy

This list looks good until #3. Why not use IL? That would be simpler and faster, right? Then portSET_INTERRUPT_MASK_FROM_ISR() would set IL to configMAX_SYSCALL_INTERRUPT_PRIORITY and return the previous IL value. And portCLEAR_INTERRUPT_MASK_FROM_ISR(x) would set IL to x.

The key difference between portENABLE_INTERRUPTS() and portCLEAR_INTERRUPT_MASK_FROM_ISR(x) is that the former can simply set IL to zero, but the latter must set IL to x.

No. You are correct. In my example the interrupt at priority 4 is not allowed to call FreeRTOS API functions. So you’re right – that kind of ISR wouldn’t call portSET_INTERRUPT_MASK_FROM_ISR() or portCLEAR_INTERRUPT_MASK_FROM_ISR() at all. That was bad example. :wink:

One other thing to realize is that the architecture in FreeRTOS needs to support all (or at least most) processors. So just because a give processor might not need to save the information, doesn’t mean the API can skip it. There are a few cases where we know that it doesn’t need to be saved, so the kernel code uses things in a slightly ‘non-standard’ way (with documented reasons for doing so), if you know that you never need to return to the previous state, but can always just re-enable all and the hardware will still work right, you could do the same (just be REAL sure you are right or you will get a hard to find problem).

Thanks for answers!

Just to make sure i got it right:

The goal from both portFunctions (portDISABLE_INTERRUPTS and portSET_INTERRUPT_MASK_FROM_ISR) is to disable all IRQs below or equal configMAX_SYSCALL_INTERRUPT_PRIORITY ?!

The portFunction portENABLE_INTERRUPTS should enable ALL IRQs.

The portFunction portCLEAR_INTERRUPT_MASK_FROM_ISR should ONLY enable these IRQs which were enabled, when portSET_INTERRUPT_MASK_FROM_ISR were called?

In which scenario the function portSET_INTERRUPT_MASK_FROM_ISR find a IRQ state with only some IRQs disabled?

I am testing my port at the moment with the demo application tasks and already noticed a problem:
In the GenQTest task (prvHighPriorityMutexTask) the firmware hang up at list.c line 150. The command above tells me already this:

/* *** NOTE ***********************************************************
If you find your application is crashing here then likely causes are
listed below. In addition see https://www.freertos.org/FAQHelp.html for
more tips, and ensure configASSERT() is defined!
https://www.freertos.org/a00110.html#configASSERT

  	1) Stack overflow -
  	   see https://www.freertos.org/Stacks-and-stack-overflow-checking.html
  	2) Incorrect interrupt priority assignment, especially on Cortex-M
  	   parts where numerically high priority values denote low actual
  	   interrupt priorities, which can seem counter intuitive.  See
  	   https://www.freertos.org/RTOS-Cortex-M3-M4.html and the definition
  	   of configMAX_SYSCALL_INTERRUPT_PRIORITY on
  	   https://www.freertos.org/a00110.html
  	3) Calling an API function from within a critical section or when
  	   the scheduler is suspended, or calling an API function that does
  	   not end in "FromISR" from an interrupt.
  	4) Using a queue or semaphore before it has been initialised or
  	   before the scheduler has been started (are interrupts firing
  	   before vTaskStartScheduler() has been called?).
  **********************************************************************/

So stack shouldn’t be the problem, i think the problem belongs to the nesting IRQ implementation…

Regards,
Andy

All of this looks good as long as enabling/disabling interrupts is done only in STATUS.IL.

The most likely scenario is when critical sections nest. For example, an ISR creates a critical section and then calls a function from within that critical section. Then that function creates “another” critical section.

For some/many processors that allow nested interrupts, that nesting is controlled by the interrupt mask. When you get a level N interrupt, the system, as part of responding to the interrupt, saves on the stack the current interrupt mask, and the masks off interrupts of level N or lower. Thus inside the ISR, for such a machine, the mask will always have some levels masked.

Other processors keep the current level elsewhere, so nesting doesn’t need to affect the mask level. On this sort of processor, it is much less likely to have some levels masked.

Since FreeRTOS is designed to work on all sorts of processors, some things that are standardized, the protocols are designed to handle the most general case, thus saving and restoring the mask, even if for a given machine it isn’t needed.

Ok, got it :nerd_face:

Any suggestens for debugging the above descripted Problem?
I reduced the demo applications, so only

  • vStartLEDFlashTasks (flash.c)
  • vStartQueuePeekTasks (QPeek.c)
  • vStartGenericQueueTasks (GenQTest.c)
  • 2 Tasks which writes and checks the registers

are running. After a few senconds (or minutes) the Firmware freezes at (example call stack)

  • list.c (line 151)
  • task.c (line 3031)
  • queue.c (line 1741)
  • qpeek.c (line 172, “prvHighestPriorityPeekTask”)

FreeRTOS Kernel V10.2.1

The demo tests dont use IRQs so far, so the problem must be somewhere else :face_with_monocle:

Regards,
Andy

Ok found the error ( it was a typo, i used the define “NIOS2_STATUS_IH_OFST” instead of “NIOS2_STATUS_IL_OFST” :unamused: :roll_eyes:)

Now I successfully run these demo apps (including 2 timer interrupts for the IntQueue example):

vStartLEDFlashTasks( mainLED_TASK_PRIORITY );
vStartIntegerMathTasks( mainGENERIC_QUEUE_PRIORITY );
vStartGenericQueueTasks( mainGENERIC_QUEUE_PRIORITY );
vStartBlockingQueueTasks( mainQUEUE_BLOCK_PRIORITY );
vStartPolledQueueTasks( mainQUEUE_POLL_PRIORITY );
vStartQueuePeekTasks();
vCreateBlockTimeTasks();
vStartSemaphoreTasks( mainSEMAPHORE_TASK_PRIORITY );
vStartCountingSemaphoreTasks();
vStartRecursiveMutexTasks();
vStartInterruptQueueTasks();

/* prvCheckTask uses sprintf so requires more stack. */
xTaskCreate( prvCheckTask, "Check", configMINIMAL_STACK_SIZE, NULL, mainCHECK_TASK_PRIORITY, NULL );

/* The RegTest tasks as described at the top of this file. */
configASSERT(
		xTaskCreate( prvFirstRegTestTask, "Rreg1", configMINIMAL_STACK_SIZE, mainREG_TEST_1_PARAMETER, mainREG_TEST_PRIORITY, NULL )
		);
configASSERT(
		xTaskCreate( prvSecondRegTestTask, "Rreg2", configMINIMAL_STACK_SIZE, mainREG_TEST_2_PARAMETER, mainREG_TEST_PRIORITY, NULL )
		);


/* This task has to be created last as it keeps account of the number of tasks
it expects to see running. */
vCreateSuicidalTasks( mainCREATOR_TASK_PRIORITY );

/* Finally start the scheduler. */
vTaskStartScheduler();

Regards,
Andy

Andy,

Glad you got it working! Out of curiosity, did you start from the official NIOS 2 port? Or did you write your port from scratch? Either way it could be helpful to other users if you upload your port in this thread.

The “official NIOS 2” only support the internal interrupt controller (only 1 ISR funnel, no nesting, priorisation, no shadow register etc possible)

I copied this port to a new folder “NiosII_EIC” and implemented the necessary changes…

I will prepare and upload the files here…

Regards
Andy