Calling function pointers from an ISR

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all,

Consider something like this:


void (*func)(void);

SIGNAL(SIG_OVERFLOW0)
{
  // Do some stuff...

  if( rare_condition )
    func();

}

The "problem" here is that gcc (or any compiler I guess) is pushing all the scratch registers on to the stack in the prologue because of the call to "func", which only happens rarely.

Assuming I really want to keep using the function pointer (this is a simplification of what I want to accomplish), does anybody have any ideas on techniques I could use to avoid the register push/pop overhead except when needed?

- kwr

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, I guess you could declare the ISR with the "naked" attribute. But then you'd need to manually take care of pushing/popping all the registers that legatamitely do need to be preserved (including pushing and re-zeroing the dedicated zero-register) and put an assembly "RETI" at the end of the routine...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I thought to do that, but I figured it might be pretty hard to make sure I was pushing all the neccessary registers.

I can declare a function to call the function pointer with the "signal" attribute. Then that function pushes the registers correctly. However, the compiler pushes all of the registers for that function call instead.


void (*func)(void); 

void __attribute__((signal)) call_func(void)
{
  func();
}

SIGNAL(SIG_OVERFLOW0)
{
  // Do some stuff...

  if( rare_condition )
    call_func();

}

Also, I tried to inline call_func, but the compiler gcc gives me an "unimplemented" error. If only gcc would realize it is calling a "signal" function, then it would not have to bother with saving registers to call it (it is equivalent to another interrupt happenning).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The problem is, the compiler cannot possibly know beforehand anything about the possible functions which will end up getting called through the function pointer, so it doesn't have any way of predicting which of the GPRs are going to be trashed.

That leaves us in the position that, in order to guarantee that the interrupted program's state will not be corrupted once the ISR returns, the compiler has no choice but to preserve save all the call-used registers, as well as any of the call-saved registers which happen to be used locally by the body of the ISR itself...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just an idea but if "rare_condition" occurs could you maybe just set a volatile flag in the ISR then call func() out in the main() code when it sees the flag or do you need instantaneous response from func() in this condition?

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In a small application I would use only global register variables in those functions which are referenced by the function pointer. The registers mustn't be used anywhere else in the programm.
then there is no need to push/pop anything else but the status register.

Regards
Sebastian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The function pointed to by "func" is not effectively under my control - imagine a timer library where you pass an expiration function in.

I wonder how hard it would be to change gcc so it didn't bother pushing all the registers in a "signal" function, provided that it only calls other "signal" functions. That would solve my problem (see above).

Any idea where to start? (I already have built my gcc from the source, for starters).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
...imagine a timer library where you pass an expiration function in.

I might consider a slightly different approach.
I'm presuming the ISR has a queue of "expiration functions" -- let's call it the "Waiting-to-run" queue -- and the ISR chooses to run those functions as they "expire".

How about, instead of running the expiration functions directly from the ISR, the ISR simply pulls the function pointers out of the WTR queue, and pushes them into another queue -- let's call it the "Ready-to-run" queue. Then, your main application's idle loop can inspect the RTR queue, and execute functions as they arrive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Consider writing the ISR in assembler.

Seems more realistic than customizing GCC.

C: i = "told you so";

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lfmorrison,

Consider the "Super Simple Tasker" from a few months ago in ESD:

http://www.embedded.com/columns/...

I want preemptive multitasking - i.e. store the context of the task running when the interrupt occured to execute a "higher priority" task. As it is, some things I do on the timer take a looong time (like do transfers on a 1-wire interface).

I still think this is a kind of compiler feature that is needed. I'll look into it today and follow up with how hard it might be.

Alas, writing the ISR(s) in assembly would be a ton of work, considering I am using similar techniques in other ISRs.

- kwr

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is the best I come up with in gcc to solve my problem:

1. Add a "nosave" attribute that can be added to a signal or isr so it gets treated as a "leaf function", even if it is not. Much more functional than the "naked" attribute, and no more dangerous.

2. Recycle the "saveall" attribute from other architectures to generate a function that saves registers like signals or isrs, but puts ret instead of reti in the epilogue.

Analyzing the signal handler for only "nosave" calls seems hard - I could not figure out how to get the function attributes from the RTL CALL_INSN.

Comments?

- kwr