Beginner question on interrupt service

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

Hi, this is a very basic question and I apologize to all the great freaks out here if these is a silly one.I wrote a simple program to find what exactly happens at the assembly during an interrupt routine . This is my program:

#include
#include

int main(void)
{
        DDRD  |= (1 << 2);
	PORTD &= ~(1 << 2);
    
    //Start the timer 
    //Timer delay of 3usec
	TCCR0B = (1 << CS02) | (0 << CS01) | (1 << CS00);
	TIMSK0 = (1 << TOIE0);
	sei();
	for(;;)
	{
        }
}

ISR(TIMER0_OVF_vect)
{
PORTD |= (1 << 2);
}

The disassembler shows :

@00000036: main
---- int-test.c -----------------------------------------------------------------------------------
6:        {
+00000036:  9A52  SBI  0x0A,2 Set bit in I/O register
9:        	PORTD &= ~(1 << 2);
+00000037:  985A  CBI  0x0B,2 Clear bit in I/Oregister
13:       	TCCR0B = (1 << CS02) | (0 << CS01) | (1 << CS00);
+00000038:   E085 LDI  R24,0x05 Load immediate
+00000039:   BD85 OUT  0x25,R24 Out to I/O location
14:       	TIMSK0 = (1 << TOIE0);
+0000003A:   E081 LDI  R24,0x01 Load immediate
+0000003B:   9380006E   STS 0x006E,R24 Store direct to data space
15:       	sei();
+0000003D:   9478  SEI  Global Interrupt Enable
+0000003E:   CFFF  RJMP PC-0x0000  Relative jump
@0000003F: __vector_16
25:       {
+0000003F:   921F  PUSH R1   Push register on stack
+00000040:   920F  PUSH R0   Push register on stack
+00000041:   B60F  IN  R0,0x3F   In from I/O location
+00000042:   920F  PUSH R0  Push register on stack
+00000043:   2411  CLR  R1  Clear Register
26:       PORTD |= (1 << 2);
+00000044:   9A5A SBI  0x0B,2  Set bit in I/O register
28:       }
+00000045:   900F POP R0   Pop register from stack
+00000046:   BE0F OUT 0x3F,R0  Out to I/O location
+00000047:   900F POP R0  Pop register from stack
+00000048:   901F POP R1  Pop register from stack
+00000049:   9518 RETI Interrupt return
+0000004A:   94F8 CLI  Global Interrupt Disable
+0000004B:   CFFF RJMP PC-0x0000  Relative jump

Now as far as I know what happens during an interrupt routine call is :
1. The peripheral device interrupts the processor.
2. Current instruction execution is completed.
3. The address of the next instruction is stored on the stack (either a hardware stack or a software stack).
4. The processor state (SREG) is stored in the Stack.
5. Address of the ISR (interrupt subroutine) are loaded into the program counter.
6. The processor executes the ISR.
7. The ISR execution completion is indicated by the RETI instruction (return from interrupt).
8. The processor loads the program counter with the value stored on the stack and normal program execution resumes.

Now at the disassembler code I find that at the ISR its like this :

@0000003F: __vector_16
25:       {
+0000003F:   921F        PUSH    R1 Push register on stack
+00000040:   920F        PUSH    R0 Push register on stack
+00000041:   B60F        IN      R0,0x3F In from I/O location
+00000042:   920F        PUSH    R0 Push register on stack
+00000043:   2411        CLR     R1               Clear Register

I know that R0 is being used to store the SREG value.But why is R1 also being pushed into the stack and popped later?

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

You are wrong in your assumptions about what happens automatically. Step 4 does not happen. It's up to the programmer (or C run time library) to ensure that SREG is maintained if need be. This is because sometimes the ISR would not change SREG and it would be a wasteful overhead for it to be saved. So it's up to the programmer to determine if it's required.

In 3 there's no question about the stack type. It's most definitely the hardware stack - that is the place where SP is currently pointing.

One action (or rather two actions) you missed: the I bit in SREG is cleared before the jump into the handler and the bit is restored as part of what RETI does.

As for R1. In the case of GCC (the AVR support of which was first done before Atmel added MUL to the instruction set of some of the later AVRs) the fact is that the compiler writers decided to use R1 to always hold the value 0. In fact you'll often see R1 referred to as __zero_reg__ in GCC generated code. Then Atmel came along and decided to use R1 in the MUL instruction which means that if the ISR (or anything it calls) happens to use MUL then it could be that R1 no longer holds 0. So the ISR prologue stores the register and the epilogue restores it just in case. I guess there's an argument to say that the optimiser should use a different prologue/epilogue when it knows that MUL is not used and also that it neen't save/restore SREG if it knows the ISR code cannot change any bit in SREG

Cliff

PS As you're specifically asking about GCC behaviour (as some of this stuff differs from compiler to compiler) I'll move this to GCC...

Last Edited: Sun. Sep 14, 2008 - 07:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I know that R0 is being used to store the SREG value.But why is R1 also being pushed into the stack and popped later?
GCC generally uses R1 to hold a value of zero. At the beginning of an ISR, it forces R1 to zero, but saves it first just in case it was not already zero.

In a simple ISR such as your example, this is somewhat inefficient. In your example, you don't even need to save SREG or save anything on the stack at all. There is room for improvement in GCCs code generation for very simple ISRs.

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

R1 is a scratch register that GCC uses. It always saves it on stack (in any function call, not just ISRs). There are times when it is pushed that it doesn't really need to be as in the case of your ISR.

As for your analysis of the interrupt procedure, there are a couple of small corrections.

1. The peripheral device interrupts the processor.
2. Current instruction execution is completed.
3. The address of the next instruction is stored on the stack (either a hardware stack or a software stack).
3a. The interrupt flag that caused the interrupt is cleared.
3b. The global interrupt flag is cleared.
4. Address of the ISR vector (interrupt subroutine) are loaded into the program counter.
5. The processor executes the ISR.
6. The processor state (SREG) is stored in the Stack.
7. The SREG is restored to the original value.
8. The ISR execution completion is indicated by the RETI instruction (return from interrupt).
9. The processor loads the program counter with the value stored on the stack and normal program execution resumes.
9a. The global interrupt flag is set.

Steps 6, 7 and 8 are the responsibility of the programmer (or compiler). Step 9 is what is happening during the RETI instruction.

Edit: I've got to learn to type faster!

Regards,
Steve A.

The Board helps those that help themselves.

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

@Cliff,Kevin,Steve

Thanks all.The explanations are wonderful.