cgdbavravr-gcc

AVR-GCC compilation hell


I have serious problems with compiling code for the Atmega328p.

#include <stdint.h>
#include <avr/io.h>

const uint32_t WHEELS_TIME_TO_GO_1_CM_MS = 10;

int main(void)
{
    for (;;) {
        uint32_t x = (uint32_t)WHEELS_TIME_TO_GO_1_CM_MS * 100;
        uint32_t y = x+3;
        for (uint32_t i = 0; i < y; ++i) {
            PORTD = i & 0xff;
        }
    }
    return 0;
}

For compilation:

avr-gcc -c -Wall -Wextra -g -O0 -DF_CPU=8000000UL -mmcu=atmega328p  testwheel.c -o testwheel.o
avr-gcc -o testwheel.elf testwheel.o -Wall -Wextra -g -O0 -DF_CPU=8000000UL -mmcu=atmega328p

I use avr-gcc (GCC) 5.4.0.

Guess what x is! It is not 1000, but 232, which is actually 1000%256 or just the lower byte of 1000.

So I continued the hell of trying to spot the error i gdb and the assembly code which I post here below.

Dump of assembler code for function main:
   0x00000096 <+0>: push    r28
   0x00000098 <+2>: push    r29
   0x0000009a <+4>: in  r28, 0x3d   ; 61
   0x0000009c <+6>: in  r29, 0x3e   ; 62
   0x0000009e <+8>: sbiw    r28, 0x08   ; 8
   0x000000a0 <+10>:    in  r0, 0x3f    ; 63
   0x000000a2 <+12>:    cli
   0x000000a4 <+14>:    out 0x3e, r29   ; 62
   0x000000a6 <+16>:    out 0x3f, r0    ; 63
   0x000000a8 <+18>:    out 0x3d, r28   ; 61
   0x000000aa <+20>:    ldi r18, 0x0A   ; 10
   0x000000ac <+22>:    ldi r19, 0x00   ; 0   
   0x000000ae <+24>:    ldi r20, 0x00   ; 0
   0x000000b0 <+26>:    ldi r21, 0x00   ; 0
   0x000000b2 <+28>:    ldi r24, 0x64   ; 100
   0x000000b4 <+30>:    ldi r25, 0x00   ; 0
   0x000000b6 <+32>:    movw    r26, r24
   0x000000b8 <+34>:    call    0x10e   ;  0x10e <__muluhisi3>
   0x000000bc <+38>:    movw    r26, r24
   0x000000be <+40>:    movw    r24, r22
   0x000000c0 <+42>:    std Y+5, r24    ; 0x05
   0x000000c2 <+44>:    std Y+6, r25    ; 0x06
   0x000000c4 <+46>:    std Y+7, r26    ; 0x07
   0x000000c6 <+48>:    std Y+8, r27    ; 0x08
   0x000000c8 <+50>:    std Y+1, r1 ; 0x01
   0x000000ca <+52>:    std Y+2, r1 ; 0x02
   0x000000cc <+54>:    std Y+3, r1 ; 0x03
   0x000000ce <+56>:    std Y+4, r1 ; 0x04
   0x000000d0 <+58>:    rjmp    .+32        ;  0xf2 <main+92>
   0x000000d2 <+60>:    ldi r24, 0x2B   ; 43
   0x000000d4 <+62>:    ldi r25, 0x00   ; 0
   0x000000d6 <+64>:    ldd r18, Y+5    ; 0x05
   0x000000d8 <+66>:    movw    r30, r24
   0x000000da <+68>:    st  Z, r18
   0x000000dc <+70>:    ldd r24, Y+1    ; 0x01
   0x000000de <+72>:    ldd r25, Y+2    ; 0x02
   0x000000e0 <+74>:    ldd r26, Y+3    ; 0x03
   0x000000e2 <+76>:    ldd r27, Y+4    ; 0x04
   0x000000e4 <+78>:    adiw    r24, 0x01   ; 1
   0x000000e6 <+80>:    adc r26, r1
   0x000000e8 <+82>:    adc r27, r1
   0x000000ea <+84>:    std Y+1, r24    ; 0x01
   0x000000ec <+86>:    std Y+2, r25    ; 0x02
   0x000000ee <+88>:    std Y+3, r26    ; 0x03
   0x000000f0 <+90>:    std Y+4, r27    ; 0x04
   0x000000f2 <+92>:    ldd r18, Y+1    ; 0x01
   0x000000f4 <+94>:    ldd r19, Y+2    ; 0x02
   0x000000f6 <+96>:    ldd r20, Y+3    ; 0x03
   0x000000f8 <+98>:    ldd r21, Y+4    ; 0x04
   0x000000fa <+100>:   ldd r24, Y+5    ; 0x05
   0x000000fc <+102>:   ldd r25, Y+6    ; 0x06
   0x000000fe <+104>:   ldd r26, Y+7    ; 0x07
   0x00000100 <+106>:   ldd r27, Y+8    ; 0x08
   0x00000102 <+108>:   cp  r18, r24
   0x00000104 <+110>:   cpc r19, r25
   0x00000106 <+112>:   cpc r20, r26
   0x00000108 <+114>:   cpc r21, r27
   0x0000010a <+116>:   brcs    .-58        ;  0xd2 <main+60>
   0x0000010c <+118>:   rjmp    .-100       ;  0xaa <main+20>
