We can call functions from ISR or not?

Status
Not open for further replies.

chsp129

Junior Member level 3
Joined
Mar 29, 2010
Messages
25
Helped
1
Reputation
2
Reaction score
1
Trophy points
1,283
Location
hyd
Activity points
1,479
HI to all

I am just writing ISR in my project,my doubt is we can call functions from ISR or not..If not why we not call..please explain me..

Thnaks
siva
 

you can call isr location from your program.but it is not a recommended practice to do so.
 
Reactions: MRAMA

    MRAMA

    Points: 2
    Helpful Answer Positive Rating
You can call a function but you should also see that it tand urns back properly.. and see that ISR also returns.

Usually the stack for ISR are 8 bytes in 8051.. so your calling function and ISR should be less than that or else you can have stack overflow problem...

And as told .. due to this problem it is not a good practice.. but it cn be called

---------- Post added at 19:56 ---------- Previous post was at 19:55 ----------

Remember you CANNOT CALL AN ISR, BUT CAN CALL A FUNCTION WITHIN IT.. BUT ABOVE LINES ARE APPLICABLE
 
You can call a function from an isr if it is only the isr that is calling the function. Then it can be considered as part of the isr.
If some other part of the program is also calling the function, then it must be a re-entrant function. The isr might interrupt when the program is inside the function.
A re-entrant function is a function that does not use global or static variables and does not call other non re-entrant functions.
Some compilers will flag an error if an isr calls a function that is also called from another part of the program.
 

ISR function should not [Not necessary] be called in any where.. While writing ISR that time you mention vector address of particular interrupt... So whenever that particular interrupt occurs automatically it goes to that specific interrupt service routine... I think now you are clear why it is not supposed to call in the program...
 

Functions can be called if they are of fixed latency and re-entrance should be kept in mind.
 

Functions can be called if they are of fixed latency and re-entrance should be kept in mind.

the basic rule is there should be no re-entrant functions called in and ISR as per the coding standards... which re-entrance are you talking about?????? what are you talking about???
 

The guy is asking whether you can call a function from within the ISR, not whether you can call an ISR!

In a multitasking program, some common functions can be called by more than one task.
If a task switch occurs while one task is inside the common function, and the new task also calls the same common function, what will happen?

If the common function uses no global or static variables and calls no other function that does, then the function is re-entrant, it can safly be called and shared by several functions at the same time.
 

Regardless of whether, or not, you call a function from your ISR (which is perfectly acceptable given the constraints others mentioned), the critical thing is the length of time the ISR service routine takes to execute. If the code takes too long to execute your system can become "interrupt bound". That is, it has no time to do anything other than service the interrupt. In the worse case, another interrupt occurs before the previous one's service routine has completed.

You should always compute the worse-case execution time of your ISR service code and ensure that it is sufficiently short.
 

The OP doesn't mention what part he is using, so the answer to his question really is "It depends."

For example, on some PIC based parts with fixed depth call stacks, you may not be able to call a function. For example, if you are already 6 calls deep when an interrupt occurs, you won't be able to call a function since there will not be room on the call stack. Some tools, such as the HiTech compiler for the PIC, will tell you the worst case call depth and give you the information you need. I've not seen many OS's run on these PICs, so I suspect this isn't what the OP is using.

However, if you are using a part with a software stack (such as a Cortex-M3), the limitation on calling functions is limited only by memory, performance, latency, and re-entrancy. For memory, you need to be sure any function calls from the ISR do not overflow the stack. For performance, you need to make sure that your interrupt processing time does not exceed some defined threshold. For latency, you need to be sure that your ISR execution time doesn't starve other interrupts or tasks for time. And for re-entrancy, you need to be sure that any function call does not have unintended side effects.

Memory
Some processors have a single stack pointer. All executing code uses that single stack pointer for all operations, such as pushing function return addresses, function parameter, automatic variables, and preserving register contents. When using an OS on such a processor, individual tasks may have separate stacks while executing and the OS is responsible for setting the processor stack pointer to the appropriate memory for the task. Frequently in such a system, the task's stack is used for interrupt processing. So, when an interrupt is recognized, the processor pushes some set of registers and the return address onto whatever stack is currently in use. And any function calls you make from that ISR will also use that stack. So, you must consider the worst case stack usage of any individual task and add to that your worst case overhead for the ISR.

However, some processors may have separate stack pointers. For example, the Cortex-M3 has a main stack pointer (MSP) and a process stack pointer (PSP). The MSP is always used for interrupts, and the PSP is handled by the OS for task switches. In this configuration, when an interrupt occurs, the processor switches from the PSP to the MSP and uses a separate stack for the ISR. Thus, you only have to ensure that your main stack is sufficiently large enough to handle all functions you call from an ISR.

Some OS's emulate the behavior of a separate stack. In this case, the OS has a hook at the start of the ISR (or ISR's if there are more than one) to switch to a separate stack. This increases the size of the ISR (and reduces its performance as there are more instructions to execute), but eliminates the issue of a potential stack overflow in the task stacks.

Performance
Some processors do not push the entire state of the processor onto the stack when calling an ISR. The Cortex-M3 only pushes 8 registers (of 21 total) onto the stack when executing an ISR. Most compilers do not do analysis to see what might change in a function (or a chain of functions) that is called from an ISR, and assumes that any of the registers may be modified. Thus calling a function from an ISR will usually cause the compiler to push the entire state of the processor onto the stack. This can be expensive in terms of execution time, and are wasted cycles if you know that the leaf functions do not modify anything beyond the already saved registers.

