In Application Flash Programming Error

cancel
Showing results for 
Search instead for 
Did you mean: 

In Application Flash Programming Error

Jump to solution
876 Views
robertogiovinet
Contributor III


Hello Everyone

I've a problem: I have to store periodically some parameters in the Non Volatile Memory. I use MKE02Z32VLC2 MCU. In previous revision of my application software I've used EEPROM space to store data, and all worked fine. Now I need to use free flash space because of the speed needed to store parameters: I need to create two separated copies of parameters so if user switch off the device while a write sequence is in progress, corrupted data will affect only one of the copy, and software can use the second one. In EEPROM this is not possible because I can only erase block (256 Bytes) or sector (2 bytes). But if I delete sector by sector in EEPROM the time needed is too much for the application.

If I use EEPROM commands I've no problem, the algorithm works fine.

The problem is when I use FLASH commands, such as ERASE SECTOR. I'm sure to use a sector that is not used by application. I've disabled interrupts, but there's still a problem, and the core goes in the lockup state and MCU resets.

I've tried to insert some delay but the problem is still present. I can't understand where's the matter.

Can anyone help me?

Thank you a lot!

Here the software I wrote:

...

...

     DisableInterrupts;

     Error = FALSE;

     do {

           Error = Flash_EraseSector(FLASH_DATA_START );

      } while (Error != FLASH_ERR_SUCCESS);

...

...

And here the function Flash_EraseSector:

uint16_t Flash_EraseSector(uint16_t FLASH_Address)

{

  uint16_t Error = FLASH_ERR_SUCCESS;

  // Check address to see if it is aligned to 4 bytes

  // Global address [1:0] must be 00.

  if (FLASH_Address & 0x03)

       {

            Error = FLASH_ERR_INVALID_PARAM;

            return (Error);

       }

  FTMRH_FSTAT = 0x30; // Clear error flags

  FTMRH_FCCOBIX = 0x0;   // Write index to specify the command code to be loaded ed first part of Global address

  FTMRH_FCCOBHI = FLASH_CMD_ERASE_SECTOR; // FLASH command 0x0A

  FTMRH_FCCOBLO = 0x0; // Global Address[23:16] = 0x0

  FTMRH_FCCOBIX = 0x1; // Write index to specify the Global Address

  FTMRH_FCCOBHI = (uint8_t) (FLASH_Address >> 8); // Global Address[15:8]

  FTMRH_FCCOBLO = (uint8_t) (FLASH_Address); // Global Address[7:0]

  FTMRH_FSTAT = 0x80; // Launch the command

  while (!(FTMRH_FSTAT & FTMRH_FSTAT_CCIF_MASK)){ }; // Wait till command is completed

  if(FTMRH_FSTAT & FTMRH_FSTAT_ACCERR_MASK) // Check error status

  {

  Error |= FLASH_ERR_ACCESS;

  }

  if(FTMRH_FSTAT & FTMRH_FSTAT_FPVIOL_MASK)

  {

  Error |= FLASH_ERR_PROTECTION;

  }

  if(FTMRH_FSTAT & FTMRH_FSTAT_MGSTAT0_MASK)

  {

  Error |= FLASH_ERR_MGSTAT0;

  }

  if(FTMRH_FSTAT & FTMRH_FSTAT_MGSTAT1_MASK)

  {

  Error |= FLASH_ERR_MGSTAT1;

  }

  return (Error);

}

Roberto

Labels (1)
0 Kudos
1 Solution
183 Views
bobpaddock
Senior Contributor III

Giovinetti Roberto wrote:

Hi

I found a solution.

I added the simple DisableInterrupts instruction in the RAM function, and now it works. I can't understand why, because the DisableInterrupts instruction was present in the calling function, and so interrupts was disabled yet. But now it works fine.

__attribute__( ( long_call, section(".data") ) ) void FlashLaunchCommand (void)

{

DisableInterrupts;

FTMRH_FSTAT = 0x80; // Launch the command

// Wait till command is completed

while (!(FTMRH_FSTAT & FTMRH_FSTAT_CCIF_MASK)){

}

}

There are two issues that may be going on.  First the while() loop has no side effects so the compiler may optimize it away.  To prevent this add this, GCC format need to match your own compiler format if not using GCC:

while (!(FTMRH_FSTAT & FTMRH_FSTAT_CCIF_MASK))

{

__asm__ __volatile__ ("nop");

}


Marking the nop as volatile will make sure the loop happens.

On some ARM processors a nop may not actually consume any time due to cache.

