nvm_hal.c

Go to the documentation of this file.
00001 /***************************************************************************/
00016 #include <stdbool.h>
00017 #include "em_msc.h"
00018 #include "nvm.h"
00019 #include "nvm_hal.h"
00020 
00021 /*******************************************************************************
00022  ******************************   CONSTANTS   **********************************
00023  ******************************************************************************/
00024 
00027 /* Padding value */
00028 #define NVMHAL_FFFFFFFF      0xffffffffUL
00029 
00030 #if (NVMHAL_DMAREAD == true)
00031 /* DMA related defines. */
00032 #define NVMHAL_DMA_CHANNELS         1
00033 #define NVMHAL_DMA_CHANNEL_FLASH    0
00034 
00035 /* DMA control block, must be aligned to 256. */
00036 #if defined (__ICCARM__)
00037 #pragma data_alignment=256
00038 static DMA_DESCRIPTOR_TypeDef NVMHAL_dmaControlBlock[DMA_CHAN_COUNT * 2];
00039 #elif defined (__CC_ARM)
00040 static DMA_DESCRIPTOR_TypeDef NVMHAL_dmaControlBlock[DMA_CHAN_COUNT * 2] __attribute__ ((aligned(256)));
00041 #elif defined (__GNUC__)
00042 static DMA_DESCRIPTOR_TypeDef NVMHAL_dmaControlBlock[DMA_CHAN_COUNT * 2] __attribute__ ((aligned(256)));
00043 #else
00044 #error Undefined toolkit, need to define alignment
00045 #endif
00046 
00047 #endif
00048 
00049 #if (NVMHAL_SLEEP == true || NVMHAL_DMAREAD == true)
00050 /* Transfer Flag. */
00051 static volatile bool NVMHAL_FlashTransferActive;
00052 #endif
00053 
00056 /*******************************************************************************
00057  ***************************   LOCAL FUNCTIONS   *******************************
00058  ******************************************************************************/
00059 
00060 
00061 /***************************************************************************/
00068 #if (NVMHAL_SLEEP == true)
00069 #ifdef __CC_ARM  /* MDK-ARM compiler */
00070 static msc_Return_TypeDef NVMHAL_MSC_WriteWord(uint32_t *address, void const *data, uint32_t numBytes);
00071 static msc_Return_TypeDef NVMHAL_MSC_ErasePage(uint32_t *startAddress);
00072 #endif /* __CC_ARM */
00073 
00074 #ifdef __ICCARM__ /* IAR compiler */
00075 __ramfunc static msc_Return_TypeDef NVMHAL_MSC_WriteWord(uint32_t *address, void const *data, uint32_t numBytes);
00076 __ramfunc static msc_Return_TypeDef NVMHAL_MSC_ErasePage(uint32_t *startAddress);
00077 #endif /* __ICCARM__ */
00078 
00079 #ifdef __GNUC__  /* GCC based compilers */
00080 #ifdef __CROSSWORKS_ARM  /* Rowley Crossworks (GCC based) */
00081 static msc_Return_TypeDef NVMHAL_MSC_WriteWord(uint32_t *address, void const *data, uint32_t numBytes) __attribute__ ((section(".fast")));
00082 static msc_Return_TypeDef NVMHAL_MSC_ErasePage(uint32_t *startAddress) __attribute__ ((section(".fast")));
00083 #else /* GCC */
00084 static msc_Return_TypeDef NVMHAL_MSC_WriteWord(uint32_t *address, void const *data, uint32_t numBytes) __attribute__ ((section(".ram")));
00085 static msc_Return_TypeDef NVMHAL_MSC_ErasePage(uint32_t *startAddress) __attribute__ ((section(".ram")));
00086 #endif /* __GNUC__ */
00087 #endif /* __CROSSWORKS_ARM */
00088 #endif
00089 
00094 #if (NVMHAL_DMAREAD == true)
00095 /**************************************************************************/
00098 static void NVM_ReadFromFlashComplete(unsigned int channel, bool primary, void *user)
00099 {
00100   /* Clearing flag to indicate that transfer is complete */
00101   NVMHAL_FlashTransferActive = false;
00102 }
00103 #endif
00104 
00105 #if (NVMHAL_SLEEP == true)
00106 /**************************************************************************/
00109 void MSC_IRQHandler(void)
00110 {
00111   /* Clear interrupt source */
00112   MSC_IntClear(MSC_IFC_ERASE);
00113   MSC_IntClear(MSC_IFC_WRITE);
00114 
00115   NVMHAL_FlashTransferActive = false;
00116 }
00117 #endif
00118 
00119 #if (NVMHAL_SLEEP_WRITE == true)
00120 
00121 /***************************************************************************/
00158 #ifdef __CC_ARM  /* MDK-ARM compiler */
00159 #pragma arm section code="ram_code"
00160 #endif /* __CC_ARM */
00161 static msc_Return_TypeDef NVMHAL_MSC_WriteWord(uint32_t *address, void const *data, uint32_t numBytes)
00162 {
00163   uint32_t timeOut;
00164   uint32_t wordCount;
00165   uint32_t numWords;
00166 
00167   /* Enable writing to the MSC */
00168   MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
00169 
00170   /* Convert bytes to words */
00171   numWords = numBytes >> 2;
00172 
00173   for (wordCount = 0; wordCount < numWords; wordCount++)
00174   {
00175     /* Load address */
00176     MSC->ADDRB    = (uint32_t)(address + wordCount);
00177     MSC->WRITECMD = MSC_WRITECMD_LADDRIM;
00178 
00179     /* Check for invalid address */
00180     if (MSC->STATUS & MSC_STATUS_INVADDR)
00181     {
00182       /* Disable writing to the MSC */
00183       MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00184       return mscReturnInvalidAddr;
00185     }
00186 
00187     /* Check for write protected page */
00188     if (MSC->STATUS & MSC_STATUS_LOCKED)
00189     {
00190       /* Disable writing to the MSC */
00191       MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00192       return mscReturnLocked;
00193     }
00194 
00195     /* Wait for the MSC to be ready for a new data word */
00196     /* Due to the timing of this function, the MSC should already by ready */
00197     timeOut = MSC_PROGRAM_TIMEOUT;
00198     while (((MSC->STATUS & MSC_STATUS_WDATAREADY) == 0) && (timeOut != 0))
00199     {
00200       timeOut--;
00201     }
00202 
00203     /* Check for timeout */
00204     if (timeOut == 0)
00205     {
00206       /* Disable writing to the MSC */
00207       MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00208       return mscReturnTimeOut;
00209     }
00210 
00211     /* Load data into write data register */
00212     MSC->WDATA = *(((uint32_t *) data) + wordCount);
00213 
00214     /* Set up interrupt. */
00215     MSC->IFC                                = MSC_IEN_WRITE;
00216     MSC->IEN                               |= MSC_IEN_WRITE;
00217     NVIC->ISER[((uint32_t)(MSC_IRQn) >> 5)] = (1 << ((uint32_t)(MSC_IRQn) & 0x1F));
00218 
00219     /* Set active flag. */
00220     NVMHAL_FlashTransferActive = true;
00221 
00222     /* Trigger write once. */
00223     MSC->WRITECMD = MSC_WRITECMD_WRITEONCE;
00224 
00225     /* Go to sleep and wait in a loop for the write operation to finish. Here
00226      * it is necessary to turn on and off interrupts in an interesting way to
00227      * avoid certain race conditions that might happen if the operation finishes
00228      * between the while is evaluated and the MCU is put in EM1.
00229      *
00230      * Short low level calls are used since this is a RAM function and we are
00231      * not allowed to call external functions. The functions we need are marked
00232      * as inline, but if the optimizer is turned off these are not inlined and
00233      * stuff hardfaults. */
00234 
00235     /* Turn off interrupts, so that we cannot get an interrupt during the while
00236      * evaluation. */
00237     INT_Disable();
00238     /* Wait for the write to complete */
00239     while ((MSC->STATUS & MSC_STATUS_BUSY) && NVMHAL_FlashTransferActive)
00240     {
00241       /* Just enter Cortex-M3 sleep mode. If there has already been an interrupt
00242        * we will wake up again immediately. */
00243       SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
00244       __WFI();
00245       /* Enable interrupts again to run interrupt functions. */
00246       INT_Enable();
00247       /* Return to disabled before re-evaluating the while condition. */
00248       INT_Disable();
00249     }
00250     /* Re-enable interrupts. */
00251     INT_Enable();
00252 
00253     /* We might have passed through the loop due to the status value, so reset
00254      * the flag here in case it hasn't been done yet. */
00255     NVMHAL_FlashTransferActive = false;
00256 
00257     /* Clear interrupt. */
00258     MSC->IFC = MSC_IEN_WRITE;
00259   }
00260 
00261   /* Disable writing to the MSC */
00262   MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00263   return mscReturnOk;
00264 }
00265 #ifdef __CC_ARM  /* MDK-ARM compiler */
00266 #pragma arm section code
00267 #endif /* __CC_ARM */
00268 
00269 #endif
00270 
00271 #if (NVMHAL_SLEEP == true)
00272 /***************************************************************************/
00303 #ifdef __CC_ARM  /* MDK-ARM compiler */
00304 #pragma arm section code="ram_code"
00305 #endif /* __CC_ARM */
00306 static msc_Return_TypeDef NVMHAL_MSC_ErasePage(uint32_t *startAddress)
00307 {
00308   /* Enable writing to the MSC */
00309   MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
00310 
00311   /* Load address */
00312   MSC->ADDRB    = (uint32_t) startAddress;
00313   MSC->WRITECMD = MSC_WRITECMD_LADDRIM;
00314 
00315   /* Check for invalid address */
00316   if (MSC->STATUS & MSC_STATUS_INVADDR)
00317   {
00318     /* Disable writing to the MSC */
00319     MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00320     return mscReturnInvalidAddr;
00321   }
00322 
00323   /* Check for write protected page */
00324   if (MSC->STATUS & MSC_STATUS_LOCKED)
00325   {
00326     /* Disable writing to the MSC */
00327     MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00328     return mscReturnLocked;
00329   }
00330 
00331   /* Set up interrupt. */
00332   MSC->IFC = MSC_IEN_ERASE;
00333   MSC->IEN |= MSC_IEN_ERASE;
00334   NVIC->ISER[((uint32_t)(MSC_IRQn) >> 5)] = (1 << ((uint32_t)(MSC_IRQn) & 0x1F));
00335 
00336   NVMHAL_FlashTransferActive = true;
00337 
00338   /* Send erase page command */
00339   MSC->WRITECMD = MSC_WRITECMD_ERASEPAGE;
00340 
00341   INT_Disable();
00342   /* Wait for the erase to complete */
00343   while ((MSC->STATUS & MSC_STATUS_BUSY) && NVMHAL_FlashTransferActive)
00344   {
00345     /* Just enter Cortex-M3 sleep mode */
00346     SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
00347     __WFI();
00348     INT_Enable();
00349     INT_Disable();
00350   }
00351   INT_Enable();
00352 
00353   NVMHAL_FlashTransferActive = false;
00354 
00355   /* Clear interrupt. */
00356   MSC->IFC = MSC_IEN_ERASE;
00357 
00358   /* Disable writing to the MSC */
00359   MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00360   return mscReturnOk;
00361 }
00362 #ifdef __CC_ARM  /* MDK-ARM compiler */
00363 #pragma arm section code
00364 #endif /* __CC_ARM */
00365 
00366 #endif
00367 
00370 /***************************************************************************/
00384 static Ecode_t NVMHAL_ReturnTypeConvert(msc_Return_TypeDef result)
00385 {
00386   /* Direct return from switch gives smallest code size (smaller than if-else).
00387    * Could try using an offset value to translate. */
00388   switch (result)
00389   {
00390   case mscReturnOk:
00391     return ECODE_EMDRV_NVM_OK;
00392   case mscReturnInvalidAddr:
00393     return ECODE_EMDRV_NVM_ADDR_INVALID;
00394   case mscReturnUnaligned:
00395     return ECODE_EMDRV_NVM_ALIGNMENT_INVALID;
00396   default:
00397     return ECODE_EMDRV_NVM_ERROR;
00398   }
00399 }
00400 
00401 /*******************************************************************************
00402  **************************   GLOBAL FUNCTIONS   *******************************
00403  ******************************************************************************/
00404 
00405 /***************************************************************************/
00414 void NVMHAL_Init(void)
00415 {
00416   MSC_Init();
00417 
00418 #if (NVMHAL_DMAREAD == true)
00419   /* Enable the DMA clock */
00420   CMU_ClockEnable(cmuClock_DMA, true);
00421 
00422   /* Initialize DMA */
00423   DMA_Init_TypeDef dmaInit;
00424   dmaInit.hprot        = 0;
00425   dmaInit.controlBlock = NVMHAL_dmaControlBlock;
00426   DMA_Init(&dmaInit);
00427 #endif
00428 }
00429 
00430 /***************************************************************************/
00438 void NVMHAL_DeInit(void)
00439 {
00440   MSC_Deinit();
00441 }
00442 
00443 /***************************************************************************/
00464 void NVMHAL_Read(uint8_t *pAddress, void *pObject, uint16_t len)
00465 {
00466 #if (NVMHAL_DMAREAD == true)
00467   /* Setting call-back function */
00468   DMA_CB_TypeDef cb[DMA_CHAN_COUNT];
00469   cb[NVMHAL_DMA_CHANNEL_FLASH].cbFunc = NVM_ReadFromFlashComplete;
00470 
00471   /* usrPtr can be used to send data to the callback function,
00472    * but this is not used here, which is indicated by the NULL pointer */
00473   cb[NVMHAL_DMA_CHANNEL_FLASH].userPtr = NULL;
00474 
00475   /* Setting up channel */
00476   DMA_CfgChannel_TypeDef chnlCfg;
00477   chnlCfg.highPri   = false;
00478   chnlCfg.enableInt = true;
00479   chnlCfg.select    = 0;
00480   chnlCfg.cb        = &(cb[NVMHAL_DMA_CHANNEL_FLASH]);
00481   DMA_CfgChannel(NVMHAL_DMA_CHANNEL_FLASH, &chnlCfg);
00482 
00483   /* Setting up transfer descriptor */
00484   DMA_CfgDescr_TypeDef descrCfg;
00485   descrCfg.dstInc  = dmaDataInc1;
00486   descrCfg.srcInc  = dmaDataInc1;
00487   descrCfg.size    = dmaDataSize1;
00488   descrCfg.arbRate = dmaArbitrate1;
00489   descrCfg.hprot   = 0;
00490   DMA_CfgDescr(NVMHAL_DMA_CHANNEL_FLASH, true, &descrCfg);
00491 
00492   /* Setting flag to indicate that transfer is in progress
00493    * will be cleared by call-back function */
00494   NVMHAL_FlashTransferActive = true;
00495 
00496   /* Starting the transfer. Using Auto (all data is transfered at once) */
00497   DMA_ActivateAuto(NVMHAL_DMA_CHANNEL_FLASH,
00498                    true,
00499                    pObject,
00500                    pAddress,
00501                    len - 1);
00502 
00503   /* Entering EM1 to wait for completion (the DMA requires EM1) */
00504   INT_Disable();
00505   while (NVMHAL_FlashTransferActive)
00506   {
00507     EMU_EnterEM1();
00508     INT_Enable();
00509     INT_Disable();
00510   }
00511   INT_Enable();
00512 #else
00513   /* Create a pointer to the void* pBuffer with type for easy movement. */
00514   uint8_t *pObjectInt = (uint8_t*) pObject;
00515 
00516   while (0 < len) /* While there is more data to fetch. */
00517   {
00518     /* Move the data from memory to the buffer. */
00519     *pObjectInt = *pAddress;
00520     /* Move active location in buffer, */
00521     ++pObjectInt;
00522     /* and in memory. */
00523     ++pAddress;
00524     /* More data is read. */
00525     --len;
00526   }
00527 #endif
00528 
00529 }
00530 
00531 /***************************************************************************/
00555 Ecode_t NVMHAL_Write(uint8_t *pAddress, void const *pObject, uint16_t len)
00556 {
00557   /* Used to carry return data. */
00558   msc_Return_TypeDef msc_Return = mscReturnOk;
00559   /* Used as a temporary variable to create the blocks to write when padding to closest word. */
00560   uint32_t tempWord;
00561 
00562   /* Pointer to let us traverse the given pObject by single bytes. */
00563   uint8_t *pObjectInt = (uint8_t*) pObject;
00564 
00565   /* Temporary variable to cache length of padding needed. */
00566   uint8_t padLen;
00567 
00568   /* Pad in front. */
00569   padLen = (uint32_t) pAddress % sizeof(tempWord);
00570 
00571   if (padLen != 0)
00572   {
00573     pAddress -= padLen;
00574 
00575     /* Get first word. */
00576     tempWord = *(uint32_t *) pObjectInt;
00577     /* Shift to offset. */
00578     tempWord = tempWord << 8 * padLen;
00579     /* Add nochanging padding. */
00580     tempWord |= NVMHAL_FFFFFFFF >> (8 * (sizeof(tempWord) - padLen));
00581 
00582     /* Special case for unaligned writes smaller than 1 word. */
00583     if (len < sizeof(tempWord) - padLen)
00584     {
00585       /* Add nochanging padding. */
00586       tempWord |= NVMHAL_FFFFFFFF << (8 * (padLen + len));
00587       len       = 0;
00588     }
00589     else
00590     {
00591       len -= sizeof(tempWord) - padLen;
00592     }
00593 
00594 #if (NVMHAL_SLEEP_WRITE == true)
00595     msc_Return = NVMHAL_MSC_WriteWord((uint32_t *) pAddress, &tempWord, sizeof(tempWord));
00596 #else
00597     msc_Return = MSC_WriteWord((uint32_t *) pAddress, &tempWord, sizeof(tempWord));
00598 #endif
00599     pObjectInt += sizeof(tempWord) - padLen;
00600     pAddress   += sizeof(tempWord);
00601   }
00602 
00603   /* Loop over body. */
00604   while ((len >= sizeof(tempWord)) && (mscReturnOk == msc_Return))
00605   {
00606 #if (NVMHAL_SLEEP_WRITE == true)
00607     msc_Return = NVMHAL_MSC_WriteWord((uint32_t *) pAddress, pObjectInt, sizeof(tempWord));
00608 #else
00609     msc_Return = MSC_WriteWord((uint32_t *) pAddress, pObjectInt, sizeof(tempWord));
00610 #endif
00611     pAddress   += sizeof(tempWord);
00612     pObjectInt += sizeof(tempWord);
00613     len        -= sizeof(tempWord);
00614   }
00615 
00616   /* Pad in back. */
00617   if ((len > 0) && (mscReturnOk == msc_Return))
00618   {
00619     /* Get final word. */
00620     tempWord = *(uint32_t *) pObjectInt;
00621     /* Fill rest of word with padding. */
00622     tempWord |= NVMHAL_FFFFFFFF << (8 * len);
00623 
00624 #if (NVMHAL_SLEEP_WRITE == true)
00625     msc_Return = NVMHAL_MSC_WriteWord((uint32_t *) pAddress, &tempWord, sizeof(tempWord));
00626 #else
00627     msc_Return = MSC_WriteWord((uint32_t *) pAddress, &tempWord, sizeof(tempWord));
00628 #endif
00629   }
00630 
00631   /* Convert between return types, and return. */
00632   return NVMHAL_ReturnTypeConvert(msc_Return);
00633 }
00634 
00635 /***************************************************************************/
00650 #if (NVMHAL_SLEEP == true)
00651 Ecode_t NVMHAL_PageErase(uint8_t *pAddress)
00652 {
00653   /* Call underlying library and convert between return types, and return. */
00654   return NVMHAL_ReturnTypeConvert(NVMHAL_MSC_ErasePage((uint32_t *) pAddress));
00655 }
00656 
00657 #else
00658 Ecode_t NVMHAL_PageErase(uint8_t *pAddress)
00659 {
00660   /* Call underlying library and convert between return types, and return. */
00661   return NVMHAL_ReturnTypeConvert(MSC_ErasePage((uint32_t *) pAddress));
00662 }
00663 #endif
00664 
00665 /***************************************************************************/
00688 void NVMHAL_Checksum(uint16_t *pChecksum, void *pMemory, uint16_t len)
00689 {
00690   uint8_t *pointer = (uint8_t *) pMemory;
00691   uint16_t crc = *pChecksum;
00692 
00693   while(len--)
00694   {
00695     crc = (crc >> 8) | (crc << 8);
00696     crc ^= *pointer++;
00697     crc ^= (crc & 0xf0) >> 4;
00698     crc ^= (crc & 0x0f) << 12;
00699     crc ^= (crc & 0xff) << 5;
00700   }
00701 
00702   *pChecksum = crc;
00703 }