software interrupts

Go To Last Post
55 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dear all,

Does anyone as a good source for a tutorial on software interrupts with AVR micro?

Thanking you

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

This appears in the mega48/88/168 datasheet (and similar text can be found in most of the datasheets):

Quote:
Observe that, if enabled, the interrupts will trigger even if the INT0 and INT1 or PCINT23..0 pins are configured as outputs. This feature provides a way of generating a software interrupt.

So you basically configure for INT0, INT1 or PCINT, set the pin to output then change it's state to trigger the interrupt.

Cliff

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

I'm not sure I understand the purpose of a software interrupt in an embedded system. Can someone explain their use and benefit?

official AVR Consultant
www.veruslogic.com

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

Well the only use I've ever heard suggested that made some sense was to use them for app to boot or boot to app communications. Say the app is running but the IVSEL means the boot code is handling the vectors then the app could force a s/w interrupt and get the boot to service the request. (or that could all be the other way round).

But otherwise, like you, I'm hard pushed to think of a reason (unless it's a "college assignment" perhaps just being done as a learning exercise?)

Maybe someone else knows of some other reason for them? (the OP perhaps as he wants to use them?)

Cliff

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

That makes sense when two sets of code linked and loaded separately need some absolute base for communicating.

It just seems to come up often for something that has such little use and other methods.

official AVR Consultant
www.veruslogic.com

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

Quote:

That makes sense when two sets of code linked and loaded separately need some absolute base for communicating.

It just seems to come up often for something that has such little use and other methods.


Then why not call or jump to that vector address? You are going to come back to the same spot anyway. Or if not (e.g., task switching) it doesn't matter how you got there.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

A software interrupt can be a handy way to make an operating system call, especially with processors (not the AVR) that support interrupt and execution priorities. The interrupter/caller doesn't need to know where the request handler is. The interrupt can also start saving context and setting a new priority level for servicing the caller's request.

- John

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

Quote:

...especially with processors (not the AVR) that support ...

Part of the point: We >>are<< on AVRs here--until the Xmega we don't have many of those toyz. If we did, perhaps we could construct a case of this SWI handler being interrupted by a "real" interrupt. But is that any different than calling/jumping to a non-interrupt routine?

Quote:

The interrupter/caller doesn't need to know where the request handler is.

Perhaps not, but it needs to know where the >>vector<< is, right? And the vector and the ISR need to be set up. And the appropriate enable bits set. And only works when global interrupts are enabled. And blocks "real" interrupts. [And if that is the aim then just disable interrupts.]

So, if I'm going to use the vector at address 0x8 for this SWI, then in the code where it is invoked is there any difference to force the interrupt, or RCALL/CALL 0x08, or RJMP/JUMP if it is not going to return normally? Perhaps a word or cycle one way or the other, and that could well be cancelled-out with the setup/enable sequences. An ISR is pretty much a void (void) function anyway.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Well, I didn't write that using software interrupts made sense on an AVR. It lacks a tolerable few things that might make them more useful.

- John

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

I think the datasheet is just pointing out that the voltage on a pin (causing an interrupt) can be just as easily generated internally as externally and to explain this "oddity" they wrap it up as a "feature" and call it "software interrupts" ;)

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

In practice I've only seen one use over the years NMI on x86 for breakpoint/single-stepping. Not really applicable on flash-based AVRs.

I searched out the prior discussions on software interrupts, and they turned out pretty much like this one. I can't disagree that they would be a valuable feature--once someone gives me the case where they have a use! The prior "examples" seem to be task switching, or building some kind of alternate context. Again, you can get there any number of ways, and whether you come back again is up to you[r code].

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

I think they write it into the datasheet so nobody accidently triggers an interrupt. And then there's always the it's not a bug, it's a feature ...

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

I know the original 68000 Macintosh used a software interrupt to implement quick draw. The application pushed all the command parameters on the stack, and did a software interrupt to have the ROM take over.

But, for the an AVR, the only application would be the boot loader thing mentioned earlier. It just doesn't seem practical.

I wish the OP would chime in and tell us his problem rather than his solution.

official AVR Consultant
www.veruslogic.com

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

Well I remember when I worked on the Sinclair Spectrum firmware we used the Z80's RST instructions - which are a bit like 68000 SWIs - to call to the most common routines (like printf etc) as it was simply a shorter opcode sequence than a full call and that 16K of firmware was completely "stuffed"

Cliff

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

I've seen arguments that it would be beneficial to restrict the situations in which interrupts are allowed to be inhibited - specifically, grant that priviledge to a specified few "trusted" routines in the OS or kernel, and use hardware means to enforce an absolute prohibition preventing untrustworthy code (any arbitrary user-supplied software) from being allowed to arbitrarily turn the global interrupt mask on and off.

But, because user-level applications can have a legitimate need for constructs such as semaphores, and since such constructs require the use of atomicity, the OS kernel provides a series of system calls implemented as software interrupts, which force the CPU into a privileged mode of operation in which trusted code (the OS itself) is allowed to make judicious use of inhibited interrupts in the act of servicing the user application's request.

Such a system is admittedly meaningless in an AVR because there is no hardware distinction between "privileged" and "user" level code.

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

One possible use that somes to mind, if an external interrupt is expected to occur after a certain period but doesn't, then a timeout routine can invoke the ISR.

Or part of a built-in test can "simulate" the external interrupt to measure the correct response.

C: i = "told you so";

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

The first use that came to my mind was, of course, the OS call, since that's the way M$ DOS/Windoze is run, as well as others.

However, one other use came to mind -- they might be useful for implementing exception throwing in C++. Errrm, or maybe not.

I'm not looking to volunteer to do that, by the way. :wink:

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

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

Ah so the rules then become "sure you can use C++ but you need to tie up one of your IO pins to do it" ? ;)

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

Quote:

...then a timeout routine can invoke the ISR.

Again, can't we already do that without a "software interrupt"? Merely call the from the "timeout routine". Or to address the "fixed address needed" situation call the vector.

A SWI for your example would invoke the SWI ISR, not the "lost/missing signal" ISR so the SWI is going to go there anyway! I guess one could have multiple vectors going to the same ISR.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Quote:
Again, can't we already do that without a "software interrupt"?
I think so except that invoking an interrupt manually is essentially:
Quote:
CLI
CALL isr

