EFR32 Blue Gecko 1 Software Documentation  efr32bg1-doc-5.1.2
nvm.c
Go to the documentation of this file.
1 /***************************************************************************/
16 #include <stdbool.h>
17 #include <stddef.h>
18 #include "nvm.h"
19 
20 /*******************************************************************************
21  ******************************* DEFINES ***********************************
22  ******************************************************************************/
23 
28 #define NVM_VERSION 0x3U
29 
30 /* Sizes. Internal sizes of different objects */
31 #define NVM_CONTENT_SIZE (NVM_PAGE_SIZE - (NVM_HEADER_SIZE + NVM_FOOTER_SIZE))
32 #define NVM_WEAR_CONTENT_SIZE (NVM_PAGE_SIZE - NVM_HEADER_SIZE)
33 
34 #define NVM_PAGE_EMPTY_VALUE 0xffffU
35 #define NVM_NO_PAGE_RETURNED 0xffffffffUL
36 #define NVM_NO_WRITE_16BIT 0xffffU
37 #define NVM_NO_WRITE_32BIT 0xffffffffUL
38 #define NVM_HIGHEST_32BIT 0xffffffffUL
39 #define NVM_FLIP_FIRST_BIT_OF_32_WHEN_WRITE 0xffff7fffUL
40 #define NVM_FIRST_BIT_ONE 0x8000U
41 #define NVM_FIRST_BIT_ZERO 0x7fffU
42 #define NVM_LAST_BIT_ZERO 0xfffeU
43 
44 #define NVM_CHECKSUM_INITIAL 0xffffU
45 #define NVM_CHECKSUM_LENGTH 0x2U
46 
47 #define NVM_PAGES_PER_WEAR_HISTORY 0x8U
48 
52 #ifndef NVM_ACQUIRE_WRITE_LOCK
53 #define NVM_ACQUIRE_WRITE_LOCK
54 #endif
55 
56 #ifndef NVM_RELEASE_WRITE_LOCK
57 #define NVM_RELEASE_WRITE_LOCK
58 #endif
59 
62 /*******************************************************************************
63  ****************************** TYPEDEFS ***********************************
64  ******************************************************************************/
65 
68 typedef enum
69 {
70  nvmValidateResultOk = 0,
71  nvmValidateResultOkMarked = 1,
72  nvmValidateResultOld = 2,
73  nvmValidateResultError = 3
74 } NVM_ValidateResult_t;
75 
78 typedef struct
79 {
80  uint32_t eraseCount;
81  uint16_t watermark;
82  uint16_t version;
83 } NVM_Page_Header_t;
84 
86 #define NVM_HEADER_SIZE (sizeof(NVM_Page_Header_t))
87 
90 typedef struct
91 {
92  uint16_t checksum;
93  uint16_t watermark;
94 } NVM_Page_Footer_t;
95 
97 #define NVM_FOOTER_SIZE (sizeof(NVM_Page_Footer_t))
98 
101 /*******************************************************************************
102  ******************************* STATICS ***********************************
103  ******************************************************************************/
104 
108 static NVM_Config_t const *nvmConfig;
109 
110 #if (NVM_FEATURE_STATIC_WEAR_ENABLED)
111 /* Static wear leveling */
112 
113 /* Bit list that records which pages have been rewritten.
114  * The array contains number of pages divided by 8 bits in a byte. */
115 static uint8_t nvmStaticWearWriteHistory[(NVM_MAX_NUMBER_OF_PAGES + (NVM_PAGES_PER_WEAR_HISTORY - 1)) / NVM_PAGES_PER_WEAR_HISTORY];
116 
117 /* Number of different page writes recorded in history. */
118 static uint16_t nvmStaticWearWritesInHistory;
119 
120 /* Number of page erases performed since last rest. */
121 static uint16_t nvmStaticWearErasesSinceReset;
122 
123 /* Stop recurring calls from causing mayhem. */
124 static bool nvmStaticWearWorking = false;
125 #endif
126 
129 /*******************************************************************************
130  ****************************** PROTOTYPES *********************************
131  ******************************************************************************/
132 
135 static uint8_t* NVM_PagePhysicalAddressGet(uint16_t pageId);
136 static uint8_t* NVM_ScratchPageFindBest(void);
137 static Ecode_t NVM_PageErase(uint8_t *pPhysicalAddress);
138 static NVM_Page_Descriptor_t NVM_PageDescriptorGet(uint16_t pageId);
139 static NVM_ValidateResult_t NVM_PageValidate(uint8_t *pPhysicalAddress);
140 
141 #if (NVM_FEATURE_WEAR_PAGES_ENABLED)
142 static uint16_t NVM_WearIndex(uint8_t *pPhysicalAddress, NVM_Page_Descriptor_t *pPageDesc);
143 static bool NVM_WearReadIndex(uint8_t *pPhysicalAddress, NVM_Page_Descriptor_t *pPageDesc, uint16_t *pIndex);
144 #endif
145 
146 static void NVM_ChecksumAdditive(uint16_t *pChecksum, void *pBuffer, uint16_t len);
147 
148 #if (NVM_FEATURE_STATIC_WEAR_ENABLED)
149 static void NVM_StaticWearReset(void);
150 static void NVM_StaticWearUpdate(uint16_t address);
151 static Ecode_t NVM_StaticWearCheck(void);
152 #endif
153 
155 /*******************************************************************************
156  *************************** GLOBAL FUNCTIONS ******************************
157  ******************************************************************************/
158 
159 /***************************************************************************/
185 Ecode_t NVM_Init(NVM_Config_t const *config)
186 {
187  uint16_t page;
188  /* Variable to store the result returned at the end. */
190 
191  /* Physical address of the current page. */
192  uint8_t *pPhysicalAddress = (uint8_t *)(config->nvmArea);
193  /* Physical address of a suspected duplicate page under observation. */
194  uint8_t *pDuplicatePhysicalAddress;
195 
196  /* Logical address of the current page. */
197  uint16_t logicalAddress;
198  /* Logical address of a duplicate page. */
199  uint16_t duplicateLogicalAddress;
200 
201  /* Temporary variable to store results of a validation operation. */
202  NVM_ValidateResult_t validationResult;
203  /* Temporary variable to store results of a erase operation. */
204  Ecode_t eraseResult;
205 
206  /* if there is no spare page, return error */
207  if( (config->pages <= config->userPages)
208  || (config->pages > NVM_MAX_NUMBER_OF_PAGES) )
209  {
210  return ECODE_EMDRV_NVM_ERROR;
211  }
212 
213  /* now check that page structures fits to physical page size */
214  {
215  uint16_t pageIdx = 0, obj = 0, sum = 0;
216  const NVM_Page_Descriptor_t *currentPage;
217 
218  for(pageIdx = 0; pageIdx < config->userPages; pageIdx++)
219  {
220  sum = 0;
221  obj = 0;
222  currentPage = &((*(config->nvmPages))[pageIdx]);
223 
224  while( (*(currentPage->page))[obj].location != 0)
225  {
226  sum += (*(currentPage->page))[obj++].size;
227  }
228 
229  if(currentPage->pageType == nvmPageTypeNormal)
230  {
231  if( sum > NVM_CONTENT_SIZE )
232  {
233  return ECODE_EMDRV_NVM_ERROR; /* objects bigger than page size */
234  }
235  }
236  else
237  {
238  if(currentPage->pageType == nvmPageTypeWear)
239  {
240  if( (sum+NVM_CHECKSUM_LENGTH) > NVM_WEAR_CONTENT_SIZE )
241  {
242  return ECODE_EMDRV_NVM_ERROR; /* objects bigger than page size */
243  }
244  } else
245  {
246  return ECODE_EMDRV_NVM_ERROR; /* unknown page type */
247  }
248  }
249  }
250  }
251 
252  nvmConfig = config;
253 
254  /* Require write lock to continue. */
255  NVM_ACQUIRE_WRITE_LOCK
256 
257  /* Initialize the NVM. */
258  NVMHAL_Init();
259 
260 #if (NVM_FEATURE_STATIC_WEAR_ENABLED)
261  /* Initialize the static wear leveling functionality. */
262  NVM_StaticWearReset();
263 #endif
264 
265  /* Run through all pages and see if they validate if they contain content. */
266  for (page = 0; page < nvmConfig->pages; ++page)
267  {
268  /* Read the logical address of the page stored at the current physical
269  * address, and compare it to the value of an empty page. */
270  NVMHAL_Read(pPhysicalAddress + offsetof(NVM_Page_Header_t, watermark),
271  &logicalAddress,
272  sizeof(logicalAddress));
273  if (NVM_PAGE_EMPTY_VALUE != logicalAddress)
274  {
275  /* Not an empty page. Check if it validates. */
276  validationResult = NVM_PageValidate(pPhysicalAddress);
277 
278  /* Three different kinds of pages. */
279  if (nvmValidateResultOk == validationResult)
280  {
281  /* We have found a valid page, so the initial error can be changed to an
282  * OK result. */
283  if (ECODE_EMDRV_NVM_ERROR == result)
284  {
285  result = ECODE_EMDRV_NVM_OK;
286  }
287  }
288  else if (nvmValidateResultOkMarked == validationResult)
289  {
290  /* Page validates, but is marked for write.
291  * There might exist a newer version. */
292 
293  /* Walk through all the possible pages looking for a page with
294  * matching watermark. */
295  pDuplicatePhysicalAddress = (uint8_t *)(nvmConfig->nvmArea)
296  + offsetof(NVM_Page_Header_t, watermark);
297  for (page = 0; (NVM_PAGE_EMPTY_VALUE != logicalAddress) && (page < nvmConfig->pages);
298  ++page)
299  {
300  NVMHAL_Read(pDuplicatePhysicalAddress + offsetof(NVM_Page_Header_t, watermark), &
301  duplicateLogicalAddress,
302  sizeof(duplicateLogicalAddress));
303 
304  if ((pDuplicatePhysicalAddress != pPhysicalAddress)
305  && ((logicalAddress | NVM_FIRST_BIT_ONE) == duplicateLogicalAddress))
306  {
307  /* Duplicate page has got the same logical address. Check if it
308  * validates. */
309  validationResult = NVM_PageValidate(pDuplicatePhysicalAddress);
310 
311  if (nvmValidateResultOk == validationResult)
312  {
313  /* The new one validates, delete the old one. */
314  eraseResult = NVM_PageErase(pPhysicalAddress);
315  }
316  else
317  {
318  /* The new one is broken, delete the new one. */
319  eraseResult = NVM_PageErase(pDuplicatePhysicalAddress);
320  }
321 
322  /* Something went wrong */
323  if (ECODE_EMDRV_NVM_OK != eraseResult)
324  {
325  result = ECODE_EMDRV_NVM_ERROR;
326  }
327  }
328 
329  /* Go to the next physical page. */
330  pDuplicatePhysicalAddress += NVM_PAGE_SIZE;
331  } /* End duplicate search loop. */
332 
333  /* If everything went OK and this is the first page we found, then
334  * we can change the status from initial error to OK. */
335  if (ECODE_EMDRV_NVM_ERROR == result)
336  {
337  result = ECODE_EMDRV_NVM_OK;
338  }
339  }
340  else
341  {
342  /* Page does not validate */
343  result = ECODE_EMDRV_NVM_ERROR;
344  }
345  } /* End - not empty if. */
346 
347  /* Go to the next physical page. */
348  pPhysicalAddress += NVM_PAGE_SIZE;
349  } /* End pages loop. */
350 
351  /* If no pages was found, the system is not in use and should be reset. */
352  if (ECODE_EMDRV_NVM_ERROR == result)
353  {
355  }
356 
357  /* Give up write lock and open for other API operations. */
358  NVM_RELEASE_WRITE_LOCK
359 
360  return result;
361 }
362 
363 
364 /***************************************************************************/
382 Ecode_t NVM_Erase(uint32_t eraseCount)
383 {
384  uint16_t page;
385  /* Result used when returning from the function. */
387 
388  /* Location of physical page. */
389  uint8_t *pPhysicalAddress = (uint8_t *)(nvmConfig->nvmArea);
390 
391  /* Container for moving old erase count, or set to new. */
392  uint32_t tempEraseCount = eraseCount;
393 
394  /* Require write lock to continue. */
395  NVM_ACQUIRE_WRITE_LOCK
396 
397  /* Loop over all the pages, as long as everything is OK. */
398  for (page = 0;
399  (page < nvmConfig->pages)
400  && ((ECODE_EMDRV_NVM_OK == result) || (ECODE_EMDRV_NVM_ERROR == result));
401  ++page)
402  {
403  /* If eraseCount input is set to the retain constant, we need to get the
404  * old erase count before we erase the page. */
405  if (NVM_ERASE_RETAINCOUNT == eraseCount)
406  {
407  /* Read old erase count. */
408  NVMHAL_Read(pPhysicalAddress + offsetof(NVM_Page_Header_t, eraseCount),
409  &tempEraseCount,
410  sizeof(tempEraseCount));
411  }
412 
413  /* Erase page. */
414  result = NVMHAL_PageErase(pPhysicalAddress);
415 
416  /* If still OK, write erase count to page. */
417  if (ECODE_EMDRV_NVM_OK == result)
418  {
419  result = NVMHAL_Write(pPhysicalAddress + offsetof(NVM_Page_Header_t, eraseCount),
420  &tempEraseCount,
421  sizeof(tempEraseCount));
422  }
423 
424  /* Go to the next physical page. */
425  pPhysicalAddress += NVM_PAGE_SIZE;
426  }
427 
428  /* Give up write lock and open for other API operations. */
429  NVM_RELEASE_WRITE_LOCK
430 
431  return result;
432 }
433 
434 
435 /***************************************************************************/
460 Ecode_t NVM_Write(uint16_t pageId, uint8_t objectId)
461 {
462  /* Result variable used as return value from the function. */
464 
465  /* Watermark to look for when finding page. First bit true. */
466  uint16_t watermark = pageId | NVM_FIRST_BIT_ONE;
467  /* Watermark used when flipping the duplication bit of a page. */
468  const uint32_t flipWatermark = NVM_FLIP_FIRST_BIT_OF_32_WHEN_WRITE;
469 
470  /* Page descriptor used for accessing page type and page objects. */
471  NVM_Page_Descriptor_t pageDesc;
472 
473  /* Page header and footer. Used to store old version and to easily update and
474  * write new version. */
475  NVM_Page_Header_t header;
476 
477  /* Variable used for checksum calculation. Starts at defined initial value. */
478  uint16_t checksum = NVM_CHECKSUM_INITIAL;
479 
480  /* Physical addresses in memory for the old and new version of the page. */
481  uint8_t *pOldPhysicalAddress = (uint8_t *) NVM_NO_PAGE_RETURNED;
482  uint8_t *pNewPhysicalAddress = (uint8_t *) NVM_NO_PAGE_RETURNED;
483 
484  /* Offset address within page. */
485  uint16_t offsetAddress;
486  /* Single byte buffer used when copying data from an old page.*/
487  uint8_t copyBuffer;
488  /* Object in page counter. */
489  uint8_t objectIndex;
490  /* Amount of bytes to copy. */
491  uint16_t copyLength;
492 
493  /* Handle wear pages. Should we handle this as an extra write to an existing
494  * page or create a new one. */
495  bool wearWrite = false;
496 
497 #if (NVM_FEATURE_WRITE_NECESSARY_CHECK_ENABLED)
498  /* Bool used when checking if a write operation is needed. */
499  bool rewriteNeeded;
500 #endif
501 
502 #if (NVM_FEATURE_WEAR_PAGES_ENABLED)
503  /* Used to hold the checksum of the wear object. */
504  uint16_t wearChecksum;
505  /* Byte size of the wear object. Includes checksum length. */
506  uint16_t wearObjectSize;
507  /* Used to specify the internal index of the wear object in a page. */
508  uint16_t wearIndex;
509 
510  #if (NVM_FEATURE_WRITE_VALIDATION_ENABLED)
511  /* The new wear index the object will be written to. */
512  uint16_t wearIndexNew;
513  #endif
514 #endif
515 
516  /* Require write lock to continue. */
517  NVM_ACQUIRE_WRITE_LOCK
518 
519  /* Find old physical address. */
520  pOldPhysicalAddress = NVM_PagePhysicalAddressGet(pageId);
521 
522  /* Get the page configuration. */
523  pageDesc = NVM_PageDescriptorGet(pageId);
524 
525 #if (NVM_FEATURE_WRITE_NECESSARY_CHECK_ENABLED)
526  /* If there is an old version of the page, it might not be necessary to update
527  * the data. Also check that this is a normal page and that the static wear
528  * leveling system is not working (this system might want to rewrite pages
529  * even if the data is similar to the old version). */
530  if (((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress)
531  && (nvmPageTypeNormal == pageDesc.pageType)
532 #if (NVM_FEATURE_STATIC_WEAR_ENABLED)
533  && !nvmStaticWearWorking
534 #endif
535 
536  )
537  {
538  rewriteNeeded = false;
539  objectIndex = 0;
540  offsetAddress = 0;
541 
542  /* Loop over items as long as no rewrite is needed and the current item has
543  * got a size other than 0. Size 0 is used as a marker for a NULL object. */
544  while (((*pageDesc.page)[objectIndex].size != 0) && !rewriteNeeded)
545  {
546  /* Check if every object should be written or if this is the object to
547  * write. */
548  if ((NVM_WRITE_ALL_CMD == objectId) ||
549  ((*pageDesc.page)[objectIndex].objectId == objectId))
550  {
551  /* Compare object to RAM. */
552 
553  copyLength = (*pageDesc.page)[objectIndex].size;
554 
555  /* Loop over each byte of the object and compare with RAM. */
556  while (copyLength != 0)
557  {
558  /* Get byte from NVM. */
559  NVMHAL_Read(pOldPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
560  &copyBuffer,
561  sizeof(copyBuffer));
562 
563  /* Check byte in NVM with the corresponding byte in RAM. */
564  if (*(uint8_t *)((*pageDesc.page)[objectIndex].location + offsetAddress) != copyBuffer)
565  {
566  rewriteNeeded = true;
567  break;
568  }
569 
570  /* Move offset. */
571  offsetAddress += sizeof(copyBuffer);
572  copyLength -= sizeof(copyBuffer);
573  }
574  }
575  else
576  {
577  /* Move offset past the object. */
578  offsetAddress += (*pageDesc.page)[objectIndex].size;
579  }
580 
581  /* Check next object. */
582  objectIndex++;
583  }
584 
585  if (!rewriteNeeded)
586  {
587  /* Release write lock before return. */
588  NVM_RELEASE_WRITE_LOCK
589 
590  return ECODE_EMDRV_NVM_OK;
591  }
592  }
593 #endif
594 
595 
596 
597 #if (NVM_FEATURE_WEAR_PAGES_ENABLED)
598  /* If this is a wear page then we can check if we can possibly squeeze another
599  * version of the object inside the already existing page. If this is possible
600  * we set the wearWrite boolean. This will then make us ignore the normal
601  * write operation. */
602  if (nvmPageTypeWear == pageDesc.pageType)
603  {
604  /* Calculate checksum. The wear page checksum is only stored in 15 bits,
605  * because we need one bit to mark that the object is written. This bit is
606  * always set to 0. */
607 
608  wearChecksum = NVM_CHECKSUM_INITIAL;
609  NVM_ChecksumAdditive(&wearChecksum,
610  (*pageDesc.page)[0].location,
611  (*pageDesc.page)[0].size);
612  wearChecksum &= NVM_LAST_BIT_ZERO;
613 
614  /* If there was an old page. */
615  if ((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress)
616  {
617  /* Find location in old page. */
618  wearIndex = NVM_WearIndex(pOldPhysicalAddress, &pageDesc);
619  wearObjectSize = (*pageDesc.page)[0].size + NVM_CHECKSUM_LENGTH;
620 
621  /* Check that the wearIndex returned is within the length of the page. */
622  if (wearIndex < ((uint16_t) NVM_WEAR_CONTENT_SIZE) / wearObjectSize)
623  {
624  result = NVMHAL_Write(pOldPhysicalAddress
625  + NVM_HEADER_SIZE
626  + (wearIndex * wearObjectSize),
627  (*pageDesc.page)[0].location,
628  (*pageDesc.page)[0].size);
629  result = NVMHAL_Write(pOldPhysicalAddress
630  + NVM_HEADER_SIZE
631  + (wearIndex * wearObjectSize)
632  + (*pageDesc.page)[0].size,
633  &wearChecksum,
634  sizeof(wearChecksum));
635 
636  /* Register that we have now written to the old page. */
637  wearWrite = true;
638 
639 #if (NVM_FEATURE_WRITE_VALIDATION_ENABLED)
640  /* Check if the newest one that is valid is the same as the one we just
641  * wrote to the NVM. */
642  if ((!NVM_WearReadIndex(pOldPhysicalAddress, &pageDesc, &wearIndexNew)) ||
643  (wearIndexNew != wearIndex))
644  {
645  result = ECODE_EMDRV_NVM_ERROR;
646  }
647 #endif
648  }
649  } /* End of old page if. */
650  } /* End of wear page if. */
651 #endif
652 
653 #if (NVM_FEATURE_WEAR_PAGES_ENABLED)
654  /* Do not create a new page if we have already done an in-page wear write. */
655  if (!wearWrite)
656  {
657 #endif
658  /* Mark any old page before creating a new one. */
659  if ((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress)
660  {
661  result = NVMHAL_Write(pOldPhysicalAddress, &flipWatermark, 4);
662 
663  if (ECODE_EMDRV_NVM_OK != result)
664  {
665  /* Give up write lock and open for other API operations. */
666  NVM_RELEASE_WRITE_LOCK
667  return result;
668  }
669  }
670 
671  /* Find new physical address to write to. */
672  pNewPhysicalAddress = NVM_ScratchPageFindBest();
673 
674  if ((uint8_t*) NVM_NO_PAGE_RETURNED == pNewPhysicalAddress)
675  {
676  /* Give up write lock and open for other API operations. */
677  NVM_RELEASE_WRITE_LOCK
678  return ECODE_EMDRV_NVM_ERROR;
679  }
680 
681  /* Generate and write header */
682  header.watermark = watermark;
683  header.eraseCount = NVM_NO_WRITE_32BIT;
684  header.version = NVM_VERSION;
685 
686  /* store header at beginning of page */
687  NVMHAL_Write(pNewPhysicalAddress + offsetof(NVM_Page_Header_t, watermark),
688  &header.watermark,
689  sizeof(header.watermark));
690  NVMHAL_Write(pNewPhysicalAddress + offsetof(NVM_Page_Header_t, eraseCount),
691  &header.eraseCount, sizeof(header.eraseCount));
692  result =
693  NVMHAL_Write(pNewPhysicalAddress + offsetof(NVM_Page_Header_t, version),
694  &header.version, sizeof(header.version));
695 
696  /* Reset address index within page. */
697  offsetAddress = 0;
698  /* Reset object in page counter. */
699  objectIndex = 0;
700 
701  /* Loop over items as long as everything is OK, and the current item has got
702  * a size other than 0. Size 0 is used as a marker for a NULL object. */
703  while (((*pageDesc.page)[objectIndex].size != 0) && (ECODE_EMDRV_NVM_OK == result))
704  {
705  /* Check if every object should be written or if this is the object to
706  * write. */
707  if ((NVM_WRITE_ALL_CMD == objectId) ||
708  ((*pageDesc.page)[objectIndex].objectId == objectId))
709  {
710  /* Write object from RAM. */
711  result = NVMHAL_Write(pNewPhysicalAddress + NVM_HEADER_SIZE + offsetAddress,
712  (*pageDesc.page)[objectIndex].location,
713  (*pageDesc.page)[objectIndex].size);
714  offsetAddress += (*pageDesc.page)[objectIndex].size;
715 
716  NVM_ChecksumAdditive(&checksum,
717  (*pageDesc.page)[objectIndex].location,
718  (*pageDesc.page)[objectIndex].size);
719  }
720  else
721  {
722  /* Get version from old page. */
723  if ((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress)
724  {
725  NVM_ChecksumAdditive(&checksum,
726  pOldPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
727  (*pageDesc.page)[objectIndex].size);
728 
729  copyLength = (*pageDesc.page)[objectIndex].size;
730 
731  while ((copyLength != 0) && (ECODE_EMDRV_NVM_OK == result))
732  {
733  /* Copies using an 1 byte buffer. Might be better to dynamically use larger if possible. */
734  NVMHAL_Read(pOldPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
735  &copyBuffer,
736  sizeof(copyBuffer));
737  result = NVMHAL_Write(pNewPhysicalAddress + NVM_HEADER_SIZE + offsetAddress,
738  &copyBuffer,
739  sizeof(copyBuffer));
740 
741  offsetAddress += sizeof(copyBuffer);
742  copyLength -= sizeof(copyBuffer);
743  }
744  } /* End if old page. */
745  } /* Else-end of NVM_WRITE_ALL if-statement. */
746 
747  objectIndex++;
748  }
749 
750  /* If we are creating a wear page, add the checksum directly after the data. */
751 #if (NVM_FEATURE_WEAR_PAGES_ENABLED)
752  if (nvmPageTypeWear == pageDesc.pageType)
753  {
754  result = NVMHAL_Write(pNewPhysicalAddress + NVM_HEADER_SIZE + offsetAddress,
755  &wearChecksum,
756  sizeof(wearChecksum));
757  }
758  /* Generate and write footer on normal pages. */
759  else
760  {
761 #endif
762  if (ECODE_EMDRV_NVM_OK == result)
763  {
764  /* write checksum and watermark to the footer */
765  result = NVMHAL_Write(pNewPhysicalAddress +
766  (NVM_PAGE_SIZE - NVM_FOOTER_SIZE) +
767  offsetof(NVM_Page_Footer_t, checksum),
768  &checksum,
769  sizeof(checksum));
770  result = NVMHAL_Write(pNewPhysicalAddress +
771  (NVM_PAGE_SIZE - NVM_FOOTER_SIZE) +
772  offsetof(NVM_Page_Footer_t, watermark),
773  &watermark,
774  sizeof(watermark));
775  }
776 
777 #if (NVM_FEATURE_WEAR_PAGES_ENABLED)
778 } /* End of else from page type. */
779 #endif
780 
781 #if (NVM_FEATURE_WRITE_VALIDATION_ENABLED)
782  /* Validate that the correct data was written. */
783  if (nvmValidateResultOk != NVM_PageValidate(pNewPhysicalAddress))
784  {
785  result = ECODE_EMDRV_NVM_ERROR;
786  }
787 #endif
788 
789 #if (NVM_FEATURE_WEAR_PAGES_ENABLED)
790 } /* End of if for normal write (!wearWrite). */
791 #endif
792 
793  /* Erase old if there was an old one and everything else have gone OK. */
794  if ((!wearWrite) &&
795  ((uint8_t *) NVM_NO_PAGE_RETURNED != pOldPhysicalAddress))
796  {
797  if (ECODE_EMDRV_NVM_OK == result)
798  {
799  result = NVM_PageErase(pOldPhysicalAddress);
800  }
801  else
802  {
803  NVM_PageErase(pNewPhysicalAddress);
804  }
805  }
806 
807  /* Give up write lock and open for other API operations. */
808  NVM_RELEASE_WRITE_LOCK
809 
810  return result;
811 }
812 
813 
814 /***************************************************************************/
834 Ecode_t NVM_Read(uint16_t pageId, uint8_t objectId)
835 {
836 #if (NVM_FEATURE_WEAR_PAGES_ENABLED)
837  /* Variable used to fetch read index. */
838  uint16_t wearIndex;
839 #endif
840 
841  /* Physical address of the page to read from. */
842  uint8_t *pPhysicalAddress;
843 
844  /* Description of the page, used to find page type and objects. */
845  NVM_Page_Descriptor_t pageDesc;
846 
847  /* Index of object in page. */
848  uint8_t objectIndex;
849  /* Address of read location within a page. */
850  uint16_t offsetAddress;
851 
852 
853  /* Require write lock to continue. */
854  NVM_ACQUIRE_WRITE_LOCK
855 
856  /* Find physical page. */
857  pPhysicalAddress = NVM_PagePhysicalAddressGet(pageId);
858 
859  /* If no page was found, we cannot read anything. */
860  if ((uint8_t*) NVM_NO_PAGE_RETURNED == pPhysicalAddress)
861  {
862  /* Give up write lock and open for other API operations. */
863  NVM_RELEASE_WRITE_LOCK
865  }
866 
867  /* Get page description. */
868  pageDesc = NVM_PageDescriptorGet(pageId);
869 
870 #if (NVM_FEATURE_WEAR_PAGES_ENABLED)
871  /* If this is a wear page, we must find out which object in the page should be
872  * read. */
873  if (nvmPageTypeWear == pageDesc.pageType)
874  {
875  /* Find valid object in wear page and read it. */
876  if (NVM_WearReadIndex(pPhysicalAddress + offsetof(NVM_Page_Header_t, eraseCount),
877  &pageDesc, &wearIndex))
878  {
879  NVMHAL_Read(pPhysicalAddress + NVM_HEADER_SIZE +
880  wearIndex * ((*pageDesc.page)[0].size + NVM_CHECKSUM_LENGTH),
881  (*pageDesc.page)[0].location,
882  (*pageDesc.page)[0].size);
883  }
884  else
885  {
886  /* No valid object was found in the page. */
887  /* Give up write lock and open for other API operations. */
888  NVM_RELEASE_WRITE_LOCK
890  }
891  }
892  else
893 #endif
894  {
895  /* Read normal page. */
896  objectIndex = 0;
897  offsetAddress = 0;
898 
899 #if (NVM_FEATURE_READ_VALIDATION_ENABLED)
900  if (nvmValidateResultError == NVM_PageValidate(pPhysicalAddress))
901  {
902  /* Give up write lock and open for other API operations. */
903  NVM_RELEASE_WRITE_LOCK
905  }
906 #endif
907 
908  /* Loop through and read the objects of a page, as long as the current item
909  * has got a size other than 0. Size 0 is a marker for the NULL object. */
910  while ((*pageDesc.page)[objectIndex].size != 0)
911  {
912  /* Check if every object should be read or if this is the object to read. */
913  if ((NVM_READ_ALL_CMD == objectId)
914  || ((*pageDesc.page)[objectIndex].objectId == objectId))
915  {
916  NVMHAL_Read(pPhysicalAddress + offsetAddress + NVM_HEADER_SIZE,
917  (*pageDesc.page)[objectIndex].location,
918  (*pageDesc.page)[objectIndex].size);
919  }
920 
921  offsetAddress += (*pageDesc.page)[objectIndex].size;
922  objectIndex++;
923  }
924  }
925 
926  /* Give up write lock and open for other API operations. */
927  NVM_RELEASE_WRITE_LOCK
928 
929  return ECODE_EMDRV_NVM_OK;
930 }
931 
932 #if (NVM_FEATURE_WEARLEVELGET_ENABLED)
933 /***************************************************************************/
944 uint32_t NVM_WearLevelGet(void)
945 {
946  uint16_t page;
947  /* Used to temporarily store the update id of the current page. */
948  uint32_t eraseCount;
949  /* Worst (highest) update id. Used as return value. */
950  uint32_t hiEraseCount = 0;
951 
952  /* Address of physical page. */
953  uint8_t *pPhysicalAddress = (uint8_t *)(nvmConfig->nvmArea);
954 
955  /* Loop through all pages in memory. */
956  for (page = 0; page < nvmConfig->pages; ++page)
957  {
958  /* Find and compare erase count. */
959  NVMHAL_Read(pPhysicalAddress + offsetof(NVM_Page_Header_t, eraseCount),
960  &eraseCount,
961  sizeof(eraseCount));
962  if (eraseCount > hiEraseCount)
963  {
964  hiEraseCount = eraseCount;
965  }
966 
967  /* Go to the next physical page. */
968  pPhysicalAddress += NVM_PAGE_SIZE;
969  }
970  return hiEraseCount;
971 }
972 #endif
973 
974 
975 /*******************************************************************************
976  *************************** LOCAL FUNCTIONS *******************************
977  ******************************************************************************/
978 
981 /***************************************************************************/
996 static uint8_t* NVM_PagePhysicalAddressGet(uint16_t pageId)
997 {
998  uint16_t page;
999  /* Physical address to return. */
1000  uint8_t *pPhysicalAddress = (uint8_t *)(nvmConfig->nvmArea);
1001  /* Temporary variable used to read and compare logical page address. */
1002  uint16_t logicalAddress;
1003 
1004  /* Loop through memory looking for a matching watermark. */
1005  for (page = 0; page < nvmConfig->pages; ++page)
1006  {
1007  /* Allow both versions of writing mark, invalid duplicates should already
1008  * have been deleted. */
1009  NVMHAL_Read(pPhysicalAddress + offsetof(NVM_Page_Header_t, watermark),
1010  &logicalAddress,
1011  sizeof(logicalAddress));
1012  if (((pageId | NVM_FIRST_BIT_ONE) == logicalAddress) || (pageId == logicalAddress))
1013  {
1014  return pPhysicalAddress;
1015  }
1016 
1017  /* Move lookup point to the next page. */
1018  pPhysicalAddress += NVM_PAGE_SIZE;
1019  }
1020 
1021  /* No page found. */
1022  return (uint8_t *) NVM_NO_PAGE_RETURNED;
1023 }
1024 
1025 
1026 /***************************************************************************/
1038 static uint8_t* NVM_ScratchPageFindBest(void)
1039 {
1040  uint16_t page;
1041  /* Address for physical page to return. */
1042  uint8_t *pPhysicalPage = (uint8_t *) NVM_NO_PAGE_RETURNED;
1043 
1044  /* Variable used to read and compare update id of physical pages. */
1045  uint32_t eraseCount = NVM_HIGHEST_32BIT;
1046  /* The best update id found. */
1047  uint32_t loEraseCount = NVM_HIGHEST_32BIT;
1048 
1049  /* Pointer to the current physical page. */
1050  uint8_t *pPhysicalAddress = (uint8_t *)(nvmConfig->nvmArea);
1051  /* Logical address that identifies the page. */
1052  uint16_t logicalAddress;
1053 
1054  /* Loop through all pages in memory. */
1055  for (page = 0; page < nvmConfig->pages; ++page)
1056  {
1057  /* Read and check logical address. */
1058  NVMHAL_Read(pPhysicalAddress + offsetof(NVM_Page_Header_t, watermark),
1059  &logicalAddress,
1060  sizeof(logicalAddress));
1061  if ((uint16_t) NVM_PAGE_EMPTY_VALUE == logicalAddress)
1062  {
1063  /* Find and compare erase count. */
1064  NVMHAL_Read(pPhysicalAddress + offsetof(NVM_Page_Header_t, eraseCount),
1065  &eraseCount,
1066  sizeof(eraseCount));
1067  if (eraseCount < loEraseCount)
1068  {
1069  loEraseCount = eraseCount;
1070  pPhysicalPage = pPhysicalAddress;
1071  }
1072  }
1073 
1074  /* Move lookup point to the next page. */
1075  pPhysicalAddress += NVM_PAGE_SIZE;
1076  }
1077 
1078  /* Return a pointer to the best/least used page. */
1079  return pPhysicalPage;
1080 }
1081 
1082 
1083 /***************************************************************************/
1097 static Ecode_t NVM_PageErase(uint8_t *pPhysicalAddress)
1098 {
1099 #if (NVM_FEATURE_STATIC_WEAR_ENABLED)
1100  /* Logical page address. */
1101  uint16_t logicalAddress;
1102 #endif
1103 
1104  /* Read out the old page update id. */
1105  uint32_t eraseCount;
1106  NVMHAL_Read(pPhysicalAddress + offsetof(NVM_Page_Header_t, eraseCount),
1107  &eraseCount,
1108  sizeof(eraseCount));
1109 
1110 #if (NVM_FEATURE_STATIC_WEAR_ENABLED)
1111  /* Get logical page address. */
1112  NVMHAL_Read(pPhysicalAddress + offsetof(NVM_Page_Header_t, watermark),
1113  &logicalAddress,
1114  sizeof(logicalAddress));
1115 
1116  /* If not empty: mark as erased and check against threshold. */
1117  if (logicalAddress != NVM_PAGE_EMPTY_VALUE)
1118  {
1119  /* Set first bit low. */
1120  logicalAddress = logicalAddress & NVM_FIRST_BIT_ZERO;
1121  NVM_StaticWearUpdate(logicalAddress);
1122  }
1123 #endif
1124 
1125  /* Erase the page. */
1126  NVMHAL_PageErase(pPhysicalAddress);
1127 
1128  /* Update erase count. */
1129  eraseCount++;
1130 
1131  /* Write increased erase count. */
1132  return NVMHAL_Write(pPhysicalAddress + offsetof(NVM_Page_Header_t, eraseCount),
1133  &eraseCount,
1134  sizeof(eraseCount));
1135 }
1136 
1137 
1138 /***************************************************************************/
1157 static NVM_Page_Descriptor_t NVM_PageDescriptorGet(uint16_t pageId)
1158 {
1159  uint8_t pageIndex;
1160  static const NVM_Page_Descriptor_t nullPage = { (uint8_t) 0, 0, (NVM_Page_Type_t) 0 };
1161 
1162  /* Step through all configured pages. */
1163  for (pageIndex = 0; pageIndex < nvmConfig->userPages; ++pageIndex)
1164  {
1165  /* If this is the page we want, return it. */
1166  if ( (*(nvmConfig->nvmPages))[pageIndex].pageId == pageId)
1167  {
1168  return (*(nvmConfig->nvmPages))[pageIndex];
1169  }
1170  }
1171 
1172  /* No page matched the ID, return a NULL page to mark the error. */
1173  return nullPage;
1174 }
1175 
1176 
1177 /***************************************************************************/
1198 static NVM_ValidateResult_t NVM_PageValidate(uint8_t *pPhysicalAddress)
1199 {
1200  /* Result used as return value from the function. */
1201  NVM_ValidateResult_t result;
1202 
1203  /* Objects used to read out the page header and footer. */
1204  NVM_Page_Header_t header;
1205  NVM_Page_Footer_t footer;
1206 
1207  /* Descriptor for the current page. */
1208  NVM_Page_Descriptor_t pageDesc;
1209 
1210  /* Variable used for calculating checksums. */
1211  uint16_t checksum;
1212 
1213  /* Offset of object in page. */
1214  uint8_t objectIndex;
1215  /* Address of read location within a page. */
1216  uint16_t offsetAddress;
1217 
1218 #if (NVM_FEATURE_WEAR_PAGES_ENABLED)
1219  /* Variable used to fetch read index. */
1220  uint16_t index;
1221 #endif
1222 
1223  /* Read page header data */
1224  NVMHAL_Read(pPhysicalAddress + offsetof(NVM_Page_Header_t, watermark),
1225  &header.watermark,
1226  sizeof(header.watermark));
1227  NVMHAL_Read(pPhysicalAddress + offsetof(NVM_Page_Header_t, eraseCount),
1228  &header.eraseCount,
1229  sizeof(header.eraseCount));
1230  NVMHAL_Read(pPhysicalAddress + offsetof(NVM_Page_Header_t, version),
1231  &header.version,
1232  sizeof(header.version));
1233 
1234  /* Stop immediately if data is from another version of the API. */
1235  if (NVM_VERSION != header.version)
1236  {
1237  return nvmValidateResultOld;
1238  }
1239 
1240  /* Get the page configuration. */
1241  pageDesc = NVM_PageDescriptorGet((header.watermark & NVM_FIRST_BIT_ZERO));
1242 
1243 
1244 #if (NVM_FEATURE_WEAR_PAGES_ENABLED)
1245  if (nvmPageTypeWear == pageDesc.pageType)
1246  {
1247  /* Wear page. */
1248 
1249  /* If first bit is already zero the page is marked as a duplicate. */
1250  if ((header.watermark & NVM_FIRST_BIT_ZERO) == header.watermark)
1251  {
1252  result = nvmValidateResultOkMarked;
1253  }
1254  else
1255  {
1256  /* Page is not marked as a duplicate. */
1257  result = nvmValidateResultOk;
1258  }
1259 
1260  /* If we do not have any valid objects in the page it is invalid. */
1261  if (!NVM_WearReadIndex(pPhysicalAddress, &pageDesc, &index))
1262  {
1263  result = nvmValidateResultError;
1264  }
1265  }
1266  else
1267 #endif
1268  {
1269  /* Normal page. */
1270  NVMHAL_Read(pPhysicalAddress + (NVM_PAGE_SIZE - NVM_FOOTER_SIZE) +
1271  offsetof(NVM_Page_Footer_t, checksum),
1272  &footer.checksum,
1273  sizeof(footer.checksum));
1274  NVMHAL_Read(pPhysicalAddress + (NVM_PAGE_SIZE - NVM_FOOTER_SIZE) +
1275  offsetof(NVM_Page_Footer_t, watermark),
1276  &footer.watermark,
1277  sizeof(footer.watermark));
1278  /* Check if watermark or watermark with flipped write bit matches. */
1279  if (header.watermark == footer.watermark)
1280  {
1281  result = nvmValidateResultOk;
1282  }
1283  else if ((header.watermark | NVM_FIRST_BIT_ONE) == footer.watermark)
1284  {
1285  result = nvmValidateResultOkMarked;
1286  }
1287  else
1288  {
1289  result = nvmValidateResultError;
1290  }
1291 
1292  /* Calculate checksum and compare with the one stored. */
1293  objectIndex = 0;
1294  offsetAddress = 0;
1295  checksum = NVM_CHECKSUM_INITIAL;
1296 
1297  /* Calculate per object using the HAL. Loop over items as long as the
1298  * current object has got a size other than 0. Size 0 is used as a marker
1299  * for a NULL object. */
1300  while ((*pageDesc.page)[objectIndex].size != 0)
1301  {
1302  NVMHAL_Checksum(&checksum,
1303  (uint8_t *) pPhysicalAddress
1304  + NVM_HEADER_SIZE + offsetAddress,
1305  (*pageDesc.page)[objectIndex].size);
1306  offsetAddress += (*pageDesc.page)[objectIndex].size;
1307  objectIndex++;
1308  }
1309 
1310  if (checksum != footer.checksum)
1311  {
1312  result = nvmValidateResultError;
1313  }
1314  }
1315  return result;
1316 }
1317 
1318 
1319 #if (NVM_FEATURE_WEAR_PAGES_ENABLED)
1320 /***************************************************************************/
1338 static uint16_t NVM_WearIndex(uint8_t *pPhysicalAddress, NVM_Page_Descriptor_t *pPageDesc)
1339 {
1340  /* Index to return. */
1341  uint16_t wearIndex = 0;
1342 
1343  /* Temporary variable used when calculating and comparing checksums. */
1344  uint16_t checksum;
1345 
1346  /* Size of the object in the wear page, including a 16 bit checksum. */
1347  uint16_t wearObjectSize = ((*pPageDesc->page)[0].size + NVM_CHECKSUM_LENGTH);
1348 
1349 
1350  /* Loop over possible pages. Stop when empty page was found. */
1351  while (wearIndex < NVM_WEAR_CONTENT_SIZE / wearObjectSize)
1352  {
1353  NVMHAL_Read((uint8_t *)(pPhysicalAddress + NVM_HEADER_SIZE
1354  + (wearIndex * wearObjectSize)
1355  + (*pPageDesc->page)[0].size
1356  ),
1357  &checksum,
1358  sizeof(checksum));
1359 
1360  /* Flip the last bit of the checksum to zero. This is a mark used to
1361  * determine that an object is written to this location. */
1362  if ((checksum & NVM_LAST_BIT_ZERO) != checksum)
1363  {
1364  /* Break the loop and accept this location. */
1365  break;
1366  }
1367 
1368  /* Not a valid position for a new write.
1369  * Increase the index and check again. */
1370  wearIndex++;
1371  }
1372  return wearIndex;
1373 }
1374 #endif
1375 
1376 
1377 #if (NVM_FEATURE_WEAR_PAGES_ENABLED)
1378 /***************************************************************************/
1399 static bool NVM_WearReadIndex(uint8_t *pPhysicalAddress,
1400  NVM_Page_Descriptor_t *pPageDesc,
1401  uint16_t *pIndex)
1402 {
1403 #if (NVM_FEATURE_READ_VALIDATION_ENABLED)
1404  /* Variable used for calculating checksum when validating. */
1405  uint16_t checksum = NVM_CHECKSUM_INITIAL;
1406 #endif
1407 
1408  /* Length of wear object plus checksum. */
1409  const uint16_t wearObjectSize = ((*pPageDesc->page)[0].size + NVM_CHECKSUM_LENGTH);
1410 
1411  /* Return value. */
1412  bool validObjectFound = false;
1413 
1414  /* Buffer used when reading checksum. */
1415  uint16_t readBuffer;
1416 
1417  /* Initialize index at max plus one object. */
1418  *pIndex = (((uint16_t) NVM_WEAR_CONTENT_SIZE) / wearObjectSize);
1419 
1420  /* Loop over possible pages. Stop when first OK page is found. */
1421  while ((*pIndex > 0) && (!validObjectFound))
1422  {
1423  (*pIndex)--;
1424 
1425  /* Initialize checksum, and then calculate it from the HAL.*/
1426  uint8_t *temp = pPhysicalAddress
1427  + NVM_HEADER_SIZE
1428  + (*pIndex) * wearObjectSize
1429  + (*pPageDesc->page)[0].size;
1430  NVMHAL_Read((uint8_t *) temp, &readBuffer, sizeof(readBuffer));
1431 
1432 #if (NVM_FEATURE_READ_VALIDATION_ENABLED)
1433  /* Calculate the checksum before accepting the object. */
1434  checksum = NVM_CHECKSUM_INITIAL;
1435  NVMHAL_Checksum(&checksum, pPhysicalAddress + NVM_HEADER_SIZE + (*pIndex) * wearObjectSize, (*pPageDesc->page)[0].size);
1436  /* Flips the last bit of the checksum to zero. This is a mark used to
1437  * determine whether we have written anything to the page. */
1438  if ((uint16_t)(checksum & NVM_LAST_BIT_ZERO) == readBuffer)
1439 #else
1440  if (NVM_NO_WRITE_16BIT != readBuffer)
1441 #endif
1442  {
1443  validObjectFound = true;
1444  }
1445  }
1446  return validObjectFound;
1447 }
1448 #endif
1449 
1450 
1451 /***************************************************************************/
1470 static void NVM_ChecksumAdditive(uint16_t *pChecksum, void *pBuffer, uint16_t len)
1471 {
1472  uint8_t *pointer = (uint8_t *) pBuffer;
1473  uint16_t crc = *pChecksum;
1474 
1475  while(len--)
1476  {
1477  crc = (crc >> 8) | (crc << 8);
1478  crc ^= *pointer++;
1479  crc ^= (crc & 0xf0) >> 4;
1480  crc ^= (crc & 0x0f) << 12;
1481  crc ^= (crc & 0xff) << 5;
1482  }
1483 
1484  *pChecksum = crc;
1485 }
1486 
1487 
1488 #if (NVM_FEATURE_STATIC_WEAR_ENABLED)
1489 /***************************************************************************/
1498 static void NVM_StaticWearReset(void)
1499 {
1500  uint16_t i;
1501  nvmStaticWearErasesSinceReset = 0;
1502  nvmStaticWearWritesInHistory = 0;
1503 
1504  for (i = 0; (NVM_PAGES_PER_WEAR_HISTORY * i) < nvmConfig->userPages; i += 1)
1505  {
1506  nvmStaticWearWriteHistory[i] = 0;
1507  }
1508 }
1509 
1510 /***************************************************************************/
1521 static void NVM_StaticWearUpdate(uint16_t address)
1522 {
1523  if (address < nvmConfig->userPages)
1524  {
1525  /* Mark page with logical address as written. */
1526 
1527  /* Bitmask to check and change the desired bit. */
1528  uint8_t mask = 1U << (address % NVM_PAGES_PER_WEAR_HISTORY);
1529 
1530  if ((nvmStaticWearWriteHistory[address / NVM_PAGES_PER_WEAR_HISTORY] & mask) == 0)
1531  {
1532  /* Flip bit. */
1533  nvmStaticWearWriteHistory[address / NVM_PAGES_PER_WEAR_HISTORY] |= mask;
1534  /* Record flip. */
1535  nvmStaticWearWritesInHistory++;
1536  }
1537 
1538  /* Record erase operation. */
1539  nvmStaticWearErasesSinceReset++;
1540 
1541  /* Call the static wear leveler. */
1542  NVM_StaticWearCheck();
1543  }
1544 }
1545 
1546 
1547 /***************************************************************************/
1556 static Ecode_t NVM_StaticWearCheck(void)
1557 {
1558  /* Check if there is a check already running. We do not need more of these. */
1559  if (!nvmStaticWearWorking)
1560  {
1561  nvmStaticWearWorking = true;
1562  while (nvmStaticWearErasesSinceReset / nvmStaticWearWritesInHistory > NVM_STATIC_WEAR_THRESHOLD)
1563  {
1564  /* If all the pages have been moved in this cycle: reset. */
1565  if (nvmStaticWearWritesInHistory >= nvmConfig->userPages)
1566  {
1567  NVM_StaticWearReset();
1568  break;
1569  }
1570 
1571  /* Find an address for a page that has not been rewritten. */
1572  uint16_t address = 0;
1573  uint8_t mask = 1U << (address % NVM_PAGES_PER_WEAR_HISTORY);
1574  while ((nvmStaticWearWriteHistory[address / NVM_PAGES_PER_WEAR_HISTORY] & mask) != 0)
1575  {
1576  address++;
1577  mask = 1U << (address % NVM_PAGES_PER_WEAR_HISTORY);
1578  }
1579 
1580  /* Check for wear page. */
1581  if (nvmPageTypeWear == NVM_PageDescriptorGet(address).pageType)
1582  {
1583  /* Flip bit. */
1584  nvmStaticWearWriteHistory[address / NVM_PAGES_PER_WEAR_HISTORY] |= mask;
1585  /* Record flip. */
1586  nvmStaticWearWritesInHistory++;
1587  }
1588  else
1589  {
1590  /* Must release write lock, run the write function to move the data,
1591  * then acquire lock again. */
1592 
1593  /* Give up write lock and open for other API operations. */
1594  NVM_RELEASE_WRITE_LOCK
1595 
1596  NVM_Write(address, NVM_WRITE_NONE_CMD);
1597 
1598  /* Require write lock to continue. */
1599  NVM_ACQUIRE_WRITE_LOCK
1600  }
1601  }
1602  nvmStaticWearWorking = false;
1603  }
1604 
1605  return ECODE_EMDRV_NVM_OK;
1606 }
1607 #endif
1608 
1612 /******** THE REST OF THE FILE IS DOCUMENTATION ONLY !**********************/
void NVMHAL_Read(uint8_t *pAddress, void *pObject, uint16_t len)
Read data from NVM.
Definition: nvm_hal.c:168
#define NVM_WRITE_ALL_CMD
Definition: nvm.h:55
void NVMHAL_Checksum(uint16_t *checksum, void *pMemory, uint16_t len)
Calculate checksum according to CCITT CRC16.
Definition: nvm_hal.c:324
Ecode_t NVM_Read(uint16_t pageId, uint8_t objectId)
Read an object or an entire page.
Definition: nvm.c:834
#define NVM_ERASE_RETAINCOUNT
Definition: nvm.h:62
Ecode_t NVMHAL_Write(uint8_t *pAddress, void const *pObject, uint16_t len)
Write data to NVM.
Definition: nvm_hal.c:207
#define ECODE_EMDRV_NVM_NO_PAGES_AVAILABLE
Initialization didn't find any pages available to allocate.
Definition: nvm.h:50
Ecode_t NVMHAL_PageErase(uint8_t *pAddress)
Erase a page in the NVM.
Definition: nvm_hal.c:294
Ecode_t NVM_Erase(uint32_t eraseCount)
Erase the entire allocated NVM area.
Definition: nvm.c:382
Non-Volatile Memory Wear-Leveling driver API.
Ecode_t NVM_Init(NVM_Config_t const *config)
Initialize the NVM manager.
Definition: nvm.c:185
Ecode_t NVM_Write(uint16_t pageId, uint8_t objectId)
Write an object or a page.
Definition: nvm.c:460
#define ECODE_EMDRV_NVM_PAGE_INVALID
Could not find the page specified.
Definition: nvm.h:51
void NVMHAL_Init(void)
Initialize NVM driver.
Definition: nvm_hal.c:127
uint32_t Ecode_t
Typedef for API function error code return values.
Definition: ecode.h:51
#define ECODE_EMDRV_NVM_ERROR
General error.
Definition: nvm.h:52
#define NVM_READ_ALL_CMD
Definition: nvm.h:59
#define ECODE_EMDRV_NVM_OK
Success return value.
Definition: nvm.h:45
#define NVM_WRITE_NONE_CMD
Definition: nvm.h:57
#define ECODE_EMDRV_NVM_DATA_INVALID
Invalid input data or format.
Definition: nvm.h:48