1
\$\begingroup\$

I'm using avr-gcc (12.1.0, built with this script: https://github.com/ZakKemble/avr-gcc-build) to compile for the ATtiny402 and encountering problems with code optimization. Specifically, the compiler appears to be 'optimizing out' repeated writes to register variables, in some circumstances. So, if I compile this with -Os (required for timing functionality, apparently, as well as for avoiding huge binaries), it doesn't work (i.e. the pin output does not change as expected):

void led (uint8_t status)
{
    if (status)
        PORTA.OUTSET |= PIN6_bm;
    else
        PORTA.OUTCLR |= PIN6_bm;
}

int main ()
{
    PORTA.DIRSET |= PIN6_bm;

    led (0);
    _delay_ms (1000);
    led (1);
    _delay_ms (100);
    led (0);
    _delay_ms (100);
    led (1);
    _delay_ms (100);
    led (0);
    _delay_ms (100);
    led (1);
    _delay_ms (100);
    led (0);

    return 0;
}

The pin output goes high, but does not go low afterwards. The above does work if I turn off gcc optimization.

The following works just fine, both with and without optimization:

int main ()
{
    PORTA.DIRSET |= PIN6_bm;

    PORTA.OUTCLR |= PIN6_bm;
    _delay_ms (1000);
    PORTA.OUTSET |= PIN6_bm;
    _delay_ms (100);
    PORTA.OUTCLR |= PIN6_bm;
    _delay_ms (100);
    PORTA.OUTSET |= PIN6_bm;
    _delay_ms (100);
    PORTA.OUTCLR |= PIN6_bm;
    _delay_ms (100);
    PORTA.OUTSET |= PIN6_bm;
    _delay_ms (100);
    PORTA.OUTCLR |= PIN6_bm;

    return 0;
}

I've taken a (very quick) look at the generated assembly language, and it does look like this is an optimization feature (it seems to have created two different versions of the led function).

I can avoid the problem by applying #pragma directives to the function, but it feels 'messy' and I would very much like to understand what's happening here.

I'm not sure this is the best place to post this, as it's a compiler issue rather than hardware, but I'm hoping that somebody here might have seen this before.

EDIT to add excerpt of generated assembly language (optimized):

_Z3ledh.part.0:
/* prologue: function */
/* outgoing args size = 0 */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
    ldi r30,0
    ldi r31,lo8(4)
    ldd r24,Z+5
    ori r24,lo8(64)
    std Z+5,r24
/* epilogue start */
    ret
    .size   _Z3ledh.part.0, .-_Z3ledh.part.0

...

_Z3ledh:
/* prologue: function */
/* outgoing args size = 0 */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
    tst r24
    breq .L6
    rcall _Z3ledh.part.0
    ret
.L6:
    lds r24,1030
    ori r24,lo8(64)
    sts 1030,r24
/* epilogue start */
    ret

... 

main:
/* prologue: function */
/* outgoing args size = 0 */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
    lds r24,1025
    ori r24,lo8(64)
    sts 1025,r24
    ldi r24,0
    rcall _Z3ledh
    ldi r18,lo8(3199999)
    ldi r24,hi8(3199999)
    ldi r25,hlo8(3199999)
1:  subi r18,1
    sbci r24,0
    sbci r25,0
    brne 1b
    rjmp .
    nop
    rcall _Z3ledh.part.0
    ldi r18,lo8(319999)
    ldi r24,hi8(319999)
    ldi r25,hlo8(319999)
1:  subi r18,1
    sbci r24,0
    sbci r25,0
    brne 1b
    rjmp .

EDIT: the following is the entire assembly generated for the original, non-working example:

    .file   "flashing-led.cpp"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__CCP__ = 0x34
__tmp_reg__ = 0
__zero_reg__ = 1
    .text
    .type   _Z3ledh.part.0, @function
_Z3ledh.part.0:
/* prologue: function */
/* outgoing args size = 0 */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
    ldi r30,0
    ldi r31,lo8(4)
    ldd r24,Z+5
    ori r24,lo8(64)
    std Z+5,r24