There is also overhead associated with function calls. It depends upon the ABI, but many compilers push all the arguments to a function onto the stack along with the return address. The more function calls you have, the more this overhead begins to become a problem. If the overhead associated with the function calls become too large, you will spend more time updating the stack than actually getting work done.

This is one of the biggest reasons why calling functions from ISRs is discouraged.

Latency
This is usually one of the hardest things for people to grasp. Remember that many systems have lots of interrupts and tasks running. When an interrupt occurs, the processor stops what it is doing and turns its attention to the interrupt. In some systems, there may only be a single interrupt handler, and during that time, only the current interrupt is processed. Any other interrupts that come in are ignored until the current one is complete. (This can be alleviated with nested interrupts, but that is a more complex topic.) In some systems, there are interrupt priorities whereby a higher priority interrupt can interrupt an already executing ISR.

The issue here is similar to the performance issue. The more time you spend in your ISR, the less time other things get to run. This latency introduced due to interrupts can be a problem if your application needs to be responsive. Whether or not you can call a function depends upon maximum amount of introduced latency you can tolerate.

Re-entrancy
Some of the answers the thread aren't totally clear, so I hope to clarify some of that here. Re-entrancy is only an issue if a function is shared across multiple execution contexts. I could put some formal definition, but I think it better to demonstrate with an example. Say we have the following (pseudo-)code:
Code:
int x;

void foo(int z)
{
  x = z;
}

void ISR()
{
  x++;
}
Now, depending upon the architecture, this code could create strange values for x depending upon when foo() and ISR() run. For example, if x is a 32-bit integer and we are on an 8-bit platform, it usually requires several instructions to update x. If the interrupt occurred in the middle of that update to x, the contents of x could be corrupted.

The typical example I've seen is similar to the above. There may be a timer interrupt that updates some global counter, and tasks use that counter to determine how much time has passed. For example:
Code:
unsigned int global_timer;

void timer_isr()
{
  global_timer++;
}

void wait_for_timeout(unsigned int timeout)
{
  unsigned int start = global_timer;

  while((global_timer - start) < timeout)
  {
  }
}
Now, what's wrong with this code? If somebody calls wait_for_timeout(), global_timer may change out from underneath it during the subtraction ("global_timer - start"), possibly corrupting the result. I'm not going to discuss the fix for this here (it is pretty straightforward), but the point is that wait_for_timeout() is not re-entrant. That is, once wait_for_timeout() is started, it will temporarily exit to handle the interrupt, and then re-enter with the function in a different state.

Conclusion
The answer to your question is "It depends". The issue of being able to call functions from an ISR depend upon a variety of issues, each of which depend upon your particular architecture (hardware and software) and application. There is no absolute ban on calling functions, but it is recommended to keep your ISRs as small, simple, and fast as possible. You will have to make the determination if calling a function does more good than harm, and only you can make that determination.
 
Well a function can be called from within an ISR. but it depends if it is absolute necessary.
But one thing Why not make it inline?
I think accessing external variables without proper synchronization in a multi tasking environment is a bad practice.

Just Wild Thought: Lets Assume we are writing a systemTimer ISR and in that we called a function x() which takes more time to complete than the Timer interrupt frequency. What will happen? Definitely i am going to loose timerClicks. and my system will crash.
 

A good practice in Interrupt handling is not call the funcion inside Interrupt vector.
It is prefereable set a flag instead :

Bad :
Code:
void ISR()
{
Function() ;
}

Good:
Code:
void ISR()
{
Flag_enable_funcion_call = true ;
}

void main()
{
if ( Flag_enable_funcion_call == true )
     {
     Flag_enable_funcion_call = false ;
     Function() ;
     }
}
 

A good practice in Interrupt handling is not call the funcion inside Interrupt vector.
It is prefereable set a flag instead :
Indeed, it is ideal to defer as much processing as possible to a task or the main loop.

However, sometimes you need to do processing immediately. For example, in a UART interrupt where there is no hardware FIFO, you need to read that character from the UART immediately and store it somewhere. I use a thread-safe queue implementation to push characters from the incoming UART. For example:
Code:
QUEUE *uart_rx_queue;
QUEUE *uart_tx_queue;

void uart_rx_isr()
{
  char c;

  c = UART->dr;

  queue_push(uart_rx_queue, &c);
}

void uart_tx_isr()
{
  char c;

  if ( queue_pop(uart_tx_queue, &c) )
  {
    UART->dr = c;
  }
  else
  {
    disable_irq(UART_TX_IRQ);
  }
}

int main()
{
  char c;

  uart_rx_queue = queue_create(QUEUE_RX_DEPTH, sizeof(char));
  uart_tx_queue = queue_create(QUEUE_TX_DEPTH, sizeof(char));

  while(1)
  {
    while(queue_pop(uart_rx_queue, &c))
    {
       /* Process the character ... */
    }
  }
}
If you were to rely upon the main loop to check some flag, it is possible that a new character might be received before UART data register is read. You could do this without a function (writing to an array, updating indexes/pointers, etc), but then the code wouldn't be very generic. You couldn't, for example, have generic queues for generic functionality.

There's nothing "BAD" per se about using functions as long as you are aware of the potential pitfalls. However, I agree with your sentiment: do as little as possible in an ISR. If you can get by with just setting a flag, do it!
 
Last edited:

hi Suudy,

You pointed a good example in wich the previous general guidance - at first sight - could not be well applicable.
However, due processing of uC´s is too high, a push/pop queue management provoke no much overhead impact.
Unless - of course - if the core processing is timered at an low-frequency XTAL without PLL overclocking.

So, a criterious system analysis must be performed to each case.

+++
 

Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…