# 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 }