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.

Cannot write to first byte at 0x1000 of Info Flash

Other Parts Discussed in Thread: MSP430G2231

Hello, first post and looking for insight. I'm trying to store four bytes of data in the Info Flash for retention between power cycles. This is an MSP430G2231 chip and I'm using the MSPGCC compiler.

 

I have no problem storing three of the four bytes, but the first byte never writes. When I look at 0x1000 with the debugger it reads:

1000: ff 02 03 04 ff ff ff ff ff ff ff ff ff ff ff ff |................|

Here's my code:

#include <io.h>  
unsigned char code1 = 1;
unsigned char code2 = 2;
unsigned char code3 = 3;
unsigned char code4 = 4;
// Function prototypes
void store_code();
void clear_flash(void);
int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
  if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF)                                     
  {  
    while(1);                               // If calibration constants erased
                                            // do not load, trap CPU!!
  } 
  BCSCTL1 = CALBC1_1MHZ;                    // Set DCO to 1MHz
  DCOCTL = CALDCO_1MHZ;
  FCTL2 = FWKEY + FSSEL_1 + FN2;             // MCLK/3 for Flash Timing Generator
  clear_flash();
  store_code();
  while(1);                                  // Repeat forever
}
void clear_flash(void)
{
  int *Flash_ptr;
  Flash_ptr = (int *)0x1000;	 //Point to Info Flash segment D
  FCTL3 = FWKEY;                            // Clear Lock bit
  FCTL1 = FWKEY + ERASE;                    // Set Erase bit
  *Flash_ptr = 0;                          // Dummy write to erase Flash segment D
  FCTL1 = FWKEY;                            // Clear WRT bit
  FCTL3 = FWKEY + LOCK;                     // Set LOCK bit
}
void store_code(void)
{
  char *Flash_ptr;
  Flash_ptr = (char *) 0x1000;	 //Point to Info Flash segment D
  FCTL3 = FWKEY;                            // Clear Lock bit
  FCTL1 = FWKEY + WRT;                      // Set WRT bit for write operation
  *Flash_ptr++ = code1;
  *Flash_ptr++ = code2;
  *Flash_ptr++ = code3;
  *Flash_ptr++ = code4;
  FCTL1 = FWKEY;                            // Clear WRT bit
  FCTL3 = FWKEY + LOCK;                     // Set LOCK bit
}

  • Mike,

    Double check your header file for the definition of FN2.  It's probably bit 2, which has a value of 4, resulting in a divisor of 5.  (Clock too slow @ 200kHz.)

    You probably meant FN = 2.  The header file might have a FN__2 symbol or similar, or you could just use bit FN1.

    Jeff

  • Thanks Jeff,

    I now see in the datasheet that the timing divider is set by FNx + 1. FN2 is defined in the header file as 0x0004 for a divisor of 5, as you mentioned. I've changed it to FN1 which is defined as 0x0002 for a divisor of 3. With a 1MHz clock this comes in at around 333 kHz which is within the range of the timing generator.

    Unfortunately, this didn't fix the problem. The first byte remains 0xFF although it should have been set to 0x01.

    Anything else catch your eye?

     

    I can work around the problem in this case. If I set the pointer to 0x1001 I have no trouble writing the four bytes and this will work for my application. But I would like to know why I'm observing this behavior for future projects.

     

    Thanks!

  • Nope, sorry.  I guess you might want to try a few experiments to get more clues.

    Try changing the starting address from 0x1000 to 0x1002.

    Try rearranging the four byte writes.

    Stuff like that.

    Jeff

  • Well I haven't gone through your code,. I have used the flash programming earlier what I noticed was if I closed the debug session very fast the flash was not programmed properly and when I let it be in debug mode for say 10 -15 seconds I got correct values. This is just suggestion, may be it would have been a coincidence and may not work for you.

  • I have no idea what's happening here. I can only guess.

    A possible reason is code optimisation.

    Neither Flash_ptr nor the values it poitns to are marked volatile. So the compiler can do any optimisation it wants. For example, it does not know that the writes need to be done between setting and clearing the WRT bit. There is no (for the compiler) obvious relation between the memory you're writing to and the flash controller registers. So the optimisation process may as well first do all the writes to FCTL1 and 3 (holding the address in a register) and then do the writes by holding Flash_ptr in the same register. The compiler will not rearrange the access to the FCTL registers as these are defined volatile, which means that the order of read and write matters, thevariable may change its value without the function knowing it (by hardware or through an ISR) and even subsequent writes will possibly have side-effects.

    The writes to Flash_ptr are not marked this way, so the compiler can do with it what it wants and when it wants.

    It would be interesting to see the generated assembly code. It will perhaps tell what really happens

    My guess is that the compiler does the first write right when the start value is assigned to Flash_ptr (before the WRT bit has been set). Which will of course have no effect.

  • I really should have thought of that. I'm no expert at reading assembly, but I can see that the call to store the first number within the main function is missing. It does, however, show up the store_code() function below. Do I need a volatile keyword on the pointer and if so, why?

    	.file	"test.c"
    	.arch msp430x2012
    	.text
    	.p2align 1,0
    .global	main
    	.type	main,@function
    /***********************
     * Function `main' 
     ***********************/
    main:
    	mov	#__stack, r1 
    	/* prologue ends here (frame size = 0) */
    .L__FrameSize_main=0x0
    .L__FrameOffset_main=0x0
    	mov	#23168, &0x0120 
    	cmp.b	#llo(-1), &0x10FF
    	jeq	.L2
    	cmp.b	#llo(-1), &0x10FE
    	jne	.L3
    .L2:
    .L6:
    	jmp	.L6
    .L3:
    	mov.b	&0x10FF, r15
    	mov.b	r15, &0x0057
    	mov.b	&0x10FE, r15
    	mov.b	r15, &0x0056
    	mov	#llo(-23166), &0x012A 
    	mov	#llo(-23296), &0x012C 
    	mov	#llo(-23294), &0x0128 
    	mov	#0, &4096 
    	mov	#llo(-23296), &0x0128 
    	mov	#llo(-23280), &0x012C 
    /* #APP */
     ;  46 "test.c" 1
    	nop
     ;  0 "" 2
    /* #NOAPP */
    	mov	#llo(-23296), &0x012C 
    	mov	#llo(-23232), &0x0128 
    	mov.b	&code2, &4097
    	mov.b	&code3, &4098
    	mov.b	&code4, &4099
    	mov	#llo(-23296), &0x0128 
    	mov	#llo(-23280), &0x012C 
    .L4:
    	jmp	.L4
    .Lfe1:
    	.size	main,.Lfe1-main
    ;; End of function 
    	.p2align 1,0
    .global	clear_flash
    	.type	clear_flash,@function
    /***********************
     * Function `clear_flash' 
     ***********************/
    clear_flash:
    	/* prologue ends here (frame size = 0) */
    .L__FrameSize_clear_flash=0x0
    .L__FrameOffset_clear_flash=0x0
    	mov	#llo(-23296), &0x012C 
    	mov	#llo(-23294), &0x0128 
    	mov	#0, &4096 
    	mov	#llo(-23296), &0x0128 
    	mov	#llo(-23280), &0x012C 
    	/* epilogue: not required */
    	ret
    .Lfe2:
    	.size	clear_flash,.Lfe2-clear_flash
    ;; End of function 
    	.p2align 1,0
    .global	store_code
    	.type	store_code,@function
    /***********************
     * Function `store_code' 
     ***********************/
    store_code:
    	/* prologue ends here (frame size = 0) */
    .L__FrameSize_store_code=0x0
    .L__FrameOffset_store_code=0x0
    	mov	#llo(-23296), &0x012C 
    	mov	#llo(-23232), &0x0128 
    	mov.b	&code1, &4096
    	mov.b	&code2, &4097
    	mov.b	&code3, &4098
    	mov.b	&code4, &4099
    	mov	#llo(-23296), &0x0128 
    	mov	#llo(-23280), &0x012C 
    	/* epilogue: not required */
    	ret
    .Lfe3:
    	.size	store_code,.Lfe3-store_code
    ;; End of function 
    .global	code1
    	.data
    	.type	code1,@object
    	.size	code1,1
    code1:
    	.byte	1
    .global	code2
    	.type	code2,@object
    	.size	code2,1
    code2:
    	.byte	2
    .global	code3
    	.type	code3,@object
    	.size	code3,1
    code3:
    	.byte	2
    .global	code4
    	.type	code4,@object
    	.size	code4,1
    code4:
    	.byte	5
    /*********************************************************************
     * File test.c: code size: 2 words (0x2)
     * incl. words in prologues: 0, epilogues: 2
     *********************************************************************/

     

  • Does the generated code look any different if you eliminate the while (1); at the end of main( )?

    Sorry it sounds weird but if you don't mind trying it.  The optimizer behaves very differently when a function never returns.

    Jeff

  • Actually, declaring the pointer to a char as volatile fixes this code right up. I'm just confused about why that pointer needs to be volatile.

    Here's the working funtion:

    void store_code(void)
    {
      volatile char *Flash_ptr;
      Flash_ptr = (char *) 0x1000;		//Point to Info Flash segment D
      FCTL3 = FWKEY;                            // Clear Lock bit
      FCTL1 = FWKEY + WRT;                      // Set WRT bit for write operation
      *Flash_ptr++ = code1;
      *Flash_ptr++ = code2;
      *Flash_ptr++ = code3;
      *Flash_ptr++ = code4;
      FCTL1 = FWKEY;                            // Clear WRT bit
      FCTL3 = FWKEY + LOCK;                     // Set LOCK bit
    }

  • Where I was heading was that it _doesn't_ need to be volatile.

    However, making it volatile fixes the problem because it stops some optimizations that are a direct result of the while (1) statement at the end of main( ).

    If you don't mind trying the original code (without the volatile keyword) and just eliminating the while (1) statement at the end of main( ).

    Jeff

  • inlined and "optimized" your code ends up looking like:

    *(int *0x1000) = 0; // from clear_flash
    *(char *0x1000) = code1;  //From store_code
    *(char *0x1001) = code2;
    *(char *0x1002) = code3;
    *(char *0x1003) = code4;

    It's not surprising that without "volatile" it decides that it can eliminate one of the writes to 0x1000.

    Though it didn't seem to do it in a correct or sensible way!  I would have expected it to get rid of the dummy write in clear_flash, causing even more mysterious symptoms.

    (I'm not very happy with gcc's inlining-happy habits.  Especially on tiny microcontrollers!)

     

  • westfw said:
    It's not surprising that without "volatile" it decides that it can eliminate one of the writes to 0x1000.


    Indeed. The compiler does not know that between the two writes to 0x1000 somethign important happened (the reprogramming of the flash controller). Since the first write is a word write, the second a byte write, it will combine both into one and drop the assumed superfluous second write.
    This happens because of several reaons:
    1) both writes go at the same address (if you'd initiate the clear by writing to 0x11fe, the code would work) and
    2) the two fucntions are so small that the compiler decided that inlining them would be smaller and faster than actually calling the ffunctions.

    If the functions would be called, there wouldn't be a problem too, as write function does not know that the erase function had been just called, so no optimization is possible.

    On MSPGCC, you can force such critical instructions by adding a __volatile__ attribute to an instruction such as ASM or (I believe, but never tried) do/while/{}, disabling optimization for this.

    Or you define the data you point to (and not the pointer itself) as volatile. But as long as I work with C, I'm still unsure each time about where to put the qualifiers in a pointer declaration (is it a constant pointer to variable data or a variablepointer to constant data? I have to look it up each time again)

    Anyway, I think the compiler is messing up things here. Let aside the fact that it is flash and teh flash controlle rprogramming, the 'optimized' code will not do what it should. If there were ram at 0x1000, this code would result in 0x00 being written to 0x1000 and not the content of code1, which is 0x01. But it should be 0x01 as this is written last. But OTOH one could say that 0x1000 isn't volatile and since it isn't used anywhere in the code later, ALL writes to 0x100x could be eliminated totally.

**Attention** This is a public forum