so during a very small amount of time another higher priority interrupt cannot supersede your SWI since interrupts are disabled. (Admittedly a very minor difference.)
Quote:
A SWI for your example would invoke the SWI ISR, not the "lost/missing signal" ISR so the SWI is going to go there anyway!
Sorry, you lost me here.

EDIT: Wrote SEI meant CLI (in code).

C: i = "told you so";

Last Edited: Fri. Nov 2, 2007 - 07:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Triggering an SWI in main code doesn't seem much different from a direct CALL (leaving the linking issue aside, which can also be solved by a function table).

Triggering an SWI from within an ISR on the other hand is different issue, because the current ISR is allowed to complete its operation before any other interrupt (including the SWI) is serviced.

Lets say you bit bang an interface and timing is critical. You detect the EOF pattern in your datastream and now have a valid byte that should get processed immediately, but you have to continue to bit bang the interface to fulfill the protocol.

Triggering an SWI in this scenario looks like the most efficient way of doing that. It also clears the interrupt flag for the original ISR as soon as possible.

Mhh, I wonder if I ever get into a situation where this might turn out to be useful ...

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

But ISRs run with interrupts disabled so you can't use an interrupt in an interrupt. Or were you suggesting to SEI inside the ISR - seems very dangerous

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

clawson wrote:
But ISRs run with interrupts disabled so you can't use an interrupt in an interrupt. Or were you suggesting to SEI inside the ISR - seems very dangerous
I'm not suggesting to enable interrupts inside an ISR (you'll probably never hear that from me ;) ).

Yes interrupts are disabled during an ISR, but the interrupt flag gets set if an interrupt is triggered. And once you return from your current ISR the AVR will service any pending interrupt, which would be the SWI.

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

I'm probably the last person who should be stepping into this, but doesn't the swi just effectively become a 'cli();my_swi();'? So instead of a pin toggle, you do those 2 instructions (cli not needed in isr, though).

Maybe it would save some pushing/popping in an isr if instead of having to call that swi function from the isr, you just do a pin toggle.

Anyway, I have lots of 'features' on my avr's I never use, but at least they are there if I need them.

(I wonder if there is any Atmel application note that makes use of an swi)

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

Curt,

Well so far the idea from Megatorr is the only thing I can see that couldn't be achieved in "normal" C. In that you can effectively queue the next interrupt to be serviced on the RETI from the current ISR. One opcode (I think) will be excuted from the mainline and then it'll immediately go into the "soft interrupt". I'm not sure how else this could be achieved - so there maybe IS some point to it after all (but I still think the existence in the datasheet is simply a case of "oops, a consequence of the hardware implementation is this - so let's dress it up and call it a feature")

Cliff

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

Hey guys - I found a situation where this "feature" proves very convenient. :D

I have an ISR which runs very frequently, so to keep it small and fast, it's coded in asm. From time to time, the ISR has to call another function. This function is fairly complex, with a large case statement so is best left coded in C.

So, to avoid having to push and pop every register on the planet in the small ISR, I have implemented the function as an external interrupt ISR, and simply trigger it from the small ISR with a single sbi instruction. Works a treat. 8)

So essentially, this is what MegaTorr and Cliff were talking about...

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

Colin wrote:
I have an ISR which runs very frequently, so to keep it small and fast, it's coded in asm. From time to time, the ISR has to call another function. This function is fairly complex, with a large case statement so is best left coded in C.

So, to avoid having to push and pop every register on the planet in the small ISR, I have implemented the function as an external interrupt ISR, and simply trigger it from the small ISR with a single sbi instruction. Works a treat. 8)

It works, but I think it could be smaller and faster.
The big ISR is saving every register on the planet.
How you invoke it doesn't change that.
To make it work at all the small ISR must have interrupts enabled.
If the big ISR is only invoked from one place,
the SEI instruction alone "compensates"
for the small size of the SBI instruction.
Simply calling the big ISR would be faster.
An RCALL instruction is the same size as an SBI instruction.
Probably you can ensure that an RCALL works by
putting both ISRs in the same user-defined section.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

Michael,

I fear you've missed the point of the very clever idea Colin was suggesting.

He's exchanging:

ISR() {
 //lots of pushes because of CALL
 invoke_function();
 //lot of pops
}

for

ISR() {
 // few pushes
 invoke ISR2() soft int
 // few pops
}

ISR2() {
 //few pushes
 code of "invoke_function()"
 //few pops
}

I thought this sounded like a really clever idea to call a function from an ISR without the huge push/pop overhead.

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

curtvm wrote:
Maybe it would save some pushing/popping in an isr if instead of having to call that swi function from the isr, you just do a pin toggle.
Turns out to be useful/correct? Will add this to the 'bag of tricks' collection.

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

I can imagine some purposes of software interrupts:
1.If you want to test the fragment of code which is evoked by external interrupt. You can just sent a value into the port and check how the interrupt routine will be working (at once you have disabled interrupts, proper port value, and its shorter than asm call interrupt).
2.Easy implementation of some protocols, for example 1-wire. If you want to send something first you need to pull down the bus for about 1micros. So all you have to do is to pull down appropriate port bit, and for example in cary flag put the bit you want to send. This evokes interrupt, which will take care of the rest of the bus timing. You can do it using out and call, but using software interrupts you just save the space required by call command.
3.Bus monitoring. Your application sends/receives some transmission, and another part of application traces the activity on the port. Its easier to use for that software interrupts, because you can easy turn on/off bus tracing without complicating actual sending routine.
4.Most important difference is when program will work with interrupts disabled, making software interrupt with disabled interrupt flag evokes the interrupt AFTER sei(), not immediately . So we can evoke interrupt routine after finishing more important things.

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

clawson wrote:
Michael,

I fear you've missed the point of the very clever idea Colin was suggesting.

He's exchanging:

ISR() {
 //lots of pushes because of CALL
 invoke_function();
 //lot of pops
}

ISR is written in assembler.
If invoke_function is an ISR, he doesn't need to include the pushes.
For that matter, even if ISR were written in C,
if the compiler were told invoke_function was an ISR,
it needn't include the pushes either.
Colin's mechanism works, but its bigger,
slower and more complicated than necessary.
Quote:
for

ISR() {
 // few pushes
 invoke ISR2() soft int
 // few pops
}

ISR2() {
 //few pushes
 code of "invoke_function()"
 //few pops
}

I thought this sounded like a really clever idea to call a function from an ISR without the huge push/pop overhead.

