Interrupts re-enabled in the middle of an SP update

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

Why are interrupts re-enabled in the middle of a stack pointer update?

This is explained in the avr-libs FAQ. But this explanation does not seem to fit on the Xmega. From both simulation and code running on real target, a pending interrupt gets executed in the middle of the stackpointer update. Which in our case results in fatal program failure.

The behaviour can be observed in the pasted code example.

Here, an array is initialized and the value in buffer[226] is set out on PORTC.OUT. (The value is 0xE2)

A timer is set so an overflow interrupt will happen in the middle of the SP update of __epilogue_restores__.
The interrupt gets executed right after the SPH is set, but before SPL is set, causing an overwrite of the buffer during the pushing of the registers. This in turn causes the PORTC.OUT to change to 0x00.

    
1064:	ce 0f       	add	r28, r30
1066:	d1 1d       	adc	r29, r1
1068:	0f b6       	in	r0, 0x3f	; 63
106a:	f8 94       	cli
106c:	de bf       	out	0x3e, r29	; 62
106e:	0f be       	out	0x3f, r0	; 63 Interrupt gets executed after this line
1070:	cd bf       	out	0x3d, r28	; 61

AVR Studio version 4.18.716
avr-gcc version (WinAVR 20081205) 4.3.2
Compiled with -Wall -gdwarf-2 -Os -std=gnu99 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums

The big question is: Where is the error? In our code? In the compiler? Or on the chip (ATxmega128A1)?

Kind regards,
Bjørn and Øyvind.

#include 
#include 

volatile char *p;

char doSomething(char inByte)
{
	float someFloat;
	someFloat=2.5*inByte;
	return (char)someFloat;
}

ISR(TCE1_OVF_vect)
{
	TCE1.INTCTRLA=0x00;
}



