diff options
| author | Yuval Adam <yuv.adm@gmail.com> | 2012-10-29 23:06:01 +0200 |
|---|---|---|
| committer | Yuval Adam <yuv.adm@gmail.com> | 2012-10-29 23:06:01 +0200 |
| commit | 4ba8614c006f9828f0796c140bc3e13c9e67938c (patch) | |
| tree | ad204651c733250d27d20fce8820f9747c43c883 /utils/flash_pb.c | |
| parent | e018ceebed74b8223482bc9d1ed0b31c938381da (diff) | |
Added utils
Diffstat (limited to 'utils/flash_pb.c')
| -rw-r--r-- | utils/flash_pb.c | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/utils/flash_pb.c b/utils/flash_pb.c new file mode 100644 index 0000000..e057120 --- /dev/null +++ b/utils/flash_pb.c @@ -0,0 +1,486 @@ +//*****************************************************************************
+//
+// flash_pb.c - Flash parameter block functions.
+//
+// Copyright (c) 2008-2012 Texas Instruments Incorporated. All rights reserved.
+// Software License Agreement
+//
+// Texas Instruments (TI) is supplying this software for use solely and
+// exclusively on TI's microcontroller products. The software is owned by
+// TI and/or its suppliers, and is protected under applicable copyright
+// laws. You may not combine this software with "viral" open-source
+// software in order to form a larger program.
+//
+// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
+// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
+// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
+// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+// DAMAGES, FOR ANY REASON WHATSOEVER.
+//
+// This is part of revision 9453 of the Stellaris Firmware Development Package.
+//
+//*****************************************************************************
+
+#include "inc/hw_flash.h"
+#include "inc/hw_types.h"
+#include "driverlib/debug.h"
+#include "driverlib/flash.h"
+#include "driverlib/sysctl.h"
+#include "utils/flash_pb.h"
+
+//*****************************************************************************
+//
+//! \addtogroup flash_pb_api
+//! @{
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// The address of the beginning of the flash used for storing parameter blocks;
+// this must be the start of an erase block in the flash.
+//
+//*****************************************************************************
+static unsigned char *g_pucFlashPBStart;
+
+//*****************************************************************************
+//
+// The address of the end of the flash used for storing parameter blocks; this
+// must be the start of an erase block in the flash, or the first location
+// after the end of the flash array if the last erase block is used for storing
+// parameters.
+//
+//*****************************************************************************
+static unsigned char *g_pucFlashPBEnd;
+
+//*****************************************************************************
+//
+// The size of the parameter block when stored in flash; this must be a power
+// of two less than or equal to 1024.
+//
+//*****************************************************************************
+static unsigned long g_ulFlashPBSize;
+
+//*****************************************************************************
+//
+// The address of the most recent parameter block in flash.
+//
+//*****************************************************************************
+static unsigned char *g_pucFlashPBCurrent;
+
+//*****************************************************************************
+//
+//! Determines if the parameter block at the given address is valid.
+//!
+//! \param pucOffset is the address of the parameter block to check.
+//!
+//! This function will compute the checksum of a parameter block in flash to
+//! determine if it is valid.
+//!
+//! \return Returns one if the parameter block is valid and zero if it is not.
+//
+//*****************************************************************************
+static unsigned long
+FlashPBIsValid(unsigned char *pucOffset)
+{
+ unsigned long ulIdx, ulSum;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(pucOffset != (void *)0);
+
+ //
+ // Loop through the bytes in the block, computing the checksum.
+ //
+ for(ulIdx = 0, ulSum = 0; ulIdx < g_ulFlashPBSize; ulIdx++)
+ {
+ ulSum += pucOffset[ulIdx];
+ }
+
+ //
+ // The checksum should be zero, so return a failure if it is not.
+ //
+ if((ulSum & 255) != 0)
+ {
+ return(0);
+ }
+
+ //
+ // If the sum is equal to the size * 255, then the block is all ones and
+ // should not be considered valid.
+ //
+ if((g_ulFlashPBSize * 255) == ulSum)
+ {
+ return(0);
+ }
+
+ //
+ // This is a valid parameter block.
+ //
+ return(1);
+}
+
+//*****************************************************************************
+//
+//! Gets the address of the most recent parameter block.
+//!
+//! This function returns the address of the most recent parameter block that
+//! is stored in flash.
+//!
+//! \return Returns the address of the most recent parameter block, or NULL if
+//! there are no valid parameter blocks in flash.
+//
+//*****************************************************************************
+unsigned char *
+FlashPBGet(void)
+{
+ //
+ // See if there is a valid parameter block.
+ //
+ if(g_pucFlashPBCurrent)
+ {
+ //
+ // Return the address of the most recent parameter block.
+ //
+ return(g_pucFlashPBCurrent);
+ }
+
+ //
+ // There are no valid parameter blocks in flash, so return NULL.
+ //
+ return(0);
+}
+
+//*****************************************************************************
+//
+//! Writes a new parameter block to flash.
+//!
+//! \param pucBuffer is the address of the parameter block to be written to
+//! flash.
+//!
+//! This function will write a parameter block to flash. Saving the new
+//! parameter blocks involves three steps:
+//!
+//! - Setting the sequence number such that it is one greater than the sequence
+//! number of the latest parameter block in flash.
+//! - Computing the checksum of the parameter block.
+//! - Writing the parameter block into the storage immediately following the
+//! latest parameter block in flash; if that storage is at the start of an
+//! erase block, that block is erased first.
+//!
+//! By this process, there is always a valid parameter block in flash. If
+//! power is lost while writing a new parameter block, the checksum will not
+//! match and the partially written parameter block will be ignored. This is
+//! what makes this fault-tolerant.
+//!
+//! Another benefit of this scheme is that it provides wear leveling on the
+//! flash. Since multiple parameter blocks fit into each erase block of flash,
+//! and multiple erase blocks are used for parameter block storage, it takes
+//! quite a few parameter block saves before flash is re-written.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+FlashPBSave(unsigned char *pucBuffer)
+{
+ unsigned char *pucNew;
+ unsigned long ulIdx, ulSum;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT(pucBuffer != (void *)0);
+
+ //
+ // See if there is a valid parameter block in flash.
+ //
+ if(g_pucFlashPBCurrent)
+ {
+ //
+ // Set the sequence number to one greater than the most recent
+ // parameter block.
+ //
+ pucBuffer[0] = g_pucFlashPBCurrent[0] + 1;
+
+ //
+ // Try to write the new parameter block immediately after the most
+ // recent parameter block.
+ //
+ pucNew = g_pucFlashPBCurrent + g_ulFlashPBSize;
+ if(pucNew == g_pucFlashPBEnd)
+ {
+ pucNew = g_pucFlashPBStart;
+ }
+ }
+ else
+ {
+ //
+ // There is not a valid parameter block in flash, so set the sequence
+ // number of this parameter block to zero.
+ //
+ pucBuffer[0] = 0;
+
+ //
+ // Try to write the new parameter block at the beginning of the flash
+ // space for parameter blocks.
+ //
+ pucNew = g_pucFlashPBStart;
+ }
+
+ //
+ // Compute the checksum of the parameter block to be written.
+ //
+ for(ulIdx = 0, ulSum = 0; ulIdx < g_ulFlashPBSize; ulIdx++)
+ {
+ ulSum -= pucBuffer[ulIdx];
+ }
+
+ //
+ // Store the checksum into the parameter block.
+ //
+ pucBuffer[1] += ulSum;
+
+ //
+ // Look for a location to store this parameter block. This infinite loop
+ // will be explicitly broken out of when a valid location is found.
+ //
+ while(1)
+ {
+ //
+ // See if this location is at the start of an erase block.
+ //
+ if(((unsigned long)pucNew & 1023) == 0)
+ {
+ //
+ // Erase this block of the flash. This does not assume that the
+ // erase succeeded in case this block of the flash has become bad
+ // through too much use. Given the extremely low frequency that
+ // the parameter blocks are written, this will likely never fail.
+ // But, that assumption is not made in order to be safe.
+ //
+ FlashErase((unsigned long)pucNew);
+ }
+
+ //
+ // Loop through this portion of flash to see if is all ones (i.e. it
+ // is an erased portion of flash).
+ //
+ for(ulIdx = 0; ulIdx < g_ulFlashPBSize; ulIdx++)
+ {
+ if(pucNew[ulIdx] != 0xff)
+ {
+ break;
+ }
+ }
+
+ //
+ // If all bytes in this portion of flash are ones, then break out of
+ // the loop since this is a good location for storing the parameter
+ // block.
+ //
+ if(ulIdx == g_ulFlashPBSize)
+ {
+ break;
+ }
+
+ //
+ // Increment to the next parameter block location.
+ //
+ pucNew += g_ulFlashPBSize;
+ if(pucNew == g_pucFlashPBEnd)
+ {
+ pucNew = g_pucFlashPBStart;
+ }
+
+ //
+ // If every possible location has been checked and none are valid, then
+ // it will not be possible to write this parameter block. Simply
+ // return without writing it.
+ //
+ if((g_pucFlashPBCurrent && (pucNew == g_pucFlashPBCurrent)) ||
+ (!g_pucFlashPBCurrent && (pucNew == g_pucFlashPBStart)))
+ {
+ return;
+ }
+ }
+
+ //
+ // Write this parameter block to flash.
+ //
+ FlashProgram((unsigned long *)pucBuffer, (unsigned long)pucNew,
+ g_ulFlashPBSize);
+
+ //
+ // Compare the parameter block data to the data that should now be in
+ // flash. Return if any of the data does not compare, leaving the previous
+ // parameter block in flash as the most recent (since the current parameter
+ // block failed to properly program).
+ //
+ for(ulIdx = 0; ulIdx < g_ulFlashPBSize; ulIdx++)
+ {
+ if(pucNew[ulIdx] != pucBuffer[ulIdx])
+ {
+ return;
+ }
+ }
+
+ //
+ // The new parameter block becomes the most recent parameter block.
+ //
+ g_pucFlashPBCurrent = pucNew;
+}
+
+//*****************************************************************************
+//
+//! Initializes the flash parameter block.
+//!
+//! \param ulStart is the address of the flash memory to be used for storing
+//! flash parameter blocks; this must be the start of an erase block in the
+//! flash.
+//! \param ulEnd is the address of the end of flash memory to be used for
+//! storing flash parameter blocks; this must be the start of an erase block in
+//! the flash (the first block that is NOT part of the flash memory to be
+//! used), or the address of the first word after the flash array if the last
+//! block of flash is to be used.
+//! \param ulSize is the size of the parameter block when stored in flash;
+//! this must be a power of two less than or equal to the flash erase block
+//! size (typically 1024).
+//!
+//! This function initializes a fault-tolerant, persistent storage mechanism
+//! for a parameter block for an application. The last several erase blocks
+//! of flash (as specified by \e ulStart and \e ulEnd are used for the
+//! storage; more than one erase block is required in order to be
+//! fault-tolerant.
+//!
+//! A parameter block is an array of bytes that contain the persistent
+//! parameters for the application. The only special requirement for the
+//! parameter block is that the first byte is a sequence number (explained
+//! in FlashPBSave()) and the second byte is a checksum used to validate the
+//! correctness of the data (the checksum byte is the byte such that the sum of
+//! all bytes in the parameter block is zero).
+//!
+//! The portion of flash for parameter block storage is split into N
+//! equal-sized regions, where each region is the size of a parameter block
+//! (\e ulSize). Each region is scanned to find the most recent valid
+//! parameter block. The region that has a valid checksum and has the highest
+//! sequence number (with special consideration given to wrapping back to zero)
+//! is considered to be the current parameter block.
+//!
+//! In order to make this efficient and effective, three conditions must be
+//! met. The first is \e ulStart and \e ulEnd must be specified such that at
+//! least two erase blocks of flash are dedicated to parameter block storage.
+//! If not, fault tolerance can not be guaranteed since an erase of a single
+//! block will leave a window where there are no valid parameter blocks in
+//! flash. The second condition is that the size (\e ulSize) of the parameter
+//! block must be an integral divisor of the size of an erase block of flash.
+//! If not, a parameter block will end up spanning between two erase blocks of
+//! flash, making it more difficult to manage. The final condition is that the
+//! size of the flash dedicated to parameter blocks (\e ulEnd - \e ulStart)
+//! divided by the parameter block size (\e ulSize) must be less than or equal
+//! to 128. If not, it will not be possible in all cases to determine which
+//! parameter block is the most recent (specifically when dealing with the
+//! sequence number wrapping back to zero).
+//!
+//! When the microcontroller is initially programmed, the flash blocks used for
+//! parameter block storage are left in an erased state.
+//!
+//! This function must be called before any other flash parameter block
+//! functions are called.
+//!
+//! \return None.
+//
+//*****************************************************************************
+void
+FlashPBInit(unsigned long ulStart, unsigned long ulEnd, unsigned long ulSize)
+{
+ unsigned char *pucOffset, *pucCurrent;
+ unsigned char ucOne, ucTwo;
+
+ //
+ // Check the arguments.
+ //
+ ASSERT((ulStart % FLASH_ERASE_SIZE) == 0);
+ ASSERT((ulEnd % FLASH_ERASE_SIZE) == 0);
+ ASSERT((FLASH_ERASE_SIZE % ulSize) == 0);
+
+ //
+ // Set the number of clocks per microsecond to enable the flash controller
+ // to properly program the flash.
+ //
+ FlashUsecSet(SysCtlClockGet() / 1000000);
+
+ //
+ // Save the characteristics of the flash memory to be used for storing
+ // parameter blocks.
+ //
+ g_pucFlashPBStart = (unsigned char *)ulStart;
+ g_pucFlashPBEnd = (unsigned char *)ulEnd;
+ g_ulFlashPBSize = ulSize;
+
+ //
+ // Loop through the portion of flash memory used for storing parameter
+ // blocks.
+ //
+ for(pucOffset = g_pucFlashPBStart, pucCurrent = 0;
+ pucOffset < g_pucFlashPBEnd; pucOffset += g_ulFlashPBSize)
+ {
+ //
+ // See if this is a valid parameter block (i.e. the checksum is
+ // correct).
+ //
+ if(FlashPBIsValid(pucOffset))
+ {
+ //
+ // See if a valid parameter block has been previously found.
+ //
+ if(pucCurrent != 0)
+ {
+ //
+ // Get the sequence numbers for the current and new parameter
+ // blocks.
+ //
+ ucOne = pucCurrent[0];
+ ucTwo = pucOffset[0];
+
+ //
+ // See if the sequence number for the new parameter block is
+ // greater than the current block. The comparison isn't
+ // straightforward since the one byte sequence number will wrap
+ // after 256 parameter blocks.
+ //
+ if(((ucOne > ucTwo) && ((ucOne - ucTwo) < 128)) ||
+ ((ucTwo > ucOne) && ((ucTwo - ucOne) > 128)))
+ {
+ //
+ // The new parameter block is older than the current
+ // parameter block, so skip the new parameter block and
+ // keep searching.
+ //
+ continue;
+ }
+ }
+
+ //
+ // The new parameter block is more recent than the current one, so
+ // make it the new current parameter block.
+ //
+ pucCurrent = pucOffset;
+ }
+ }
+
+ //
+ // Save the address of the most recent parameter block found. If no valid
+ // parameter blocks were found, this will be a NULL pointer.
+ //
+ g_pucFlashPBCurrent = pucCurrent;
+}
+
+//*****************************************************************************
+//
+// Close the Doxygen group.
+//! @}
+//
+//*****************************************************************************
|
