This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430F149: MSPGCC optimizes out volatile variable accesses

Part Number: MSP430F149

Hello!
I'm working on program for custom version of MSP430F149 chip and face following problem:

There is peripheral device (read FIFO) mapped to 0x104, 0x106 addresses. GCC compile only first access to it and optimize out all others.

I tried next:

[VERSION_0]

func.h:

uint8_t read_fifo (uint16_t *d_l, uint16_t *d_h);


func.c:

extern volatile uint16_t RFIFO_L;
extern volatile uint16_t RFIFO_H;

uint8_t read_fifo (uint16_t *d_l, uint16_t *d_h) {
    *d_l = RFIFO_L;
    *d_h = RFIFO_H;
    return 0;
}


main.c:

#include "func.h"

int main () {
    // Stop the watchdog
    WDTCTL = WDTPW + WDTHOLD;

    uint16_t d[2];
    while (1) {
        read_fifo(d, d + 1);

        if (d[0] == some_value0 && d[1] == another_value0) {
            // some processing
        }
        else if (d[0] == some_value1 && d[1] == another_value1) {
            // another processing processing
        }
    }
}


/some/path/ti/msp430-gcc/include/msp430f149_symbols.ld:

// added two lines
PROVIDE(RFIFO_L        = 0x0104);
PROVIDE(RFIFO_H        = 0x0106);

RESULT: there is only first read access to FIFO, which returns zero as expectable, but no any other access (I expected that it will read fifo untill power off).

[VERSION_1]

remove extern volatile declarations from func.c and add to /some/path/ti/msp430-gcc/include/msp430f149.h two lines:

sfr_w(RFIFO_L);
sfr_w(RFIFO_H);


RESULT: the same

[VERSION_2]

modify SFR entry length in /some/path/ti/msp430-gcc/include/msp430f149.ld to 0x110 address:

SFR              : ORIGIN = 0x0000, LENGTH = 0x0110

RESULT: the same

[VERSION_3]

restore msp430f149_symbols.ld, msp430f149.ld and msp430f149.h, in func.c added defines instead of extern volatile:

#define RFIFO_L (*(volatile uint16_t *)(0x0104))
#define RFIFO_H (*(volatile uint16_t *)(0x0106))


RESULT: the same

What I did wrong and how to tell MSPGCC not optimize access to these volatile variables?

Compilation command:
/some/path/ti/msp430-gcc/bin/msp430-elf-gcc -I/some/path/ti/msp430-gcc/include -mmcu=MSP430F149 -Wall -O0 -msmall -flto -fwrapv -mtiny-printf -I./ -L/some/path/ti/msp430-gcc/include -Wl,-Map,test.map,--gc-sections,--just-symbols,msp430f149_symbols.ld,--oformat=ihex main.c func.c -o MSP430F149.a43

GCC version output:
gcc version 9.2.0 (Mitto Systems Limited - msp430-gcc 9.2.0.50)
Configured with: ../../gcc/configure --target=msp430-elf --enable-languages=c,c++ --disable-nls --enable-initfini-array --build=x86_64-pc-linux-gnu --host=x86_64-pc-linux-gnu --enable-target-optspace --enable-newlib-nano-formatted-io --with-pkgversion='Mitto Systems Limited - msp430-gcc 9.2.0.50'
Thread model: single

  • I think that the problem here isn't the locations declared as volatile but that access is via a function which results in that volatile specification being lost. Even if you added "volatile" to the declaration of the intermediate array d[], it wouldn't help.

  • Hmm, ok, I moved code with that volatiles vars into main function and it's been work until I switched optimization flag from -O0 to -Os. With -Os behaviour the same - first acces ok, all following optimized out.

    Also I think that restriction of using global volatile variables only in main function is  weird and it looks like GCC bug.

    David Schultz36 said:

    I think that the problem here isn't the locations declared as volatile but that access is via a function which results in that volatile specification being lost. Even if you added "volatile" to the declaration of the intermediate array d[], it wouldn't help.

  • What does the assembly code (--save-temps) look like? I don't see this behavior with v7.3.2.154 (Mitto), at least judging by the assembly code. (I don't have an F149.)

    read_fifo() is required to read from R_FIFO_x because of volatile, but it's required to write to *d_X because they're pointers (in most cases the optimizer must assume that an assignment through a pointer stores "anywhere and everywhere"). Or is main just not calling read_fifo() at all?

  • Thank you very much for -save-temps option, that the one I really need. After inspecting assembler code I found out that problem not in volatile accesses. It's delay function:

    inline void delayNtick (uint32_t nticks) {
      volatile unsigned long i = nticks;
      while (i--);
    }

    which've been compiled into that:

            ; start of prologue
            SUB.W   #4, R1
            ; end of prologue
            MOV.W   #0, @R1
            MOV.W   #0, 2(R1)
    .L2:
            MOV.W   @R1, R12
            MOV.W   2(R1), R13
            MOV.W   R12, R14
            ADD     #-1, R14 ; cy
            MOV.W   R13, R15
            ADDC    #-1, R15
            MOV.W   R14, @R1
            MOV.W   R15, 2(R1)
            BIS.W   R13, R12
            CMP.W   #0, R12 { JNE   .L2
            ; start of epilogue
            ADD.W   #4, R1
            RET
    

    So calling delayNtick(0) for minimal latency, will result in maximum latency. Rewriting delay function to while (i) --i; fixed the problem.

    Bruce McKenney47378 said:

    What does the assembly code (--save-temps) look like? I don't see this behavior with v7.3.2.154 (Mitto), at least judging by the assembly code. (I don't have an F149.)

    read_fifo() is required to read from R_FIFO_x because of volatile, but it's required to write to *d_X because they're pointers (in most cases the optimizer must assume that an assignment through a pointer stores "anywhere and everywhere"). Or is main just not calling read_fifo() at all?