AVR Freaks

AVR GCC forum - How to avoid PUSH and POP in ISR?

S-Sohn - Feb 22, 2005 - 01:35 PM
Post subject: How to avoid PUSH and POP in ISR?
Hi,

I have programmed a AT90S2313. The µC polls some inputs. To get a timing context I have done this in a timer ISR. Most work is done in the ISR. All other functions only handle the receiving of data via UART (2400 Baud) and storing of data in the EEPROM without using any ISR.

Now my problem: I use some variables in the ISR. All used registers are pushed to the stack on calling the ISR (like described in many FAQ's). My ISR is terminated in less than 160 cycles (testet with the AVR Studio Simulator). But the additional PUSH's and POP's increase the ISR duration to almost 200 cycles. I think this is big lost of performance.

So what have I done?
I put all my variables in GLOBAL REGISTER VARIABLES Mad !!!
And I use each register only in the ISR or outside the ISR.
So at the end I had only PUSH/POP for SREG, r0, r1.
I know some of you would like to forbid me to ever hold an AVR µC in my hands again.
I know that my solution is a very bad style - And I'd like to do it better.

Maybe you can show me a better way!

Thanks for each good idea!
Sebastian
stevech - Feb 24, 2005 - 04:52 AM
Post subject: RE: How to avoid PUSH and POP in ISR?
The variables you use in the ISR which do need to have values persist for use in the next call to the ISR, or variables shared between the ISR and non-ISR - have to be local statics to the ISR or global to the C module.

Other variables could be manipulated off of a stack frame by declaring them in the ISR (not static).

Generally, to speed up an ISR, you have to change the algorithm or technique assuming your code is not poorlyl written. 160 cycles is not many.

If you have an interrupt every 200 or so CPU clock cycles, I suggest you have the wrong design approach.
S-Sohn - Feb 25, 2005 - 12:41 PM
Post subject: RE: How to avoid PUSH and POP in ISR?
Hi,
Thanks for your answer!
I think I shall give you more background:

All ISR data are stored in a struct located in the SRAM. I have programmed the
AT90S2313 to bahave as a 6 channel non-inverting monoflop with selectable
trigger source (High / LOW) for the inputs and configurable time delay for the
outputs. The device reaches 40000 Samples per second @ 8MHz. And yes that
means one interrupt every 200 clock cycles.

All non ISR code handles receiving configuration data via UART. The baudrate
is with 2400Baud slow (max. 1 byte in 4.17ms). The ISR needs in the worst case
185 cycles (including PUSH, POP, etc.). That means I have 2500 clock cycles left
for handling a single byte received via UART. And that's much more than I ever
would need.

When I started programming the device I set the sample rate to 20000. My ISR was
to slow for higher sample rates because of so many PUSH / POP instructions and
unefficiency coding. So I did my best to increase the sample rate.
I found out that the compiler choosed the same registers for the ISR variables
and the non ISR variables. And those registers must be rescued before they're
overwritten in the ISR. So I thought I shall prevent that the compiler uses
the same registers inside and outside the ISR.

I ended up with using global register variables. But that was not enough.
Many times the compiler still uses other (temporary) registers. Example:

if ((input & mask) == triggerLevel)
{
... // Do something
}

The choosen temporary register by the compiler was also used outside
the ISR and was therefore pushed on the stack. So I declared another global
register variable as a temporary storage:

temp = input;
temp &= mask;

if (temp == triggerlevel)
{
... // Do something
}

The global register variables are only used in the ISR. Because of they're
declared global, the compiler can't use these registers outside the ISR.
So they're not pushed on the stack on calling the ISR.

The device works fine but the compiler puts out many warnings because I
offend completly against the compiler strategy to keep global variables in
SRAM.

SO I AM ASKING FOR A BETTER SOLUTION OF MY PROJECT WHICH WORKS
CONFORM WITH ANY COMPILER STRATEGY!

To put the reception of data via UART into the timer ISR won't work because there
might be only 15 clock cycles left until the next timer interrupt.

To only set a flag in the timer ISR won't work because I'd have to react
in less than 15 clock cycles on a set flag.

I read in the AVR GCC forum about empty interrupt handler functions. I think
here I could also avoid pushing the registers r0 and r1 which are never
used in my ISR (they're always rescued as default by the compiler).

I hope you see that my problem is not that my device won't work but that I'm
unhappy how I have solved the challenge to get a higher performance and that a
better solution isn't so easy (or i just can't see it).

Regards
Sebastian
feralbeagle - Feb 26, 2005 - 12:30 AM
Post subject: RE: How to avoid PUSH and POP in ISR?
Wow, If it's that time critical, it looks like you'll be doing some assembly programming. That should speed things up a bit. It may be easier than working around the compiler too. I have not mixed assembly and C in this way so I can't offer a concrete solution but it's worth looking at. I have programmed some time critical application in assembly only and was very please with the results.

Actually, if your program is really that simple, just write the whole thing is assembly. A lot of folks write only in assembly for small micros like the AT90S2313.
S-Sohn - Feb 26, 2005 - 06:40 AM
Post subject: RE: How to avoid PUSH and POP in ISR?
Yes, maybe it would work in assembly better but I was often very surprised how efficiency the gcc compiler works. Many times I discovered that if I had programmed it by myself in assembler it won't be better than the gcc. I don't have much experience in assembler, too.
I know the AVR instruction set (how else shall I be able to rate the gcc) but that doesn't make me a good assembler programmer. I have learned, if I know how the gcc works I can get very efficiency code.
But another thing is what if I have to program a much more complex project. Programming all in assembler would cost to much time and in C I wouldn't have enough registers to use them in single ISR. So I would need in this case a completly another strategy.
sparkymark567 - Mar 01, 2005 - 10:03 PM
Post subject:
try the naked attribute, that will kill all the pushing and popping, apart form the stack pointer.

Code:
void SIG_OUTPUT_COMPARE1A (void) __attribute__ ((naked));
void SIG_OUTPUT_COMPARE1A (void)


if that's what you really want.

You could also maybe use some inline asembly code in your c code.
All times are GMT + 1 Hour
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits