Interrupt Vetor Table

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

Hello friends,
I want to know if I'm understanding the handling of an interrupt correctly.

Let's say for example the external interrupt 0 INT0 has an address 0x0002.
If I'm rigth this is the address of the flash memory where the PC should get the address of the code to service the routine. Is this true??

So if for example in 0x0002 there is a 0x3000;
when the interrupt occurs, the program will make a jmp to 0x3000??
Thanks a lot and excuse me if there is an answer to this question in an older post.

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

You have to supply the full jmp or rjmp instruction at that vector address.

If you are programming in C, all this is done for you.

In assembler, this has to be done in code. .org statements are often used to position the jump statements correctly.

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Yup. That's how it works.

In GCC, it all gets build automagically when you include interrupt.h and define

ISR(anyinterrupt_vect)
{
}

The largest known prime number: 282589933-1

In my humble opinion, I'm always right. 

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

Quote:
Is this true??
Yes just like any other brand of processors.
Quote:
the program will make a jmp to 0x3000??
Only if the flash size is trat big otherwise it will be a rjmp for smaller processors.
Quote:
excuse me if there is an answer to this question in an older post.
Well this is not Atmel specific, how do the interrpt vectors work for the processors you have used in the past?

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

js wrote:
Quote:
Is this true??
Yes just like any other brand of processors.
Quote:
the program will make a jmp to 0x3000??
Only if the flash size is trat big otherwise it will be a rjmp for smaller processors.
Quote:
excuse me if there is an answer to this question in an older post.
Well this is not Atmel specific, how do the interrpt vectors work for the processors you have used in the past?

well ATMEGA its kinda the only processors I have used, I still got a long road ahead...

Thanks a lot for the quick responses friends.

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

Quote:
well ATMEGA its kinda the only processors I have used, I still got a long road ahead...

The AtMega is a nice regular processor with lots of capabilties.

If this is where you are starting out then you are starting in a nice place!

Take some time to write some C programmes and then step through them in the simulator. You will see how the C and assembler relate to each other. This is a really valuable skill when working with uCs.

regards
Greg

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

Quote:
Quote:
Is this true??

Yes just like any other brand of processors.
No, not the way that it is stated by the OP. It does not get the address of where to jump from what is at the interrupt vector, the address of the vector is where the code jumps to. It then runs the code that is at that vector. What is at that vector is usually a jmp or rjmp to where the ISR code is, but it doesn't have to be. It could be any code at all including the entire code of the ISR (though doing so might overwrite the vectors of other interrupts).

Regards,
Steve A.

The Board helps those that help themselves.

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

...just like any other processor... :wink:

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

i've worked with other processors that have a single interrupt vector.
you had to poll each interrupt flag to identify the source.

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

As this is posted in the GCC forum can we assume avr-gcc is being used? If so then why not examine the mechanism for yourself. Just build a short C program:

#include 

int main(void) {
}

ISR(TIMER0_OVF_vect) {
  PORTB = 0x55;
}

If you build that for mega16 (say) then you can check the datasheet and see that the timer 0 overflow vector is the 9th vector after the reset jump. So now look at the .lss file that was created:

Disassembly of section .text:

00000000 <__vectors>:
   0:	0c 94 2a 00 	jmp	0x54	; 0x54 <__ctors_end>
   4:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
   8:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
   c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  10:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  14:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  18:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  1c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  20:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
  24:	0c 94 39 00 	jmp	0x72	; 0x72 <__vector_9>
  28:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
etc.

So at location 0: you can see there is a "jmp 0x54". That is the reset jump and takes execution to the first instructions after this vector table where code finally goes on to "call main".

At location 24: you can see that our use of ISR() has lead to a "jmp __vector_9" being created and the code for that is at location 0x72.

In passing note what the compiler has done with all the other interrupt vectors for which we didn't provide ISR(). They all have a "jmp __bad_interrupt" and that code is located at address 0x68. It's worth just having a quick look at that:

00000068 <__bad_interrupt>:
  68:	0c 94 00 00 	jmp	0	; 0x0 <__vectors>

So the code called __bad_interrupt() actually just has a "jmp 0". This is explained in the "Catch all" section of the user manual:

http://www.nongnu.org/avr-libc/u...

It is the reason why, if you enable an interrupt source and don't provide an ISR() for it then when the event occurs it does a "jmp 0" and the AVR looks like it reset.

Anyway back to our ISR(). We saw that simply by writing:

