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.

#pragma PERSISTENT is broken?

Other Parts Discussed in Thread: MSP430FR5969

I just ran out of memory. After some digging I learned that the compiler likes to put all variable variables into SRAM unless otherwise instructed. Fair enough, off I go, find SLAA628 which also points to SLAU132L . There's are also some threads about people having trouble with FRAM.

Now after some fiddling I found out it seems #pragma PERSISTENT(foo) actually does nothing. In other words, this does not work:

#pragma PERSISTENT(ADC_buffer)
ADC_buf ADC_buffer;

In the header file I've declared

typedef struct ADC_buffer_{
	USHORT volatile sensor[SENSORS];
	char result[256];
	char sensor_value[SENSORS][SENSOR_STRING];
}ADC_buf;

extern ADC_buf ADC_buffer;

If I change the PERSISTENT to use explicity .TI.persistent data section, it'll work just fine:

#pragma DATA_SECTION(ADC_buffer, ".TI.persistent")
ADC_buf ADC_buffer;

I thought the PERSISTENT does that as well? Is it broken or I'm doing something wrong?

I'm using MSP430FR5969 and  CCS Version: 6.2.0.00050 with 2.0.0.15 FRAM modules. 

  • Hi Olli,

    I've moved your thread into our Compiler forum to get you the best answer.

    Regards,

    James

    MSP Customer Applications
  • Funny, I just tried building some code with the same version CCS and it seems to work for me.

    #pragma PERSISTENT ( count )
    uint16_t count = 0;
    
    typedef struct ADC_buffer {
    	uint16_t count2;
    } ADC_buffer;
    
    #pragma PERSISTENT ( ADC_buf )
    ADC_buffer ADC_buf = { 0 };
    

    My code originally just defined the scalar "count" as persistent and the tools automatically placed it into "FRAM" memory at 4400h. I thought, 'maybe structures were handled differently?' So I added a simple structure definition to my program as shown above. Once again, both ADC_buf and count were linked into FRAM.

    As James referenced, maybe the compiler team will be able to help explain why your code isn't responding correctly - though, they'll probably need more information in order to replicate the problem. For example, it would be helpful to provide a simple example project that fails to build correctly (which includes all the files needed to build the project).

    Good Luck,
    Scott

  • I cannot reproduce the error.  Here are more details on what I tried.

    I used the example code from the first post to create this file ...

    /* file.c */
    typedef unsigned short USHORT;
    #define SENSORS 10
    #define SENSOR_STRING 100
    
    typedef struct ADC_buffer_{
        USHORT volatile sensor[SENSORS];
        char result[256];
        char sensor_value[SENSORS][SENSOR_STRING];
    }ADC_buf;
    
    extern ADC_buf ADC_buffer;
    
    #pragma PERSISTENT(ADC_buffer)
    ADC_buf ADC_buffer;

    When I build it I get this diagnostic ...

    "file.c", line 15 (col. 9): warning: variable "ADC_buffer" was declared persistent and should be explicitly initialized

    And ADC_buffer is not placed in the section .TI.persistent.  

    So I change the last line to ...

    ADC_buf ADC_buffer = { 0 };

    This is a minimal (and likely incorrect) initialization of ADC_buffer.  The point is to silence the diagnostic.  Now ADC_buffer is placed in the section .TI.persistent.

    If you see different behavior, or something else that is a problem, please share the details in a similar manner.  Also show the compiler version, and the build options exactly as the compiler sees them.

    Thanks and regards,

    -George

  • That's what happened to me as well. In other words, yes I was doing something wrong.

    This still leaves the odd behavior - Uninitialized variable go to FRAM without issues when I use the DATA_SECTION pragma and there is no warning. With PERSISTENT it does not and I get a warning. What's the difference between the two? I thought PERSISTENT more or less does the same as #pragma DATA_SECTION(SPI_packet_e, "TI.persistent").

    Initializing the struct at program start could be a problem if you really do want non-volatile storage.
  • I went back and double-checked. I get this same warning when my scalar variable (i.e. 'count') is not initialized. Guess I never ran across this before as I always prefer to explicitly initialize my variables.

    Olli Mannisto said:
    Initializing the struct at program start could be a problem if you really do want non-volatile storage.

    I'm not sure why initializing your variables (struct or otherwise) would be a problem?

    That said, I'm not in favor with how this is handled. It seems either of these options would be better:

    • Allow non-initialized persistent variables (as Olli suggests).
    • Change this to an error (rather than a warning), because as it's implemented, an uninitialized variable does not get placed into persistent memory when the warning occurs. This is contrary to the pragma statement.

    Of course, others may disagree with me on the error vs warning comment. Everyone probably has their own preference.

    Take Care,
    Scott

  • If you want something truly persistent like "How many times watchdog has kicked the CPU over" or "How many overheat events have been logged" you don't obviously want them to be initialized at start.

    Incidentally, how would I go about doing "once and once only" type initialization except by implementing a "factory reset" style command for the external I/O? Can I somehow set a variable value only during flashing?
  • Olli Mannisto said:
    Can I somehow set a variable value only during flashing?

    The PERSISTENT pragma should achieve that, since it is described in SLAU132N as:

    The PERSISTENT pragma is similar to the NOINIT pragma, except that it may only be used with statically-initialized variables. Persistent variables disable startup initialization; they are given an initial value when the code is loaded, but are never again initialized.

  • Olli Mannisto said:
    If you want something truly persistent like "How many times watchdog has kicked the CPU over" or "How many overheat events have been logged" you don't obviously want them to be initialized at start.

    Actually, I would say you definitely want the variable to be initialized at the "start". In the non-volatile memory chapter of the MSP430 Workshop, our lab exercise uses FRAM (or FLASH) to count the number of times the system has been reset. The easiest solution is to use the Persistent pragma with the FRAM devices. As the solution shows, we initialize count to 0.

    As I'm writing this, I realize the confusion may be in how #prama PERSISTENT works. It actually does two things:

    1. Locates the variable into persistent memory. That is, the ".TI.persistent" section as you indicated.
    2. It provides "load time" initialization for the variable.

    In other words, the value is only initialized when the program is loaded, but not at every reset, as you would find for normal C variables. So I think using the PERSISTENT pragma with initialization is what you really want to do. 

    You can find this described in the MSP430 Compiler User's Guide in the section titled "The NOINIT and PERSISTENT Pragmas". (It's on page 107 of the current version of the document.)

    Rereading this section, I stand corrected about my earlier comments. PERSISTENT does require a load-time initialization value. (Though I'd say an error would still be a more correct response for the compiler, as opposed to a warning.) 

    Olli Mannisto said:
    Incidentally, how would I go about doing "once and once only" type initialization except by implementing a "factory reset" style command for the external I/O? Can I somehow set a variable value only during flashing?

    Based on the definition above, the PERSISTENT pragma is still the right solution here. In fact, that pragma's "load-time" initialization is critical feature. For example:

    #pragma PERSISTENT ( DoItOnce )
    uint16_t DoItOnce = 0;

    Setting DoItOnce to zero will only occur when the firmware code is written to the device; and it won't happen again after resetting the device. This means, that once you change the value of DoItOnce to "1", it won't change again. This is a great way to implement what you're asking for.

    Hope this helps,
    Scott