1. Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
    Dismiss Notice

Avr-Gcc sillyness?

Discussion in 'AVR' started by boxer4, Aug 21, 2007.

  1. boxer4

    boxer4 New Member

    Aug 18, 2007
    I suppose this should be best posted to some AVR-specific forum but I'll give it a shot here.

    I have a function that requires the use of local variables. In order to prevent the stack from being corrupted it will add cli's into the resultant code. However it seems that gcc never reenables interrupts after finishing with the critical code (??!!) Thus I appear to be losing clock ticks with my timer interrupt.

    I'm using gcc-3.4.6 and avr-libc 1.4.6. I'm compiling -Os ...
  2. Sceadwian

    Sceadwian Banned

    Oct 27, 2006
    Rochester, US
    If you're placing the CLI's then you should be placing the SEI's you can't rely on the compiler to do it for you, it neither knows nor cares if interupts are enabled. If even after placing the sei's yourself you're still mising interupts you're going to have to redesign your code to avoid it or program it in assembly where you have a much higher degree of control over interupts and program timeing in general.

    Post your current code name the AVR you're using it's clock rate and any other information you can provide, specifically describe the stimulus the AVR is receiving and the interupt rate for the timer etc..
  3. boxer4

    boxer4 New Member

    Aug 18, 2007
    Actually the CLI's are being inserted automatically by gcc.

    The code I'm trying to compile is a function to print integers to the UART in human-readable form (excuse me for being lazy and using avr-libc...):

    void UART_Printint(int Data)
    char tempbuf[6]; /* temp buffer to hold ascii string */
    unsigned char ptr=0; /* one at a time */
    if(Data<0) { /* handle negatives */
    itoa(Data,tempbuf,10); /* using avr-libc to convert */
    while(tempbuf[ptr]) { /* output string to UART */

    Baud rate is 9600 bps. This routine is _not_ part of the interrupt handler, it's part of the main program.
    AVR is 90S4433 with 7.3728MHz crystal
    Timer routine is using channel 0, tuned to be 30 interrupts per second (I wanted it to be less, but channel 1 was used for something else and it's as far as I can divide it down with the prescalers.)

    Symptom: The timer is used for a RTC, and it's not holding time. I can visually see it missing ticks when running it in parallel with another clock.
    Technically this should be easy for the AVR to hold time, but if interrupts are held while printing the whole string, it will clearly miss pulses.

    How I discovered the CLIs : I looked at the .lst file output. I didn't explicitly put in the CLIs, they're put in implicitly just before handling the 16 bit pointers. However there's one issue I haven't accounted for: if interrupts are _never_ reenabled it should stop responding to the clock interrupt... but I haven't found where it's reenabling interrupts... reti should do it, but how did it get into the interrupt with CLI in play? ... weird.

    This is the code that's emitted: I can _NOT_ have interrupts held off for as long as this function runs!!! In my opinion, unless I missed a flag somewhere, the CLI handling seems wrong:

    266 .global UART_Printint
    267 .type UART_Printint, @function
    268 UART_Printint:
    269 /* prologue: frame size=6 */
    270 013c FF92 push r15
    271 013e 0F93 push r16
    272 0140 1F93 push r17
    273 0142 CF93 push r28
    274 0144 DF93 push r29
    275 0146 CDB7 in r28,__SP_L__
    276 0148 DEB7 in r29,__SP_H__
    277 014a 2697 sbiw r28,6
    278 014c 0FB6 in __tmp_reg__,__SREG__
    279 014e F894 cli /***************CLI IS HERE********/
    280 0150 DEBF out __SP_H__,r29
    281 0152 0FBE out __SREG__,__tmp_reg__
    282 0154 CDBF out __SP_L__,r28
    283 /* prologue end (size=13) */
    284 0156 082F mov r16,r24
    285 0158 192F mov r17,r25
    286 015a FF24 clr r15
    287 015c 97FF sbrs r25,7
    288 015e 05C0 rjmp .L28
    289 0160 8DE2 ldi r24,lo8(45)
    290 0162 89DF rcall UART_SendByte
    291 0164 1095 com r17
    292 0166 0195 neg r16
    293 0168 1F4F sbci r17,lo8(-1)
    294 .L28:
    295 016a 4AE0 ldi r20,lo8(10)
    296 016c 50E0 ldi r21,hi8(10)
    297 016e 6C2F mov r22,r28
    298 0170 7D2F mov r23,r29
    299 0172 6F5F subi r22,lo8(-(1))
    300 0174 7F4F sbci r23,hi8(-(1))
    301 0176 912F mov r25,r17
    302 0178 802F mov r24,r16
    303 017a 00D0 rcall itoa
    304 017c 0C2F mov r16,r28
    305 017e 1D2F mov r17,r29
    306 0180 0F5F subi r16,lo8(-(1))
    307 0182 1F4F sbci r17,hi8(-(1))
    308 0184 8981 ldd r24,Y+1
    309 0186 8823 tst r24
    310 0188 71F0 breq .L33
    311 .L31:
    312 018a F12F mov r31,r17
    313 018c E02F mov r30,r16
    314 018e EF0D add r30,r15
    315 0190 F11D adc r31,__zero_reg__
    316 0192 8081 ld r24,Z
    317 0194 70DF rcall UART_SendByte
    318 0196 F394 inc r15
    319 0198 F12F mov r31,r17
    320 019a E02F mov r30,r16
    321 019c EF0D add r30,r15
    322 019e F11D adc r31,__zero_reg__
    323 01a0 8081 ld r24,Z
    324 01a2 8823 tst r24
    325 01a4 91F7 brne .L31
    326 .L33:
    327 /* epilogue: frame size=6 */
    328 01a6 2696 adiw r28,6
    329 01a8 0FB6 in __tmp_reg__,__SREG__
    330 01aa F894 cli /***************CLI IS HERE********/
    331 01ac DEBF out __SP_H__,r29
    332 01ae 0FBE out __SREG__,__tmp_reg__
    333 01b0 CDBF out __SP_L__,r28
    334 01b2 DF91 pop r29
    335 01b4 CF91 pop r28
    336 01b6 1F91 pop r17
    337 01b8 0F91 pop r16
    338 01ba FF90 pop r15
    339 01bc 0895 ret

    Note: UART_Sendbyte is a spinloop-wait before writing to the UDR. No interrupts are used for UART handling.
    Last edited: Aug 21, 2007
  4. dave

    Dave New Member

    Jan 12, 1997

  5. eblc1388

    eblc1388 Active Member

    Jan 25, 2005

    Code (text):

     269                    /* prologue: frame size=6 */
     270 013c FF92                  push r15
     271 013e 0F93                  push r16
     272 0140 1F93                  push r17
     273 0142 CF93                  push r28
     274 0144 DF93                  push r29
     275 0146 CDB7                  in r28,__SP_L__
     276 0148 DEB7                  in r29,__SP_H__
     277 014a 2697                  sbiw r28,6
     278 014c 0FB6                  in __tmp_reg__,__SREG__    ;<<<<< Store SREG
     279 014e F894                  cli /***************CLI IS HERE********/
     280 0150 DEBF                  out __SP_H__,r29
     281 0152 0FBE                  out __SREG__,__tmp_reg__  ;<<<<< Restore SREG
     282 0154 CDBF                  out __SP_L__,r28
     283                    /* prologue end (size=13) */
    You've been tricked by the generated ASM code. The global interrupt is controlled via bit7 of the SREG register value which is actually what cli()/sei() instruction do.

    The original value of SREG is safely stored in line 278 into __tmp_reg__, and is then restored afterwards in line 281. What operations happens to interrupt flag in between(line 279,280) will be restored to the previous value of SREG.

    I'll leave you to figure out why this SREG restore operation is carried out in between writing the high and low byte of a 16-bit register(stackpointer) rather than placed after finished with writing SP_L.
    Last edited: Aug 21, 2007
  6. boxer4

    boxer4 New Member

    Aug 18, 2007
    Nice... I totally missed that (well, not being totally familiar with the architecture yet hurts.) Sometimes having evil little architecture/microarchitecture tricks are what really annoys people...

    Not knowing enough about the uarch I bet the reason why the juxtaposition of instruction scheduling is the same reason as MIPS' delayed branch....

    and google confirms it.... ack. now i hate avr! :p hehehe j/k

Share This Page