From 990090a4cc9070837d31e66b58d40f0c3d038741 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Sun, 16 Mar 2014 14:41:11 +0200 Subject: Add usblib and utils --- utils/flash_pb.c | 493 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 493 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..ac85830 --- /dev/null +++ b/utils/flash_pb.c @@ -0,0 +1,493 @@ +//***************************************************************************** +// +// flash_pb.c - Flash parameter block functions. +// +// Copyright (c) 2008-2014 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 2.1.0.12573 of the Tiva Utility Library. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_flash.h" +#include "inc/hw_types.h" +#include "inc/hw_sysctl.h" +#include "driverlib/debug.h" +#include "driverlib/flash.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.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 uint8_t *g_pui8FlashPBStart; + +//***************************************************************************** +// +// 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 uint8_t *g_pui8FlashPBEnd; + +//***************************************************************************** +// +// 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 sector size such that a single +// flash sector contains an integral number of parameter blocks. +// +//***************************************************************************** +static uint32_t g_ui32FlashPBSize; + +//***************************************************************************** +// +// The address of the most recent parameter block in flash. +// +//***************************************************************************** +static uint8_t *g_pui8FlashPBCurrent; + +//***************************************************************************** +// +// The erase sector size of the current flash. +// +//***************************************************************************** +#define FLASH_SECTOR_SIZE MAP_SysCtlFlashSectorSizeGet() + +//***************************************************************************** +// +//! Determines if the parameter block at the given address is valid. +//! +//! \param pui8Offset 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 uint32_t +FlashPBIsValid(uint8_t *pui8Offset) +{ + uint32_t ui32Idx, ui32Sum; + + // + // Check the arguments. + // + ASSERT(pui8Offset != (void *)0); + + // + // Loop through the bytes in the block, computing the checksum. + // + for(ui32Idx = 0, ui32Sum = 0; ui32Idx < g_ui32FlashPBSize; ui32Idx++) + { + ui32Sum += pui8Offset[ui32Idx]; + } + + // + // The checksum should be zero, so return a failure if it is not. + // + if((ui32Sum & 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_ui32FlashPBSize * 255) == ui32Sum) + { + 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. +// +//***************************************************************************** +uint8_t * +FlashPBGet(void) +{ + // + // See if there is a valid parameter block. + // + if(g_pui8FlashPBCurrent) + { + // + // Return the address of the most recent parameter block. + // + return(g_pui8FlashPBCurrent); + } + + // + // There are no valid parameter blocks in flash, so return NULL. + // + return(0); +} + +//***************************************************************************** +// +//! Writes a new parameter block to flash. +//! +//! \param pui8Buffer 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(uint8_t *pui8Buffer) +{ + uint8_t *pui8New; + uint32_t ui32Idx, ui32Sum; + + // + // Check the arguments. + // + ASSERT(pui8Buffer != (void *)0); + + // + // See if there is a valid parameter block in flash. + // + if(g_pui8FlashPBCurrent) + { + // + // Set the sequence number to one greater than the most recent + // parameter block. + // + pui8Buffer[0] = g_pui8FlashPBCurrent[0] + 1; + + // + // Try to write the new parameter block immediately after the most + // recent parameter block. + // + pui8New = g_pui8FlashPBCurrent + g_ui32FlashPBSize; + if(pui8New == g_pui8FlashPBEnd) + { + pui8New = g_pui8FlashPBStart; + } + } + else + { + // + // There is not a valid parameter block in flash, so set the sequence + // number of this parameter block to zero. + // + pui8Buffer[0] = 0; + + // + // Try to write the new parameter block at the beginning of the flash + // space for parameter blocks. + // + pui8New = g_pui8FlashPBStart; + } + + // + // Compute the checksum of the parameter block to be written. + // + for(ui32Idx = 0, ui32Sum = 0; ui32Idx < g_ui32FlashPBSize; ui32Idx++) + { + ui32Sum -= pui8Buffer[ui32Idx]; + } + + // + // Store the checksum into the parameter block. + // + pui8Buffer[1] += ui32Sum; + + // + // 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(((uint32_t)pui8New & (FLASH_SECTOR_SIZE - 1)) == 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. + // + MAP_FlashErase((uint32_t)pui8New); + } + + // + // Loop through this portion of flash to see if is all ones (in other + // words, it is an erased portion of flash). + // + for(ui32Idx = 0; ui32Idx < g_ui32FlashPBSize; ui32Idx++) + { + if(pui8New[ui32Idx] != 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(ui32Idx == g_ui32FlashPBSize) + { + break; + } + + // + // Increment to the next parameter block location. + // + pui8New += g_ui32FlashPBSize; + if(pui8New == g_pui8FlashPBEnd) + { + pui8New = g_pui8FlashPBStart; + } + + // + // 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_pui8FlashPBCurrent && (pui8New == g_pui8FlashPBCurrent)) || + (!g_pui8FlashPBCurrent && (pui8New == g_pui8FlashPBStart))) + { + return; + } + } + + // + // Write this parameter block to flash. + // + MAP_FlashProgram((uint32_t *)pui8Buffer, (uint32_t)pui8New, + g_ui32FlashPBSize); + + // + // 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(ui32Idx = 0; ui32Idx < g_ui32FlashPBSize; ui32Idx++) + { + if(pui8New[ui32Idx] != pui8Buffer[ui32Idx]) + { + return; + } + } + + // + // The new parameter block becomes the most recent parameter block. + // + g_pui8FlashPBCurrent = pui8New; +} + +//***************************************************************************** +// +//! Initializes the flash parameter block. +//! +//! \param ui32Start 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 ui32End 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 ui32Size 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 ui32Start and \e ui32End 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 ui32Size). 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 ui32Start and \e ui32End 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 ui32Size) 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 ui32End - +//! \e ui32Start) divided by the parameter block size (\e ui32Size) 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(uint32_t ui32Start, uint32_t ui32End, uint32_t ui32Size) +{ + uint8_t *pui8Offset, *pui8Current; + uint8_t ui8One, ui8Two; + + // + // Check the arguments. + // + ASSERT((ui32Start % FLASH_SECTOR_SIZE) == 0); + ASSERT((ui32End % FLASH_SECTOR_SIZE) == 0); + ASSERT((FLASH_SECTOR_SIZE % ui32Size) == 0); + + // + // Save the characteristics of the flash memory to be used for storing + // parameter blocks. + // + g_pui8FlashPBStart = (uint8_t *)ui32Start; + g_pui8FlashPBEnd = (uint8_t *)ui32End; + g_ui32FlashPBSize = ui32Size; + + // + // Loop through the portion of flash memory used for storing parameter + // blocks. + // + for(pui8Offset = g_pui8FlashPBStart, pui8Current = 0; + pui8Offset < g_pui8FlashPBEnd; pui8Offset += g_ui32FlashPBSize) + { + // + // See if this is a valid parameter block (in other words, the checksum + // is correct). + // + if(FlashPBIsValid(pui8Offset)) + { + // + // See if a valid parameter block has been previously found. + // + if(pui8Current != 0) + { + // + // Get the sequence numbers for the current and new parameter + // blocks. + // + ui8One = pui8Current[0]; + ui8Two = pui8Offset[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(((ui8One > ui8Two) && ((ui8One - ui8Two) < 128)) || + ((ui8Two > ui8One) && ((ui8Two - ui8One) > 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. + // + pui8Current = pui8Offset; + } + } + + // + // Save the address of the most recent parameter block found. If no valid + // parameter blocks were found, this will be a NULL pointer. + // + g_pui8FlashPBCurrent = pui8Current; +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** -- cgit v1.3.1