(gdb) x/20x &y 
0x8008f8:   0x000003eb  0x4700ff08  Cannot access memory at address 0x800900

Looking at it, it correctly computes x as 232 low and 3 high stored in r24-r25, but x is not used in the right way. Somehow it is truncated and uint32_t is ignored. The memory around y is also suspicious. Atmega328p's SRAM does not reach 0x8008f8! Further it never enters the loop, but jumps back and forth like this:

9           uint32_t x = (uint32_t)WHEELS_TIME_TO_GO_1_CM_MS * 100;
(gdb) n
10          uint32_t y = x+3;
(gdb) n
11          for (uint32_t i = 0; i < y; ++i) {
(gdb) s
10          uint32_t y = x+3;
(gdb) 
11          for (uint32_t i = 0; i < y; ++i) {
(gdb) 
10          uint32_t y = x+3;
(gdb) s
11          for (uint32_t i = 0; i < y; ++i) {
(gdb) 
10          uint32_t y = x+3;

More crazy things going on:

(gdb) p y-1
$2 = 1002
(gdb) p y
$3 = 235
(gdb) p y+1
$4 = 1004
(gdb) p sizeof(y)
$5 = 4
(gdb) p sizeof(i)
$6 = 4
(gdb) p sizeof(x)
$7 = 4

Is my tool chain broken/corrupt or what is wrong?


Solution

  • SOLUTION:

    After installing a recent tool chain with a new avr-gdb and avr-gcc, the strange behavior disappeared, at least in the debugger. I tried to use the new avr-gdb with the file compiled with the older avr-gcc and much of the strange errors was still there. Using the new compiler worked fine though. The assembly looks very different in many places, although about the same in the function main. If both the gdb and gcc where contributing to the behavior is still hard to say, but I did find a bug in my original code, which was related to the _delay_us function. From the documentation it says:

    In order for these functions to work as intended, compiler optimizations must be enabled, and the delay time must be an expression that is a known constant at compile-time. If these requirements are not met, the resulting delay will be much longer (and basically unpredictable), and applications that otherwise do not use floating-point calculations will experience severe code bloat by the floating-point library routines linked into the application.

    I had the optimizations on when I discovered the errors, but I was calculating the delays at run time. This was the main cause of the strange behaviors. So I fixed all my problems after this. Still, I'm happy I moved away from a 10 year old tool chain that obviously produced buggy code that even the new state of the art debugger couldn't understand.