# nvm.c

Go to the documentation of this file.

    00001 /***************************************************************************/
    00053 #include <stdbool.h>
    00054 #include "nvm.h"
    00055 #include "nvm_hal.h"
    00056
    00057 /***************************************************************************/
    00062 /***************************************************************************/
    00067 /*******************************************************************************
    00068  *******************************   DEFINES   ***********************************
    00069  ******************************************************************************/
    00070
    00075 #define NVM_VERSION              0x2U
    00076
    00077 /* Sizes. Internal sizes of different objects */
    00078 #define NVM_CONTENT_SIZE         (NVM_PAGE_SIZE - (NVM_HEADER_SIZE + NVM_FOOTER_SIZE))
    00079 #define NVM_WEAR_CONTENT_SIZE    (NVM_PAGE_SIZE - NVM_HEADER_SIZE)
    00080
    00081 /* Generic constants. Clears up the code and make it more readable. */
    00082 #define NVM_PAGE_EMPTY_VALUE                   0xffffU
    00083 #define NVM_NO_PAGE_RETURNED                   0xffffffffUL
    00084 #define NVM_NO_WRITE_16BIT                     0xffffU
    00085 #define NVM_NO_WRITE_32BIT                     0xffffffffUL
    00086 #define NVM_HIGHEST_32BIT                      0xffffffffUL
    00087 #define NVM_FLIP_FIRST_BIT_OF_32_WHEN_WRITE    0xffff7fffUL
    00088 #define NVM_FIRST_BIT_ONE                      0x8000U
    00089 #define NVM_FIRST_BIT_ZERO                     0x7fffU
    00090 #define NVM_LAST_BIT_ZERO                      0xfffeU
    00091
    00092 #define NVM_CHECKSUM_INITIAL                   0xffffU
    00093 #define NVM_CHECKSUM_LENGTH                    2U
    00094
    00095 #define NVM_PAGES_PER_WEAR_HISTORY             8U
    00096
    00097 /* Macros for acquiring and releasing write lock. Currently empty but could be redefined */
    00098 /* in RTOSes to add resources protection. Without it, it is not smart to call NVM module */
    00099 /* from interrupts or other tasks without ensuring that it is not used by main thread.   */
    00100 #ifndef NVM_ACQUIRE_WRITE_LOCK
    00101 #define NVM_ACQUIRE_WRITE_LOCK
    00102 #endif
    00103
    00104 #ifndef NVM_RELEASE_WRITE_LOCK
    00105 #define NVM_RELEASE_WRITE_LOCK
    00106 #endif
    00107
    00110 /*******************************************************************************
    00111  ******************************   TYPEDEFS   ***********************************
    00112  ******************************************************************************/
    00113
    00116 typedef enum
    00117 {
    00118   nvmValidateResultOk       = 0,
    00119   nvmValidateResultOkMarked = 1,
    00120   nvmValidateResultOld      = 2,
    00121   nvmValidateResultError    = 3
    00122 } NVM_ValidateResult_t;
    00123
    00126 typedef struct
    00127 {
    00128   uint16_t watermark;
    00129   uint32_t updateId;
    00130   uint16_t version;
    00131 } NVM_Page_Header_t;
    00132
    00134 #define NVM_HEADER_SIZE          (2*sizeof(uint16_t)+sizeof(uint32_t))
    00135
    00138 typedef struct
    00139 {
    00140   uint16_t checksum;
    00141   uint16_t watermark;
    00142 } NVM_Page_Footer_t;
    00143
    00145 #define NVM_FOOTER_SIZE          (2*sizeof(uint16_t))
    00146
    00149 /*******************************************************************************
    00150  *******************************   STATICS   ***********************************
    00151  ******************************************************************************/
    00152
    00156 static NVM_Config_t const *nvmConfig;
    00157
    00158 #if (NVM_FEATURE_STATIC_WEAR_ENABLED == true)
    00159 /* Static wear leveling */
    00160
    00161 /* Bit list that records which pages have been rewritten.
    00162  * The array contains number of pages divided by 8 bits in a byte. */
    00163 static uint8_t nvmStaticWearWriteHistory[(NVM_MAX_NUMBER_OF_PAGES+(NVM_PAGES_PER_WEAR_HISTORY-1))/NVM_PAGES_PER_WEAR_HISTORY];
    00164
    00165 /* Number of different page writes recorded in history. */
    00166 static uint16_t nvmStaticWearWritesInHistory;
    00167
    00168 /* Number of page erases performed since last rest. */
    00169 static uint16_t nvmStaticWearErasesSinceReset;
    00170
    00171 /* Stop recurring calls from causing mayhem. */
    00172 static bool nvmStaticWearWorking = false;
    00173 #endif
    00174
    00177 /*******************************************************************************
    00178  ******************************   PROTOTYPES   *********************************
    00179  ******************************************************************************/
    00180
    00183 static uint8_t* NVM_PageFind(uint16_t pageId);
    00184 static uint8_t* NVM_ScratchPageFindBest(void);
    00185 static NVM_Result_t NVM_PageErase(uint8_t *pPhysicalAddress);
    00186 static NVM_Page_Descriptor_t NVM_PageGet(uint16_t pageId);
    00187 static NVM_ValidateResult_t NVM_PageValidate(uint8_t *pPhysicalAddress);
    00188
    00189 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
    00190 static uint16_t NVM_WearIndex(uint8_t *pPhysicalAddress, NVM_Page_Descriptor_t *pPageDesc);
    00191 static bool NVM_WearReadIndex(uint8_t *pPhysicalAddress, NVM_Page_Descriptor_t *pPageDesc, uint16_t *pIndex);
    00192 #endif
    00193
    00194 static void NVM_ChecksumAdditive(uint16_t *pChecksum, void *pBuffer, uint16_t len);
    00195
    00196 #if (NVM_FEATURE_STATIC_WEAR_ENABLED == true)
    00197 static void NVM_StaticWearReset(void);
    00198 static void NVM_StaticWearUpdate(uint16_t address);
    00199 static NVM_Result_t NVM_StaticWearCheck(void);
    00200 #endif
    00201
    00203 /*******************************************************************************
    00204  ***************************   GLOBAL FUNCTIONS   ******************************
    00205  ******************************************************************************/
    00206
    00207 /***************************************************************************/
    00234 NVM_Result_t NVM_Init(NVM_Config_t const *config)
    00235 {
    00236   uint16_t     page;
    00237   /* Variable to store the result returned at the end. */
    00238   NVM_Result_t result = nvmResultErrorInitial;
    00239
    00240   /* Physical address of the current page. */
    00241   uint8_t *pPhysicalAddress = (uint8_t *)(config->nvmArea);
    00242   /* Physical address of a suspected duplicate page under observation. */
    00243   uint8_t *pDuplicatePhysicalAddress;
    00244
    00245   /* Logical address of the current page. */
    00246   uint16_t logicalAddress;
    00247   /* Logical address of a duplicate page. */
    00248   uint16_t duplicateLogicalAddress;
    00249
    00250   /* Temporary variable to store results of a validation operation. */
    00251   NVM_ValidateResult_t validationResult;
    00252   /* Temporary variable to store results of a erase operation. */
    00253   NVM_Result_t         eraseResult;
    00254
    00255   /* if there is no spare page, return error */
    00256   if( (config->pages <= config->userPages) || (config->pages > NVM_MAX_NUMBER_OF_PAGES) )
    00257     return nvmResultError;
    00258
    00259   /* now check that page structures fits to physical page size */
    00260   {
    00261     uint16_t pageIdx = 0, obj = 0, sum = 0;
    00262     const NVM_Page_Descriptor_t *current_page;
    00263
    00264     for(pageIdx=0;pageIdx < config->userPages; pageIdx++)
    00265     {
    00266       sum = 0;
    00267       obj = 0;
    00268       current_page = &((*(config->nvmPages))[pageIdx]);
    00269
    00270       while( (*(current_page->page))[obj].location != 0)
    00271         sum += (*(current_page->page))[obj++].size;
    00272
    00273       if(current_page->pageType == nvmPageTypeNormal)
    00274       {
    00275         if( sum > NVM_CONTENT_SIZE )
    00276         {
    00277           return nvmResultError; /* objects bigger than page size */
    00278         }
    00279       }
    00280       else
    00281       {
    00282         if(current_page->pageType == nvmPageTypeWear)
    00283         {
    00284           if( (sum+NVM_CHECKSUM_LENGTH) > NVM_WEAR_CONTENT_SIZE )
    00285           {
    00286             return nvmResultError; /* objects bigger than page size */
    00287           }
    00288         } else
    00289           {
    00290             return nvmResultError; /* unknown page type */
    00291           }
    00292       }
    00293     }
    00294   }
    00295
    00296   nvmConfig = config;
    00297
    00298   /* Require write lock to continue. */
    00299   NVM_ACQUIRE_WRITE_LOCK
    00300
    00301   /* Initialize the NVM. */
    00302   NVMHAL_Init();
    00303
    00304 #if (NVM_FEATURE_STATIC_WEAR_ENABLED == true)
    00305   /* Initialize the static wear leveling functionality. */
    00306   NVM_StaticWearReset();
    00307 #endif
    00308
    00309   /* Run through all pages and see if they validate if they contain content. */
    00310   for (page = 0; page < nvmConfig->pages; ++page)
    00311   {
    00312     /* Read the logical address of the page stored at the current physical
    00313      * address, and compare it to the value of an empty page. */
    00314     NVMHAL_Read(pPhysicalAddress, &logicalAddress, sizeof(logicalAddress));
    00315     if (NVM_PAGE_EMPTY_VALUE != logicalAddress)
    00316     {
    00317       /* Not an empty page. Check if it validates. */
    00318       validationResult = NVM_PageValidate(pPhysicalAddress);
    00319
    00320       /* Three different kinds of pages. */
    00321       if (nvmValidateResultOk == validationResult)
    00322       {
    00323         /* We have found a valid page, so the initial error can be changed to an
    00324          * OK result. */
    00325         if (nvmResultErrorInitial == result)
    00326         {
    00327           result = nvmResultOk;
    00328         }
    00329       }
    00330       else if (nvmValidateResultOkMarked == validationResult)
    00331       {
    00332         /* Page validates, but is marked for write.
    00333          * There might exist a newer version. */
    00334
    00335         /* Walk through all the possible pages looking for a page with
    00336          * matching watermark. */
    00337         pDuplicatePhysicalAddress = (uint8_t *)(nvmConfig->nvmArea);
    00338         for (page = 0; (NVM_PAGE_EMPTY_VALUE != logicalAddress) && (page < nvmConfig->pages);
    00339              ++page)
    00340         {
    00341           NVMHAL_Read(pDuplicatePhysicalAddress, &duplicateLogicalAddress, sizeof(duplicateLogicalAddress));
    00342
    00343           if ((pDuplicatePhysicalAddress != pPhysicalAddress) && ((logicalAddress | NVM_FIRST_BIT_ONE) == duplicateLogicalAddress))
    00344           {
    00345             /* Duplicate page has got the same logical address. Check if it
    00346              * validates. */
    00347             validationResult = NVM_PageValidate(pDuplicatePhysicalAddress);
    00348
    00349             if (nvmValidateResultOk == validationResult)
    00350             {
    00351               /* The new one validates, delete the old one. */
    00352               eraseResult = NVM_PageErase(pPhysicalAddress);
    00353             }
    00354             else
    00355             {
    00356               /* The new one is broken, delete the new one. */
    00357               eraseResult = NVM_PageErase(pDuplicatePhysicalAddress);
    00358             }
    00359
    00360             /* Something went wrong */
    00361             if (nvmResultOk != eraseResult)
    00362             {
    00363               result = nvmResultError;
    00364             }
    00365           }
    00366
    00367           /* Go to the next physical page. */
    00368           pDuplicatePhysicalAddress += NVM_PAGE_SIZE;
    00369         } /* End duplicate search loop. */
    00370
    00371         /* If everything went OK and this is the first page we found, then
    00372          * we can change the status from initial error to OK. */
    00373         if (nvmResultErrorInitial == result)
    00374         {
    00375           result = nvmResultOk;
    00376         }
    00377       }
    00378       else
    00379       {
    00380         /* Page does not validate */
    00381         result = nvmResultError;
    00382       }
    00383     } /* End - not empty if. */
    00384
    00385     /* Go to the next physical page. */
    00386     pPhysicalAddress += NVM_PAGE_SIZE;
    00387   } /* End pages loop. */
    00388
    00389   /* If no pages was found, the system is not in use and should be reset. */
    00390   if (nvmResultErrorInitial == result)
    00391   {
    00392     result = nvmResultNoPages;
    00393   }
    00394
    00395   /* Give up write lock and open for other API operations. */
    00396   NVM_RELEASE_WRITE_LOCK
    00397
    00398   return result;
    00399 }
    00400
    00401 /***************************************************************************/
    00419 NVM_Result_t NVM_Erase(uint32_t erasureCount)
    00420 {
    00421   uint16_t     page;
    00422   /* Result used when returning from the function. */
    00423   NVM_Result_t result = nvmResultErrorInitial;
    00424
    00425   /* Location of physical page. */
    00426   uint8_t *pPhysicalAddress = (uint8_t *)(nvmConfig->nvmArea);
    00427
    00428   /* Container for moving old erasure count, or set to new. */
    00429   uint32_t tempErasureCount = erasureCount;
    00430
    00431
    00432   /* Require write lock to continue. */
    00433   NVM_ACQUIRE_WRITE_LOCK
    00434
    00435
    00436   /* Loop over all the pages, as long as everything is OK. */
    00437   for (page = 0;
    00438        (page < nvmConfig->pages) && ((nvmResultOk == result) || (nvmResultErrorInitial == result));
    00439        ++page)
    00440   {
    00441     /* If erasureCount input is set to the retain constant, we need to get the
    00442      * old erasure count before we erase the page. */
    00443     if (NVM_ERASE_RETAINCOUNT == erasureCount)
    00444     {
    00445       /* Read old erasure count. */
    00446       NVMHAL_Read(pPhysicalAddress + 2, &tempErasureCount, sizeof(tempErasureCount));
    00447     }
    00448
    00449     /* Erase page. */
    00450     result = NVMHAL_PageErase(pPhysicalAddress);
    00451
    00452     /* If still OK, write erasure count to page. */
    00453     if (nvmResultOk == result)
    00454     {
    00455       result = NVMHAL_Write(pPhysicalAddress + 2, &tempErasureCount, sizeof(tempErasureCount));
    00456     }
    00457
    00458     /* Go to the next physical page. */
    00459     pPhysicalAddress += NVM_PAGE_SIZE;
    00460   }
    00461
    00462   /* Give up write lock and open for other API operations. */
    00463   NVM_RELEASE_WRITE_LOCK
    00464
    00465   return result;
    00466 }
    00467
    00468 /***************************************************************************/
    00493 NVM_Result_t NVM_Write(uint16_t pageId, uint8_t objectId)
    00494 {
    00495   /* Result variable used as return value from the function. */
    00496   NVM_Result_t result = nvmResultErrorInitial;
    00497
    00498   /* Watermark to look for when finding page. First bit true. */
    00499   uint16_t              watermark = pageId | NVM_FIRST_BIT_ONE;
    00500   /* Watermark used when flipping the duplication bit of a page. */
    00501   const uint32_t flipWatermark = NVM_FLIP_FIRST_BIT_OF_32_WHEN_WRITE;
    00502
    00503   /* Page descriptor used for accessing page type and page objects. */
    00504   NVM_Page_Descriptor_t pageDesc;
    00505
    00506   /* Page header and footer. Used to store old version and to easily update and
    00507    * write new version. */
    00508   NVM_Page_Header_t header;
    00509
    00510   /* Variable used for checksum calculation. Starts at defined initial value. */
    00511   uint16_t checksum = NVM_CHECKSUM_INITIAL;
    00512
    00513   /* Physical addresses in memory for the old and new version of the page. */
    00514   uint8_t *pOldPhysicalAddress = (uint8_t *) NVM_NO_PAGE_RETURNED;
    00515   uint8_t *pNewPhysicalAddress = (uint8_t *) NVM_NO_PAGE_RETURNED;
    00516
    00517   /* Offset address within page. */
    00518   uint16_t offsetAddress;
    00519   /* Single byte buffer used when copying data from an old page.*/
    00520   uint8_t  copyBuffer;
    00521   /* Object in page counter. */
    00522   uint8_t  objectIndex;
    00523   /* Amount of bytes to copy. */
    00524   uint16_t copyLength;
    00525
    00526   /* Handle wear pages. Should we handle this as an extra write to an existing
    00527    * page or create a new one. */
    00528   bool wearWrite = false;
    00529
    00530 #if (NVM_FEATURE_WRITE_NECESSARY_CHECK_ENABLED == true)
    00531   /* Bool used when checking if a write operation is needed. */
    00532   bool rewriteNeeded;
    00533 #endif
    00534
    00535 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
    00536   /* Used to hold the checksum of the wear object. */
    00537   uint16_t wearChecksum;
    00538   /* Byte size of the wear object. Includes checksum length. */
    00539   uint16_t wearObjectSize;
    00540   /* Used to specify the internal index of the wear object in a page. */
    00541   uint16_t wearIndex;
    00542
    00543   #if (NVM_FEATURE_WRITE_VALIDATION_ENABLED == true)
    00544   /* The new wear index the object will be written to. */
    00545   uint16_t wearIndexNew;
    00546   #endif
    00547 #endif
    00548
    00549   /* Require write lock to continue. */
    00550   NVM_ACQUIRE_WRITE_LOCK
    00551
    00552   /* Find old physical address. */
    00553   pOldPhysicalAddress = NVM_PageFind(pageId);
    00554
    00555   /* Get the page configuration. */
    00556   pageDesc = NVM_PageGet(pageId);
    00557
    00558 #if (NVM_FEATURE_WRITE_NECESSARY_CHECK_ENABLED == true)
    00559   /* If there is an old version of the page, it might not be necessary to update
    00560    * the data. Also check that this is a normal page and that the static wear
    00561    * leveling system is not working (this system might want to rewrite pages
    00562    * even if the data is similar to the old version). */
    00563   if (((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress)
    00564       && (nvmPageTypeNormal == pageDesc.pageType)
    00565 #if (NVM_FEATURE_STATIC_WEAR_ENABLED == true)
    00566       && !nvmStaticWearWorking
    00567 #endif
    00568
    00569       )
    00570   {
    00571     rewriteNeeded = false;
    00572     objectIndex   = 0;
    00573     offsetAddress = 0;
    00574
    00575     /* Loop over items as long as no rewrite is needed and the current item has
    00576     * got a size other than 0. Size 0 is used as a marker for a NULL object. */
    00577     while (((*pageDesc.page)[objectIndex].size != 0) && !rewriteNeeded)
    00578     {
    00579       /* Check if every object should be written or if this is the object to
    00580        * write. */
    00581       if ((NVM_WRITE_ALL_CMD == objectId) ||
    00582           ((*pageDesc.page)[objectIndex].objectId == objectId))
    00583       {
    00584         /* Compare object to RAM. */
    00585
    00586         copyLength = (*pageDesc.page)[objectIndex].size;
    00587
    00588         /* Loop over each byte of the object and compare with RAM. */
    00589         while (copyLength != 0)
    00590         {
    00591           /* Get byte from NVM. */
    00592           NVMHAL_Read(pOldPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
    00593                       &copyBuffer,
    00594                       sizeof(copyBuffer));
    00595
    00596           /* Check byte in NVM with the corresponding byte in RAM. */
    00597           if (*(uint8_t *)((*pageDesc.page)[objectIndex].location + offsetAddress) != copyBuffer)
    00598           {
    00599             rewriteNeeded = true;
    00600             break;
    00601           }
    00602
    00603           /* Move offset. */
    00604           offsetAddress += sizeof(copyBuffer);
    00605           copyLength    -= sizeof(copyBuffer);
    00606         }
    00607       }
    00608       else
    00609       {
    00610         /* Move offset past the object. */
    00611         offsetAddress += (*pageDesc.page)[objectIndex].size;
    00612       }
    00613
    00614       /* Check next object. */
    00615       objectIndex++;
    00616     }
    00617
    00618     if (!rewriteNeeded)
    00619     {
    00620       /* Release write lock before return. */
    00621       NVM_RELEASE_WRITE_LOCK
    00622
    00623       return nvmResultOk;
    00624     }
    00625   }
    00626 #endif
    00627
    00628
    00629
    00630 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
    00631   /* If this is a wear page then we can check if we can possibly squeeze another
    00632    * version of the object inside the already existing page. If this is possible
    00633    * we set the wearWrite boolean. This will then make us ignore the normal
    00634    * write operation. */
    00635   if (nvmPageTypeWear == pageDesc.pageType)
    00636   {
    00637     /* Calculate checksum. The wear page checksum is only stored in 15 bits,
    00638      * because we need one bit to mark that the object is written. This bit is
    00639      * always set to 0. */
    00640
    00641     wearChecksum = NVM_CHECKSUM_INITIAL;
    00642     NVM_ChecksumAdditive(&wearChecksum, (*pageDesc.page)[0].location, (*pageDesc.page)[0].size);
    00643     wearChecksum &= NVM_LAST_BIT_ZERO;
    00644
    00645     /* If there was an old page. */
    00646     if ((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress)
    00647     {
    00648       /* Find location in old page. */
    00649       wearIndex      = NVM_WearIndex(pOldPhysicalAddress, &pageDesc);
    00650       wearObjectSize = (*pageDesc.page)[0].size + NVM_CHECKSUM_LENGTH;
    00651
    00652       /* Check that the wearIndex returned is within the length of the page. */
    00653       if (wearIndex < ((uint16_t) NVM_WEAR_CONTENT_SIZE) / wearObjectSize)
    00654       {
    00655         result = NVMHAL_Write(pOldPhysicalAddress + NVM_HEADER_SIZE + wearIndex * wearObjectSize,
    00656                               (*pageDesc.page)[0].location,
    00657                               (*pageDesc.page)[0].size);
    00658         result = NVMHAL_Write(pOldPhysicalAddress + NVM_HEADER_SIZE + wearIndex * wearObjectSize + (*pageDesc.page)[0].size,
    00659                               &wearChecksum,
    00660                               sizeof(wearChecksum));
    00661
    00662         /* Register that we have now written to the old page. */
    00663         wearWrite = true;
    00664
    00665 #if (NVM_FEATURE_WRITE_VALIDATION_ENABLED == true)
    00666         /* Check if the newest one that is valid is the same as the one we just
    00667          * wrote to the NVM. */
    00668         if ((!NVM_WearReadIndex(pOldPhysicalAddress, &pageDesc, &wearIndexNew)) ||
    00669             (wearIndexNew != wearIndex))
    00670         {
    00671           result = nvmResultError;
    00672         }
    00673 #endif
    00674       }
    00675     } /* End of old page if. */
    00676   }   /* End of wear page if. */
    00677 #endif
    00678
    00679 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
    00680   /* Do not create a new page if we have already done an in-page wear write. */
    00681   if (!wearWrite)
    00682   {
    00683 #endif
    00684   /* Mark any old page before creating a new one. */
    00685   if ((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress)
    00686   {
    00687     result = NVMHAL_Write(pOldPhysicalAddress, &flipWatermark, 4);
    00688
    00689     if (nvmResultOk != result)
    00690     {
    00691       /* Give up write lock and open for other API operations. */
    00692       NVM_RELEASE_WRITE_LOCK
    00693       return result;
    00694     }
    00695   }
    00696
    00697   /* Find new physical address to write to. */
    00698   pNewPhysicalAddress = NVM_ScratchPageFindBest();
    00699
    00700   if ((uint8_t*) NVM_NO_PAGE_RETURNED == pNewPhysicalAddress)
    00701   {
    00702     /* Give up write lock and open for other API operations. */
    00703     NVM_RELEASE_WRITE_LOCK
    00704     return nvmResultError;
    00705   }
    00706
    00707   /* Generate and write header */
    00708   header.watermark = watermark;
    00709   header.updateId  = NVM_NO_WRITE_32BIT;
    00710   header.version   = NVM_VERSION;
    00711
    00712   /* store header at beginning of page */
    00713   result = NVMHAL_Write(pNewPhysicalAddress, &header.watermark, sizeof(header.watermark));
    00714   result = NVMHAL_Write(pNewPhysicalAddress + sizeof(header.watermark), &header.updateId, sizeof(header.updateId));
    00715   result = NVMHAL_Write(pNewPhysicalAddress + sizeof(header.watermark)+ + sizeof(header.updateId), &header.version, sizeof(header.version));
    00716
    00717   /* Reset address index within page. */
    00718   offsetAddress = 0;
    00719   /* Reset object in page counter. */
    00720   objectIndex = 0;
    00721
    00722   /* Loop over items as long as everything is OK, and the current item has got
    00723    * a size other than 0. Size 0 is used as a marker for a NULL object. */
    00724   while (((*pageDesc.page)[objectIndex].size != 0) && (nvmResultOk == result))
    00725   {
    00726     /* Check if every object should be written or if this is the object to
    00727      * write. */
    00728     if ((NVM_WRITE_ALL_CMD == objectId) ||
    00729         ((*pageDesc.page)[objectIndex].objectId == objectId))
    00730     {
    00731       /* Write object from RAM. */
    00732       result = NVMHAL_Write(pNewPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
    00733                             (*pageDesc.page)[objectIndex].location,
    00734                             (*pageDesc.page)[objectIndex].size);
    00735       offsetAddress += (*pageDesc.page)[objectIndex].size;
    00736
    00737       NVM_ChecksumAdditive(&checksum, (*pageDesc.page)[objectIndex].location, (*pageDesc.page)[objectIndex].size);
    00738     }
    00739     else
    00740     {
    00741       /* Get version from old page. */
    00742       if ((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress)
    00743       {
    00744         NVM_ChecksumAdditive(&checksum, pOldPhysicalAddress + offsetAddress + NVM_HEADER_SIZE, (*pageDesc.page)[objectIndex].size);
    00745
    00746         copyLength = (*pageDesc.page)[objectIndex].size;
    00747
    00748         while ((copyLength != 0) && (nvmResultOk == result))
    00749         {
    00750           /* Copies using an 1 byte buffer. Might be better to dynamically use larger if possible. */
    00751           NVMHAL_Read(pOldPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
    00752                       &copyBuffer,
    00753                       sizeof(copyBuffer));
    00754           result = NVMHAL_Write(pNewPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
    00755                                 &copyBuffer,
    00756                                 sizeof(copyBuffer));
    00757
    00758           offsetAddress += sizeof(copyBuffer);
    00759           copyLength    -= sizeof(copyBuffer);
    00760         }
    00761       }  /* End if old page. */
    00762     }   /* Else-end of NVM_WRITE_ALL if-statement. */
    00763
    00764     objectIndex++;
    00765   }
    00766
    00767   /* If we are creating a wear page, add the checksum directly after the data. */
    00768 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
    00769   if (nvmPageTypeWear == pageDesc.pageType)
    00770   {
    00771     result = NVMHAL_Write(pNewPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
    00772                           &wearChecksum,
    00773                           sizeof(wearChecksum));
    00774   }
    00775   /* Generate and write footer on normal pages. */
    00776   else
    00777   {
    00778 #endif
    00779   if (nvmResultOk == result)
    00780   {
    00781     /* write checksum at end of page */
    00782     result = NVMHAL_Write(pNewPhysicalAddress + (NVM_PAGE_SIZE - NVM_FOOTER_SIZE), &checksum, sizeof(checksum));
    00783     /* write watermark just after checksum at end of page */
    00784     result = NVMHAL_Write(pNewPhysicalAddress + (NVM_PAGE_SIZE + sizeof(checksum) - NVM_FOOTER_SIZE), &watermark, sizeof(watermark));
    00785   }
    00786
    00787 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
    00788 }     /* End of else from page type. */
    00789 #endif
    00790
    00791 #if (NVM_FEATURE_WRITE_VALIDATION_ENABLED == true)
    00792   /* Validate that the correct data was written. */
    00793   if (nvmValidateResultOk != NVM_PageValidate(pNewPhysicalAddress))
    00794   {
    00795     result = nvmResultError;
    00796   }
    00797 #endif
    00798
    00799 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
    00800 }   /* End of if for normal write (!wearWrite). */
    00801 #endif
    00802
    00803   /* Erase old if there was an old one and everything else have gone OK. */
    00804   if ((!wearWrite) &&
    00805       ((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress))
    00806   {
    00807     if (nvmResultOk == result)
    00808     {
    00809       result = NVM_PageErase(pOldPhysicalAddress);
    00810     }
    00811     else
    00812     {
    00813       NVM_PageErase(pNewPhysicalAddress);
    00814     }
    00815   }
    00816
    00817   /* Give up write lock and open for other API operations. */
    00818   NVM_RELEASE_WRITE_LOCK
    00819
    00820   return result;
    00821 }
    00822
    00823 /***************************************************************************/
    00843 NVM_Result_t NVM_Read(uint16_t pageId, uint8_t objectId)
    00844 {
    00845 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
    00846   /* Variable used to fetch read index. */
    00847   uint16_t wearIndex;
    00848 #endif
    00849
    00850   /* Physical address of the page to read from. */
    00851   uint8_t *pPhysicalAddress;
    00852
    00853   /* Description of the page, used to find page type and objects. */
    00854   NVM_Page_Descriptor_t pageDesc;
    00855
    00856   /* Index of object in page. */
    00857   uint8_t  objectIndex;
    00858   /* Address of read location within a page. */
    00859   uint16_t offsetAddress;
    00860
    00861
    00862   /* Require write lock to continue. */
    00863   NVM_ACQUIRE_WRITE_LOCK
    00864
    00865   /* Find physical page. */
    00866   pPhysicalAddress = NVM_PageFind(pageId);
    00867
    00868   /* If no page was found, we cannot read anything. */
    00869   if ((uint8_t*) NVM_NO_PAGE_RETURNED == pPhysicalAddress)
    00870   {
    00871     /* Give up write lock and open for other API operations. */
    00872     NVM_RELEASE_WRITE_LOCK
    00873     return nvmResultNoPage;
    00874   }
    00875
    00876   /* Get page description. */
    00877   pageDesc = NVM_PageGet(pageId);
    00878
    00879 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
    00880   /* If this is a wear page, we must find out which object in the page should be
    00881    * read. */
    00882   if (nvmPageTypeWear == pageDesc.pageType)
    00883   {
    00884     /* Find valid object in wear page and read it. */
    00885     if (NVM_WearReadIndex(pPhysicalAddress, &pageDesc, &wearIndex))
    00886     {
    00887       NVMHAL_Read(pPhysicalAddress + NVM_HEADER_SIZE +
    00888                   wearIndex * ((*pageDesc.page)[0].size + NVM_CHECKSUM_LENGTH),
    00889                   (*pageDesc.page)[0].location,
    00890                   (*pageDesc.page)[0].size);
    00891     }
    00892     else
    00893     {
    00894       /* No valid object was found in the page. */
    00895       /* Give up write lock and open for other API operations. */
    00896       NVM_RELEASE_WRITE_LOCK
    00897       return nvmResultDataInvalid;
    00898     }
    00899   }
    00900   else
    00901 #endif
    00902   {
    00903     /* Read normal page. */
    00904     objectIndex   = 0;
    00905     offsetAddress = 0;
    00906
    00907 #if (NVM_FEATURE_READ_VALIDATION_ENABLED == true)
    00908     if (nvmValidateResultError == NVM_PageValidate(pPhysicalAddress))
    00909     {
    00910       /* Give up write lock and open for other API operations. */
    00911       NVM_RELEASE_WRITE_LOCK
    00912       return nvmResultDataInvalid;
    00913     }
    00914 #endif
    00915
    00916     /* Loop through and read the objects of a page, as long as the current item
    00917      * has got a size other than 0. Size 0 is a marker for the NULL object. */
    00918     while ((*pageDesc.page)[objectIndex].size != 0)
    00919     {
    00920       /* Check if every object should be read or if this is the object to read. */
    00921       if ((NVM_READ_ALL_CMD == objectId) || ((*pageDesc.page)[objectIndex].objectId == objectId))
    00922       {
    00923         NVMHAL_Read(pPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
    00924                     (*pageDesc.page)[objectIndex].location,
    00925                     (*pageDesc.page)[objectIndex].size);
    00926       }
    00927
    00928       offsetAddress += (*pageDesc.page)[objectIndex].size;
    00929       objectIndex++;
    00930     }
    00931   }
    00932
    00933   /* Give up write lock and open for other API operations. */
    00934   NVM_RELEASE_WRITE_LOCK
    00935
    00936   return nvmResultOk;
    00937 }
    00938
    00939 /***************************************************************************/
    00950 #if (NVM_FEATURE_WEARLEVELGET_ENABLED == true)
    00951 uint32_t NVM_WearLevelGet(void)
    00952 {
    00953   uint16_t page;
    00954   /* Used to temporarily store the update id of the current page. */
    00955   uint32_t updateId;
    00956   /* Worst (highest) update id. Used as return value. */
    00957   uint32_t worstUpdateId = 0;
    00958
    00959   /* Address of physical page. */
    00960   uint8_t *pPhysicalAddress = (uint8_t *)(nvmConfig->nvmArea);
    00961
    00962   /* Loop through all pages in memory. */
    00963   for (page = 0; page < nvmConfig->pages; ++page)
    00964   {
    00965     /* Find and compare erasure count. */
    00966     NVMHAL_Read(pPhysicalAddress + 2, &updateId, sizeof(updateId));
    00967     if (updateId > worstUpdateId)
    00968     {
    00969       worstUpdateId = updateId;
    00970     }
    00971
    00972     /* Go to the next physical page. */
    00973     pPhysicalAddress += NVM_PAGE_SIZE;
    00974   }
    00975
    00976   return worstUpdateId;
    00977 }
    00978 #endif
    00979
    00980 /*******************************************************************************
    00981  ***************************   LOCAL FUNCTIONS   *******************************
    00982  ******************************************************************************/
    00983
    00986 /***************************************************************************/
    01001 static uint8_t* NVM_PageFind(uint16_t pageId)
    01002 {
    01003   uint16_t page;
    01004   /* Physical address to return. */
    01005   uint8_t  *pPhysicalAddress = (uint8_t *)(nvmConfig->nvmArea);
    01006   /* Temporary variable used to read and compare logical page address. */
    01007   uint16_t logicalAddress;
    01008
    01009   /* Loop through memory looking for a matching watermark. */
    01010   for (page = 0; page < nvmConfig->pages; ++page)
    01011   {
    01012     /* Allow both versions of writing mark, invalid duplicates should already
    01013      * have been deleted. */
    01014     NVMHAL_Read(pPhysicalAddress, &logicalAddress, sizeof(logicalAddress));
    01015     if (((pageId | NVM_FIRST_BIT_ONE) == logicalAddress) || (pageId == logicalAddress))
    01016     {
    01017       return pPhysicalAddress;
    01018     }
    01019
    01020     /* Move lookup point to the next page. */
    01021     pPhysicalAddress += NVM_PAGE_SIZE;
    01022   }
    01023
    01024   /* No page found. */
    01025   return (uint8_t *) NVM_NO_PAGE_RETURNED;
    01026 }
    01027
    01028 /***************************************************************************/
    01040 static uint8_t* NVM_ScratchPageFindBest(void)
    01041 {
    01042   uint16_t page;
    01043   /* Address for physical page to return. */
    01044   uint8_t  *pPhysicalPage = (uint8_t *) NVM_NO_PAGE_RETURNED;
    01045
    01046   /* Variable used to read and compare update id of physical pages. */
    01047   uint32_t updateId = NVM_HIGHEST_32BIT;
    01048   /* The best update id found. */
    01049   uint32_t bestUpdateId = NVM_HIGHEST_32BIT;
    01050
    01051   /* Pointer to the current physical page. */
    01052   uint8_t  *pPhysicalAddress = (uint8_t *)(nvmConfig->nvmArea);
    01053   /* Logical address that identifies the page. */
    01054   uint16_t logicalAddress;
    01055
    01056   /* Loop through all pages in memory. */
    01057   for (page = 0; page < nvmConfig->pages; ++page)
    01058   {
    01059     /* Read and check logical address. */
    01060     NVMHAL_Read(pPhysicalAddress, &logicalAddress, sizeof(logicalAddress));
    01061     if ((uint16_t) NVM_PAGE_EMPTY_VALUE == logicalAddress)
    01062     {
    01063       /* Find and compare erasure count. */
    01064       NVMHAL_Read(pPhysicalAddress + 2, &updateId, sizeof(updateId));
    01065       if (updateId < bestUpdateId)
    01066       {
    01067         bestUpdateId  = updateId;
    01068         pPhysicalPage = pPhysicalAddress;
    01069       }
    01070     }
    01071
    01072     /* Move lookup point to the next page. */
    01073     pPhysicalAddress += NVM_PAGE_SIZE;
    01074   }
    01075
    01076   /* Return a pointer to the best/least used page. */
    01077   return pPhysicalPage;
    01078 }
    01079
    01080 /***************************************************************************/
    01094 static NVM_Result_t NVM_PageErase(uint8_t *pPhysicalAddress)
    01095 {
    01096 #if (NVM_FEATURE_STATIC_WEAR_ENABLED == true)
    01097   /* Logical page address. */
    01098   uint16_t logicalAddress;
    01099 #endif
    01100
    01101   /* Read out the old page update id. */
    01102   uint32_t updateId;
    01103   NVMHAL_Read(pPhysicalAddress + 2, &updateId, sizeof(updateId));
    01104
    01105 #if (NVM_FEATURE_STATIC_WEAR_ENABLED == true)
    01106   /* Get logical page address. */
    01107   NVMHAL_Read(pPhysicalAddress, &logicalAddress, sizeof(logicalAddress));
    01108
    01109   /* If not empty: mark as erased and check against threshold. */
    01110   if (logicalAddress != NVM_PAGE_EMPTY_VALUE)
    01111   {
    01112     /* Set first bit low. */
    01113     logicalAddress = logicalAddress & NVM_FIRST_BIT_ZERO;
    01114     NVM_StaticWearUpdate(logicalAddress);
    01115   }
    01116 #endif
    01117
    01118   /* Erase the page. */
    01119   NVMHAL_PageErase(pPhysicalAddress);
    01120
    01121   /* Update erasure count. */
    01122   updateId++;
    01123
    01124   /* Write increased erasure count. */
    01125   return NVMHAL_Write(pPhysicalAddress + 2, &updateId, sizeof(updateId));
    01126 }
    01127
    01128 /***************************************************************************/
    01147 static NVM_Page_Descriptor_t NVM_PageGet(uint16_t pageId)
    01148 {
    01149   uint8_t                            pageIndex;
    01150   static const NVM_Page_Descriptor_t nullPage = { (uint8_t) 0, 0, (NVM_Page_Type_t) 0 };
    01151
    01152   /* Step through all configured pages. */
    01153   for (pageIndex = 0; pageIndex < nvmConfig->userPages; ++pageIndex)
    01154   {
    01155     /* If this is the page we want, return it. */
    01156     if ( (*(nvmConfig->nvmPages))[pageIndex].pageId == pageId)
    01157     {
    01158       return (*(nvmConfig->nvmPages))[pageIndex];
    01159     }
    01160   }
    01161
    01162   /* No page matched the ID, return a NULL page to mark the error. */
    01163   return nullPage;
    01164 }
    01165
    01166 /***************************************************************************/
    01187 static NVM_ValidateResult_t NVM_PageValidate(uint8_t *pPhysicalAddress)
    01188 {
    01189   /* Result used as return value from the function. */
    01190   NVM_ValidateResult_t result;
    01191
    01192   /* Objects used to read out the page header and footer. */
    01193   NVM_Page_Header_t header;
    01194   NVM_Page_Footer_t footer;
    01195
    01196   /* Descriptor for the current page. */
    01197   NVM_Page_Descriptor_t pageDesc;
    01198
    01199   /* Variable used for calculating checksums. */
    01200   uint16_t checksum;
    01201
    01202   /* Offset of object in page. */
    01203   uint8_t  objectIndex;
    01204   /* Address of read location within a page. */
    01205   uint16_t offsetAddress;
    01206
    01207 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
    01208   /* Variable used to fetch read index. */
    01209   uint16_t index;
    01210 #endif
    01211
    01212   /* Read page header data */
    01213   NVMHAL_Read(pPhysicalAddress, &header.watermark, sizeof(header.watermark));
    01214   NVMHAL_Read(pPhysicalAddress + sizeof(header.watermark), &header.updateId, sizeof(header.updateId));
    01215   NVMHAL_Read(pPhysicalAddress + sizeof(header.watermark) + sizeof(header.updateId), &header.version, sizeof(header.version));
    01216
    01217   /* Stop immediately if data is from another version of the API. */
    01218   if (NVM_VERSION != header.version)
    01219   {
    01220     return nvmValidateResultOld;
    01221   }
    01222
    01223   /* Get the page configuration. */
    01224   pageDesc = NVM_PageGet((header.watermark & NVM_FIRST_BIT_ZERO));
    01225
    01226
    01227 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
    01228   if (nvmPageTypeWear == pageDesc.pageType)
    01229   {
    01230     /* Wear page. */
    01231
    01232     /* If first bit is already zero the page is marked as a duplicate. */
    01233     if ((header.watermark & NVM_FIRST_BIT_ZERO) == header.watermark)
    01234     {
    01235       result = nvmValidateResultOkMarked;
    01236     }
    01237     else
    01238     {
    01239       /* Page is not marked as a duplicate. */
    01240       result = nvmValidateResultOk;
    01241     }
    01242
    01243     /* If we do not have any valid objects in the page it is invalid. */
    01244     if (!NVM_WearReadIndex(pPhysicalAddress, &pageDesc, &index))
    01245     {
    01246       result = nvmValidateResultError;
    01247     }
    01248   }
    01249   else
    01250 #endif
    01251   {
    01252     /* Normal page. */
    01253     NVMHAL_Read(pPhysicalAddress + (NVM_PAGE_SIZE - NVM_FOOTER_SIZE), &footer.checksum, sizeof(footer.checksum));
    01254     NVMHAL_Read(pPhysicalAddress + (NVM_PAGE_SIZE + sizeof(checksum) - NVM_FOOTER_SIZE), &footer.watermark, sizeof(footer.watermark));
    01255     /* Check if watermark or watermark with flipped write bit matches. */
    01256     if (header.watermark == footer.watermark)
    01257     {
    01258       result = nvmValidateResultOk;
    01259     }
    01260     else if ((header.watermark | NVM_FIRST_BIT_ONE) == footer.watermark)
    01261     {
    01262       result = nvmValidateResultOkMarked;
    01263     }
    01264     else
    01265     {
    01266       result = nvmValidateResultError;
    01267     }
    01268
    01269     /* Calculate checksum and compare with the one stored. */
    01270     objectIndex   = 0;
    01271     offsetAddress = 0;
    01272     checksum      = NVM_CHECKSUM_INITIAL;
    01273
    01274     /* Calculate per object using the HAL. Loop over items as long as the
    01275      * current object has got a size other than 0. Size 0 is used as a marker
    01276      * for a NULL object. */
    01277     while ((*pageDesc.page)[objectIndex].size != 0)
    01278     {
    01279       NVMHAL_Checksum(&checksum, (uint8_t *) pPhysicalAddress + NVM_HEADER_SIZE + offsetAddress, (*pageDesc.page)[objectIndex].size);
    01280       offsetAddress += (*pageDesc.page)[objectIndex].size;
    01281       objectIndex++;
    01282     }
    01283
    01284     if (checksum != footer.checksum)
    01285     {
    01286       result = nvmValidateResultError;
    01287     }
    01288   }
    01289
    01290   return result;
    01291 }
    01292
    01293 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
    01294 /***************************************************************************/
    01312 static uint16_t NVM_WearIndex(uint8_t *pPhysicalAddress, NVM_Page_Descriptor_t *pPageDesc)
    01313 {
    01314   /* Index to return. */
    01315   uint16_t wearIndex = 0;
    01316
    01317   /* Temporary variable used when calculating and comparing checksums. */
    01318   uint16_t checksum;
    01319
    01320   /* Size of the object in the wear page, including a 16 bit checksum. */
    01321   uint16_t wearObjectSize = ((*pPageDesc->page)[0].size + NVM_CHECKSUM_LENGTH);
    01322
    01323
    01324   /* Loop over possible pages. Stop when empty page was found. */
    01325   while (wearIndex < NVM_WEAR_CONTENT_SIZE / wearObjectSize)
    01326   {
    01327     NVMHAL_Read((uint8_t *)(pPhysicalAddress + NVM_HEADER_SIZE +
    01328                             wearIndex * wearObjectSize +
    01329                             (*pPageDesc->page)[0].size
    01330                             ),
    01331                 &checksum,
    01332                 sizeof(checksum));
    01333
    01334     /* Flip the last bit of the checksum to zero. This is a mark used to
    01335      * determine that an object is written to this location. */
    01336     if ((checksum & NVM_LAST_BIT_ZERO) != checksum)
    01337     {
    01338       /* Break the loop and accept this location. */
    01339       break;
    01340     }
    01341
    01342     /* Not a valid position for a new write.
    01343     * Increase the index and check again. */
    01344     wearIndex++;
    01345   }
    01346
    01347   return wearIndex;
    01348 }
    01349 #endif
    01350
    01351 /***************************************************************************/
    01372 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
    01373 static bool NVM_WearReadIndex(uint8_t *pPhysicalAddress, NVM_Page_Descriptor_t *pPageDesc, uint16_t *pIndex)
    01374 {
    01375 #if (NVM_FEATURE_READ_VALIDATION_ENABLED == true)
    01376   /* Variable used for calculating checksum when validating. */
    01377   uint16_t checksum = NVM_CHECKSUM_INITIAL;
    01378 #endif
    01379
    01380   /* Length of wear object plus checksum. */
    01381   const uint16_t wearObjectSize = ((*pPageDesc->page)[0].size + NVM_CHECKSUM_LENGTH);
    01382
    01383   /* Return value. */
    01384   bool validObjectFound = false;
    01385
    01386   /* Buffer used when reading checksum. */
    01387   uint16_t readBuffer;
    01388
    01389   /* Initialize index at max plus one object. */
    01390   *pIndex = (((uint16_t) NVM_WEAR_CONTENT_SIZE) / wearObjectSize);
    01391
    01392   /* Loop over possible pages. Stop when first OK page is found. */
    01393   while ((*pIndex > 0) && (!validObjectFound))
    01394   {
    01395     (*pIndex)--;
    01396
    01397     /* Initialize checksum, and then calculate it from the HAL.*/
    01398     uint8_t *temp = pPhysicalAddress + NVM_HEADER_SIZE + (*pIndex) * wearObjectSize + (*pPageDesc->page)[0].size;
    01399     NVMHAL_Read((uint8_t *) temp, &readBuffer, sizeof(readBuffer));
    01400
    01401 #if (NVM_FEATURE_READ_VALIDATION_ENABLED == true)
    01402     /* Calculate the checksum before accepting the object. */
    01403     checksum = NVM_CHECKSUM_INITIAL;
    01404     NVMHAL_Checksum(&checksum, pPhysicalAddress + NVM_HEADER_SIZE + (*pIndex) * wearObjectSize, (*pPageDesc->page)[0].size);
    01405     /* Flips the last bit of the checksum to zero. This is a mark used to
    01406      * determine whether we have written anything to the page. */
    01407     if ((uint16_t)(checksum & NVM_LAST_BIT_ZERO) == readBuffer)
    01408 #else
    01409     if (NVM_NO_WRITE_16BIT != readBuffer)
    01410 #endif
    01411     {
    01412       validObjectFound = true;
    01413     }
    01414   }
    01415
    01416   return validObjectFound;
    01417 }
    01418 #endif
    01419
    01420 /***************************************************************************/
    01439 static void NVM_ChecksumAdditive(uint16_t *pChecksum, void *pBuffer, uint16_t len)
    01440 {
    01441   uint8_t *pointer = (uint8_t *) pBuffer;
    01442   uint16_t crc = *pChecksum;
    01443
    01444   while(len--)
    01445   {
    01446     crc = (crc >> 8) | (crc << 8);
    01447     crc ^= *pointer++;
    01448     crc ^= (crc & 0xf0) >> 4;
    01449     crc ^= (crc & 0x0f) << 12;
    01450     crc ^= (crc & 0xff) << 5;
    01451   }
    01452
    01453   *pChecksum = crc;
    01454 }
    01455
    01456 #if (NVM_FEATURE_STATIC_WEAR_ENABLED == true)
    01457 /***************************************************************************/
    01466 static void NVM_StaticWearReset(void)
    01467 {
    01468   uint16_t i;
    01469   nvmStaticWearErasesSinceReset = 0;
    01470   nvmStaticWearWritesInHistory  = 0;
    01471
    01472   for (i = 0; (NVM_PAGES_PER_WEAR_HISTORY * i) < nvmConfig->userPages; i += 1)
    01473   {
    01474     nvmStaticWearWriteHistory[i] = 0;
    01475   }
    01476 }
    01477
    01478 /***************************************************************************/
    01489 static void NVM_StaticWearUpdate(uint16_t address)
    01490 {
    01491   if (address < nvmConfig->userPages)
    01492   {
    01493     /* Mark page with logical address as written. */
    01494
    01495     /* Bitmask to check and change the desired bit. */
    01496     uint8_t mask = 1U << (address % NVM_PAGES_PER_WEAR_HISTORY);
    01497
    01498     if ((nvmStaticWearWriteHistory[address / NVM_PAGES_PER_WEAR_HISTORY] & mask) == 0)
    01499     {
    01500       /* Flip bit. */
    01501       nvmStaticWearWriteHistory[address / NVM_PAGES_PER_WEAR_HISTORY] |= mask;
    01502       /* Record flip. */
    01503       nvmStaticWearWritesInHistory++;
    01504     }
    01505
    01506     /* Record erase operation. */
    01507     nvmStaticWearErasesSinceReset++;
    01508
    01509     /* Call the static wear leveler. */
    01510     NVM_StaticWearCheck();
    01511   }
    01512 }
    01513
    01514 /***************************************************************************/
    01523 static NVM_Result_t NVM_StaticWearCheck(void)
    01524 {
    01525   /* Check if there is a check already running. We do not need more of these. */
    01526   if (!nvmStaticWearWorking)
    01527   {
    01528     nvmStaticWearWorking = true;
    01529     while (nvmStaticWearErasesSinceReset / nvmStaticWearWritesInHistory > NVM_STATIC_WEAR_THRESHOLD)
    01530     {
    01531       /* If all the pages have been moved in this cycle: reset. */
    01532       if (nvmStaticWearWritesInHistory >= nvmConfig->userPages)
    01533       {
    01534         NVM_StaticWearReset();
    01535         break;
    01536       }
    01537
    01538       /* Find an address for a page that has not been rewritten. */
    01539       uint16_t address = 0;
    01540       uint8_t  mask    = 1U << (address % NVM_PAGES_PER_WEAR_HISTORY);
    01541       while ((nvmStaticWearWriteHistory[address / NVM_PAGES_PER_WEAR_HISTORY] & mask) != 0)
    01542       {
    01543         address++;
    01544         mask = 1U << (address % NVM_PAGES_PER_WEAR_HISTORY);
    01545       }
    01546
    01547       /* Check for wear page. */
    01548       if (nvmPageTypeWear == NVM_PageGet(address).pageType)
    01549       {
    01550         /* Flip bit. */
    01551         nvmStaticWearWriteHistory[address / NVM_PAGES_PER_WEAR_HISTORY] |= mask;
    01552         /* Record flip. */
    01553         nvmStaticWearWritesInHistory++;
    01554       }
    01555       else
    01556       {
    01557         /* Must release write lock, run the write function to move the data,
    01558          * then acquire lock again. */
    01559
    01560         /* Give up write lock and open for other API operations. */
    01561         NVM_RELEASE_WRITE_LOCK
    01562
    01563         NVM_Write(address, NVM_WRITE_NONE_CMD);
    01564
    01565         /* Require write lock to continue. */
    01566         NVM_ACQUIRE_WRITE_LOCK
    01567       }
    01568     }
    01569     nvmStaticWearWorking = false;
    01570   }
    01571
    01572   return nvmResultOk;
    01573 }
    01574
    01575 #endif
    01576