From 4ba8614c006f9828f0796c140bc3e13c9e67938c Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Mon, 29 Oct 2012 23:06:01 +0200 Subject: Added utils --- utils/flash_pb.c | 486 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 486 insertions(+) create mode 100644 utils/flash_pb.c (limited to 'utils/flash_pb.c') 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. +//! @} +// +//***************************************************************************** -- cgit v1.3.1