Activating USART-interrupt makes controller reset

General discussions about V-USB, our firmware-only implementation of a low speed USB device on Atmel's AVR microcontrollers
Post Reply
Alloc
Rank 1
Rank 1
Posts: 20
Joined: Tue Feb 19, 2008 10:52 pm
Location: Germany (Hessen)
Contact:

Activating USART-interrupt makes controller reset

Post by Alloc » Sat Nov 29, 2008 4:11 am

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

Alloc
Rank 1
Rank 1
Posts: 20
Joined: Tue Feb 19, 2008 10:52 pm
Location: Germany (Hessen)
Contact:

Post by Alloc » Sat Nov 29, 2008 4:34 pm

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

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Post by christian » Sat Nov 29, 2008 11:12 pm

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.

Alloc
Rank 1
Rank 1
Posts: 20
Joined: Tue Feb 19, 2008 10:52 pm
Location: Germany (Hessen)
Contact:

Post by Alloc » Sun Nov 30, 2008 1:20 am

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

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Post by christian » Sun Nov 30, 2008 10:16 pm

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).

Alloc
Rank 1
Rank 1
Posts: 20
Joined: Tue Feb 19, 2008 10:52 pm
Location: Germany (Hessen)
Contact:

Post by Alloc » Sun Nov 30, 2008 10:34 pm

I use the 16 MHz variant ... unfortunately there's no information on "allowable interrupt latency". Only 12 and 16.5 MHz include that information.

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Post by christian » Sun Nov 30, 2008 10:40 pm

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.

Alloc
Rank 1
Rank 1
Posts: 20
Joined: Tue Feb 19, 2008 10:52 pm
Location: Germany (Hessen)
Contact:

Post by Alloc » Sun Nov 30, 2008 11:15 pm

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.

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Post by christian » Mon Dec 01, 2008 12:08 pm

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.

Grendel
Rank 4
Rank 4
Posts: 167
Joined: Sat Dec 16, 2006 9:53 pm
Location: Oregon, USA
Contact:

Post by Grendel » Mon Dec 01, 2008 10:07 pm

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.

Post Reply