int main(void)
 {
 	int j;
 	char buffer[231];
	p=&(buffer[226]);

	for(j=0;j

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

Exactly what bit of C code does that assembly correspond to?

106a:   f8 94          cli
106c:   de bf          out   0x3e, r29   ; 62
106e:   0f be          out   0x3f, r0   ; 63 Interrupt gets executed after this line

How could the interrupt be happening at all at that spot if the global interrupt flag was just cleared?

Regards,
Steve A.

The Board helps those that help themselves.

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

On the AVR8s the assembly actually does do this, and it trips everyone up when they first see it. The reason on the AVR8s at least is that the datasheet explicitly says that at least one instruction following a SEI (or other setting of the global interrupt enable bit) will be executed, making the code sequence safe, and the latency one cycle shorter.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Koshchi, as a superfreak you should know better. The global interrupt is re-enabled with the instruction "out 0x3f, r0;"
Your question tells me that you have not understood the problem.
In our case this code demonstrate that the avrgcc way of re-enabling interrupt in the middle of a sph-spl update could result in fatal program crash (faulty stackpointer sph/spl register).

Would have been nice to have this problem/behavio of the xmega confirmed by some other. Acording to our test result on our xmega, this is a faulty statement:

"after re-enabling global interrupt, the instruction following the re-enable will get executed, before any pending interrupt are executed"

//birk

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

Hi, Øyvind here, colleague of 'birk'.

Koshchi: To answer your last question first: No, the interrupt shouldn't be allowed to happen here at all. As abcminiuser says:

Quote:
at least one instruction following a SEI (or other setting of the global interrupt enable bit) will be executed, making the code sequence safe, and the latency one cycle shorter.

The timer is set so the interrupt is trigged right before the update of the status register. When stepping through the assembly code we see that the interrupt is run before the update of SPL. I know it says in the datasheet that it shouldn't, but in our case it still does.

Quote:
Exactly what bit of C code does that assembly correspond to?

The code is the last part of what is called __epilogue_restores__ in my lss-file. Here's more from it:

1064:	ce 0f       	add	r28, r30
1066:	d1 1d       	adc	r29, r1
1068:	0f b6       	in	r0, 0x3f	; 63
106a:	f8 94       	cli
106c:	de bf       	out	0x3e, r29	; 62
106e:	0f be       	out	0x3f, r0	; 63
1070:	cd bf       	out	0x3d, r28	; 61
1072:	ed 01       	movw	r28, r26
1074:	08 95       	ret

An interrupt between line 106e and 1070 would as far as I can see only cause trouble if SPH is changed in line 106c. That's the reason the buffer size in our code example is rather odd. It causes the SP to vary between 0x3F%% and 0x3E%%

It would be very interesting if anyone could test this to see if the same thing happen. My test procedure is to put a breakpoint at the

i=doSomething(TCE1.CNT);

open the disassembler window, put a new breakpoint at the cli-line in the __epilogue_restores__ (in my case, line 835. Not sure if this will be the same everywhere?) Keep an eye on the highest addresses of the Data-segment (0x3FFF and lower). You'll see the buffer is put from address 0x3F13 to 0x3FF9. Stepping through the code, you will (hopefully, I might add) see that after updating SPH, an interrupt occurs, causing the buffer to be overwritten.

Kind regards,
Øyvind.

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

can you manually disable interrupts before your portc write?

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

I'm confused. Unless R0 has been corrupted (it *should* always contain 0) then the comment in this cannot be true can it?

106a:   f8 94          cli 
106c:   de bf          out   0x3e, r29   ; 62 
106e:   0f be          out   0x3f, r0   ; 63 Interrupt gets executed after this line 
1070:   cd bf          out   0x3d, r28   ; 61 

It's true that a change of I only comes into effect after the NEXT opcode so assuming I is set at the start then the CLI at 106A and the first OUT at 106C might be interrupted but then at 106E the SREG is just set to 0, simply reinforcing the fact that I is already 0. So 106E and 1070 cannot be interrupted as the comment suggests?? (or does R0 contain something other than 0x00?)

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

clawson: As can be seen from my second assembly listing, R0 is a copy of the SREG as it was before the cli, hence line 106e is enabling the interrupts.

GordonFreeman: Not sure what you're asking. The program is created to demonstrate what looks like a bug in the compiler to us. (But we're open for the fact that the error might be behind this keyboard ... )

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

R0 is not the zero register, it is R1 which by avrgcc is always kept cleared if I'am not wrong. R0 in our case, contains a copy of SREG before CLI is executed. Hence when R0 is re-stored back (out 0x3f,r0) to SREG, I-flag will be set to what ever state it had before CLI where executed.

//birk

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

Very interresting:

http://comments.gmane.org/gmane.comp.hardware.avr.libc.devel/5161

When we are building code using Makefile we can turn on or off use of prolog/epilog section with the compiler directive -mcall-prologues. But building the code from inside AVR Studio seems to force prologues/epilog handling on, allbeit there is no '-mcall-prologues' switch on the output window of AVR studio. Does -mcall-prologues has an alternate command switch ?

The point is that disabling prologue/epilog generation, prevents the faulty SP-update section.
But does anyone know how to turn off prolog/epilog generation when building directly from inside AVR Studio?
Or even better, how to pathc avrgcc to generate correct prolog/epilog sequence?

Sorry for all the writings...
//birk

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

Problem seems to be solved. Installed AVR Toolchain downloaded from http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 and now the compiled assembly code in the epilogue-restores looks like this:

1066:	ce 0f       	add	r28, r30
1068:	d1 1d       	adc	r29, r1
106a:	cd bf       	out	0x3d, r28	; 61
106c:	de bf       	out	0x3e, r29	; 62

Since interrupt is disabled for four clock cycles after an update of SPL (according to the manual), this should work.

Hooray!

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

Glad you got it working!

birk wrote:
When we are building code using Makefile we can turn on or off use of prolog/epilog section with the compiler directive -mcall-prologues. But building the code from inside AVR Studio seems to force prologues/epilog handling on, allbeit there is no '-mcall-prologues' switch on the output window of AVR studio. Does -mcall-prologues has an alternate command switch

Your project gets compiled with a makefile regardless if it's in AVR Studio or not. AVR Studio just happens to include its own makefile unless an external one is specified. You should be able to see the makefile generated by avr studio in the /default/ directory of your project.