Continue to Site

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.

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

Avr-Gcc sillyness?

Status
Not open for further replies.

boxer4

New Member
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 ...
 
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..
 
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 */
UART_SendByte('-');
Data=-Data;
}
itoa(Data,tempbuf,10); /* using avr-libc to convert */
while(tempbuf[ptr]) { /* output string to UART */
UART_SendByte(tempbuf[ptr]);
++ptr;
}
}

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:
boxer4 said:
Actually the CLI's are being inserted automatically by gcc.
Code:
 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:
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
 
Status
Not open for further replies.

Latest threads

Back
Top