403 lines
13 KiB
C
403 lines
13 KiB
C
/** @file hal/micro/cortexm3/nvm.c
|
|
* @brief Cortex-M3 Non-Volatile Memory data storage system.
|
|
*
|
|
* This file implements the NVM data storage system. Refer to nvm.h for
|
|
* full documentation of how the NVM data storage system works, is configured,
|
|
* and is accessed.
|
|
*
|
|
* <!--(C) COPYRIGHT 2010 STMicroelectronics. All rights reserved. -->
|
|
*/
|
|
|
|
#include PLATFORM_HEADER
|
|
#include "error.h"
|
|
|
|
#ifdef NVM_RAM_EMULATION
|
|
|
|
static int16u calibrationData[32+2]={
|
|
0xFFFF, 0xFFFF,
|
|
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
|
|
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
|
|
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
|
|
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
|
|
};
|
|
|
|
int8u halCommonReadFromNvm(void *data, int32u offset, int16u length)
|
|
{
|
|
halCommonMemCopy(data, ((int8u *) calibrationData) + offset, length);
|
|
return ST_SUCCESS;
|
|
}
|
|
int8u halCommonWriteToNvm(const void *data, int32u offset, int16u length)
|
|
{
|
|
halCommonMemCopy(((int8u *) calibrationData) + offset, data, length);
|
|
return ST_SUCCESS;
|
|
}
|
|
|
|
#else
|
|
|
|
//flash.h gives access to halInternalFlashErase and halInternalFlashWrite.
|
|
#include "hal/micro/cortexm3/flash.h"
|
|
//nvm.h includes memmap.h. These two headers define the key parameters:
|
|
// MFB_PAGE_SIZE_B
|
|
// MFB_TOP
|
|
// NVM_LEFT_PAGE
|
|
// NVM_RIGHT_PAGE
|
|
// NVM_DATA_SIZE_B
|
|
// NVM_FLASH_PAGE_COUNT
|
|
// NVM_MGMT_SIZE_B
|
|
#include "hal/micro/cortexm3/nvm.h"
|
|
|
|
//Define two variables that hold the actual NVM data storage. LEFT and RIGHT
|
|
//are not required to be continuous memory blocks so they can be define
|
|
//separately. The linker is responsible for placing these storage containers
|
|
//on flash page boundaries.
|
|
NO_STRIPPING __no_init VAR_AT_SEGMENT (const int8u nvmStorageLeft[NVM_DATA_SIZE_B], __NVM__);
|
|
NO_STRIPPING __no_init VAR_AT_SEGMENT (const int8u nvmStorageRight[NVM_DATA_SIZE_B], __NVM__);
|
|
|
|
static int8u determineState(void)
|
|
{
|
|
int32u leftMgmt = *(int32u *)NVM_LEFT_PAGE;
|
|
int32u rightMgmt = *(int32u *)NVM_RIGHT_PAGE;
|
|
int8u state=0;
|
|
|
|
if((leftMgmt==0xFFFF0000) && (rightMgmt==0xFFFFFFFF)) {
|
|
//State 1 and state 4 use identical mgmt words. The function
|
|
//determineState() is only called at the start of a NVM read
|
|
//or write. During a read, state 1 and 4 both read from the
|
|
//LEFT so there is no reason to make a distinction. During
|
|
//a write, the system will see the current page as LEFT and
|
|
//therefore be transitioning from LEFT to RIGHT so state 4 is
|
|
//correct. State 1 is only required to transition from 0 to 2.
|
|
state = 4;
|
|
} else if((leftMgmt==0xFFFF0000) && (rightMgmt==0xFF00FFFF)) {
|
|
state = 2;
|
|
} else if((leftMgmt==0xFFFF0000) && (rightMgmt==0xFF000000)) {
|
|
state = 3;
|
|
} else if((leftMgmt==0xFFFF0000) && (rightMgmt==0xFFFFFFFF)) {
|
|
state = 4;
|
|
} else if((leftMgmt==0xFFFF0000) && (rightMgmt==0xFFFFFF00)) {
|
|
state = 5;
|
|
} else if((leftMgmt==0xFF000000) && (rightMgmt==0xFFFFFF00)) {
|
|
state = 6;
|
|
} else if((leftMgmt==0xFF000000) && (rightMgmt==0xFFFF0000)) {
|
|
state = 7;
|
|
} else if((leftMgmt==0xFFFFFFFF) && (rightMgmt==0xFFFF0000)) {
|
|
state = 8;
|
|
} else if((leftMgmt==0xFFFFFF00) && (rightMgmt==0xFFFF0000)) {
|
|
state = 9;
|
|
} else if((leftMgmt==0xFFFFFF00) && (rightMgmt==0xFF000000)) {
|
|
state = 10;
|
|
} else {
|
|
//State 0 is used to indicate erased or invalid.
|
|
state = 0;
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
|
|
int8u halCommonReadFromNvm(void *data, int32u offset, int16u length)
|
|
{
|
|
int16u i;
|
|
int16u *flash;
|
|
//Remember: all flash writes are 16bits.
|
|
int16u *ram = (int16u*)data;
|
|
|
|
//The NVM data storage system cannot function if the LEFT and RIGHT
|
|
//storage are not aligned to physical flash pages.
|
|
assert((NVM_LEFT_PAGE%MFB_PAGE_SIZE_B)==0);
|
|
assert((NVM_RIGHT_PAGE%MFB_PAGE_SIZE_B)==0);
|
|
//The offset of the NVM data must be 16bit aligned.
|
|
assert((offset&0x1)==0);
|
|
//The length of the NVM data must be 16bit aligned.
|
|
assert((length&0x1)==0);
|
|
|
|
assert(offset+length<NVM_DATA_SIZE_B);
|
|
|
|
//Obtain the data from NVM storage.
|
|
switch(determineState()) {
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 9:
|
|
case 10:
|
|
flash = (int16u *)(NVM_LEFT_PAGE+offset);
|
|
for(i=0;i<(length/2);i++) {
|
|
ram[i] = flash[i];
|
|
}
|
|
break;
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
flash = (int16u *)(NVM_RIGHT_PAGE+offset);
|
|
for(i=0;i<(length/2);i++) {
|
|
ram[i] = flash[i];
|
|
}
|
|
break;
|
|
case 0:
|
|
default:
|
|
//Reading from NVM while the mgmt bytes are in an invalid state
|
|
//should not return any bytes actually found in flash. Instead,
|
|
//return nothing but 0xFF. This is legitimate because the next
|
|
//call to the write function will also find invalid mgmt bytes
|
|
//and trigger an erasure of NVM, after which the NVM really will
|
|
//contain just 0xFF for data (plus the new data supplied during
|
|
//the write call).
|
|
for(i=0;i<(length/2);i++) {
|
|
ram[i] = 0xFFFF;
|
|
}
|
|
//Inform the calling code. using ST_ERR_FATAL, that there were
|
|
//invalid mgmt bytes and 0xFF was forcefully returned.
|
|
return ST_ERR_FATAL;
|
|
}
|
|
|
|
return ST_SUCCESS;
|
|
}
|
|
|
|
int16u *halCommonGetAddressFromNvm(int32u offset)
|
|
{
|
|
int16u *flash;
|
|
|
|
//The NVM data storage system cannot function if the LEFT and RIGHT
|
|
//storage are not aligned to physical flash pages.
|
|
assert((NVM_LEFT_PAGE%MFB_PAGE_SIZE_B)==0);
|
|
assert((NVM_RIGHT_PAGE%MFB_PAGE_SIZE_B)==0);
|
|
//The offset of the NVM data must be 16bit aligned.
|
|
assert((offset&0x1)==0);
|
|
|
|
//Obtain the data from NVM storage.
|
|
switch(determineState()) {
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 9:
|
|
case 10:
|
|
flash = (int16u *)(NVM_LEFT_PAGE+offset);
|
|
break;
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
flash = (int16u *)(NVM_RIGHT_PAGE+offset);
|
|
break;
|
|
case 0:
|
|
default:
|
|
// Flash is in an invalid state
|
|
// Fix it with a dummy write and then return the flash page left
|
|
{
|
|
int16u dummy = 0xFFFF;
|
|
halCommonWriteToNvm(&dummy, 0, 2);
|
|
flash = (int16u *)(NVM_LEFT_PAGE+offset);
|
|
}
|
|
}
|
|
|
|
return flash;
|
|
}
|
|
|
|
|
|
static int8u erasePage(int32u page)
|
|
{
|
|
StStatus status;
|
|
int32u i, k;
|
|
int32u address;
|
|
int8u *flash;
|
|
|
|
//Erasing a LEFT or RIGHT page requires erasing all of the flash pages.
|
|
//Since the mgmt bytes are stored at the bottom of a page, the flash pages
|
|
//are erased from the top down ensuring that that mgmt words are the last
|
|
//data to be erased. This way, if a reset occurs while erasing, the mgmt
|
|
//words are still valid the next time determineState() is called.
|
|
for(i=NVM_FLASH_PAGE_COUNT;i>0;i--) {
|
|
address = (page+((i-1)*MFB_PAGE_SIZE_B));
|
|
flash = (int8u *)address;
|
|
//Scan the page to determine if it is fully erased already.
|
|
//If the flash is not erased, erase it. The purpose of scanning
|
|
//first is to save a little time if erasing is not required.
|
|
for(k=0;k<MFB_PAGE_SIZE_B;k++,flash++) {
|
|
if(*flash != 0xFF) {
|
|
status = halInternalFlashErase(MFB_PAGE_ERASE, address);
|
|
if(status != ST_SUCCESS) {
|
|
return status;
|
|
}
|
|
//Don't bother looking at the rest of this flash page and just
|
|
//move to the next.
|
|
k=MFB_PAGE_SIZE_B;
|
|
}
|
|
}
|
|
}
|
|
return ST_SUCCESS;
|
|
}
|
|
|
|
|
|
//This macro is responsible for erasing an NVM page (LEFT or RIGHT).
|
|
#define ERASE_PAGE(page) \
|
|
do { \
|
|
status = erasePage(page); \
|
|
if(status != ST_SUCCESS) { \
|
|
return status; \
|
|
} \
|
|
} while(0)
|
|
|
|
|
|
//This macro is responsible for writing the new data into the destination
|
|
//page and copying existing data from the source page to the
|
|
//destination page.
|
|
#define WRITE_DATA(destPage, srcPage, offset, length) \
|
|
do { \
|
|
/*Copy all data below the new data from the srcPage to the destPage*/ \
|
|
status = halInternalFlashWrite(destPage+NVM_MGMT_SIZE_B, \
|
|
(int16u *)(srcPage+NVM_MGMT_SIZE_B), \
|
|
(offset-NVM_MGMT_SIZE_B)/2); \
|
|
if(status != ST_SUCCESS) { return status; } \
|
|
/*Write the new data*/ \
|
|
status = halInternalFlashWrite(destPage+offset, \
|
|
ram, \
|
|
(length)/2); \
|
|
if(status != ST_SUCCESS) { return status; } \
|
|
/*Copy all data above the new data from the srcPage to the destPage*/ \
|
|
status = halInternalFlashWrite(destPage+offset+length, \
|
|
(int16u *)(srcPage+offset+length), \
|
|
(NVM_DATA_SIZE_B- \
|
|
length-offset- \
|
|
NVM_MGMT_SIZE_B)/2); \
|
|
if(status != ST_SUCCESS) { return status; } \
|
|
} while(0)
|
|
|
|
//This macro is responsible for writing 16bits of management data to
|
|
//the proper management address.
|
|
#define WRITE_MGMT_16BITS(address, data) \
|
|
do{ \
|
|
int16u value = data; \
|
|
status = halInternalFlashWrite((address), &value, 1); \
|
|
if(status != ST_SUCCESS) { \
|
|
return status; \
|
|
} \
|
|
} while(0)
|
|
|
|
|
|
int8u halCommonWriteToNvm(const void *data, int32u offset, int16u length)
|
|
{
|
|
StStatus status;
|
|
int8u state, exitState;
|
|
int32u srcPage;
|
|
int32u destPage;
|
|
//Remember: NVM data storage works on 16bit quantities.
|
|
int16u *ram = (int16u*)data;
|
|
|
|
//The NVM data storage system cannot function if the LEFT and RIGHT
|
|
//storage are not aligned to physical flash pages.
|
|
assert((NVM_LEFT_PAGE%MFB_PAGE_SIZE_B)==0);
|
|
assert((NVM_RIGHT_PAGE%MFB_PAGE_SIZE_B)==0);
|
|
//The offset of the NVM data must be 16bit aligned.
|
|
assert((offset&0x1)==0);
|
|
//The length of the NVM data must be 16bit aligned.
|
|
assert((length&0x1)==0);
|
|
//It is illegal to write to an offset outside of NVM storage.
|
|
assert(offset+length<NVM_DATA_SIZE_B);
|
|
|
|
|
|
state = determineState();
|
|
|
|
switch(state) {
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 9:
|
|
case 10:
|
|
srcPage = NVM_LEFT_PAGE;
|
|
destPage = NVM_RIGHT_PAGE;
|
|
exitState = 7;
|
|
break;
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
case 8:
|
|
srcPage = NVM_RIGHT_PAGE;
|
|
destPage = NVM_LEFT_PAGE;
|
|
exitState = 3;
|
|
break;
|
|
case 0:
|
|
default:
|
|
//Invalid state. Default to writing to the LEFT page. Defaulting to
|
|
//using RIGHT as the source page is valid since the RIGHT page
|
|
//will also be erased and therefore produce 0xFF for data values.
|
|
state = 0;
|
|
srcPage = NVM_RIGHT_PAGE;
|
|
destPage = NVM_LEFT_PAGE;
|
|
exitState = 3;
|
|
break;
|
|
}
|
|
|
|
//Advance the state machine. Starting on state 3 requires state 7 to
|
|
//exit and starting on state 7 requires state 3 to exit. Starting on
|
|
//any other state requires either 3 or 7 to exit.
|
|
//NOTE: Refer to nvm.h for a description of the states and how the
|
|
// state transitions correspond to erasing, writing data, and
|
|
// writing mgmt values.
|
|
while(TRUE) {
|
|
switch(state) {
|
|
case 0:
|
|
//State 0 is the only state where the source page needs to be erased.
|
|
ERASE_PAGE(srcPage);
|
|
ERASE_PAGE(destPage);
|
|
WRITE_DATA(destPage, srcPage, offset, length);
|
|
WRITE_MGMT_16BITS(NVM_LEFT_PAGE+0, 0x0000);
|
|
state=1;
|
|
break;
|
|
case 1:
|
|
WRITE_MGMT_16BITS(NVM_RIGHT_PAGE+2, 0xFF00);
|
|
state=2;
|
|
break;
|
|
case 2:
|
|
WRITE_MGMT_16BITS(NVM_RIGHT_PAGE+0, 0x0000);
|
|
state=3;
|
|
break;
|
|
case 3:
|
|
if(exitState==3) {
|
|
return ST_SUCCESS;
|
|
}
|
|
ERASE_PAGE(destPage);
|
|
state=4;
|
|
break;
|
|
case 4:
|
|
WRITE_DATA(destPage, srcPage, offset, length);
|
|
WRITE_MGMT_16BITS(NVM_RIGHT_PAGE+0, 0xFF00);
|
|
state=5;
|
|
break;
|
|
case 5:
|
|
WRITE_MGMT_16BITS(NVM_LEFT_PAGE+2, 0xFF00);
|
|
state=6;
|
|
break;
|
|
case 6:
|
|
WRITE_MGMT_16BITS(NVM_RIGHT_PAGE+0, 0x0000);
|
|
state=7;
|
|
break;
|
|
case 7:
|
|
if(exitState==7) {
|
|
return ST_SUCCESS;
|
|
}
|
|
ERASE_PAGE(destPage);
|
|
state=8;
|
|
break;
|
|
case 8:
|
|
WRITE_DATA(destPage, srcPage, offset, length);
|
|
WRITE_MGMT_16BITS(NVM_LEFT_PAGE+0, 0xFF00);
|
|
state=9;
|
|
break;
|
|
case 9:
|
|
WRITE_MGMT_16BITS(NVM_RIGHT_PAGE+2, 0xFF00);
|
|
state=10;
|
|
break;
|
|
case 10:
|
|
WRITE_MGMT_16BITS(NVM_LEFT_PAGE+0, 0x0000);
|
|
state=3;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // NVM_RAM_EMULATION
|