Interrupt Handler

yevpator75 wrote on Thursday, December 13, 2018:

Hi folks,

In the Micrium uC/OS-II, which is the RTOS I used to work in my previous work, there is a pair of API functions OSIntEnter and OSIntExit intended to notify the Kernel about the interrupt being processed, I think in order to prevent Scheduler switching to some task during the interrupt and also to keep track of interrupt nesting. This example is taken from the Micrium web-site:

MyISR:
Save CPU registers;
OSIntEnter(); /* Or, OSIntNestingCtr++ */
:
Process ISR;
:
OSIntExit();
Restore CPU registers;
Return from interrupt;

The same approach exists in the Segger’s embOS. They have a similar API: OS_INT_Enter() and OS_INT_Exit()
I don’t see a similar API in the FreeRTOS (checked in the User Manual). If it is indeed true, would one here be so kind as to explain me why in the FreeRTOS I don’t need to notify the Scheduler about the interrupt being processed?

Thanks in advance for any help!

heinbali01 wrote on Thursday, December 13, 2018:

For most ports ( platforms ), FreeRTOS does not have an equivalent of OSIntEnter()/OSIntExit()

But for instance, for Atmel’s UC3A, there is such a thing:

__attribute__((naked)) void spi_0_interrupt_handler(void)
{
    portENTER_SWITCHING_ISR();
    {
        handler_spi(0);
    }
    portEXIT_SWITCHING_ISR();
}

The interrupt is declared as naked, i.e. without the usual save and restore context. In stead, the two macro’s take care of this.
Note that in this case, the return value of handler_spi() determines if a task switch is required ( register r0 ).

For platforms using ARM, the interrupt handlers are simpler: they look like ordinary functions.

In all cases, within an interrupt handler, you can only use a subset of the FreeRTOS API’s, i.e. the ones that end with FromISR

yevpator75 wrote on Thursday, December 13, 2018:

Thank you very much for spending your time to answer my question.

For most ports ( platforms ), FreeRTOS does not have an equivalent of OSIntEnter()/OSIntExit()
So the question is how the Scheduler knows the code is running from interrupt? Isn’t there a danger that it will switch the interrupt to some task ? Doesn’t the scheduler run from PendSV , so seemingly it can do this unwanted context switching.

The interrupt is declared as naked, i.e. without the usual save and restore context. In stead, the two macro’s take care of this.
Actually I found the function portENTER_SWITCHING_ISR(); on the web, but by some reason I didn’t find it in the User Guide, therefore I didn’t understand what is the function intended for. I also didn’t find this function in my FreeRTOS project (FreeRTOS 9.0 for now). Did you mean to say that I have to always call portENTER_SWITCHING_ISR() and portEXIT_SWITCHING_ISR inside my ISR?

Also can I assume that this specific ISR API takes care to prevent the context switching while an ISR running is ongoing?

heinbali01 wrote on Thursday, December 13, 2018:

So the question is how the Scheduler knows the code is running from interrupt?
Isn’t there a danger that it will switch the interrupt to some task?
Doesn’t the scheduler run from PendSV , so seemingly it can do this unwanted context switching.

You do not have to worry about this. You just write the ISR code, keeping it as short as possible. If a context switch becomes necessary, it will take place after your ISR is ready.

Did you mean to say that I have to always call portENTER_SWITCHING_ISR()
and portEXIT_SWITCHING_ISR inside my ISR?

No, that is not necessary, unless you’re using a platform like Atmel UC3.

Also can I assume that this specific ISR API takes care to prevent the context
switching while an ISR running is ongoing?

As for ARM: this has to do with priorities: the priority of your ISR’s should higher than the priority of PendSV. If you have configASSERT(), the CPU priority will be checked in every API you call from an ISR.

yevpator75 wrote on Thursday, December 13, 2018:

As for ARM: this has to do with priorities: the priority of your ISR’s should higher than the priority of PendSV.

But PendSV does have a higher priority… As I understand the priority of PendSV is configurable, but… doesn’t kernel itself takes care for that ?

If you have configASSERT(), the CPU priority will be checked in every API you call from an ISR.

I checked a dozen of API , but didn’t find where the priority is checked. Do you have a single example? What exactly is checked with the configASSERT?

Very appreciate your help, thank you!

yevpator75 wrote on Thursday, December 13, 2018:

Just found some staff that is helpfull for my recent questions that I would like to share here:
https://www.freertos.org/RTOS-Cortex-M3-M4.html

Frankly, I didn’t like this approach, too confusing IMHO.

heinbali01 wrote on Friday, December 14, 2018:

About configASSERT() : it is a macro that will halt your program in case an expression can not be asserted ( i.e. evaluates to zero ). Read more about configASSERT() here.

But PendSV does have a higher priority…

The higher the number, the lower the priority

As I understand the priority of PendSV is configurable

Sure it is. You can use ISR’s with a lower priority as long as they do nothing with the FreeRTOS kernel.

but… doesn’t kernel itself takes care for that ?

Like I said, the kernel can check the current ISR priority during every FromISR() API.
This is being done for instance in xQueueGenericSendFromISR() when it calls the macro portASSERT_IF_INTERRUPT_PRIORITY_INVALID().

The page you mention is indeed very clear and helpful

yevpator75 wrote on Sunday, December 16, 2018:

Hello Hein

Given the mentioned above OSIntEnter()/OSIntExit() do not exist in FreeRTOS, may I assume that the RTOS Interrupt Latency is zero in the FreeRTOS? So far, I used to think that the overhead of the OSIntEnter()/OSIntExit() is the “Interrupt Latency” added by RTOS. Am i mistaken?

rtel wrote on Sunday, December 16, 2018:

In FreeRTOS there is a separate API for use in interrupts - namely the
functions with “FromISR” in their name. There are pros and cons to any
approach of course, but FreeRTOS’s approach here means:

  1. You do not need to tell the kernel you are in an interrupt, you just
    have to use the right API functions when you are.
  2. The API functions can be tuned for use when inside interrupts - for
    example you can’t block inside an interrupt so there is no block time
    parameter to pass into an ISR safe function and no logic in the
    implementation of the ISR function to handle blocking.
  3. The non ISR functions can also be tuned as they do not need to check
    to see if you are inside an interrupt before deciding what to do.

yevpator75 wrote on Sunday, December 16, 2018:

Thanks!