/* epilogue start */
    ret
    .size   _Z3ledh.part.0, .-_Z3ledh.part.0
.global _Znwj
    .type   _Znwj, @function
_Znwj:
/* prologue: function */
/* outgoing args size = 0 */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
    rcall malloc
/* epilogue start */
    ret
    .size   _Znwj, .-_Znwj
.global _ZdlPv
    .type   _ZdlPv, @function
_ZdlPv:
/* prologue: function */
/* outgoing args size = 0 */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
    rcall free
/* epilogue start */
    ret
    .size   _ZdlPv, .-_ZdlPv
.global _ZdlPvj
    .type   _ZdlPvj, @function
_ZdlPvj:
/* prologue: function */
/* outgoing args size = 0 */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
    rcall free
/* epilogue start */
    ret
    .size   _ZdlPvj, .-_ZdlPvj
.global _Z3ledh
    .type   _Z3ledh, @function
_Z3ledh:
/* prologue: function */
/* outgoing args size = 0 */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
    tst r24
    breq .L6
    rcall _Z3ledh.part.0
    ret
.L6:
    lds r24,1030
    ori r24,lo8(64)
    sts 1030,r24
/* epilogue start */
    ret
    .size   _Z3ledh, .-_Z3ledh
    .section    .text.startup,"ax",@progbits
.global main
    .type   main, @function
main:
/* prologue: function */
/* outgoing args size = 0 */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
    ldi r30,0
    ldi r31,lo8(4)
    ldd r24,Z+1
    ori r24,lo8(64)
    std Z+1,r24
    ldi r24,0
    rcall _Z3ledh
    ldi r18,lo8(3199999)
    ldi r24,hi8(3199999)
    ldi r25,hlo8(3199999)
1:  subi r18,1
    sbci r24,0
    sbci r25,0
    brne 1b
    rjmp .
    nop
    rcall _Z3ledh.part.0
    ldi r18,lo8(319999)
    ldi r24,hi8(319999)
    ldi r25,hlo8(319999)
1:  subi r18,1
    sbci r24,0
    sbci r25,0
    brne 1b
    rjmp .
    nop
    ldi r24,0
    rcall _Z3ledh
    ldi r18,lo8(319999)
    ldi r24,hi8(319999)
    ldi r25,hlo8(319999)
1:  subi r18,1
    sbci r24,0
    sbci r25,0
    brne 1b
    rjmp .
    nop
    rcall _Z3ledh.part.0
    ldi r18,lo8(319999)
    ldi r24,hi8(319999)
    ldi r25,hlo8(319999)
1:  subi r18,1
    sbci r24,0
    sbci r25,0
    brne 1b
    rjmp .
    nop
    ldi r24,0
    rcall _Z3ledh
    ldi r18,lo8(319999)
    ldi r24,hi8(319999)
    ldi r25,hlo8(319999)
1:  subi r18,1
    sbci r24,0
    sbci r25,0
    brne 1b
    rjmp .
    nop
    rcall _Z3ledh.part.0
    ldi r18,lo8(319999)
    ldi r24,hi8(319999)
    ldi r25,hlo8(319999)
1:  subi r18,1
    sbci r24,0
    sbci r25,0
    brne 1b
    rjmp .
    nop
    ldi r24,0
    rcall _Z3ledh
    ldi r24,0
    ldi r25,0
/* epilogue start */
    ret
    .size   main, .-main
    .ident  "GCC: (GNU) 12.1.0"
\$\endgroup\$
1

1 Answer 1

2
\$\begingroup\$

While having no idea why the code presented shouldn't work,
led(uint8_t status) should look

void led (uint8_t status)
{
    if (status)
        PORTA.OUTSET = PIN6_bm;
    else
        PORTA.OUTCLR = PIN6_bm;
}

to take advantage of those separate SET&CLR addresses.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you very much; that fixed the problem. \$\endgroup\$
    – andy28
    Mar 24, 2023 at 13:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.