nvm_hal.c

Go to the documentation of this file.
00001 /***************************************************************************/
00035 #include <stdbool.h>
00036 #include "em_msc.h"
00037 #include "nvm.h"
00038 #include "nvm_hal.h"
00039 
00040 /*******************************************************************************
00041  ******************************   CONSTANTS   **********************************
00042  ******************************************************************************/
00043 
00046 /* Magic numbers. */
00047 #define NVMHAL_FFFFFFFF      0xffffffffUL
00048 
00049 #if (NVMHAL_DMAREAD == true)
00050 /* DMA related defines. */
00051 #define NVMHAL_DMA_CHANNELS         1
00052 #define NVMHAL_DMA_CHANNEL_FLASH    0
00053 
00054 /* DMA control block, must be aligned to 256. */
00055 #if defined (__ICCARM__)
00056 #pragma data_alignment=256
00057 static DMA_DESCRIPTOR_TypeDef NVMHAL_dmaControlBlock[DMA_CHAN_COUNT * 2];
00058 #elif defined (__CC_ARM)
00059 static DMA_DESCRIPTOR_TypeDef NVMHAL_dmaControlBlock[DMA_CHAN_COUNT * 2] __attribute__ ((aligned(256)));
00060 #elif defined (__GNUC__)
00061 static DMA_DESCRIPTOR_TypeDef NVMHAL_dmaControlBlock[DMA_CHAN_COUNT * 2] __attribute__ ((aligned(256)));
00062 #else
00063 #error Undefined toolkit, need to define alignment
00064 #endif
00065 
00066 #endif
00067 
00068 #if (NVMHAL_SLEEP == true || NVMHAL_DMAREAD == true)
00069 /* Transfer Flag. */
00070 static volatile bool NVMHAL_FlashTransferActive;
00071 #endif
00072 
00075 /*******************************************************************************
00076  ***************************   LOCAL FUNCTIONS   *******************************
00077  ******************************************************************************/
00078 
00079 
00080 /***************************************************************************/
00087 #if (NVMHAL_SLEEP == true)
00088 #ifdef __CC_ARM  /* MDK-ARM compiler */
00089 static msc_Return_TypeDef NVMHAL_MSC_WriteWord(uint32_t *address, void const *data, uint32_t numBytes);
00090 static msc_Return_TypeDef NVMHAL_MSC_ErasePage(uint32_t *startAddress);
00091 #endif /* __CC_ARM */
00092 
00093 #ifdef __ICCARM__ /* IAR compiler */
00094 __ramfunc static msc_Return_TypeDef NVMHAL_MSC_WriteWord(uint32_t *address, void const *data, uint32_t numBytes);
00095 __ramfunc static msc_Return_TypeDef NVMHAL_MSC_ErasePage(uint32_t *startAddress);
00096 #endif /* __ICCARM__ */
00097 
00098 #ifdef __GNUC__  /* GCC based compilers */
00099 #ifdef __CROSSWORKS_ARM  /* Rowley Crossworks */
00100 static msc_Return_TypeDef NVMHAL_MSC_WriteWord(uint32_t *address, void const *data, uint32_t numBytes) __attribute__ ((section(".fast")));
00101 static msc_Return_TypeDef NVMHAL_MSC_ErasePage(uint32_t *startAddress) __attribute__ ((section(".fast")));
00102 #else /* Sourcery G++ */
00103 static msc_Return_TypeDef NVMHAL_MSC_WriteWord(uint32_t *address, void const *data, uint32_t numBytes) __attribute__ ((section(".ram")));
00104 static msc_Return_TypeDef NVMHAL_MSC_ErasePage(uint32_t *startAddress) __attribute__ ((section(".ram")));
00105 #endif /* __GNUC__ */
00106 #endif /* __CROSSWORKS_ARM */
00107 #endif
00108 
00113 #if (NVMHAL_DMAREAD == true)
00114 /**************************************************************************/
00117 static void NVM_ReadFromFlashComplete(unsigned int channel, bool primary, void *user)
00118 {
00119   /* Clearing flag to indicate that transfer is complete */
00120   NVMHAL_FlashTransferActive = false;
00121 }
00122 #endif
00123 
00124 #if (NVMHAL_SLEEP == true)
00125 /**************************************************************************/
00128 void MSC_IRQHandler(void)
00129 {
00130   /* Clear interrupt source */
00131   MSC_IntClear(MSC_IFC_ERASE);
00132   MSC_IntClear(MSC_IFC_WRITE);
00133 
00134   NVMHAL_FlashTransferActive = false;
00135 }
00136 #endif
00137 
00138 #if (NVMHAL_SLEEP_WRITE == true)
00139 
00140 /***************************************************************************/
00177 #ifdef __CC_ARM  /* MDK-ARM compiler */
00178 #pragma arm section code="ram_code"
00179 #endif /* __CC_ARM */
00180 static msc_Return_TypeDef NVMHAL_MSC_WriteWord(uint32_t *address, void const *data, uint32_t numBytes)
00181 {
00182   uint32_t timeOut;
00183   uint32_t wordCount;
00184   uint32_t numWords;
00185 
00186   /* Enable writing to the MSC */
00187   MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
00188 
00189   /* Convert bytes to words */
00190   numWords = numBytes >> 2;
00191 
00192   for (wordCount = 0; wordCount < numWords; wordCount++)
00193   {
00194     /* Load address */
00195     MSC->ADDRB    = (uint32_t)(address + wordCount);
00196     MSC->WRITECMD = MSC_WRITECMD_LADDRIM;
00197 
00198     /* Check for invalid address */
00199     if (MSC->STATUS & MSC_STATUS_INVADDR)
00200     {
00201       /* Disable writing to the MSC */
00202       MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00203       return mscReturnInvalidAddr;
00204     }
00205 
00206     /* Check for write protected page */
00207     if (MSC->STATUS & MSC_STATUS_LOCKED)
00208     {
00209       /* Disable writing to the MSC */
00210       MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00211       return mscReturnLocked;
00212     }
00213 
00214     /* Wait for the MSC to be ready for a new data word */
00215     /* Due to the timing of this function, the MSC should already by ready */
00216     timeOut = MSC_PROGRAM_TIMEOUT;
00217     while (((MSC->STATUS & MSC_STATUS_WDATAREADY) == 0) && (timeOut != 0))
00218     {
00219       timeOut--;
00220     }
00221 
00222     /* Check for timeout */
00223     if (timeOut == 0)
00224     {
00225       /* Disable writing to the MSC */
00226       MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00227       return mscReturnTimeOut;
00228     }
00229 
00230     /* Load data into write data register */
00231     MSC->WDATA = *(((uint32_t *) data) + wordCount);
00232 
00233     /* Set up interrupt. */
00234     MSC->IFC                                = MSC_IEN_WRITE;
00235     MSC->IEN                               |= MSC_IEN_WRITE;
00236     NVIC->ISER[((uint32_t)(MSC_IRQn) >> 5)] = (1 << ((uint32_t)(MSC_IRQn) & 0x1F));
00237 
00238     /* Set active flag. */
00239     NVMHAL_FlashTransferActive = true;
00240 
00241     /* Trigger write once. */
00242     MSC->WRITECMD = MSC_WRITECMD_WRITEONCE;
00243 
00244     /* Go to sleep and wait in a loop for the write operation to finish. Here
00245      * it is necessary to turn on and off interrupts in an interesting way to
00246      * avoid certain race conditions that might happen if the operation finishes
00247      * between the while is evaluated and the MCU is put in EM1.
00248      *
00249      * Short low level calls are used since this is a RAM function and we are
00250      * not allowed to call external functions. The functions we need are marked
00251      * as inline, but if the optimizer is turned off these are not inlined and
00252      * stuff hardfaults. */
00253 
00254     /* Turn off interrupts, so that we cannot get an interrupt during the while
00255      * evaluation. */
00256     INT_Disable();
00257     /* Wait for the write to complete */
00258     while ((MSC->STATUS & MSC_STATUS_BUSY) && NVMHAL_FlashTransferActive)
00259     {
00260       /* Just enter Cortex-M3 sleep mode. If there has already been an interrupt
00261        * we will wake up again immediately. */
00262       SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
00263       __WFI();
00264       /* Enable interrupts again to run interrupt functions. */
00265       INT_Enable();
00266       /* Return to disabled before re-evaluating the while condition. */
00267       INT_Disable();
00268     }
00269     /* Re-enable interrupts. */
00270     INT_Enable();
00271 
00272     /* We might have passed through the loop due to the status value, so reset
00273      * the flag here in case it hasn't been done yet. */
00274     NVMHAL_FlashTransferActive = false;
00275 
00276     /* Clear interrupt. */
00277     MSC->IFC = MSC_IEN_WRITE;
00278   }
00279 
00280   /* Disable writing to the MSC */
00281   MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00282   return mscReturnOk;
00283 }
00284 #ifdef __CC_ARM  /* MDK-ARM compiler */
00285 #pragma arm section code
00286 #endif /* __CC_ARM */
00287 
00288 #endif
00289 
00290 #if (NVMHAL_SLEEP == true)
00291 /***************************************************************************/
00322 #ifdef __CC_ARM  /* MDK-ARM compiler */
00323 #pragma arm section code="ram_code"
00324 #endif /* __CC_ARM */
00325 static msc_Return_TypeDef NVMHAL_MSC_ErasePage(uint32_t *startAddress)
00326 {
00327   /* Enable writing to the MSC */
00328   MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
00329 
00330   /* Load address */
00331   MSC->ADDRB    = (uint32_t) startAddress;
00332   MSC->WRITECMD = MSC_WRITECMD_LADDRIM;
00333 
00334   /* Check for invalid address */
00335   if (MSC->STATUS & MSC_STATUS_INVADDR)
00336   {
00337     /* Disable writing to the MSC */
00338     MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00339     return mscReturnInvalidAddr;
00340   }
00341 
00342   /* Check for write protected page */
00343   if (MSC->STATUS & MSC_STATUS_LOCKED)
00344   {
00345     /* Disable writing to the MSC */
00346     MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00347     return mscReturnLocked;
00348   }
00349 
00350   /* Set up interrupt. */
00351   MSC->IFC                                = MSC_IEN_ERASE;
00352   MSC->IEN                               |= MSC_IEN_ERASE;
00353   NVIC->ISER[((uint32_t)(MSC_IRQn) >> 5)] = (1 << ((uint32_t)(MSC_IRQn) & 0x1F));
00354 
00355   NVMHAL_FlashTransferActive = true;
00356 
00357   /* Send erase page command */
00358   MSC->WRITECMD = MSC_WRITECMD_ERASEPAGE;
00359 
00360   INT_Disable();
00361   /* Wait for the erase to complete */
00362   while ((MSC->STATUS & MSC_STATUS_BUSY) && NVMHAL_FlashTransferActive)
00363   {
00364     /* Just enter Cortex-M3 sleep mode */
00365     SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
00366     __WFI();
00367     INT_Enable();
00368     INT_Disable();
00369   }
00370   INT_Enable();
00371 
00372   NVMHAL_FlashTransferActive = false;
00373 
00374   /* Clear interrupt. */
00375   MSC->IFC = MSC_IEN_ERASE;
00376 
00377   /* Disable writing to the MSC */
00378   MSC->WRITECTRL &= ~MSC_WRITECTRL_WREN;
00379   return mscReturnOk;
00380 }
00381 #ifdef __CC_ARM  /* MDK-ARM compiler */
00382 #pragma arm section code
00383 #endif /* __CC_ARM */
00384 
00385 #endif
00386 
00389 /***************************************************************************/
00403 static NVM_Result_t NVMHAL_ReturnTypeConvert(msc_Return_TypeDef result)
00404 {
00405   /* Direct return from switch gives smallest code size (smaller than if-else).
00406    * Could try using an offset value to translate. */
00407   switch (result)
00408   {
00409   case mscReturnOk:
00410     return nvmResultOk;
00411   case mscReturnInvalidAddr:
00412     return nvmResultAddrInvalid;
00413   case mscReturnUnaligned:
00414     return nvmResultInputInvalid;
00415   default:
00416     return nvmResultError;
00417   }
00418 }
00419 
00420 /*******************************************************************************
00421  **************************   GLOBAL FUNCTIONS   *******************************
00422  ******************************************************************************/
00423 
00424 /***************************************************************************/
00433 void NVMHAL_Init(void)
00434 {
00435   MSC_Init();
00436 
00437 #if (NVMHAL_DMAREAD == true)
00438   /* Enable the DMA clock */
00439   CMU_ClockEnable(cmuClock_DMA, true);
00440 
00441   /* Initialize DMA */
00442   DMA_Init_TypeDef dmaInit;
00443   dmaInit.hprot        = 0;
00444   dmaInit.controlBlock = NVMHAL_dmaControlBlock;
00445   DMA_Init(&dmaInit);
00446 #endif
00447 }
00448 
00449 /***************************************************************************/
00457 void NVMHAL_DeInit(void)
00458 {
00459   MSC_Deinit();
00460 }
00461 
00462 /***************************************************************************/
00483 void NVMHAL_Read(uint8_t *pAddress, void *pObject, uint16_t len)
00484 {
00485 #if (NVMHAL_DMAREAD == true)
00486   /* Setting call-back function */
00487   DMA_CB_TypeDef cb[DMA_CHAN_COUNT];
00488   cb[NVMHAL_DMA_CHANNEL_FLASH].cbFunc = NVM_ReadFromFlashComplete;
00489 
00490   /* usrPtr can be used to send data to the callback function,
00491    * but this is not used here, which is indicated by the NULL pointer */
00492   cb[NVMHAL_DMA_CHANNEL_FLASH].userPtr = NULL;
00493 
00494   /* Setting up channel */
00495   DMA_CfgChannel_TypeDef chnlCfg;
00496   chnlCfg.highPri   = false;
00497   chnlCfg.enableInt = true;
00498   chnlCfg.select    = 0;
00499   chnlCfg.cb        = &(cb[NVMHAL_DMA_CHANNEL_FLASH]);
00500   DMA_CfgChannel(NVMHAL_DMA_CHANNEL_FLASH, &chnlCfg);
00501 
00502   /* Setting up transfer descriptor */
00503   DMA_CfgDescr_TypeDef descrCfg;
00504   descrCfg.dstInc  = dmaDataInc1;
00505   descrCfg.srcInc  = dmaDataInc1;
00506   descrCfg.size    = dmaDataSize1;
00507   descrCfg.arbRate = dmaArbitrate1;
00508   descrCfg.hprot   = 0;
00509   DMA_CfgDescr(NVMHAL_DMA_CHANNEL_FLASH, true, &descrCfg);
00510 
00511   /* Setting flag to indicate that transfer is in progress
00512    * will be cleared by call-back function */
00513   NVMHAL_FlashTransferActive = true;
00514 
00515   /* Starting the transfer. Using Auto (all data is transfered at once) */
00516   DMA_ActivateAuto(NVMHAL_DMA_CHANNEL_FLASH,
00517                    true,
00518                    pObject,
00519                    pAddress,
00520                    len - 1);
00521 
00522   /* Entering EM1 to wait for completion (the DMA requires EM1) */
00523   INT_Disable();
00524   while (NVMHAL_FlashTransferActive)
00525   {
00526     EMU_EnterEM1();
00527     INT_Enable();
00528     INT_Disable();
00529   }
00530   INT_Enable();
00531 #else
00532   /* Create a pointer to the void* pBuffer with type for easy movement. */
00533   uint8_t *pObjectInt = (uint8_t*) pObject;
00534 
00535   while (0 < len) /* While there is more data to fetch. */
00536   {
00537     /* Move the data from memory to the buffer. */
00538     *pObjectInt = *pAddress;
00539     /* Move active location in buffer, */
00540     ++pObjectInt;
00541     /* and in memory. */
00542     ++pAddress;
00543     /* More data is read. */
00544     --len;
00545   }
00546 #endif
00547 
00548 }
00549 
00550 /***************************************************************************/
00574 NVM_Result_t NVMHAL_Write(uint8_t *pAddress, void const *pObject, uint16_t len)
00575 {
00576   /* Used to carry return data. */
00577   msc_Return_TypeDef msc_Return = mscReturnOk;
00578   /* Used as a temporary variable to create the blocks to write when padding to closest word. */
00579   uint32_t           tempWord;
00580 
00581   /* Pointer to let us traverse the given pObject by single bytes. */
00582   uint8_t *pObjectInt = (uint8_t*) pObject;
00583 
00584   /* Temporary variable to cache length of padding needed. */
00585   uint8_t padLen;
00586 
00587 
00588   /* Pad in front. */
00589   padLen = (uint32_t) pAddress % sizeof(tempWord);
00590 
00591   if (padLen != 0)
00592   {
00593     pAddress -= padLen;
00594 
00595     /* Get first word. */
00596     tempWord = *(uint32_t *) pObjectInt;
00597     /* Shift to offset. */
00598     tempWord = tempWord << 8 * padLen;
00599     /* Add nochanging padding. */
00600     tempWord |= NVMHAL_FFFFFFFF >> (8 * (sizeof(tempWord) - padLen));
00601 
00602     /* Special case for unaligned writes smaller than 1 word. */
00603     if (len < sizeof(tempWord) - padLen)
00604     {
00605       /* Add nochanging padding. */
00606       tempWord |= NVMHAL_FFFFFFFF << (8 * (padLen + len));
00607       len       = 0;
00608     }
00609     else
00610     {
00611       len -= sizeof(tempWord) - padLen;
00612     }
00613 
00614 #if (NVMHAL_SLEEP_WRITE == true)
00615     msc_Return = NVMHAL_MSC_WriteWord((uint32_t *) pAddress, &tempWord, sizeof(tempWord));
00616 #else
00617     msc_Return = MSC_WriteWord((uint32_t *) pAddress, &tempWord, sizeof(tempWord));
00618 #endif
00619     pObjectInt += sizeof(tempWord) - padLen;
00620     pAddress   += sizeof(tempWord);
00621   }
00622 
00623   /* Loop over body. */
00624   while ((len >= sizeof(tempWord)) && (mscReturnOk == msc_Return))
00625   {
00626 #if (NVMHAL_SLEEP_WRITE == true)
00627     msc_Return = NVMHAL_MSC_WriteWord((uint32_t *) pAddress, pObjectInt, sizeof(tempWord));
00628 #else
00629     msc_Return = MSC_WriteWord((uint32_t *) pAddress, pObjectInt, sizeof(tempWord));
00630 #endif
00631     pAddress   += sizeof(tempWord);
00632     pObjectInt += sizeof(tempWord);
00633     len        -= sizeof(tempWord);
00634   }
00635 
00636   /* Pad in back. */
00637   if ((len > 0) && (mscReturnOk == msc_Return))
00638   {
00639     /* Get final word. */
00640     tempWord = *(uint32_t *) pObjectInt;
00641     /* Fill rest of word with padding. */
00642     tempWord |= NVMHAL_FFFFFFFF << (8 * len);
00643 
00644 #if (NVMHAL_SLEEP_WRITE == true)
00645     msc_Return = NVMHAL_MSC_WriteWord((uint32_t *) pAddress, &tempWord, sizeof(tempWord));
00646 #else
00647     msc_Return = MSC_WriteWord((uint32_t *) pAddress, &tempWord, sizeof(tempWord));
00648 #endif
00649   }
00650 
00651   /* Convert between return types, and return. */
00652   return NVMHAL_ReturnTypeConvert(msc_Return);
00653 }
00654 
00655 /***************************************************************************/
00670 #if (NVMHAL_SLEEP == true)
00671 NVM_Result_t NVMHAL_PageErase(uint8_t *pAddress)
00672 {
00673   /* Call underlying library and convert between return types, and return. */
00674   return NVMHAL_ReturnTypeConvert(NVMHAL_MSC_ErasePage((uint32_t *) pAddress));
00675 }
00676 
00677 #else
00678 NVM_Result_t NVMHAL_PageErase(uint8_t *pAddress)
00679 {
00680   /* Call underlying library and convert between return types, and return. */
00681   return NVMHAL_ReturnTypeConvert(MSC_ErasePage((uint32_t *) pAddress));
00682 }
00683 #endif
00684 
00685 /***************************************************************************/
00708 void NVMHAL_Checksum(uint16_t *pChecksum, void *pMemory, uint16_t len)
00709 {
00710   uint8_t *pointer = (uint8_t *) pMemory;
00711   uint16_t crc = *pChecksum;
00712 
00713   while(len--)
00714   {
00715     crc = (crc >> 8) | (crc << 8);
00716     crc ^= *pointer++;
00717     crc ^= (crc & 0xf0) >> 4;
00718     crc ^= (crc & 0x0f) << 12;
00719     crc ^= (crc & 0xff) << 5;
00720   }
00721 
00722   *pChecksum = crc;
00723 }