That is not really an issue here.  This should also fix the problem seen with the caller containing the disable IRQ, as the 'volatile' will prevent the compiler from reordering the code across sequence points.

I use the code below for GCC, which this form editor is going to mess up. :-(
Example that solves the above problems:

    ATOMIC_BLOCK( ATOMIC_RESTORESTATE )

    {

       while (!(FTMRH_FSTAT & FTMRH_FSTAT_CCIF_MASK))

         {

            __asm__ __volatile__ ("nop");

          }

     }

/** @(#)atomic.h     <18-Aug-2014 13:31:09 bob p>

*  Last Time-stamp: <19-Jan-2016 09:02:58 bob p>

*

*  \file atomic.h

*  \brief  Port of Dean Camera's ATOMIC_BLOCK macros for AVR to ARM Cortex M3.

*

*/

/*lint -save -e755 -e756 Disable warning(s), this file only, global macro/typedef 'Symbol' (Location) not referenced */

#ifndef _ATOMIC_H_

#define _ATOMIC_H_ (1)

#ifdef  DEFINE_SPACE_ATOMIC_H

#define EXTERN_ATOMIC

#else

#define EXTERN_ATOMIC extern

#endif

#if defined(__cplusplus) && __cplusplus

extern "C" {

#endif

/*

* This is port of Dean Camera's ATOMIC_BLOCK macros for AVR to ARM Cortex M3

* v1.0

* Mark Pendrith, Nov 27, 2012.

*

* From Mark:

* >When I ported the macros I emailed Dean to ask what attribution would be

* >appropriate, and here is his response:

* >

* >>Mark,

* >>I think it's great that you've ported the macros; consider them

* >>public domain, to do with whatever you wish. I hope you find them useful .

* >>

* >>Cheers!

* >>- Dean

*/

#ifdef __arm__

#ifndef _CORTEX_M3_ATOMIC_H_

#define _CORTEX_M3_ATOMIC_H_

static __inline__ uint32_t __get_primask(void) \

{ uint32_t primask = 0; \

  __asm__ volatile ("MRS %[result], PRIMASK\n\t":[result]"=r"(primask)::); \

  return primask; } // returns 0 if interrupts enabled, 1 if disabled

static __inline__ void __set_primask(uint32_t setval) \

{ __asm__ volatile ("MSR PRIMASK, %[value]\n\t""dmb\n\t""dsb\n\t""isb\n\t"::[value]"r"(setval):);

  __asm__ volatile ("" ::: "memory");}

static __inline__ uint32_t __iSeiRetVal(void) \

{ __asm__ volatile ("CPSIE i\n\t""dmb\n\t""dsb\n\t""isb\n\t"); \

  __asm__ volatile ("" ::: "memory"); return 1; }

static __inline__ uint32_t __iCliRetVal(void) \

{ __asm__ volatile ("CPSID i\n\t""dmb\n\t""dsb\n\t""isb\n\t"); \

  __asm__ volatile ("" ::: "memory"); return 1; }

static __inline__ void    __iSeiParam(const uint32_t *__s) \

{ __asm__ volatile ("CPSIE i\n\t""dmb\n\t""dsb\n\t""isb\n\t"); \

  __asm__ volatile ("" ::: "memory"); (void)__s; }

static __inline__ void    __iCliParam(const uint32_t *__s) \

{ __asm__ volatile ("CPSID i\n\t""dmb\n\t""dsb\n\t""isb\n\t"); \

  __asm__ volatile ("" ::: "memory"); (void)__s; }

static __inline__ void    __iRestore(const  uint32_t *__s) \

{ __set_primask(*__s); __asm__ volatile ("dmb\n\t""dsb\n\t""isb\n\t"); \

  __asm__ volatile ("" ::: "memory"); }

#define ATOMIC_BLOCK(type) \

for ( type, __ToDo = __iCliRetVal(); __ToDo ; __ToDo = 0 )

#define ATOMIC_RESTORESTATE \

uint32_t primask_save __attribute__((__cleanup__(__iRestore)))  = __get_primask()

#define ATOMIC_FORCEON \

uint32_t primask_save __attribute__((__cleanup__(__iSeiParam))) = 0

#define NONATOMIC_BLOCK(type) \

for ( type, __ToDo = __iSeiRetVal(); __ToDo ;  __ToDo = 0 )

#define NONATOMIC_RESTORESTATE \

uint32_t primask_save __attribute__((__cleanup__(__iRestore))) = __get_primask()

#define NONATOMIC_FORCEOFF \

uint32_t primask_save __attribute__((__cleanup__(__iCliParam))) = 0

#endif

#endif

#if defined(__cplusplus) && __cplusplus

}

#endif

#endif /* _ATOMIC_H_ */

/*lint -restore */

View solution in original post

0 Kudos
5 Replies
183 Views
robertogiovinet
Contributor III

Hi

I found a solution.

I added the simple DisableInterrupts instruction in the RAM function, and now it works. I can't understand why, because the DisableInterrupts instruction was present in the calling function, and so interrupts was disabled yet. But now it works fine.

__attribute__( ( long_call, section(".data") ) ) void FlashLaunchCommand (void)

{

  DisableInterrupts;

  FTMRH_FSTAT = 0x80; // Launch the command

  // Wait till command is completed

  while (!(FTMRH_FSTAT & FTMRH_FSTAT_CCIF_MASK)){

  }

}

0 Kudos
184 Views
bobpaddock
Senior Contributor III

Giovinetti Roberto wrote:

Hi

I found a solution.

I added the simple DisableInterrupts instruction in the RAM function, and now it works. I can't understand why, because the DisableInterrupts instruction was present in the calling function, and so interrupts was disabled yet. But now it works fine.

__attribute__( ( long_call, section(".data") ) ) void FlashLaunchCommand (void)

{

DisableInterrupts;

FTMRH_FSTAT = 0x80; // Launch the command

// Wait till command is completed

while (!(FTMRH_FSTAT & FTMRH_FSTAT_CCIF_MASK)){

}

}

There are two issues that may be going on.  First the while() loop has no side effects so the compiler may optimize it away.  To prevent this add this, GCC format need to match your own compiler format if not using GCC:

while (!(FTMRH_FSTAT & FTMRH_FSTAT_CCIF_MASK))

{

__asm__ __volatile__ ("nop");

}


Marking the nop as volatile will make sure the loop happens.

On some ARM processors a nop may not actually consume any time due to cache.

That is not really an issue here.  This should also fix the problem seen with the caller containing the disable IRQ, as the 'volatile' will prevent the compiler from reordering the code across sequence points.

I use the code below for GCC, which this form editor is going to mess up. :-(
Example that solves the above problems:

    ATOMIC_BLOCK( ATOMIC_RESTORESTATE )

    {

       while (!(FTMRH_FSTAT & FTMRH_FSTAT_CCIF_MASK))

         {

            __asm__ __volatile__ ("nop");

          }

     }

/** @(#)atomic.h     <18-Aug-2014 13:31:09 bob p>

*  Last Time-stamp: <19-Jan-2016 09:02:58 bob p>

*

*  \file atomic.h

*  \brief  Port of Dean Camera's ATOMIC_BLOCK macros for AVR to ARM Cortex M3.

*

*/

/*lint -save -e755 -e756 Disable warning(s), this file only, global macro/typedef 'Symbol' (Location) not referenced */

#ifndef _ATOMIC_H_

#define _ATOMIC_H_ (1)

#ifdef  DEFINE_SPACE_ATOMIC_H

#define EXTERN_ATOMIC

#else

#define EXTERN_ATOMIC extern

#endif

#if defined(__cplusplus) && __cplusplus

extern "C" {

#endif

/*

* This is port of Dean Camera's ATOMIC_BLOCK macros for AVR to ARM Cortex M3

* v1.0

* Mark Pendrith, Nov 27, 2012.

*

* From Mark:

* >When I ported the macros I emailed Dean to ask what attribution would be

* >appropriate, and here is his response:

* >

* >>Mark,

* >>I think it's great that you've ported the macros; consider them

* >>public domain, to do with whatever you wish. I hope you find them useful .

* >>

* >>Cheers!

* >>- Dean

*/

#ifdef __arm__

#ifndef _CORTEX_M3_ATOMIC_H_

#define _CORTEX_M3_ATOMIC_H_

static __inline__ uint32_t __get_primask(void) \

{ uint32_t primask = 0; \

  __asm__ volatile ("MRS %[result], PRIMASK\n\t":[result]"=r"(primask)::); \

  return primask; } // returns 0 if interrupts enabled, 1 if disabled

static __inline__ void __set_primask(uint32_t setval) \

{ __asm__ volatile ("MSR PRIMASK, %[value]\n\t""dmb\n\t""dsb\n\t""isb\n\t"::[value]"r"(setval):);

  __asm__ volatile ("" ::: "memory");}

static __inline__ uint32_t __iSeiRetVal(void) \

{ __asm__ volatile ("CPSIE i\n\t""dmb\n\t""dsb\n\t""isb\n\t"); \

  __asm__ volatile ("" ::: "memory"); return 1; }

static __inline__ uint32_t __iCliRetVal(void) \

{ __asm__ volatile ("CPSID i\n\t""dmb\n\t""dsb\n\t""isb\n\t"); \

  __asm__ volatile ("" ::: "memory"); return 1; }

static __inline__ void    __iSeiParam(const uint32_t *__s) \

{ __asm__ volatile ("CPSIE i\n\t""dmb\n\t""dsb\n\t""isb\n\t"); \

  __asm__ volatile ("" ::: "memory"); (void)__s; }

static __inline__ void    __iCliParam(const uint32_t *__s) \

{ __asm__ volatile ("CPSID i\n\t""dmb\n\t""dsb\n\t""isb\n\t"); \

  __asm__ volatile ("" ::: "memory"); (void)__s; }

static __inline__ void    __iRestore(const  uint32_t *__s) \

{ __set_primask(*__s); __asm__ volatile ("dmb\n\t""dsb\n\t""isb\n\t"); \

  __asm__ volatile ("" ::: "memory"); }

#define ATOMIC_BLOCK(type) \

for ( type, __ToDo = __iCliRetVal(); __ToDo ; __ToDo = 0 )

#define ATOMIC_RESTORESTATE \

uint32_t primask_save __attribute__((__cleanup__(__iRestore)))  = __get_primask()

#define ATOMIC_FORCEON \

uint32_t primask_save __attribute__((__cleanup__(__iSeiParam))) = 0

#define NONATOMIC_BLOCK(type) \

for ( type, __ToDo = __iSeiRetVal(); __ToDo ;  __ToDo = 0 )

#define NONATOMIC_RESTORESTATE \

uint32_t primask_save __attribute__((__cleanup__(__iRestore))) = __get_primask()

#define NONATOMIC_FORCEOFF \

uint32_t primask_save __attribute__((__cleanup__(__iCliParam))) = 0

#endif

#endif

#if defined(__cplusplus) && __cplusplus

}

#endif

#endif /* _ATOMIC_H_ */

/*lint -restore */

View solution in original post

0 Kudos
183 Views
mjbcswitzerland
Specialist V

Hi

I recommend studying the assembler code generated in both cases. It sounds as though you have some compiler intervention/optimisation and it is probably best to understand because it may start failing again in the future (small program changes, different compiler version etc.).

Having a potential race-state or such in the code is a very dangerous situation and the original probem needs to be understood in order to be sure that your product will remain reliable in the future - the position of the disable-interrupt call (or in-lined code) shouldn't have any effect so there must be something else modified through it....

Regards

Mark

0 Kudos
183 Views
robertogiovinet
Contributor III

Hi

I modified Flash driver so a part of the ERASE and PROGRAM functions resides in RAM. I used the function listed below:

__attribute__( ( long_call, section(".data") ) ) void FlashLaunchCommand (void)

{

  FTMRH_FSTAT = 0x80; // Launch the command

  // Wait till command is completed

  while (!(FTMRH_FSTAT & FTMRH_FSTAT_CCIF_MASK)){

  }

}

Now the method works but not everytime! Some times I've the same problem with LOCKUP bit that causes a reset.

Does anybody Know a solution?

Regards

Thanks

0 Kudos
183 Views
mjbcswitzerland
Specialist V

Hi

I have attached the Flash driver from the uTasker project, which includes a swap-block which allows parameter storage without risk of loss or corruption due to resets or power cycles. It is documented at http://www.utasker.com/docs/uTasker/uTaskerFileSystem_3.PDF

You can load a binary to reference boards to test its operation (using commands on the UART command line interface) or to test reading, writing and erasing specific addresses in EEPROM and/or Flash.

- http://www.utasker.com/kinetis/FRDM-KE02Z.html

- http://www.utasker.com/kinetis/FRDM-KE02Z40M.html

See http://www.utasker.com/kinetis/KE_EEPROM.html for EEPROM and Flash details, whereby both can be tested.

You can either use it as comparison to solve your problems or else simply use the uTasker project to enable this and enable you to solve your complete project requirements.

The operation (peripherals, including Flash and EEPROM) is accurately simulated in the uTasker KE simulator in case you need to be able to increase your overall developement efficiency over traditional load/debug cycling.

Regards

Mark

0 Kudos