Antonio
Antonio

Reputation: 651

Problem with avr-g++ 13.3.0 passing const char array as parameter in ATmega4809 (40 pins)

The following code doesn't work in ATmega4809:


#include <avr/io.h>
#include <util/delay.h>

void f(const char str[])
{
    if (str[0] == 'a'){ // <-- here is the problem!!! The program thinks str[0] != 'a'
        PORTC.OUT |= PIN0_bm;
        _delay_ms(500);
        PORTC.OUT &= ~PIN0_bm;
        _delay_ms(500);

    } else { // blink a led in pin PC1
    
        PORTC.OUT |= PIN1_bm;
        _delay_ms(500);
        PORTC.OUT &= ~PIN1_bm;
        _delay_ms(500);

    }

}

int main()
{
    PORTC.DIR |= PIN0_bm; // init pin PC0 
    PORTC.DIR |= PIN1_bm; // init pin PC1

    while (1) { f("abc"); }

}

When I pass a const char array to a function (in the code when I pass str to function f) the value of the string is wrong (in this case, the value of str[0] it's not 'a' but a different char). To implement a minimal program to show the problem I'm using 2 leds: if str[0] is 'a' (the right value) one led blinks; if it is not, other led blinks. The problem is that the wrong led blinks.

I detect this problem in a different program that compile and work fine with atmega328p but has this weird behavior with atmega4809 (of 40 pins).

What am I doing wrong? It is a compiler problem?

================

I am compiling it with avr-g++ 13.3.0:

avr-g++ -Os -std=c++23 -Wall -pedantic  -funsigned-char -funsigned-bitfields -fshort-enums  -ffunction-sections -fdata-sections   -save-temps -fverbose-asm -DMCU=atmega4809 -DF_CPU=2666667UL  -mmcu=atmega4809 -c -o test.o test.cpp

I'm using the atmega4809 of 40 pins. I know that I have to initialize pins PB[5:0] and PC[7:6] and I do it in my program. I don't show the init function in the previous code because the program behaves the same calling that init function or not.

============

I'm not used to read assembler, but here (I think) is the relevant asm code:

The string "abc":

.LC0:
    .string "abc"
    .section    .text.startup.main,"ax",@progbits

The call of f("abc"):

    ldi r24,lo8(.LC0)    ; ,
    ldi r25,hi8(.LC0)    ; ,
    call _Z1fPKc     ; 

and the if of f function:

    .section    .text._Z1fPKc,"ax",@progbits
.global _Z1fPKc
    .type   _Z1fPKc, @function
_Z1fPKc:
.L__stack_usage = 0
 ;  main.cpp:43:     if (str[0] == 'a'){
    movw r30,r24     ; , tmp62
    ld r24,Z         ;  *str_9(D), *str_9(D)
    cpi r24,lo8(97)  ;  *str_9(D),
    brne .L3         ; ,
    // blink PC0
    // ...

==========

EDIT: Adding the result of avr-objdump -D test.elf (relevant parts, only):

Disassembly of section .rodata:

0000414c <_end-0x7fe6b4>:
    414c:   61 62           ori r22, 0x21   ; 33
    414e:   63 00           .word   0x0063  ; ????

Why avr-objdump writes those signs: ???? <-- is this the problem?

Call of f in main:

 13e:   8c e4           ldi r24, 0x4C   ; 76
 140:   91 e4           ldi r25, 0x41   ; 65
 142:   0e 94 5c 00     call    0xb8    ; 0xb8 <_Z1fPKc>

Function f (till if...):

000000b8 <_Z1fPKc>:
  b8:   fc 01           movw    r30, r24
  ba:   80 81           ld  r24, Z
  bc:   81 36           cpi r24, 0x61   ; 97

=========

EDIT2: I've written the same program in atmega328p and it works fine. The difference in the result of avr-objdump -D between atmega328p and atmega4809 is that in the case of atmega328p the "abc" string is store in the .data section, while in the case of atmega4809 it stores it in the .rodata section.

==========

EDIT3: The problem is with the .rodata section. If I change the code using a variable str:

const char str[] = "abc";

and then call f(str) the program doesn't work, but if I force to write str into the .data section:

const char str[] __attribute__((section(".data")))  = "abc";

the progam works.

Is this a problem with avr-gcc 13.3.0?

Upvotes: 3

Views: 79

Answers (1)

emacs drives me nuts
emacs drives me nuts

Reputation: 3983

The problem is with the .rodata section. [...] but if I force to write str into the .data section [...] the progam works. Is this a problem with avr-gcc 13.3.0?

No. With almost certainty, you forgot to upload the .rodata section to the ATmega4809. Notice that avrxmega3 devices see program memory in the RAM address space, and hence .rodata is not a part of .data.

Guessing further, you are brewing an Intel Hex file to flash with avrdude or similar. Just flash the ELF file will prevent such headaches, since ELF knows what's part of flash memory.

To see what I mean, you can disassemble the ATmega4809 ELF file like:

$ avr-objdump -h -d -S -r -j .text -j .data -j .rodata -j .eeprom <file>.elf

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .data         0000001c  00802800  00000176  0000022a  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  1 .text         00000172  00000000  00000000  000000b4  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .rodata       00000004  00004172  00000172  00000226  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
...

Notice there is a dedicated .rodata output section. It is required since VMAs must be at an offset of 0x4000 after the LMAs, which would not be the case if .rodata input sections were allocated to the .text output section

Upvotes: 1

Related Questions