ISR(TIMER0_OVF_vect) {

this lead to vector 9 being directed to code at 0x72:

00000072 <__vector_9>:

ISR(TIMER0_OVF_vect) {
  72:	1f 92       	push	r1
  74:	0f 92       	push	r0
  76:	0f b6       	in	r0, 0x3f	; 63
  78:	0f 92       	push	r0
  7a:	11 24       	eor	r1, r1
  7c:	8f 93       	push	r24
	PORTB = 0x55;
  7e:	85 e5       	ldi	r24, 0x55	; 85
  80:	88 bb       	out	0x18, r24	; 24
}
  82:	8f 91       	pop	r24
  84:	0f 90       	pop	r0
  86:	0f be       	out	0x3f, r0	; 63
  88:	0f 90       	pop	r0
  8a:	1f 90       	pop	r1
  8c:	18 95       	reti

This shows that in addition to the code that sets PORTB to 0x55 there's a little more "overhead" generated. In this case it's pointless but, as yet, the compiler isn't smart enough to recognise that so it generates it anyway. It preserves R1 and later ensures that it holds 0 (internally the compiler calls this __zero_reg__ and uses it whenever a value of 0 is required). It also preserves R0 then uses the same register to read in and preserve SREG. At the end it unwinds all these things and ends with RETI. As the interrupting process that selected vector 9 in the first place will have pushed the PC value of the next opcode in main() before it jumped then this RETI will both restore that value to PC and also set the I bit in SREG back to one (like SEI) because another part of the vector jumping mechanism was that I was cleared to zero (like CLI) before the "jmp __vector_9" was performed.

And that's pretty much all there is to it though you may be interested to learn how the ISR() line we wrote became a target for vector 9. If you look at the header file for mega16 (the one I built for) you will see:

#define TIMER0_OVF_vect			_VECTOR(9)

So the line:

ISR(TIMER0_OVF_vect) {

really says:

ISR(_VECTOR(9)) {

What's more the definition of _VECTOR is in sfr_defs.h and consists of:

#define _VECTOR(N) __vector_ ## N

so the line actually says:

ISR(__vector_9) {

If you want to see the full expansion then interrupt.h also has:

#  define ISR(vector, ...)            \
    void vector (void) __attribute__ ((signal,__INTR_ATTRS)) __VA_ARGS__; \
    void vector (void)

So the line really looks as follows to the C compiler:

void __vector_9 (void) __attribute__ ((signal,used, externally_visible)) ; 
void __vector_9 (void) {

That is creating a void/void function called __vector_9 but the additional attribute stuff is what says "this is not a normal function but requires all that extra stuff to save R0/R1/SREG and must end in a RETI not a RET".

Finally, the way the entry down at 24: turns a default "jmp __bad_interrupt" into a "jmp __vector_9" uses a technique called "weak linking" but I won't try to explain that now as it's probably too complex at this stage.

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

thanks again Clawson, your response was very clear.
Now in relation to the Koshchi's response:

Quote:
What is at that vector is usually a jmp or rjmp to where the ISR code is, but it doesn't have to be. It could be any code at all including the entire code of the ISR

If this was the case, the other interrupt vectors would be useless, because the code would occupy their memory??

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

Quote:

If this was the case, the other interrupt vectors would be useless, because the code would occupy their memory??

Spot on! You only do this when you have one ISR that absolutely must execute as fast as possible and you don't plan to use any of the vector jumps beyond it.

It's also quite tricky to do in C. By default the linker adds a "C Run Time" (CRT) to the start of all C programs you write. This includes the reset jump, that full vector table (with all entries defaulting to "jmp __bad_interupt"), initialisation code and so on. Because there is a full vector table if you start trying to locate a code sequence at the __vector_9 location (for example) you will get all kinds of dire warnings about "your section .newvectors overlays .text at location 0x0024" or whatever.

What you'd have to do is build with -nostartfiles which means the first .s or .c file you include in the link would locate at 0x0000 and there'd be no CRT. So you'd have to arrange for a jmp at the reset vector (to avoid the rest of the interrupt table), your code at 0x0024 (or whatever), a replacement for the CRT and so on.

Not worth doing unless you are REALLY pressed and have to get something to happen as soon as an interrupt even occurs. Otherwise accept the indirection of that additional JMP at the vector location and use the CRT mechanism.

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

> ...just like any other processor...

Nope. ARMs, for example, work differently. They indeed have a table of
addresses (as opposed to a table of jumps), where the first table slot
is the initial value of the stack pointer, the second slot is the
address of the reset handler, and so on. (Actually, that's how the
Cortex-M ARM devices are organized.)

So no processor is like the other one ...

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

Last Edited: Thu. May 16, 2013 - 04:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Clawson!!!

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

Quote:

If this was the case, the other interrupt vectors would be useless, because the code would occupy their memory??

Correct. Mostly.

Let's say I have a Mega164 which has two-word vector table entries, to accommodate the normal two-word JMP instruction that can reach to all of program memory.

A minimal ISR could change a register bit and RETI, and nothing would be occupied or useless. Examples of "useful" one-word instructions, that save the 2/3 cycles for RJMP/JMP:

   GPIOR0 |= 0x01; // set a bit in I/O space
    PINB |= 0x02;  // toggle an I/O port bit
    regvar |= 0x04;    // set a bit in a register-based variable [YMMV]
 

Generally, enabled vectors are sparse in the table. If one gets totally squeezed on flash space, then one must trust the program and bet that no spurious interrupts will be enabled. The small chunks of flash in the vector table look pretty good. In a past life I put a small lookup table into AT90S4433 vector table.

Now, back when I was your age, one could use an AT90S4433 or AT90S8535 (or a couple of other models). Each cost about $5.

Nowadays, you kids have it so easy. Mega48 family has four (?) models, and an "upgrade" to the bigger model can cost like $0.25 more. Not a big deal unless you are making a zillion of them.

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

Thank you very much, hahhaha a zillion ahahaha,

friends, can you see this post
https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=132740

I am kinda stuck there :S