The SEI to enable interrupts is missing.
"invoke ISR2() soft int" can be replaced
by the instruction "CALL ISR2".
Probably it can be replaced by "RCALL ISR2".

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

Michael,

No no I'm not sure if you are being deliberately obtuse or not but we're talking about C. I added the "// lost of pushes" comment to imply that "behind" the C there will be a lot of pushes/pops generated by the C compiler that you cannot control. This is the inevitable consequence of invoking another function from within an ISR. Colin's idea avoids this happening. Without resorting to implementing the ISRs in assembler (which is not what was being discussed here) I cannot think of another way of achieveing the same result in C alone.

And of course I didn't include the obvious SEI as this was PSUEDO-CODE. Clearly there's no such macro as ISR2() - again this was PSUEDO-CODE to differentiate the two. But I kind of wonder if you are just a troll trying to wind me up or not (in which case I regret rising to your bait and typing this post!)

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

The superfreak needs more ketchup.

clawson wrote:
Michael,

No no I'm not sure if you are being deliberately obtuse or not but we're talking about C. I added the "// lost of pushes" comment to imply that "behind" the C there will be a lot of pushes/pops generated by the C compiler that you cannot control. This is the inevitable consequence of invoking another function from within an ISR. Colin's idea avoids this happening. Without resorting to implementing the ISRs in assembler (which is not what was being discussed here) I cannot think of another way of achieveing the same result in C alone.

Emphasis added:
Colin wrote:
I have an ISR which runs very frequently, so to keep it small and fast, it's coded in asm. From time to time, the ISR has to call another function. This function is fairly complex, with a large case statement so is best left coded in C.

So, to avoid having to push and pop every register on the planet in the small ISR, I have implemented the function as an external interrupt ISR, and simply trigger it from the small ISR with a single sbi instruction. Works a treat.

If both functions are written in C, they can be placed in the same file so optimization can do its job.
Admittedly, optimization might not do its job.
On ISRs, avr-gcc seems to go overboard with register-saving.
In that case, from C, an ISR can be invoked with a single inline assembler instruction.
Declaring ISR2 static and inline might work too.
clawson wrote:
And of course I didn't include the obvious SEI as this was PSUEDO-CODE. Clearly there's no such macro as ISR2() - again this was PSUEDO-CODE to differentiate the two. But I kind of wonder if you are just a troll trying to wind me up or not (in which case I regret rising to your bait and typing this post!)
The mention of SEI was not gratuitous.
SEI and SBI are 1 word each.
CALL is 2 words.
I wanted to make it clear that Colin's method had no memory advantage.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

Michael,

OK, I'll bite - what does the fact that my fictious ISR() might be written in asm rather than C have to do with it? Colin said he was calling a large and complex C function so at the time of writing the ISR he faced exactly the same conundrum as the C compiler of not knowing which registers might be clobbered within that C function so, whether ISR() is implemented in Asm or C either the programmer (Colin) or the C compiler can't "see" what's going on inside invoke_function() so both have no option to protectively save the complete register state BEFORE the call into the unknown function contents.

In the software interrupt case it's when the compiler comes to compile ISR2() that it can "see" what registers are being used WITHIN it and it can put just the necessary protection on the prologue and epilogue of that ISR.

In what sense does this not save PUSH/POPs ?

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

clawson wrote:
No no I'm not sure if you are being deliberately obtuse or not but we're talking about C.

clawson wrote:
Michael,

OK, I'll bite - what does the fact that my fictious ISR() might be written in asm rather than C have to do with it?

Pardon me for thinking it was important.
I seem to have been misinformed.
clawson wrote:
Colin said he was calling a large and complex C function so at the time of writing the ISR he faced exactly the same conundrum as the C compiler of not knowing which registers might be clobbered within that C function so, whether ISR() is implemented in Asm or C either the programmer (Colin) or the C compiler can't "see" what's going on inside invoke_function() so both have no option to protectively save the complete register state BEFORE the call into the unknown function contents.

In the software interrupt case it's when the compiler comes to compile ISR2() that it can "see" what registers are being used WITHIN it and it can put just the necessary protection on the prologue and epilogue of that ISR.

In what sense does this not save PUSH/POPs ?

The saving is accomplished by making the large and complex callee an ISR.
Invoking it as such saves at most a word of flash per invocation.
Calling it directly is faster and less complex and does not require pushing any
registers because calling an ISR does not require pushing any registers.
User-defined code sections could be used to eliminate the flash cost.
From C, calling it directly might be more complex.
Inline assembler might be required to avoid the pushes.
Perhaps this is why it was important that the caller was written in assembly.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

The essential usefulness of interrupts, in my opinion, comes from the fact that they are asynchronous to other program execution. They're not some kind of coverup or conspiracy or afterthought by Atmel, they are a legitimate and useful feature, supported in many processor families.

This is true of external and hardware generated interrupts as well as those generated by software.

If your program needs to execute a task asynchronously - and the task isn't supported directly by hardware that generates an interrupt, then you will need a software interrupt.

You might say "but I can just use a periodic timer interrupt to run my task." In this case, your software is no longer event driven (you are doing a form of polling), which might be a serious issue for you, especially if you are doing low power design.

