Page 1 of 1

Activating USART-interrupt makes controller reset

Posted: Sat Nov 29, 2008 4:11 am
by Alloc
Hi,

I'm currently trying to get a mega16 to act as a bridge between USB and USART. Everything works fine, as long as I don't enable the USART Data Register Empty Interrupt (USART_UDRE_vect). As soon as I enable it the uC hangs and after a short period does a watchdog-reset, *but only* if the interrupt is declared as UTIL_INTERRUPT.

Code: Select all

#define UTIL_INTERRUPT(signame)                         \
    void signame (void) __attribute__ ((interrupt));    \
    void signame (void)

Declaring the interrupt the avrlibc-way ( ISR(USART_UDRE_vect) ) does work though. The assembler listing only differs in the sei() instruction appearing at the beginning of the vector in the UTIL_INTERRUPT-version.

Testing by sending a few bytes over USB to the device which puts those bytes in a new buffer and enables the UDRE_vect. The vector itself only disables itself again (for debugging purposes ... should do the sending of the data later on). Don't see any reason why it should reset when actually reactivating the interrupts and not when keeping them disabled :(

Any hints?

uC-Code (USB-stuff in main.c -> usbFunctionWrite() and main() -> close to the end, USART stuff in usart.c/h)
Java app
Java lib
LibUsbJava

Regards,
Chris

Posted: Sat Nov 29, 2008 4:34 pm
by Alloc
Hi,

think I understood the reason ... The UDRE-flag is set as long as UDR is not written to. By enabling interrupts at the beginning of the UDRE-interrupt another interrupt is executed ... endless loop.

Is the USART_UDRE_vect0-ISR small enough for not enabling interrupts?

Btw, you (christian) suggested in other threads to define a macro for non-blocking interrupts. Why not simply use the avr-libc ISR-macro with the attribute ISR_NOBLOCK like

Code: Select all

ISR (intvector, ISR_NOBLOCK) { ... }
?

Regards,
Chris

Posted: Sat Nov 29, 2008 11:12 pm
by christian
For USART_UDRE, you probably need to write an assembler routine. Executing sei() after resetting the pending flag might be too slow. But look at the disassembler listing to be sure.

In an assembler routine, you may push only one or two registers, handle the pending flag and then execute sei.


Btw, you (christian) suggested in other threads to define a macro for non-blocking interrupts. Why not simply use the avr-libc ISR-macro with the attribute ISR_NOBLOCK like


The macros for declaring this type of interrupt have changed during previous releases. To be compatible, I usually take my own macro.

Posted: Sun Nov 30, 2008 1:20 am
by Alloc
christian wrote:For USART_UDRE, you probably need to write an assembler routine. Executing sei() after resetting the pending flag might be too slow. But look at the disassembler listing to be sure.

At how many cycles starts "too slow"? :D

Code: Select all

00000282 <__vector_14>:
 282:   1f 92          push   r1
 284:   0f 92          push   r0
 286:   0f b6          in   r0, 0x3f   ; 63
 288:   0f 92          push   r0
 28a:   11 24          eor   r1, r1
 28c:   8f 93          push   r24
 28e:   9f 93          push   r25
 290:   ef 93          push   r30
 292:   ff 93          push   r31
 294:   55 98          cbi   0x0a, 5   ; 10
 296:   78 94          sei

That's how the Vector starts ... I don't get why the compiler does all those pushes before the cbi though...

The macros for declaring this type of interrupt have changed during previous releases. To be compatible, I usually take my own macro.

Ah, ok =)

Chris

Posted: Sun Nov 30, 2008 10:16 pm
by christian
At how many cycles starts "too slow"?

See the comment at the beginning of the respective assembler module. For 12 MHz:

Code: Select all

;max allowable interrupt latency: 34 cycles -> max 25 cycles interrupt disable

Your interrupt takes 3 cycles until it executes, 2 cycles for the jump to the vector and 18 cycles until sei. This adds to any interrupt-disable time in the main code (which is at least 3 cycles since there may be 3 cycle instructions).

Posted: Sun Nov 30, 2008 10:34 pm
by Alloc
I use the 16 MHz variant ... unfortunately there's no information on "allowable interrupt latency". Only 12 and 16.5 MHz include that information.

Posted: Sun Nov 30, 2008 10:40 pm
by christian
Without looking at the details: I think the 12 and 16 MHz module have the same latency from start of interrupt to reading the first bit. It should therefore be OK to scale the 25 cycles with 16/12. This would be 33 cycles.

You can probably reduce the number of pushes in your interrupt if you avoid subroutine calls which can't be inlined.

Posted: Sun Nov 30, 2008 11:15 pm
by Alloc
Since there are no subroutine calls in the vector (or did you talk about sub-calls in the whole source?) that's probly not possible. Does avr-gcc use any registers as special functions? Eg, why does avr-gcc push all those registers at the beginning of the interrupt?

Code: Select all

 282:   1f 92          push   r1
 284:   0f 92          push   r0
 286:   0f b6          in   r0, 0x3f   ; 63
 288:   0f 92          push   r0
 28a:   11 24          eor   r1, r1
 28c:   8f 93          push   r24
 28e:   9f 93          push   r25
 290:   ef 93          push   r30
 292:   ff 93          push   r31

Ok, r24/25 and 30/31 seem to be because they're used in the vector by instruction a few lines after that. But what is that push r0 -> in r0 -> push r0?

Regards,
Chris

/Edit:
Ok, 0x3f is SREG, but why is it saved before executing the routine? The code doesn't access the stuff in there anyway?!

/Edit2:
Okay, forget that ... I assume SREG has to be saved in case that some instructions were aborted before entering the interrupt which have some results in the flags in SREG like signbit or half carry ...
But why does avr-gcc save r0 itself before that? :D


/Edit3:
What I have in mind is simply make the ISR naked, have it only push SREG and clear the UDRIE-bit directly afterwards. After that I'd manually place all the other push operations I kicked out by making the ISR naked.

Posted: Mon Dec 01, 2008 12:08 pm
by christian
You have found out most of the details yourself. R0 is used for temporary results and R1 should always be zero.

If you make the routine naked, you have to push registers from inline assembler without knowing what to push. A new compiler version might assign registers in a different way. But at least it's a viable solution.

Posted: Mon Dec 01, 2008 10:07 pm
by Grendel
Alloc wrote:I assume SREG has to be saved in case that some instructions were aborted before entering the interrupt which have some results in the flags in SREG like signbit or half carry ...
But why does avr-gcc save r0 itself before that? :D

The AVR interrupt mechanism just sets the I flag, pushes the return address, and jumps to the ISR. The CPU is still in the state where it got interrupted and needs to be preserved. In order to save the SR you have to load it into a register, that register needs to be saved beforehand -- hence the 1st "push r0" ;) r1 could be pushed after that sequence tho.