nvm.c

Go to the documentation of this file.
00001 /***************************************************************************/
00016 #include <stdbool.h>
00017 #include "nvm.h"
00018 
00019 /*******************************************************************************
00020  *******************************   DEFINES   ***********************************
00021  ******************************************************************************/
00022 
00027 #define NVM_VERSION                            0x2U
00028 
00029 /* Sizes. Internal sizes of different objects */
00030 #define NVM_CONTENT_SIZE         (NVM_PAGE_SIZE - (NVM_HEADER_SIZE + NVM_FOOTER_SIZE))
00031 #define NVM_WEAR_CONTENT_SIZE    (NVM_PAGE_SIZE - NVM_HEADER_SIZE)
00032 
00033 #define NVM_PAGE_EMPTY_VALUE                   0xffffU
00034 #define NVM_NO_PAGE_RETURNED                   0xffffffffUL
00035 #define NVM_NO_WRITE_16BIT                     0xffffU
00036 #define NVM_NO_WRITE_32BIT                     0xffffffffUL
00037 #define NVM_HIGHEST_32BIT                      0xffffffffUL
00038 #define NVM_FLIP_FIRST_BIT_OF_32_WHEN_WRITE    0xffff7fffUL
00039 #define NVM_FIRST_BIT_ONE                      0x8000U
00040 #define NVM_FIRST_BIT_ZERO                     0x7fffU
00041 #define NVM_LAST_BIT_ZERO                      0xfffeU
00042 
00043 #define NVM_CHECKSUM_INITIAL                   0xffffU
00044 #define NVM_CHECKSUM_LENGTH                    0x2U
00045 
00046 #define NVM_PAGES_PER_WEAR_HISTORY             0x8U
00047 
00048 /* Macros for acquiring and releasing write lock. Currently empty but could be redefined 
00049    in RTOSes to add resources protection. It is not recommended to call the NVM module 
00050    from interrupts or other tasks without ensuring that it is not used by main thread.   */
00051 #ifndef NVM_ACQUIRE_WRITE_LOCK
00052 #define NVM_ACQUIRE_WRITE_LOCK
00053 #endif
00054 
00055 #ifndef NVM_RELEASE_WRITE_LOCK
00056 #define NVM_RELEASE_WRITE_LOCK
00057 #endif
00058 
00061 /*******************************************************************************
00062  ******************************   TYPEDEFS   ***********************************
00063  ******************************************************************************/
00064 
00067 typedef enum
00068 {
00069   nvmValidateResultOk       = 0, 
00070   nvmValidateResultOkMarked = 1, 
00071   nvmValidateResultOld      = 2, 
00072   nvmValidateResultError    = 3  
00073 } NVM_ValidateResult_t;
00074 
00077 typedef struct
00078 {
00079   uint16_t watermark; 
00080   uint32_t updateId;  
00081   uint16_t version;   
00082 } NVM_Page_Header_t;
00083 
00085 #define NVM_HEADER_SIZE          (2 * sizeof(uint16_t) + sizeof(uint32_t))
00086 
00089 typedef struct
00090 {
00091   uint16_t checksum;  
00092   uint16_t watermark; 
00093 } NVM_Page_Footer_t;
00094 
00096 #define NVM_FOOTER_SIZE          (2 * sizeof(uint16_t))
00097 
00100 /*******************************************************************************
00101  *******************************   STATICS   ***********************************
00102  ******************************************************************************/
00103 
00107 static NVM_Config_t const *nvmConfig;
00108 
00109 #if (NVM_FEATURE_STATIC_WEAR_ENABLED == true)
00110 /* Static wear leveling */
00111 
00112 /* Bit list that records which pages have been rewritten.
00113  * The array contains number of pages divided by 8 bits in a byte. */
00114 static uint8_t nvmStaticWearWriteHistory[(NVM_MAX_NUMBER_OF_PAGES + (NVM_PAGES_PER_WEAR_HISTORY - 1)) / NVM_PAGES_PER_WEAR_HISTORY];
00115 
00116 /* Number of different page writes recorded in history. */
00117 static uint16_t nvmStaticWearWritesInHistory;
00118 
00119 /* Number of page erases performed since last rest. */
00120 static uint16_t nvmStaticWearErasesSinceReset;
00121 
00122 /* Stop recurring calls from causing mayhem. */
00123 static bool nvmStaticWearWorking = false;
00124 #endif
00125 
00128 /*******************************************************************************
00129  ******************************   PROTOTYPES   *********************************
00130  ******************************************************************************/
00131 
00134 static uint8_t* NVM_PageFind(uint16_t pageId);
00135 static uint8_t* NVM_ScratchPageFindBest(void);
00136 static Ecode_t NVM_PageErase(uint8_t *pPhysicalAddress);
00137 static NVM_Page_Descriptor_t NVM_PageGet(uint16_t pageId);
00138 static NVM_ValidateResult_t NVM_PageValidate(uint8_t *pPhysicalAddress);
00139 
00140 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
00141 static uint16_t NVM_WearIndex(uint8_t *pPhysicalAddress, NVM_Page_Descriptor_t *pPageDesc);
00142 static bool NVM_WearReadIndex(uint8_t *pPhysicalAddress, NVM_Page_Descriptor_t *pPageDesc, uint16_t *pIndex);
00143 #endif
00144 
00145 static void NVM_ChecksumAdditive(uint16_t *pChecksum, void *pBuffer, uint16_t len);
00146 
00147 #if (NVM_FEATURE_STATIC_WEAR_ENABLED == true)
00148 static void NVM_StaticWearReset(void);
00149 static void NVM_StaticWearUpdate(uint16_t address);
00150 static Ecode_t NVM_StaticWearCheck(void);
00151 #endif
00152 
00154 /*******************************************************************************
00155  ***************************   GLOBAL FUNCTIONS   ******************************
00156  ******************************************************************************/
00157 
00158 /***************************************************************************/
00184 Ecode_t NVM_Init(NVM_Config_t const *config)
00185 {
00186   uint16_t page;
00187   /* Variable to store the result returned at the end. */
00188   Ecode_t result = ECODE_EMDRV_NVM_ERROR;
00189 
00190   /* Physical address of the current page. */
00191   uint8_t *pPhysicalAddress = (uint8_t *)(config->nvmArea);
00192   /* Physical address of a suspected duplicate page under observation. */
00193   uint8_t *pDuplicatePhysicalAddress;
00194 
00195   /* Logical address of the current page. */
00196   uint16_t logicalAddress;
00197   /* Logical address of a duplicate page. */
00198   uint16_t duplicateLogicalAddress;
00199 
00200   /* Temporary variable to store results of a validation operation. */
00201   NVM_ValidateResult_t validationResult;
00202   /* Temporary variable to store results of a erase operation. */
00203   Ecode_t eraseResult;
00204 
00205   /* if there is no spare page, return error */
00206   if( (config->pages <= config->userPages) || (config->pages > NVM_MAX_NUMBER_OF_PAGES) )
00207     return ECODE_EMDRV_NVM_ERROR;
00208 
00209   /* now check that page structures fits to physical page size */
00210   { 
00211     uint16_t pageIdx = 0, obj = 0, sum = 0;
00212     const NVM_Page_Descriptor_t *currentPage;
00213     
00214     for(pageIdx = 0; pageIdx < config->userPages; pageIdx++)
00215     { 
00216       sum = 0;
00217       obj = 0;
00218       currentPage = &((*(config->nvmPages))[pageIdx]);
00219 
00220       while( (*(currentPage->page))[obj].location != 0)
00221         sum += (*(currentPage->page))[obj++].size;
00222 
00223       if(currentPage->pageType == nvmPageTypeNormal)
00224       {
00225         if( sum > NVM_CONTENT_SIZE )
00226         {
00227           return ECODE_EMDRV_NVM_ERROR; /* objects bigger than page size */
00228         }
00229       } 
00230       else
00231       {
00232         if(currentPage->pageType == nvmPageTypeWear)
00233         {
00234           if( (sum+NVM_CHECKSUM_LENGTH) > NVM_WEAR_CONTENT_SIZE )
00235           {
00236             return ECODE_EMDRV_NVM_ERROR; /* objects bigger than page size */
00237           }
00238         } else 
00239           {
00240             return ECODE_EMDRV_NVM_ERROR; /* unknown page type */
00241           }
00242       }
00243     }
00244   }
00245 
00246   nvmConfig = config;
00247 
00248   /* Require write lock to continue. */
00249   NVM_ACQUIRE_WRITE_LOCK
00250 
00251   /* Initialize the NVM. */
00252   NVMHAL_Init();
00253 
00254 #if (NVM_FEATURE_STATIC_WEAR_ENABLED == true)
00255   /* Initialize the static wear leveling functionality. */
00256   NVM_StaticWearReset();
00257 #endif
00258 
00259   /* Run through all pages and see if they validate if they contain content. */
00260   for (page = 0; page < nvmConfig->pages; ++page)
00261   {
00262     /* Read the logical address of the page stored at the current physical
00263      * address, and compare it to the value of an empty page. */
00264     NVMHAL_Read(pPhysicalAddress, &logicalAddress, sizeof(logicalAddress));
00265     if (NVM_PAGE_EMPTY_VALUE != logicalAddress)
00266     {
00267       /* Not an empty page. Check if it validates. */
00268       validationResult = NVM_PageValidate(pPhysicalAddress);
00269 
00270       /* Three different kinds of pages. */
00271       if (nvmValidateResultOk == validationResult)
00272       {
00273         /* We have found a valid page, so the initial error can be changed to an
00274          * OK result. */
00275         if (ECODE_EMDRV_NVM_ERROR == result)
00276         {
00277           result = ECODE_EMDRV_NVM_OK;
00278         }
00279       }
00280       else if (nvmValidateResultOkMarked == validationResult)
00281       {
00282         /* Page validates, but is marked for write.
00283          * There might exist a newer version. */
00284 
00285         /* Walk through all the possible pages looking for a page with
00286          * matching watermark. */
00287         pDuplicatePhysicalAddress = (uint8_t *)(nvmConfig->nvmArea);
00288         for (page = 0; (NVM_PAGE_EMPTY_VALUE != logicalAddress) && (page < nvmConfig->pages);
00289              ++page)
00290         {
00291           NVMHAL_Read(pDuplicatePhysicalAddress, &duplicateLogicalAddress, sizeof(duplicateLogicalAddress));
00292 
00293           if ((pDuplicatePhysicalAddress != pPhysicalAddress) && ((logicalAddress | NVM_FIRST_BIT_ONE) == duplicateLogicalAddress))
00294           {
00295             /* Duplicate page has got the same logical address. Check if it
00296              * validates. */
00297             validationResult = NVM_PageValidate(pDuplicatePhysicalAddress);
00298 
00299             if (nvmValidateResultOk == validationResult)
00300             {
00301               /* The new one validates, delete the old one. */
00302               eraseResult = NVM_PageErase(pPhysicalAddress);
00303             }
00304             else
00305             {
00306               /* The new one is broken, delete the new one. */
00307               eraseResult = NVM_PageErase(pDuplicatePhysicalAddress);
00308             }
00309 
00310             /* Something went wrong */
00311             if (ECODE_EMDRV_NVM_OK != eraseResult)
00312             {
00313               result = ECODE_EMDRV_NVM_ERROR;
00314             }
00315           }
00316 
00317           /* Go to the next physical page. */
00318           pDuplicatePhysicalAddress += NVM_PAGE_SIZE;
00319         } /* End duplicate search loop. */
00320 
00321         /* If everything went OK and this is the first page we found, then
00322          * we can change the status from initial error to OK. */
00323         if (ECODE_EMDRV_NVM_ERROR == result)
00324         {
00325           result = ECODE_EMDRV_NVM_OK;
00326         }
00327       }
00328       else
00329       {
00330         /* Page does not validate */
00331         result = ECODE_EMDRV_NVM_ERROR;
00332       }
00333     } /* End - not empty if. */
00334 
00335     /* Go to the next physical page. */
00336     pPhysicalAddress += NVM_PAGE_SIZE;
00337   } /* End pages loop. */
00338 
00339   /* If no pages was found, the system is not in use and should be reset. */
00340   if (ECODE_EMDRV_NVM_ERROR == result)
00341   {
00342     result = ECODE_EMDRV_NVM_NO_PAGES_AVAILABLE;
00343   }
00344 
00345   /* Give up write lock and open for other API operations. */
00346   NVM_RELEASE_WRITE_LOCK
00347 
00348   return result;
00349 }
00350 
00351 /***************************************************************************/
00369 Ecode_t NVM_Erase(uint32_t erasureCount)
00370 {
00371   uint16_t page;
00372   /* Result used when returning from the function. */
00373   Ecode_t result = ECODE_EMDRV_NVM_ERROR;
00374 
00375   /* Location of physical page. */
00376   uint8_t *pPhysicalAddress = (uint8_t *)(nvmConfig->nvmArea);
00377 
00378   /* Container for moving old erasure count, or set to new. */
00379   uint32_t tempErasureCount = erasureCount;
00380 
00381   /* Require write lock to continue. */
00382   NVM_ACQUIRE_WRITE_LOCK
00383 
00384   /* Loop over all the pages, as long as everything is OK. */
00385   for (page = 0;
00386        (page < nvmConfig->pages) && ((ECODE_EMDRV_NVM_OK == result) || (ECODE_EMDRV_NVM_ERROR == result));
00387        ++page)
00388   {
00389     /* If erasureCount input is set to the retain constant, we need to get the
00390      * old erasure count before we erase the page. */
00391     if (NVM_ERASE_RETAINCOUNT == erasureCount)
00392     {
00393       /* Read old erasure count. */
00394       NVMHAL_Read(pPhysicalAddress + 2, &tempErasureCount, sizeof(tempErasureCount));
00395     }
00396 
00397     /* Erase page. */
00398     result = NVMHAL_PageErase(pPhysicalAddress);
00399 
00400     /* If still OK, write erasure count to page. */
00401     if (ECODE_EMDRV_NVM_OK == result)
00402     {
00403       result = NVMHAL_Write(pPhysicalAddress + 2, &tempErasureCount, sizeof(tempErasureCount));
00404     }
00405 
00406     /* Go to the next physical page. */
00407     pPhysicalAddress += NVM_PAGE_SIZE;
00408   }
00409 
00410   /* Give up write lock and open for other API operations. */
00411   NVM_RELEASE_WRITE_LOCK
00412 
00413   return result;
00414 }
00415 
00416 /***************************************************************************/
00441 Ecode_t NVM_Write(uint16_t pageId, uint8_t objectId)
00442 {
00443   /* Result variable used as return value from the function. */
00444   Ecode_t result = ECODE_EMDRV_NVM_ERROR;
00445 
00446   /* Watermark to look for when finding page. First bit true. */
00447   uint16_t watermark = pageId | NVM_FIRST_BIT_ONE;
00448   /* Watermark used when flipping the duplication bit of a page. */
00449   const uint32_t flipWatermark = NVM_FLIP_FIRST_BIT_OF_32_WHEN_WRITE;
00450 
00451   /* Page descriptor used for accessing page type and page objects. */
00452   NVM_Page_Descriptor_t pageDesc;
00453 
00454   /* Page header and footer. Used to store old version and to easily update and
00455    * write new version. */
00456   NVM_Page_Header_t header;
00457 
00458   /* Variable used for checksum calculation. Starts at defined initial value. */
00459   uint16_t checksum = NVM_CHECKSUM_INITIAL;
00460 
00461   /* Physical addresses in memory for the old and new version of the page. */
00462   uint8_t *pOldPhysicalAddress = (uint8_t *) NVM_NO_PAGE_RETURNED;
00463   uint8_t *pNewPhysicalAddress = (uint8_t *) NVM_NO_PAGE_RETURNED;
00464 
00465   /* Offset address within page. */
00466   uint16_t offsetAddress;
00467   /* Single byte buffer used when copying data from an old page.*/
00468   uint8_t  copyBuffer;
00469   /* Object in page counter. */
00470   uint8_t  objectIndex;
00471   /* Amount of bytes to copy. */
00472   uint16_t copyLength;
00473 
00474   /* Handle wear pages. Should we handle this as an extra write to an existing
00475    * page or create a new one. */
00476   bool wearWrite = false;
00477 
00478 #if (NVM_FEATURE_WRITE_NECESSARY_CHECK_ENABLED == true)
00479   /* Bool used when checking if a write operation is needed. */
00480   bool rewriteNeeded;
00481 #endif
00482 
00483 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
00484   /* Used to hold the checksum of the wear object. */
00485   uint16_t wearChecksum;
00486   /* Byte size of the wear object. Includes checksum length. */
00487   uint16_t wearObjectSize;
00488   /* Used to specify the internal index of the wear object in a page. */
00489   uint16_t wearIndex;
00490 
00491   #if (NVM_FEATURE_WRITE_VALIDATION_ENABLED == true)
00492   /* The new wear index the object will be written to. */
00493   uint16_t wearIndexNew;
00494   #endif
00495 #endif
00496 
00497   /* Require write lock to continue. */
00498   NVM_ACQUIRE_WRITE_LOCK
00499 
00500   /* Find old physical address. */
00501   pOldPhysicalAddress = NVM_PageFind(pageId);
00502 
00503   /* Get the page configuration. */
00504   pageDesc = NVM_PageGet(pageId);
00505 
00506 #if (NVM_FEATURE_WRITE_NECESSARY_CHECK_ENABLED == true)
00507   /* If there is an old version of the page, it might not be necessary to update
00508    * the data. Also check that this is a normal page and that the static wear
00509    * leveling system is not working (this system might want to rewrite pages
00510    * even if the data is similar to the old version). */
00511   if (((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress)
00512       && (nvmPageTypeNormal == pageDesc.pageType)
00513 #if (NVM_FEATURE_STATIC_WEAR_ENABLED == true)
00514       && !nvmStaticWearWorking
00515 #endif
00516 
00517       )
00518   {
00519     rewriteNeeded = false;
00520     objectIndex   = 0;
00521     offsetAddress = 0;
00522 
00523     /* Loop over items as long as no rewrite is needed and the current item has
00524     * got a size other than 0. Size 0 is used as a marker for a NULL object. */
00525     while (((*pageDesc.page)[objectIndex].size != 0) && !rewriteNeeded)
00526     {
00527       /* Check if every object should be written or if this is the object to
00528        * write. */
00529       if ((NVM_WRITE_ALL_CMD == objectId) ||
00530           ((*pageDesc.page)[objectIndex].objectId == objectId))
00531       {
00532         /* Compare object to RAM. */
00533 
00534         copyLength = (*pageDesc.page)[objectIndex].size;
00535 
00536         /* Loop over each byte of the object and compare with RAM. */
00537         while (copyLength != 0)
00538         {
00539           /* Get byte from NVM. */
00540           NVMHAL_Read(pOldPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
00541                       &copyBuffer,
00542                       sizeof(copyBuffer));
00543 
00544           /* Check byte in NVM with the corresponding byte in RAM. */
00545           if (*(uint8_t *)((*pageDesc.page)[objectIndex].location + offsetAddress) != copyBuffer)
00546           {
00547             rewriteNeeded = true;
00548             break;
00549           }
00550 
00551           /* Move offset. */
00552           offsetAddress += sizeof(copyBuffer);
00553           copyLength    -= sizeof(copyBuffer);
00554         }
00555       }
00556       else
00557       {
00558         /* Move offset past the object. */
00559         offsetAddress += (*pageDesc.page)[objectIndex].size;
00560       }
00561 
00562       /* Check next object. */
00563       objectIndex++;
00564     }
00565 
00566     if (!rewriteNeeded)
00567     {
00568       /* Release write lock before return. */
00569       NVM_RELEASE_WRITE_LOCK
00570 
00571       return ECODE_EMDRV_NVM_OK;
00572     }
00573   }
00574 #endif
00575 
00576 
00577 
00578 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
00579   /* If this is a wear page then we can check if we can possibly squeeze another
00580    * version of the object inside the already existing page. If this is possible
00581    * we set the wearWrite boolean. This will then make us ignore the normal
00582    * write operation. */
00583   if (nvmPageTypeWear == pageDesc.pageType)
00584   {
00585     /* Calculate checksum. The wear page checksum is only stored in 15 bits,
00586      * because we need one bit to mark that the object is written. This bit is
00587      * always set to 0. */
00588 
00589     wearChecksum = NVM_CHECKSUM_INITIAL;
00590     NVM_ChecksumAdditive(&wearChecksum, (*pageDesc.page)[0].location, (*pageDesc.page)[0].size);
00591     wearChecksum &= NVM_LAST_BIT_ZERO;
00592 
00593     /* If there was an old page. */
00594     if ((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress)
00595     {
00596       /* Find location in old page. */
00597       wearIndex      = NVM_WearIndex(pOldPhysicalAddress, &pageDesc);
00598       wearObjectSize = (*pageDesc.page)[0].size + NVM_CHECKSUM_LENGTH;
00599 
00600       /* Check that the wearIndex returned is within the length of the page. */
00601       if (wearIndex < ((uint16_t) NVM_WEAR_CONTENT_SIZE) / wearObjectSize)
00602       {
00603         result = NVMHAL_Write(pOldPhysicalAddress + NVM_HEADER_SIZE + wearIndex * wearObjectSize,
00604                               (*pageDesc.page)[0].location,
00605                               (*pageDesc.page)[0].size);
00606         result = NVMHAL_Write(pOldPhysicalAddress + NVM_HEADER_SIZE + wearIndex * wearObjectSize + (*pageDesc.page)[0].size,
00607                               &wearChecksum,
00608                               sizeof(wearChecksum));
00609 
00610         /* Register that we have now written to the old page. */
00611         wearWrite = true;
00612 
00613 #if (NVM_FEATURE_WRITE_VALIDATION_ENABLED == true)
00614         /* Check if the newest one that is valid is the same as the one we just
00615          * wrote to the NVM. */
00616         if ((!NVM_WearReadIndex(pOldPhysicalAddress, &pageDesc, &wearIndexNew)) ||
00617             (wearIndexNew != wearIndex))
00618         {
00619           result = ECODE_EMDRV_NVM_ERROR;
00620         }
00621 #endif
00622       }
00623     } /* End of old page if. */
00624   }   /* End of wear page if. */
00625 #endif
00626 
00627 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
00628   /* Do not create a new page if we have already done an in-page wear write. */
00629   if (!wearWrite)
00630   {
00631 #endif
00632   /* Mark any old page before creating a new one. */
00633   if ((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress)
00634   {
00635     result = NVMHAL_Write(pOldPhysicalAddress, &flipWatermark, 4);
00636 
00637     if (ECODE_EMDRV_NVM_OK != result)
00638     {
00639       /* Give up write lock and open for other API operations. */
00640       NVM_RELEASE_WRITE_LOCK
00641       return result;
00642     }
00643   }
00644 
00645   /* Find new physical address to write to. */
00646   pNewPhysicalAddress = NVM_ScratchPageFindBest();
00647 
00648   if ((uint8_t*) NVM_NO_PAGE_RETURNED == pNewPhysicalAddress)
00649   {
00650     /* Give up write lock and open for other API operations. */
00651     NVM_RELEASE_WRITE_LOCK
00652     return ECODE_EMDRV_NVM_ERROR;
00653   }
00654 
00655   /* Generate and write header */
00656   header.watermark = watermark;
00657   header.updateId  = NVM_NO_WRITE_32BIT;
00658   header.version   = NVM_VERSION;
00659 
00660   /* store header at beginning of page */
00661   result = NVMHAL_Write(pNewPhysicalAddress, &header.watermark, sizeof(header.watermark));
00662   result = NVMHAL_Write(pNewPhysicalAddress + sizeof(header.watermark), &header.updateId, sizeof(header.updateId));
00663   result = NVMHAL_Write(pNewPhysicalAddress + sizeof(header.watermark)+ + sizeof(header.updateId), &header.version, sizeof(header.version));
00664 
00665   /* Reset address index within page. */
00666   offsetAddress = 0;
00667   /* Reset object in page counter. */
00668   objectIndex = 0;
00669 
00670   /* Loop over items as long as everything is OK, and the current item has got
00671    * a size other than 0. Size 0 is used as a marker for a NULL object. */
00672   while (((*pageDesc.page)[objectIndex].size != 0) && (ECODE_EMDRV_NVM_OK == result))
00673   {
00674     /* Check if every object should be written or if this is the object to
00675      * write. */
00676     if ((NVM_WRITE_ALL_CMD == objectId) ||
00677         ((*pageDesc.page)[objectIndex].objectId == objectId))
00678     {
00679       /* Write object from RAM. */
00680       result = NVMHAL_Write(pNewPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
00681                             (*pageDesc.page)[objectIndex].location,
00682                             (*pageDesc.page)[objectIndex].size);
00683       offsetAddress += (*pageDesc.page)[objectIndex].size;
00684 
00685       NVM_ChecksumAdditive(&checksum, (*pageDesc.page)[objectIndex].location, (*pageDesc.page)[objectIndex].size);
00686     }
00687     else
00688     {
00689       /* Get version from old page. */
00690       if ((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress)
00691       {
00692         NVM_ChecksumAdditive(&checksum, pOldPhysicalAddress + offsetAddress + NVM_HEADER_SIZE, (*pageDesc.page)[objectIndex].size);
00693 
00694         copyLength = (*pageDesc.page)[objectIndex].size;
00695 
00696         while ((copyLength != 0) && (ECODE_EMDRV_NVM_OK == result))
00697         {
00698           /* Copies using an 1 byte buffer. Might be better to dynamically use larger if possible. */
00699           NVMHAL_Read(pOldPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
00700                       &copyBuffer,
00701                       sizeof(copyBuffer));
00702           result = NVMHAL_Write(pNewPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
00703                                 &copyBuffer,
00704                                 sizeof(copyBuffer));
00705 
00706           offsetAddress += sizeof(copyBuffer);
00707           copyLength    -= sizeof(copyBuffer);
00708         }
00709       }  /* End if old page. */
00710     }   /* Else-end of NVM_WRITE_ALL if-statement. */
00711 
00712     objectIndex++;
00713   }
00714 
00715   /* If we are creating a wear page, add the checksum directly after the data. */
00716 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
00717   if (nvmPageTypeWear == pageDesc.pageType)
00718   {
00719     result = NVMHAL_Write(pNewPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
00720                           &wearChecksum,
00721                           sizeof(wearChecksum));
00722   }
00723   /* Generate and write footer on normal pages. */
00724   else
00725   {
00726 #endif
00727   if (ECODE_EMDRV_NVM_OK == result)
00728   {
00729     /* write checksum at end of page */
00730     result = NVMHAL_Write(pNewPhysicalAddress + (NVM_PAGE_SIZE - NVM_FOOTER_SIZE), &checksum, sizeof(checksum));
00731     /* write watermark just after checksum at end of page */
00732     result = NVMHAL_Write(pNewPhysicalAddress + (NVM_PAGE_SIZE + sizeof(checksum) - NVM_FOOTER_SIZE), &watermark, sizeof(watermark));
00733   }
00734 
00735 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
00736 }     /* End of else from page type. */
00737 #endif
00738 
00739 #if (NVM_FEATURE_WRITE_VALIDATION_ENABLED == true)
00740   /* Validate that the correct data was written. */
00741   if (nvmValidateResultOk != NVM_PageValidate(pNewPhysicalAddress))
00742   {
00743     result = ECODE_EMDRV_NVM_ERROR;
00744   }
00745 #endif
00746 
00747 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
00748 }   /* End of if for normal write (!wearWrite). */
00749 #endif
00750 
00751   /* Erase old if there was an old one and everything else have gone OK. */
00752   if ((!wearWrite) &&
00753       ((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress))
00754   {
00755     if (ECODE_EMDRV_NVM_OK == result)
00756     {
00757       result = NVM_PageErase(pOldPhysicalAddress);
00758     }
00759     else
00760     {
00761       NVM_PageErase(pNewPhysicalAddress);
00762     }
00763   }
00764 
00765   /* Give up write lock and open for other API operations. */
00766   NVM_RELEASE_WRITE_LOCK
00767 
00768   return result;
00769 }
00770 
00771 /***************************************************************************/
00791 Ecode_t NVM_Read(uint16_t pageId, uint8_t objectId)
00792 {
00793 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
00794   /* Variable used to fetch read index. */
00795   uint16_t wearIndex;
00796 #endif
00797 
00798   /* Physical address of the page to read from. */
00799   uint8_t *pPhysicalAddress;
00800 
00801   /* Description of the page, used to find page type and objects. */
00802   NVM_Page_Descriptor_t pageDesc;
00803 
00804   /* Index of object in page. */
00805   uint8_t  objectIndex;
00806   /* Address of read location within a page. */
00807   uint16_t offsetAddress;
00808 
00809 
00810   /* Require write lock to continue. */
00811   NVM_ACQUIRE_WRITE_LOCK
00812 
00813   /* Find physical page. */
00814   pPhysicalAddress = NVM_PageFind(pageId);
00815 
00816   /* If no page was found, we cannot read anything. */
00817   if ((uint8_t*) NVM_NO_PAGE_RETURNED == pPhysicalAddress)
00818   {
00819     /* Give up write lock and open for other API operations. */
00820     NVM_RELEASE_WRITE_LOCK
00821     return ECODE_EMDRV_NVM_PAGE_INVALID;
00822   }
00823 
00824   /* Get page description. */
00825   pageDesc = NVM_PageGet(pageId);
00826 
00827 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
00828   /* If this is a wear page, we must find out which object in the page should be
00829    * read. */
00830   if (nvmPageTypeWear == pageDesc.pageType)
00831   {
00832     /* Find valid object in wear page and read it. */
00833     if (NVM_WearReadIndex(pPhysicalAddress, &pageDesc, &wearIndex))
00834     {
00835       NVMHAL_Read(pPhysicalAddress + NVM_HEADER_SIZE +
00836                   wearIndex * ((*pageDesc.page)[0].size + NVM_CHECKSUM_LENGTH),
00837                   (*pageDesc.page)[0].location,
00838                   (*pageDesc.page)[0].size);
00839     }
00840     else
00841     {
00842       /* No valid object was found in the page. */
00843       /* Give up write lock and open for other API operations. */
00844       NVM_RELEASE_WRITE_LOCK
00845       return ECODE_EMDRV_NVM_DATA_INVALID;
00846     }
00847   }
00848   else
00849 #endif
00850   {
00851     /* Read normal page. */
00852     objectIndex   = 0;
00853     offsetAddress = 0;
00854 
00855 #if (NVM_FEATURE_READ_VALIDATION_ENABLED == true)
00856     if (nvmValidateResultError == NVM_PageValidate(pPhysicalAddress))
00857     {
00858       /* Give up write lock and open for other API operations. */
00859       NVM_RELEASE_WRITE_LOCK
00860       return ECODE_EMDRV_NVM_DATA_INVALID;
00861     }
00862 #endif
00863 
00864     /* Loop through and read the objects of a page, as long as the current item
00865      * has got a size other than 0. Size 0 is a marker for the NULL object. */
00866     while ((*pageDesc.page)[objectIndex].size != 0)
00867     {
00868       /* Check if every object should be read or if this is the object to read. */
00869       if ((NVM_READ_ALL_CMD == objectId) || ((*pageDesc.page)[objectIndex].objectId == objectId))
00870       {
00871         NVMHAL_Read(pPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
00872                     (*pageDesc.page)[objectIndex].location,
00873                     (*pageDesc.page)[objectIndex].size);
00874       }
00875 
00876       offsetAddress += (*pageDesc.page)[objectIndex].size;
00877       objectIndex++;
00878     }
00879   }
00880 
00881   /* Give up write lock and open for other API operations. */
00882   NVM_RELEASE_WRITE_LOCK
00883 
00884   return ECODE_EMDRV_NVM_OK;
00885 }
00886 
00887 /***************************************************************************/
00898 #if (NVM_FEATURE_WEARLEVELGET_ENABLED == true)
00899 uint32_t NVM_WearLevelGet(void)
00900 {
00901   uint16_t page;
00902   /* Used to temporarily store the update id of the current page. */
00903   uint32_t updateId;
00904   /* Worst (highest) update id. Used as return value. */
00905   uint32_t worstUpdateId = 0;
00906 
00907   /* Address of physical page. */
00908   uint8_t *pPhysicalAddress = (uint8_t *)(nvmConfig->nvmArea);
00909 
00910   /* Loop through all pages in memory. */
00911   for (page = 0; page < nvmConfig->pages; ++page)
00912   {
00913     /* Find and compare erasure count. */
00914     NVMHAL_Read(pPhysicalAddress + 2, &updateId, sizeof(updateId));
00915     if (updateId > worstUpdateId)
00916     {
00917       worstUpdateId = updateId;
00918     }
00919 
00920     /* Go to the next physical page. */
00921     pPhysicalAddress += NVM_PAGE_SIZE;
00922   }
00923 
00924   return worstUpdateId;
00925 }
00926 #endif
00927 
00928 /*******************************************************************************
00929  ***************************   LOCAL FUNCTIONS   *******************************
00930  ******************************************************************************/
00931 
00934 /***************************************************************************/
00949 static uint8_t* NVM_PageFind(uint16_t pageId)
00950 {
00951   uint16_t page;
00952   /* Physical address to return. */
00953   uint8_t  *pPhysicalAddress = (uint8_t *)(nvmConfig->nvmArea);
00954   /* Temporary variable used to read and compare logical page address. */
00955   uint16_t logicalAddress;
00956 
00957   /* Loop through memory looking for a matching watermark. */
00958   for (page = 0; page < nvmConfig->pages; ++page)
00959   {
00960     /* Allow both versions of writing mark, invalid duplicates should already
00961      * have been deleted. */
00962     NVMHAL_Read(pPhysicalAddress, &logicalAddress, sizeof(logicalAddress));
00963     if (((pageId | NVM_FIRST_BIT_ONE) == logicalAddress) || (pageId == logicalAddress))
00964     {
00965       return pPhysicalAddress;
00966     }
00967 
00968     /* Move lookup point to the next page. */
00969     pPhysicalAddress += NVM_PAGE_SIZE;
00970   }
00971 
00972   /* No page found. */
00973   return (uint8_t *) NVM_NO_PAGE_RETURNED;
00974 }
00975 
00976 /***************************************************************************/
00988 static uint8_t* NVM_ScratchPageFindBest(void)
00989 {
00990   uint16_t page;
00991   /* Address for physical page to return. */
00992   uint8_t  *pPhysicalPage = (uint8_t *) NVM_NO_PAGE_RETURNED;
00993 
00994   /* Variable used to read and compare update id of physical pages. */
00995   uint32_t updateId = NVM_HIGHEST_32BIT;
00996   /* The best update id found. */
00997   uint32_t bestUpdateId = NVM_HIGHEST_32BIT;
00998 
00999   /* Pointer to the current physical page. */
01000   uint8_t  *pPhysicalAddress = (uint8_t *)(nvmConfig->nvmArea);
01001   /* Logical address that identifies the page. */
01002   uint16_t logicalAddress;
01003 
01004   /* Loop through all pages in memory. */
01005   for (page = 0; page < nvmConfig->pages; ++page)
01006   {
01007     /* Read and check logical address. */
01008     NVMHAL_Read(pPhysicalAddress, &logicalAddress, sizeof(logicalAddress));
01009     if ((uint16_t) NVM_PAGE_EMPTY_VALUE == logicalAddress)
01010     {
01011       /* Find and compare erasure count. */
01012       NVMHAL_Read(pPhysicalAddress + 2, &updateId, sizeof(updateId));
01013       if (updateId < bestUpdateId)
01014       {
01015         bestUpdateId  = updateId;
01016         pPhysicalPage = pPhysicalAddress;
01017       }
01018     }
01019 
01020     /* Move lookup point to the next page. */
01021     pPhysicalAddress += NVM_PAGE_SIZE;
01022   }
01023 
01024   /* Return a pointer to the best/least used page. */
01025   return pPhysicalPage;
01026 }
01027 
01028 /***************************************************************************/
01042 static Ecode_t NVM_PageErase(uint8_t *pPhysicalAddress)
01043 {
01044 #if (NVM_FEATURE_STATIC_WEAR_ENABLED == true)
01045   /* Logical page address. */
01046   uint16_t logicalAddress;
01047 #endif
01048 
01049   /* Read out the old page update id. */
01050   uint32_t updateId;
01051   NVMHAL_Read(pPhysicalAddress + 2, &updateId, sizeof(updateId));
01052 
01053 #if (NVM_FEATURE_STATIC_WEAR_ENABLED == true)
01054   /* Get logical page address. */
01055   NVMHAL_Read(pPhysicalAddress, &logicalAddress, sizeof(logicalAddress));
01056 
01057   /* If not empty: mark as erased and check against threshold. */
01058   if (logicalAddress != NVM_PAGE_EMPTY_VALUE)
01059   {
01060     /* Set first bit low. */
01061     logicalAddress = logicalAddress & NVM_FIRST_BIT_ZERO;
01062     NVM_StaticWearUpdate(logicalAddress);
01063   }
01064 #endif
01065 
01066   /* Erase the page. */
01067   NVMHAL_PageErase(pPhysicalAddress);
01068 
01069   /* Update erasure count. */
01070   updateId++;
01071 
01072   /* Write increased erasure count. */
01073   return NVMHAL_Write(pPhysicalAddress + 2, &updateId, sizeof(updateId));
01074 }
01075 
01076 /***************************************************************************/
01095 static NVM_Page_Descriptor_t NVM_PageGet(uint16_t pageId)
01096 {
01097   uint8_t pageIndex;
01098   static const NVM_Page_Descriptor_t nullPage = { (uint8_t) 0, 0, (NVM_Page_Type_t) 0 };
01099 
01100   /* Step through all configured pages. */
01101   for (pageIndex = 0; pageIndex < nvmConfig->userPages; ++pageIndex)
01102   {
01103     /* If this is the page we want, return it. */
01104     if ( (*(nvmConfig->nvmPages))[pageIndex].pageId == pageId)
01105     {
01106       return (*(nvmConfig->nvmPages))[pageIndex];
01107     }
01108   }
01109 
01110   /* No page matched the ID, return a NULL page to mark the error. */
01111   return nullPage;
01112 }
01113 
01114 /***************************************************************************/
01135 static NVM_ValidateResult_t NVM_PageValidate(uint8_t *pPhysicalAddress)
01136 {
01137   /* Result used as return value from the function. */
01138   NVM_ValidateResult_t result;
01139 
01140   /* Objects used to read out the page header and footer. */
01141   NVM_Page_Header_t header;
01142   NVM_Page_Footer_t footer;
01143 
01144   /* Descriptor for the current page. */
01145   NVM_Page_Descriptor_t pageDesc;
01146 
01147   /* Variable used for calculating checksums. */
01148   uint16_t checksum;
01149 
01150   /* Offset of object in page. */
01151   uint8_t  objectIndex;
01152   /* Address of read location within a page. */
01153   uint16_t offsetAddress;
01154 
01155 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
01156   /* Variable used to fetch read index. */
01157   uint16_t index;
01158 #endif
01159 
01160   /* Read page header data */
01161   NVMHAL_Read(pPhysicalAddress, &header.watermark, sizeof(header.watermark));
01162   NVMHAL_Read(pPhysicalAddress + sizeof(header.watermark), &header.updateId, sizeof(header.updateId));
01163   NVMHAL_Read(pPhysicalAddress + sizeof(header.watermark) + sizeof(header.updateId), &header.version, sizeof(header.version));
01164 
01165   /* Stop immediately if data is from another version of the API. */
01166   if (NVM_VERSION != header.version)
01167   {
01168     return nvmValidateResultOld;
01169   }
01170 
01171   /* Get the page configuration. */
01172   pageDesc = NVM_PageGet((header.watermark & NVM_FIRST_BIT_ZERO));
01173 
01174 
01175 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
01176   if (nvmPageTypeWear == pageDesc.pageType)
01177   {
01178     /* Wear page. */
01179 
01180     /* If first bit is already zero the page is marked as a duplicate. */
01181     if ((header.watermark & NVM_FIRST_BIT_ZERO) == header.watermark)
01182     {
01183       result = nvmValidateResultOkMarked;
01184     }
01185     else
01186     {
01187       /* Page is not marked as a duplicate. */
01188       result = nvmValidateResultOk;
01189     }
01190 
01191     /* If we do not have any valid objects in the page it is invalid. */
01192     if (!NVM_WearReadIndex(pPhysicalAddress, &pageDesc, &index))
01193     {
01194       result = nvmValidateResultError;
01195     }
01196   }
01197   else
01198 #endif
01199   {
01200     /* Normal page. */
01201     NVMHAL_Read(pPhysicalAddress + (NVM_PAGE_SIZE - NVM_FOOTER_SIZE), &footer.checksum, sizeof(footer.checksum));
01202     NVMHAL_Read(pPhysicalAddress + (NVM_PAGE_SIZE + sizeof(checksum) - NVM_FOOTER_SIZE), &footer.watermark, sizeof(footer.watermark));
01203     /* Check if watermark or watermark with flipped write bit matches. */
01204     if (header.watermark == footer.watermark)
01205     {
01206       result = nvmValidateResultOk;
01207     }
01208     else if ((header.watermark | NVM_FIRST_BIT_ONE) == footer.watermark)
01209     {
01210       result = nvmValidateResultOkMarked;
01211     }
01212     else
01213     {
01214       result = nvmValidateResultError;
01215     }
01216 
01217     /* Calculate checksum and compare with the one stored. */
01218     objectIndex   = 0;
01219     offsetAddress = 0;
01220     checksum      = NVM_CHECKSUM_INITIAL;
01221 
01222     /* Calculate per object using the HAL. Loop over items as long as the
01223      * current object has got a size other than 0. Size 0 is used as a marker
01224      * for a NULL object. */
01225     while ((*pageDesc.page)[objectIndex].size != 0)
01226     {
01227       NVMHAL_Checksum(&checksum, (uint8_t *) pPhysicalAddress + NVM_HEADER_SIZE + offsetAddress, (*pageDesc.page)[objectIndex].size);
01228       offsetAddress += (*pageDesc.page)[objectIndex].size;
01229       objectIndex++;
01230     }
01231 
01232     if (checksum != footer.checksum)
01233     {
01234       result = nvmValidateResultError;
01235     }
01236   }
01237 
01238   return result;
01239 }
01240 
01241 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
01242 /***************************************************************************/
01260 static uint16_t NVM_WearIndex(uint8_t *pPhysicalAddress, NVM_Page_Descriptor_t *pPageDesc)
01261 {
01262   /* Index to return. */
01263   uint16_t wearIndex = 0;
01264 
01265   /* Temporary variable used when calculating and comparing checksums. */
01266   uint16_t checksum;
01267 
01268   /* Size of the object in the wear page, including a 16 bit checksum. */
01269   uint16_t wearObjectSize = ((*pPageDesc->page)[0].size + NVM_CHECKSUM_LENGTH);
01270 
01271 
01272   /* Loop over possible pages. Stop when empty page was found. */
01273   while (wearIndex < NVM_WEAR_CONTENT_SIZE / wearObjectSize)
01274   {
01275     NVMHAL_Read((uint8_t *)(pPhysicalAddress + NVM_HEADER_SIZE +
01276                             wearIndex * wearObjectSize +
01277                             (*pPageDesc->page)[0].size
01278                             ),
01279                 &checksum,
01280                 sizeof(checksum));
01281 
01282     /* Flip the last bit of the checksum to zero. This is a mark used to
01283      * determine that an object is written to this location. */
01284     if ((checksum & NVM_LAST_BIT_ZERO) != checksum)
01285     {
01286       /* Break the loop and accept this location. */
01287       break;
01288     }
01289 
01290     /* Not a valid position for a new write.
01291     * Increase the index and check again. */
01292     wearIndex++;
01293   }
01294 
01295   return wearIndex;
01296 }
01297 #endif
01298 
01299 /***************************************************************************/
01320 #if (NVM_FEATURE_WEAR_PAGES_ENABLED == true)
01321 static bool NVM_WearReadIndex(uint8_t *pPhysicalAddress, NVM_Page_Descriptor_t *pPageDesc, uint16_t *pIndex)
01322 {
01323 #if (NVM_FEATURE_READ_VALIDATION_ENABLED == true)
01324   /* Variable used for calculating checksum when validating. */
01325   uint16_t checksum = NVM_CHECKSUM_INITIAL;
01326 #endif
01327 
01328   /* Length of wear object plus checksum. */
01329   const uint16_t wearObjectSize = ((*pPageDesc->page)[0].size + NVM_CHECKSUM_LENGTH);
01330 
01331   /* Return value. */
01332   bool validObjectFound = false;
01333 
01334   /* Buffer used when reading checksum. */
01335   uint16_t readBuffer;
01336 
01337   /* Initialize index at max plus one object. */
01338   *pIndex = (((uint16_t) NVM_WEAR_CONTENT_SIZE) / wearObjectSize);
01339 
01340   /* Loop over possible pages. Stop when first OK page is found. */
01341   while ((*pIndex > 0) && (!validObjectFound))
01342   {
01343     (*pIndex)--;
01344 
01345     /* Initialize checksum, and then calculate it from the HAL.*/
01346     uint8_t *temp = pPhysicalAddress + NVM_HEADER_SIZE + (*pIndex) * wearObjectSize + (*pPageDesc->page)[0].size;
01347     NVMHAL_Read((uint8_t *) temp, &readBuffer, sizeof(readBuffer));
01348 
01349 #if (NVM_FEATURE_READ_VALIDATION_ENABLED == true)
01350     /* Calculate the checksum before accepting the object. */
01351     checksum = NVM_CHECKSUM_INITIAL;
01352     NVMHAL_Checksum(&checksum, pPhysicalAddress + NVM_HEADER_SIZE + (*pIndex) * wearObjectSize, (*pPageDesc->page)[0].size);
01353     /* Flips the last bit of the checksum to zero. This is a mark used to
01354      * determine whether we have written anything to the page. */
01355     if ((uint16_t)(checksum & NVM_LAST_BIT_ZERO) == readBuffer)
01356 #else
01357     if (NVM_NO_WRITE_16BIT != readBuffer)
01358 #endif
01359     {
01360       validObjectFound = true;
01361     }
01362   }
01363 
01364   return validObjectFound;
01365 }
01366 #endif
01367 
01368 /***************************************************************************/
01387 static void NVM_ChecksumAdditive(uint16_t *pChecksum, void *pBuffer, uint16_t len)
01388 {
01389   uint8_t *pointer = (uint8_t *) pBuffer;
01390   uint16_t crc = *pChecksum;
01391 
01392   while(len--)
01393   {
01394     crc = (crc >> 8) | (crc << 8);
01395     crc ^= *pointer++;
01396     crc ^= (crc & 0xf0) >> 4;
01397     crc ^= (crc & 0x0f) << 12;
01398     crc ^= (crc & 0xff) << 5;
01399   }
01400 
01401   *pChecksum = crc;
01402 }
01403 
01404 #if (NVM_FEATURE_STATIC_WEAR_ENABLED == true)
01405 /***************************************************************************/
01414 static void NVM_StaticWearReset(void)
01415 {
01416   uint16_t i;
01417   nvmStaticWearErasesSinceReset = 0;
01418   nvmStaticWearWritesInHistory  = 0;
01419 
01420   for (i = 0; (NVM_PAGES_PER_WEAR_HISTORY * i) < nvmConfig->userPages; i += 1)
01421   {
01422     nvmStaticWearWriteHistory[i] = 0;
01423   }
01424 }
01425 
01426 /***************************************************************************/
01437 static void NVM_StaticWearUpdate(uint16_t address)
01438 {
01439   if (address < nvmConfig->userPages)
01440   {
01441     /* Mark page with logical address as written. */
01442 
01443     /* Bitmask to check and change the desired bit. */
01444     uint8_t mask = 1U << (address % NVM_PAGES_PER_WEAR_HISTORY);
01445 
01446     if ((nvmStaticWearWriteHistory[address / NVM_PAGES_PER_WEAR_HISTORY] & mask) == 0)
01447     {
01448       /* Flip bit. */
01449       nvmStaticWearWriteHistory[address / NVM_PAGES_PER_WEAR_HISTORY] |= mask;
01450       /* Record flip. */
01451       nvmStaticWearWritesInHistory++;
01452     }
01453 
01454     /* Record erase operation. */
01455     nvmStaticWearErasesSinceReset++;
01456 
01457     /* Call the static wear leveler. */
01458     NVM_StaticWearCheck();
01459   }
01460 }
01461 
01462 /***************************************************************************/
01471 static Ecode_t NVM_StaticWearCheck(void)
01472 {
01473   /* Check if there is a check already running. We do not need more of these. */
01474   if (!nvmStaticWearWorking)
01475   {
01476     nvmStaticWearWorking = true;
01477     while (nvmStaticWearErasesSinceReset / nvmStaticWearWritesInHistory > NVM_STATIC_WEAR_THRESHOLD)
01478     {
01479       /* If all the pages have been moved in this cycle: reset. */
01480       if (nvmStaticWearWritesInHistory >= nvmConfig->userPages)
01481       {
01482         NVM_StaticWearReset();
01483         break;
01484       }
01485 
01486       /* Find an address for a page that has not been rewritten. */
01487       uint16_t address = 0;
01488       uint8_t  mask    = 1U << (address % NVM_PAGES_PER_WEAR_HISTORY);
01489       while ((nvmStaticWearWriteHistory[address / NVM_PAGES_PER_WEAR_HISTORY] & mask) != 0)
01490       {
01491         address++;
01492         mask = 1U << (address % NVM_PAGES_PER_WEAR_HISTORY);
01493       }
01494 
01495       /* Check for wear page. */
01496       if (nvmPageTypeWear == NVM_PageGet(address).pageType)
01497       {
01498         /* Flip bit. */
01499         nvmStaticWearWriteHistory[address / NVM_PAGES_PER_WEAR_HISTORY] |= mask;
01500         /* Record flip. */
01501         nvmStaticWearWritesInHistory++;
01502       }
01503       else
01504       {
01505         /* Must release write lock, run the write function to move the data,
01506          * then acquire lock again. */
01507 
01508         /* Give up write lock and open for other API operations. */
01509         NVM_RELEASE_WRITE_LOCK
01510 
01511         NVM_Write(address, NVM_WRITE_NONE_CMD);
01512 
01513         /* Require write lock to continue. */
01514         NVM_ACQUIRE_WRITE_LOCK
01515       }
01516     }
01517     nvmStaticWearWorking = false;
01518   }
01519 
01520   return ECODE_EMDRV_NVM_OK;
01521 }
01522 
01523 #endif
01524 
01528 /******** THE REST OF THE FILE IS DOCUMENTATION ONLY !**********************/