If you could imagine a FIFO into which your task would enqueue data, perhaps received from a serial port, then that data would be dequeued from the fifo, but at a rate slower than the input (maybe you're relaying the data out to a very slow network) -
The question is: how could you continue to empty the FIFO while still enqueueing data, only using a single task? Your FIFO would shortly fill up, and your task would be stuck in a polling loop that would require some asynchronous signal to break out of. Since you most likely don't have a preemptive OS, this is where a software interrupt would come in - you would have a separate task, triggered initially when data was enqueued, and which ran, triggered by an SWI for every element in the FIFO, until emptied.

I agree that for most simple stuff, using a software interrupt is not necessary, but there are design problems where they are the only effective solution.

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

If you want a software interrupt, simple enable the SPM_RDY interrupt.

During program execution this interrupt fires instantly, as it was enabled.

Peter

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

skeeve wrote:

SEI and SBI are 1 word each.
CALL is 2 words.
I wanted to make it clear that Colin's method had no memory advantage.

Well, I didn't claim a memory advantage! Of course, if one codes an application entirely in assembler, you can always make it smaller, faster or whatever. Optimisation means different things in different circumstances, not necessarily just smaller code.

My point was that to call a C function from within an ISR (whether written in C or ASM) requires that the ISR pushes and pops everything, because the compiler (or programmer) doesn't know what registers will be used by the C function. This carries a time penalty which is undesirable in a frequently running ISR. The key point is that the C function is called infrequently. My technique results in a time advantage in this circumstance, not a memory advantage. :wink:

Cheers,

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

Colin wrote:
My point was that to call a C function from within an ISR (whether written in C or ASM) requires that the ISR pushes and pops everything, because the compiler (or programmer) doesn't know what registers will be used by the C function. This carries a time penalty which is undesirable in a frequently running ISR. The key point is that the C function is called infrequently. My technique results in a time advantage in this circumstance, not a memory advantage.
My point was that there is no time advantage to a software interrupt over simply calling the handler. A call to an interrupt handler declared as such should compile to a single instruction. If it does not, the inline assembler isn't all that hard. Also, if one is just going to call an interrupt handler, there need not be a corresponding interrupt:
ISR(fred)  // this will get a warning
{
// something you rarely need
}

ISR(timer0_vect)
{
// whatever you need
if(rare_event) fred();
// whatever else you need
}

This works even if you need all your pin change interrupt handlers for other things.

If fred() compiles to more than one instruction, replace it with asm volatile ("call fred").

With the software interrupt, you might also need to inhibit optimization by making some variables volatile.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

Quote:

A call to an interrupt handler declared as such should compile to a single instruction

Yes but the "time penalty" is all the push/pop's around it that are/aren't needed.

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

Emphasis added

clawson wrote:
Quote:

A call to an interrupt handler declared as such should compile to a single instruction

Yes but the "time penalty" is all the push/pop's around it that are/aren't needed.
Are you being deliberately obtuse?
There should be no push/pops around it.
They are not needed.
The push/pops needed are in the interrupt handler.
If the call compiles to more than one instruction,
it should be replaced by inline assembler.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

Michael,

Then sorry to labour the point but I'm obviously missing some point you are trying to make. Here's a piece of test code:

#include  
#include  

//#define MICHAELS_SUGGESTION

#ifdef MICHAELS_SUGGESTION
void my_fn(void) __attribute__ ((signal, used, externally_visible));
#endif
void my_fn(void) {
 uint32_t fred;

 fred = PINA + ((uint32_t)PINB << 24);
 fred *= 3;
 fred >>= 16;
 PORTB = fred & 0xFF;
 PORTA = fred >> 24;
}

ISR(INT0_vect) {
  my_fn();
}

int main(void) {
	while(1);
	return 0;
}

Now if I don't define MICHAELS_SUGGESTION then the code generated (built -Os) is:

void my_fn(void) {
  92:	89 b3       	in	r24, 0x19	; 25
  94:	26 b3       	in	r18, 0x16	; 22
  96:	33 27       	eor	r19, r19
  98:	44 27       	eor	r20, r20
  9a:	55 27       	eor	r21, r21
  9c:	52 2f       	mov	r21, r18
  9e:	44 27       	eor	r20, r20
  a0:	33 27       	eor	r19, r19
  a2:	22 27       	eor	r18, r18
  a4:	28 0f       	add	r18, r24
  a6:	31 1d       	adc	r19, r1
  a8:	41 1d       	adc	r20, r1
  aa:	51 1d       	adc	r21, r1
 uint32_t fred;

 fred = PINA + ((uint32_t)PINB << 24);
 fred *= 3;
  ac:	da 01       	movw	r26, r20
  ae:	c9 01       	movw	r24, r18
  b0:	88 0f       	add	r24, r24
  b2:	99 1f       	adc	r25, r25
  b4:	aa 1f       	adc	r26, r26
  b6:	bb 1f       	adc	r27, r27
  b8:	82 0f       	add	r24, r18
  ba:	93 1f       	adc	r25, r19
  bc:	a4 1f       	adc	r26, r20
  be:	b5 1f       	adc	r27, r21
 fred >>= 16;
  c0:	cd 01       	movw	r24, r26
  c2:	aa 27       	eor	r26, r26
  c4:	bb 27       	eor	r27, r27
 PORTB = fred & 0xFF;
  c6:	88 bb       	out	0x18, r24	; 24
 PORTA = fred >> 24;
  c8:	8b 2f       	mov	r24, r27
  ca:	99 27       	eor	r25, r25
  cc:	aa 27       	eor	r26, r26
  ce:	bb 27       	eor	r27, r27
  d0:	8b bb       	out	0x1b, r24	; 27
  d2:	08 95       	ret

000000d4 <__vector_1>:
}

ISR(INT0_vect) {
  d4:	1f 92       	push	r1
  d6:	0f 92       	push	r0
  d8:	0f b6       	in	r0, 0x3f	; 63
  da:	0f 92       	push	r0
  dc:	11 24       	eor	r1, r1
  de:	2f 93       	push	r18
  e0:	3f 93       	push	r19
  e2:	4f 93       	push	r20
  e4:	5f 93       	push	r21
  e6:	6f 93       	push	r22
  e8:	7f 93       	push	r23
  ea:	8f 93       	push	r24
  ec:	9f 93       	push	r25
  ee:	af 93       	push	r26
  f0:	bf 93       	push	r27
  f2:	ef 93       	push	r30
  f4:	ff 93       	push	r31
  my_fn();
  f6:	cd df       	rcall	.-102    	; 0x92 
  f8:	ff 91       	pop	r31
  fa:	ef 91       	pop	r30
  fc:	bf 91       	pop	r27
  fe:	af 91       	pop	r26
 100:	9f 91       	pop	r25
 102:	8f 91       	pop	r24
 104:	7f 91       	pop	r23
 106:	6f 91       	pop	r22
 108:	5f 91       	pop	r21
 10a:	4f 91       	pop	r20
 10c:	3f 91       	pop	r19
 10e:	2f 91       	pop	r18
 110:	0f 90       	pop	r0
 112:	0f be       	out	0x3f, r0	; 63
 114:	0f 90       	pop	r0
 116:	1f 90       	pop	r1
 118:	18 95       	reti

0000011a 
: } int main(void) { 11a: ff cf rjmp .-2 ; 0x11a

while if I make the #define (so that my_fn is declared as an ISR in the same way that the ISR() macro would define an ISR) I get:

void my_fn(void) {
  92:	1f 92       	push	r1
  94:	0f 92       	push	r0
  96:	0f b6       	in	r0, 0x3f	; 63
  98:	0f 92       	push	r0
  9a:	11 24       	eor	r1, r1
  9c:	2f 93       	push	r18
  9e:	3f 93       	push	r19
  a0:	4f 93       	push	r20
  a2:	5f 93       	push	r21
  a4:	8f 93       	push	r24
  a6:	9f 93       	push	r25
  a8:	af 93       	push	r26
  aa:	bf 93       	push	r27
 uint32_t fred;

 fred = PINA + ((uint32_t)PINB << 24);
  ac:	89 b3       	in	r24, 0x19	; 25
  ae:	26 b3       	in	r18, 0x16	; 22
  b0:	33 27       	eor	r19, r19
  b2:	44 27       	eor	r20, r20
  b4:	55 27       	eor	r21, r21
  b6:	52 2f       	mov	r21, r18
  b8:	44 27       	eor	r20, r20
  ba:	33 27       	eor	r19, r19
  bc:	22 27       	eor	r18, r18
  be:	28 0f       	add	r18, r24
  c0:	31 1d       	adc	r19, r1
  c2:	41 1d       	adc	r20, r1
  c4:	51 1d       	adc	r21, r1
 fred *= 3;
  c6:	da 01       	movw	r26, r20
  c8:	c9 01       	movw	r24, r18
  ca:	88 0f       	add	r24, r24
  cc:	99 1f       	adc	r25, r25
  ce:	aa 1f       	adc	r26, r26
  d0:	bb 1f       	adc	r27, r27
  d2:	82 0f       	add	r24, r18
  d4:	93 1f       	adc	r25, r19
  d6:	a4 1f       	adc	r26, r20
  d8:	b5 1f       	adc	r27, r21
 fred >>= 16;
  da:	cd 01       	movw	r24, r26
  dc:	aa 27       	eor	r26, r26
  de:	bb 27       	eor	r27, r27
 PORTB = fred & 0xFF;
  e0:	88 bb       	out	0x18, r24	; 24
 PORTA = fred >> 24;
  e2:	8b 2f       	mov	r24, r27
  e4:	99 27       	eor	r25, r25
  e6:	aa 27       	eor	r26, r26
  e8:	bb 27       	eor	r27, r27
  ea:	8b bb       	out	0x1b, r24	; 27
  ec:	bf 91       	pop	r27
  ee:	af 91       	pop	r26
  f0:	9f 91       	pop	r25
  f2:	8f 91       	pop	r24
  f4:	5f 91       	pop	r21
  f6:	4f 91       	pop	r20
  f8:	3f 91       	pop	r19
  fa:	2f 91       	pop	r18
  fc:	0f 90       	pop	r0
  fe:	0f be       	out	0x3f, r0	; 63
 100:	0f 90       	pop	r0
 102:	1f 90       	pop	r1
 104:	18 95       	reti

00000106 <__vector_1>:
}

ISR(INT0_vect) {
 106:	1f 92       	push	r1
 108:	0f 92       	push	r0
 10a:	0f b6       	in	r0, 0x3f	; 63
 10c:	0f 92       	push	r0
 10e:	11 24       	eor	r1, r1
 110:	2f 93       	push	r18
 112:	3f 93       	push	r19
 114:	4f 93       	push	r20
 116:	5f 93       	push	r21
 118:	6f 93       	push	r22
 11a:	7f 93       	push	r23
 11c:	8f 93       	push	r24
 11e:	9f 93       	push	r25
 120:	af 93       	push	r26
 122:	bf 93       	push	r27
 124:	ef 93       	push	r30
 126:	ff 93       	push	r31
  my_fn();
 128:	b4 df       	rcall	.-152    	; 0x92 
 12a:	ff 91       	pop	r31
 12c:	ef 91       	pop	r30
 12e:	bf 91       	pop	r27
 130:	af 91       	pop	r26
 132:	9f 91       	pop	r25
 134:	8f 91       	pop	r24
 136:	7f 91       	pop	r23
 138:	6f 91       	pop	r22
 13a:	5f 91       	pop	r21
 13c:	4f 91       	pop	r20
 13e:	3f 91       	pop	r19
 140:	2f 91       	pop	r18
 142:	0f 90       	pop	r0
 144:	0f be       	out	0x3f, r0	; 63
 146:	0f 90       	pop	r0
 148:	1f 90       	pop	r1
 14a:	18 95       	reti

0000014c 
: } int main(void) { 14c: ff cf rjmp .-2 ; 0x14c

If I now change it to Colin's ISR invoked from ISR (except that I'm not bothering with the code to enable the soft int):

#include  
#include  

ISR(INT1_vect) {
 uint32_t fred;

 fred = PINA + ((uint32_t)PINB << 24);
 fred *= 3;
 fred /= 22;
 PORTB = fred & 0xFF;
 PORTA = fred >> 24;
}

ISR(INT0_vect) {
  sei();
  PORTD |= (1<<PD3);
}

int main(void) {
	while(1);
	return 0;
}

I get:

00000092 <__vector_2>:
#include  
#include  

ISR(INT1_vect) {
  92:	1f 92       	push	r1
  94:	0f 92       	push	r0
  96:	0f b6       	in	r0, 0x3f	; 63
  98:	0f 92       	push	r0
  9a:	11 24       	eor	r1, r1
  9c:	2f 93       	push	r18
  9e:	3f 93       	push	r19
  a0:	4f 93       	push	r20
  a2:	5f 93       	push	r21
  a4:	8f 93       	push	r24
  a6:	9f 93       	push	r25
  a8:	af 93       	push	r26
  aa:	bf 93       	push	r27
 uint32_t fred;

 fred = PINA + ((uint32_t)PINB << 24);
  ac:	89 b3       	in	r24, 0x19	; 25
  ae:	26 b3       	in	r18, 0x16	; 22
  b0:	33 27       	eor	r19, r19
  b2:	44 27       	eor	r20, r20
  b4:	55 27       	eor	r21, r21
  b6:	52 2f       	mov	r21, r18
  b8:	44 27       	eor	r20, r20
  ba:	33 27       	eor	r19, r19
  bc:	22 27       	eor	r18, r18
  be:	28 0f       	add	r18, r24
  c0:	31 1d       	adc	r19, r1
  c2:	41 1d       	adc	r20, r1
  c4:	51 1d       	adc	r21, r1
 fred *= 3;
  c6:	da 01       	movw	r26, r20
  c8:	c9 01       	movw	r24, r18
  ca:	88 0f       	add	r24, r24
  cc:	99 1f       	adc	r25, r25
  ce:	aa 1f       	adc	r26, r26
  d0:	bb 1f       	adc	r27, r27
  d2:	82 0f       	add	r24, r18
  d4:	93 1f       	adc	r25, r19
  d6:	a4 1f       	adc	r26, r20
  d8:	b5 1f       	adc	r27, r21
 fred >>= 16;
  da:	cd 01       	movw	r24, r26
  dc:	aa 27       	eor	r26, r26
  de:	bb 27       	eor	r27, r27
 PORTB = fred & 0xFF;
  e0:	88 bb       	out	0x18, r24	; 24
 PORTA = fred >> 24;
  e2:	8b 2f       	mov	r24, r27
  e4:	99 27       	eor	r25, r25
  e6:	aa 27       	eor	r26, r26
  e8:	bb 27       	eor	r27, r27
  ea:	8b bb       	out	0x1b, r24	; 27
  ec:	bf 91       	pop	r27
  ee:	af 91       	pop	r26
  f0:	9f 91       	pop	r25
  f2:	8f 91       	pop	r24
  f4:	5f 91       	pop	r21
  f6:	4f 91       	pop	r20
  f8:	3f 91       	pop	r19
  fa:	2f 91       	pop	r18
  fc:	0f 90       	pop	r0
  fe:	0f be       	out	0x3f, r0	; 63
 100:	0f 90       	pop	r0
 102:	1f 90       	pop	r1
 104:	18 95       	reti

00000106 <__vector_1>:
}

ISR(INT0_vect) {
 106:	1f 92       	push	r1
 108:	0f 92       	push	r0
 10a:	0f b6       	in	r0, 0x3f	; 63
 10c:	0f 92       	push	r0
 10e:	11 24       	eor	r1, r1
  sei();
 110:	78 94       	sei
  PORTD |= (1<<PD3);
 112:	93 9a       	sbi	0x12, 3	; 18
 114:	0f 90       	pop	r0
 116:	0f be       	out	0x3f, r0	; 63
 118:	0f 90       	pop	r0
 11a:	1f 90       	pop	r1
 11c:	18 95       	reti

0000011e 
: } int main(void) { 11e: ff cf rjmp .-2 ; 0x11e

Unless my eyes deceive me the latter is smaller/faster than either of the first two. Now what point is it that you are making that I'm clearly missing?

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

For those of us who have seen the compiler output because we wondered why our byte count was so bloated, it was clear what you were talking about -

It looks like the misunderstanding has to do with the word "call". I think Michael was trying to say that there would be no pushes and pops around the CALL to the interrupt handler, not the handler itself.

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

clawson wrote:
Then sorry to labour the point but I'm obviously missing some point you are trying to make.
The compiler seems to be missing it also.
Even when it has the necessary information,
it tends to go overboard with the pushes and pops in interrupt handlers.
Try defining MICHAELS_SUGGESTION and replacing my_fn() with asm volatile ("CALL my_fn").
The compiler shouldn't push and pop any more registers than it does for an empty interrupt handler.
Quote:
Here's a piece of test code:

#include  
#include  

//#define MICHAELS_SUGGESTION

#ifdef MICHAELS_SUGGESTION
void my_fn(void) __attribute__ ((signal, used, externally_visible));
#endif
void my_fn(void) {
 uint32_t fred;

 fred = PINA + ((uint32_t)PINB << 24);
 fred *= 3;
 fred >>= 16;
 PORTB = fred & 0xFF;
 PORTA = fred >> 24;
}

ISR(INT0_vect) {
  my_fn();
}

int main(void) {
	while(1);
	return 0;
}

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

Here is some more test code.

#include 
#include 

int dest, src;

ISR(PCINT0_vect)
{
dest=src;
}

ISR(fred)
{
dest=src;
}


ISR(TIMER0_COMPA_vect)
{
fred();
}

ISR(TIMER0_COMPB_vect)
{
asm volatile ("CALL fred");
}

ISR(TIMER2_COMPA_vect)
{
// should compile SBI, RETI
PORTB|=0x01;
}

ISR(TIMER2_COMPB_vect)
{
// should compile to RETI
}

int main()
{
while(1) {}
}

Here is the result. See interleaved comments.

isr_test.elf:     file format elf32-avr

Sections:
Idx Name          Size      VMA       LMA       File off  Algn  Flags
  0 .text         0000018c  00000000  00000000  00000074  2**1  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .bss          00000004  00800100  00800100  00000200  2**0  ALLOC
  2 .stab         00000378  00000000  00000000  00000200  2**2  CONTENTS, READONLY, DEBUGGING
  3 .stabstr      00000071  00000000  00000000  00000578  2**0  CONTENTS, READONLY, DEBUGGING
  4 .debug_aranges 00000020  00000000  00000000  000005e9  2**0  CONTENTS, READONLY, DEBUGGING
  5 .debug_pubnames 00000082  00000000  00000000  00000609  2**0  CONTENTS, READONLY, DEBUGGING
  6 .debug_info   00000111  00000000  00000000  0000068b  2**0  CONTENTS, READONLY, DEBUGGING
  7 .debug_abbrev 00000076  00000000  00000000  0000079c  2**0  CONTENTS, READONLY, DEBUGGING
  8 .debug_line   000000f4  00000000  00000000  00000812  2**0  CONTENTS, READONLY, DEBUGGING
  9 .debug_frame  00000080  00000000  00000000  00000908  2**2  CONTENTS, READONLY, DEBUGGING
 10 .debug_str    000000c1  00000000  00000000  00000988  2**0  CONTENTS, READONLY, DEBUGGING
Disassembly of section .text:

00000000 <__vectors>:
   0:	0c 94 34 00 	jmp	0x68	; 0x68 <__ctors_end>
   4:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
   8:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
   c:	0c 94 53 00 	jmp	0xa6	; 0xa6 <__vector_3>
  10:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  14:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  18:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  1c:	0c 94 af 00 	jmp	0x15e	; 0x15e <__vector_7>
  20:	0c 94 ba 00 	jmp	0x174	; 0x174 <__vector_8>
  24:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  28:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  2c:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  30:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  34:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  38:	0c 94 7f 00 	jmp	0xfe	; 0xfe <__vector_14>
  3c:	0c 94 a3 00 	jmp	0x146	; 0x146 <__vector_15>
  40:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  44:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  48:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  4c:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  50:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  54:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  58:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  5c:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  60:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>
  64:	0c 94 51 00 	jmp	0xa2	; 0xa2 <__bad_interrupt>

00000068 <__ctors_end>:
  68:	11 24       	eor	r1, r1
  6a:	1f be       	out	0x3f, r1	; 63
  6c:	cf ef       	ldi	r28, 0xFF	; 255
  6e:	d4 e0       	ldi	r29, 0x04	; 4
  70:	de bf       	out	0x3e, r29	; 62
  72:	cd bf       	out	0x3d, r28	; 61

00000074 <__do_copy_data>:
  74:	11 e0       	ldi	r17, 0x01	; 1
  76:	a0 e0       	ldi	r26, 0x00	; 0
  78:	b1 e0       	ldi	r27, 0x01	; 1
  7a:	ec e8       	ldi	r30, 0x8C	; 140
  7c:	f1 e0       	ldi	r31, 0x01	; 1
  7e:	02 c0       	rjmp	.+4      	; 0x84 <.do_copy_data_start>

00000080 <.do_copy_data_loop>:
  80:	05 90       	lpm	r0, Z+
  82:	0d 92       	st	X+, r0

00000084 <.do_copy_data_start>:
  84:	a0 30       	cpi	r26, 0x00	; 0
  86:	b1 07       	cpc	r27, r17
  88:	d9 f7       	brne	.-10     	; 0x80 <.do_copy_data_loop>

0000008a <__do_clear_bss>:
  8a:	11 e0       	ldi	r17, 0x01	; 1
  8c:	a0 e0       	ldi	r26, 0x00	; 0
  8e:	b1 e0       	ldi	r27, 0x01	; 1
  90:	01 c0       	rjmp	.+2      	; 0x94 <.do_clear_bss_start>

00000092 <.do_clear_bss_loop>:
  92:	1d 92       	st	X+, r1

00000094 <.do_clear_bss_start>:
  94:	a4 30       	cpi	r26, 0x04	; 4
  96:	b1 07       	cpc	r27, r17
  98:	e1 f7       	brne	.-8      	; 0x92 <.do_clear_bss_loop>
  9a:	0e 94 c4 00 	call	0x188	; 0x188 
9e: 0c 94 c5 00 jmp 0x18a ; 0x18a <_exit> 000000a2 <__bad_interrupt>: a2: 0c 94 00 00 jmp 0 ; 0x0 <__vectors> 000000a6 <__vector_3>: int dest, src;

The first two only need one register, but avr-gcc saves more.

ISR(PCINT0_vect)
{
  a6:	1f 92       	push	r1
  a8:	0f 92       	push	r0
  aa:	0f b6       	in	r0, 0x3f	; 63
  ac:	0f 92       	push	r0
  ae:	11 24       	eor	r1, r1
  b0:	8f 93       	push	r24
  b2:	9f 93       	push	r25
dest=src;
  b4:	80 91 02 01 	lds	r24, 0x0102
  b8:	90 91 03 01 	lds	r25, 0x0103
  bc:	90 93 01 01 	sts	0x0101, r25
  c0:	80 93 00 01 	sts	0x0100, r24
  c4:	9f 91       	pop	r25
  c6:	8f 91       	pop	r24
  c8:	0f 90       	pop	r0
  ca:	0f be       	out	0x3f, r0	; 63
  cc:	0f 90       	pop	r0
  ce:	1f 90       	pop	r1
  d0:	18 95       	reti

000000d2 :
}

ISR(fred)
{
  d2:	1f 92       	push	r1
  d4:	0f 92       	push	r0
  d6:	0f b6       	in	r0, 0x3f	; 63
  d8:	0f 92       	push	r0
  da:	11 24       	eor	r1, r1
  dc:	8f 93       	push	r24
  de:	9f 93       	push	r25
dest=src;
  e0:	80 91 02 01 	lds	r24, 0x0102
  e4:	90 91 03 01 	lds	r25, 0x0103
  e8:	90 93 01 01 	sts	0x0101, r25
  ec:	80 93 00 01 	sts	0x0100, r24
  f0:	9f 91       	pop	r25
  f2:	8f 91       	pop	r24
  f4:	0f 90       	pop	r0
  f6:	0f be       	out	0x3f, r0	; 63
  f8:	0f 90       	pop	r0
  fa:	1f 90       	pop	r1
  fc:	18 95       	reti

000000fe <__vector_14>:
}

avr-gcc has the machine code for fred, but it saves more registers than necessary.
That is why I suggested inline assembler.

ISR(TIMER0_COMPA_vect)
{
  fe:	1f 92       	push	r1
 100:	0f 92       	push	r0
 102:	0f b6       	in	r0, 0x3f	; 63
 104:	0f 92       	push	r0
 106:	11 24       	eor	r1, r1
 108:	2f 93       	push	r18
 10a:	3f 93       	push	r19
 10c:	4f 93       	push	r20
 10e:	5f 93       	push	r21
 110:	6f 93       	push	r22
 112:	7f 93       	push	r23
 114:	8f 93       	push	r24
 116:	9f 93       	push	r25
 118:	af 93       	push	r26
 11a:	bf 93       	push	r27
 11c:	ef 93       	push	r30
 11e:	ff 93       	push	r31
fred();
 120:	0e 94 69 00 	call	0xd2	; 0xd2 
 124:	ff 91       	pop	r31
 126:	ef 91       	pop	r30
 128:	bf 91       	pop	r27
 12a:	af 91       	pop	r26
 12c:	9f 91       	pop	r25
 12e:	8f 91       	pop	r24
 130:	7f 91       	pop	r23
 132:	6f 91       	pop	r22
 134:	5f 91       	pop	r21
 136:	4f 91       	pop	r20
 138:	3f 91       	pop	r19
 13a:	2f 91       	pop	r18
 13c:	0f 90       	pop	r0
 13e:	0f be       	out	0x3f, r0	; 63
 140:	0f 90       	pop	r0
 142:	1f 90       	pop	r1
 144:	18 95       	reti

00000146 <__vector_15>:
}

With inline assembler, avr-gcc still saves more registers than necessary,
but the boilerplate is down to that for an empty interrupt handler.

ISR(TIMER0_COMPB_vect)
{
 146:	1f 92       	push	r1
 148:	0f 92       	push	r0
 14a:	0f b6       	in	r0, 0x3f	; 63
 14c:	0f 92       	push	r0
 14e:	11 24       	eor	r1, r1
asm volatile ("CALL fred");
 150:	0e 94 69 00 	call	0xd2	; 0xd2 
 154:	0f 90       	pop	r0
 156:	0f be       	out	0x3f, r0	; 63
 158:	0f 90       	pop	r0
 15a:	1f 90       	pop	r1
 15c:	18 95       	reti

0000015e <__vector_7>:
}

Slower, but one word smaller than the previous.
Had I used an RCALL, it wouldn't be smaller either.

ISR(TIMER2_COMPA_vect)
{
 15e:	1f 92       	push	r1
 160:	0f 92       	push	r0
 162:	0f b6       	in	r0, 0x3f	; 63
 164:	0f 92       	push	r0
 166:	11 24       	eor	r1, r1
// should compile SBI, RETI
PORTB|=0x01;
 168:	28 9a       	sbi	0x05, 0	; 5
 16a:	0f 90       	pop	r0
 16c:	0f be       	out	0x3f, r0	; 63
 16e:	0f 90       	pop	r0
 170:	1f 90       	pop	r1
 172:	18 95       	reti

00000174 <__vector_8>:
}

An empty interrupt handler really doesn't need any saving and restoring.

ISR(TIMER2_COMPB_vect)
{
 174:	1f 92       	push	r1
 176:	0f 92       	push	r0
 178:	0f b6       	in	r0, 0x3f	; 63
 17a:	0f 92       	push	r0
 17c:	11 24       	eor	r1, r1
 17e:	0f 90       	pop	r0
 180:	0f be       	out	0x3f, r0	; 63
 182:	0f 90       	pop	r0
 184:	1f 90       	pop	r1
 186:	18 95       	reti

00000188 
: // should compile to RETI } int main() { 188: ff cf rjmp .-2 ; 0x188
0000018a <_exit>: 18a: ff cf rjmp .-2 ; 0x18a <_exit>

fred has the additional advantage of not using up an interrupt.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

Quote:
fred has the additional advantage of not using up an interrupt.
Not that I understand any of this anymore, but making 'fred' a normal function that takes on the properties of an interrupt, you have a few things to consider. 'fred' now has no priority over any interrupt, which may or may not be important (but assume it is since we are talking about software 'interrupts' and not software 'functions'). And since 'fred' is now an 'interrupt' function, you get a 'reti' at the end. So if I wanted 'fred' to run from an interrupt (call), you will get irq's turned back on at the end of 'fred'. I haven't thought it all through, but I suspect that may be 'dangerous' unless its carefully thought out. Having that 'reti' also prevents using 'fred' inside any code that has irq's turned off (although a cli() right after the call would take care of it).

Unless I'm mistaken (odds not in my favor), making 'fred' into an INT0/INT1/PCINTn isr, and simply toggle a pin to trigger it, would take care of the above problems. 'fred' could be 'triggered' anywhere, without having to worry about where it was triggered from.

I think.

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

curtvm wrote:
Quote:
fred has the additional advantage of not using up an interrupt.
Not that I understand any of this anymore, but making 'fred' a normal function that takes on the properties of an interrupt, you have a few things to consider. 'fred' now has no priority over any interrupt, which may or may not be important (but assume it is since we are talking about software 'interrupts' and not software 'functions'). And since 'fred' is now an 'interrupt' function, you get a 'reti' at the end. So if I wanted 'fred' to run from an interrupt (call), you will get irq's turned back on at the end of 'fred'. I haven't thought it all through, but I suspect that may be 'dangerous' unless its carefully thought out. Having that 'reti' also prevents using 'fred' inside any code that has irq's turned off (although a cli() right after the call would take care of it).
The original proposal was for an efficient replacement for a call of a function from an interrupt-handler.
Normally a function call is taken immediately.
To get the same effect with a software interrupt,
interrupts have to be enabled anyway.
Even then, an instruction or two will be executed before the interrupt,
more if an earlier or higher priority interrupt gets in the way.
This is a possible problem one doesn't have with an ordinary call of an ordinary function.
If following CALL fred by CLI works,
it is still faster than the software interrupt method,
though it isn't smaller.
If it doesn't work, one could edit the intermediate assembler.
An automagically generated sed script wouldn't be too hard,
but if would be more complicaed than just adding CLI.
I think that following PORTB|=1 by CLI would not work as desired
because the CLI would be executed before B's interrupt handler.
Quote:
Unless I'm mistaken (odds not in my favor), making 'fred' into an INT0/INT1/PCINTn isr, and simply toggle a pin to trigger it, would take care of the above problems. 'fred' could be 'triggered' anywhere, without having to worry about where it was triggered from.
In my work, there is no available INT0/INT1/PCINTn.
They are alredy in use.
I might be able to do something tricky with a timer.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

@Hangaround nailed it.

 

My serial buffering ISR detects end-of-packet, and needs to tell my code (which is 100% flat-out doing slow screen IO) to stop what it's doing and process the packet ASAP - which can't be done inside the ISR itself because the process takes way too much time; software interrupt lets my hardware ISR end so it's free to handle the next byte this way...

cndgavruser

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

Eh? Surely you just move execution from one ISR to another? You still take as much time within ISRs to handle it. (in fact slightly more as there's the overhead of getting from one ISR to another).

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

I apologize for digging up such an ancient thread, but I ran into a similar problem--which seems to have a similar answer of calling an ISR to run slow (non blocking) code from within an ISR that needs to be fast/small....

Anyone who has disassembled code with an ISR with a simple c call of another function has seen this push/pop issue and besides we need to get those global ints re-enabled as fast as possible....

 

clawson wrote:

Eh? Surely you just move execution from one ISR to another? You still take as much time within ISRs to handle it. (in fact slightly more as there's the overhead of getting from one ISR to another).

 

Ah but you may be forgetting about the ISR_NOBLOCK for the second ISR so that the second isr can be interrupted while your larger bits of code can still be triggered by the original ISR....

Would this allow us to have our "cake" and eat it too?

 

I am going to try this solution with the 

danni wrote:
SPM_RDY
ISR and see if it works.  Thanks all for the great reading!

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

You can easily run out of ram with nested interrupts. You have been warned! The general solution is to use a RTOS.

Pages