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