Microprocessor and Development Tools - Embedded Control - Communications - Connectivity
   
Low-Cost Dev Kits
Application Kits
RabbitCores
Latest Downloads
Single-Board Computers
Rabbit Support Forums
Training/Events



Support > Ask Larry > July 22st, 2008

Ask Larry

Dear Larry,

Is it OK to write an Interrupt Service Routine in C?

Chris M.

Hello Chris,

Thank you for sending in the question. I can take this opportunity to cover a commonly asked question as well as well as others having to do with Interrupt Service Routines (ISRs).

The main design goal of most ISRs should be to execute as quickly as possible. Usually this means that they should have a minimum of instructions. An ISR written in C, in most cases, will violate this goal. In general, you have very little idea as to how long C statements take to execute. This is mainly due to the compiler inserting function calls into the compiled C code. I usually tell customers that there is roughly a 5 to 1 ratio in execution time between C and assembly language.

Here are a few guidelines for writing ISRs:

  • Write the ISR in assembly language.
  • Do not have any looping constructions.
  • Determine the clock cycles for each instruction and add them so you know how long the ISR takes – don’t forget to take into account the interrupt latency time! Worst case interrupt latency for the Rabbit® processors is 30 clocks.
  • Set the Interrupt Priority Level (IPL) appropriately – in most cases the lowest active level.
  • Determine the percentage of CPU time the ISR is going to take. This may not be easy but is worthwhile. This will give you an idea of what percentage of processor time is going to be used by the USR.

Here is a sample program with has a short example ISR that follows the above criteria. The numbers in the comments are the number of CPU clocks required to execute the instruction in a Rabbit 4000.

unsigned long TimeCheck;          // used to check CPU load
unsigned int TA7count;            // TA7 interrupt count
#define	TA7DIVISOR	200

#asm
TAISR::                           ; xx = nbr of CPU clocks to execute the instruction
         push af                  ; 10 save registers
         push hl                  ; 10
   ioi   ld a, (TACSR)            ; 11 clear the interrupt
; update counter
         ld hl, (TA7count)        ; 11 get current count
         inc hl                   ;  2 update ISR count
         ld (TA7count), hl        ; 13 save new count value
; cleanup before returning
         pop hl                   ;  7 restore registers
         pop af                   ;  7
         ipres	                   ;  4 restore IPL
         ret                      ;  8
; total ISR time = 83 + 30 = 113 worst case
#endasm

// main program to set up the above ISR
main ()
{
   int i;

   TA7count = 0;                             // init count value
   WrPortI ( TAT7R, NULL, TA7DIVISOR-1);     // set Timer A7 interval

   SetVectIntern ( 0x0A, TAISR );            // set interrupt vector
   WrPortI ( TACSR, &TACSRShadow, 0x81 );    // enable interrupts on Timer A7
   WrPortI ( TACR, &TACRShadow, 1 );         // enable Timer A interrupts at IPL = 1

// the following code is used to determine the processor load created by the ISR
   MS_TIMER = 0;
   while ( MS_TIMER < 10 )
   {   TimeCheck+=1;
   }
   WrPortI ( TACR, &TACRShadow, 0 );         // disable Timer A interrupts
   printf ( "%ul", TimeCheck );
}

I used this program to test the CPU load. On an RCM4100 the value of TimeCheck printed was: 20561

When I commented out the statement which enables Timer A interrupts the value printed was: 27161

The ratio 20561/27161 is 0.75. Note that this is very close to the 28% load calculated below.

This ISR places a fairly heavy load on the processor. With the divisor set to 200, the timer will cause an interrupt every 400 clock cycles because its default input signal is Pclk/2. Since the ISR takes 113 clocks to execute that means that 113 out of every 400 clocks will be occupied in servicing the interrupt! That is about 28% of the CPU time. Please note that if you change TIMERA_PRESCALE to 1, the percentage would double, leaving much less time for the “normal” program. Also note that this analysis is totally independent of the actual CPU clock frequency.

I also attempted a test which replaced the assembly language ISR with the equivalent C code:

Interrupt void TAISR ( void )
{
   TA7count += 1;
}

I said “attempted” because the program would not even run, indicating that the ISR time was so long that Dynamic C could not even communicate with the target.

We once received code from a customer who had a “very short ISR written in C” only to find that the ISR called a library function that included thousands of lines of C code. It certainly looked short on the screen, but the code he was running was anything but.

- Larry C.

Larry Cicchinelli is Rabbit’s Technical Support Manager. He has 30 years of embedded experience, and is considered one of the foremost authorities on Rabbit products. Larry and his staff offer comprehensive technical support to Rabbit customers.

Submit your questions for Larry via email at AskLarry@rabbit.com

Read more Ask Larry Answers




   Site Map | Privacy Policy | Contact Us | Feedback Copyright © Rabbit All Rights Reserved    A Digi International® Brand  
View Cart | Contact Us
View Cart
Products Solutions Support Company Channel partners Careers Ordering Information