EFM32 Happy Gecko Software Documentation  efm32hg-doc-5.1.2
nvm_hal.c
Go to the documentation of this file.
1 /***************************************************************************/
16 #include <stdbool.h>
17 #include "em_msc.h"
18 #include "nvm.h"
19 #include "nvm_hal.h"
20 
21 
22 /*******************************************************************************
23  ****************************** CONSTANTS **********************************
24  ******************************************************************************/
25 
28 /* Padding value */
29 #define NVMHAL_FFFFFFFF 0xffffffffUL
30 
34 /*******************************************************************************
35  *************************** LOCAL FUNCTIONS *******************************
36  ******************************************************************************/
37 
41 /**************************************************************************/
46 static uint32_t readUnalignedWord(volatile uint8_t *addr)
47 {
48  /* Check if the unaligned access trap is enabled. */
49  if (SCB->CCR & SCB_CCR_UNALIGN_TRP_Msk)
50  {
51  /* Read word as bytes (always aligned).
52  *
53  * Note that gcc with -O3 will optimize this into a single ldr instruction
54  * because it knows that the Cortex-M3 is able to load unaligned words.
55  * This will fail on the Cortex-M3 if the SCB_CCR_UNALIGNE_TRP is set.
56  * There are two ways to get gcc to issue byte loads instead of word loads.
57  *
58  * 1. Specify addr to be volatile
59  * 2. Specify -mno-unaligned-access as an option to gcc
60  *
61  * We choose to implement #1 because it does not depend on modifying build
62  * scripts.
63  */
64  uint8_t b0 = *addr++;
65  uint8_t b1 = *addr++;
66  uint8_t b2 = *addr++;
67  uint8_t b3 = *addr++;
68  return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
69  }
70  else
71  {
72  /* Use unaligned access */
73  return *(uint32_t *)addr;
74  }
75 }
76 
77 
78 /***************************************************************************/
92 static Ecode_t returnTypeConvert(MSC_Status_TypeDef result)
93 {
94  /* Direct return from switch gives smallest code size (smaller than if-else).
95  * Could try using an offset value to translate. */
96  switch (result)
97  {
98  case mscReturnOk:
99  return ECODE_EMDRV_NVM_OK;
100 
103 
104  case mscReturnUnaligned:
106 
107  default:
108  return ECODE_EMDRV_NVM_ERROR;
109  }
110 }
111 
114 /*******************************************************************************
115  ************************** GLOBAL FUNCTIONS *******************************
116  ******************************************************************************/
117 
118 /***************************************************************************/
127 void NVMHAL_Init(void)
128 {
129  MSC_Init();
130 }
131 
132 
133 /***************************************************************************/
141 void NVMHAL_DeInit(void)
142 {
143  MSC_Deinit();
144 }
145 
146 
147 /***************************************************************************/
168 void NVMHAL_Read(uint8_t *pAddress, void *pObject, uint16_t len)
169 {
170  /* Create a pointer to the void* pBuffer with type for easy movement. */
171  uint8_t *pObjectInt = (uint8_t*) pObject;
172 
173  while (0 < len) /* While there is more data to fetch. */
174  {
175  /* Move the data from memory to the buffer. */
176  *pObjectInt = *pAddress;
177  /* Move active location in buffer, */
178  ++pObjectInt;
179  /* and in memory. */
180  ++pAddress;
181  /* More data is read. */
182  --len;
183  }
184 }
185 
186 
187 /***************************************************************************/
207 Ecode_t NVMHAL_Write(uint8_t *pAddress, void const *pObject, uint16_t len)
208 {
209  /* Used to carry return data. */
210  MSC_Status_TypeDef mscStatus = mscReturnOk;
211  /* Used as a temporary variable to create the blocks to write when padding to closest word. */
212  uint32_t tempWord;
213 
214  /* Pointer to let us traverse the given pObject by single bytes. */
215  uint8_t *pObjectInt = (uint8_t*)pObject;
216 
217  /* Temporary variable to cache length of padding needed. */
218  uint8_t padLen;
219 
220  /* Get length of pad in front. */
221  padLen = (uint32_t) pAddress % sizeof(tempWord);
222 
223  if (padLen != 0)
224  {
225  /* Get first word. */
226  tempWord = readUnalignedWord((uint8_t *)pObject);
227 
228  /* Shift to offset. */
229  tempWord = tempWord << 8 * padLen;
230  /* Add nochanging padding. */
231  tempWord |= NVMHAL_FFFFFFFF >> (8 * (sizeof(tempWord) - padLen));
232 
233  /* Special case for unaligned writes smaller than 1 word. */
234  if (len < sizeof(tempWord) - padLen)
235  {
236  /* Add nochanging padding. */
237  tempWord |= NVMHAL_FFFFFFFF << (8 * (padLen + len));
238  len = 0;
239  }
240  else
241  {
242  len -= sizeof(tempWord) - padLen;
243  }
244 
245  /* Align the NVM write address */
246  pAddress -= padLen;
247  mscStatus = MSC_WriteWord((uint32_t *) pAddress, &tempWord, sizeof(tempWord));
248 
249  pObjectInt += sizeof(tempWord) - padLen;
250  pAddress += sizeof(tempWord);
251  }
252 
253 
254  /* Loop over body. */
255  while ((len >= sizeof(tempWord)) && (mscReturnOk == mscStatus))
256  {
257  tempWord = readUnalignedWord((uint8_t *)pObjectInt);
258  mscStatus = MSC_WriteWord((uint32_t *) pAddress, &tempWord, sizeof(tempWord));
259 
260  pAddress += sizeof(tempWord);
261  pObjectInt += sizeof(tempWord);
262  len -= sizeof(tempWord);
263  }
264 
265  /* Pad at the end */
266  if ((len > 0) && (mscReturnOk == mscStatus))
267  {
268  /* Get final word. */
269  tempWord = readUnalignedWord((uint8_t *)pObjectInt);
270 
271  /* Fill rest of word with padding. */
272  tempWord |= NVMHAL_FFFFFFFF << (8 * len);
273  mscStatus = MSC_WriteWord((uint32_t *) pAddress, &tempWord, sizeof(tempWord));
274  }
275 
276  /* Convert between return types, and return. */
277  return returnTypeConvert(mscStatus);
278 }
279 
280 
281 /***************************************************************************/
294 Ecode_t NVMHAL_PageErase(uint8_t *pAddress)
295 {
296  /* Call underlying library and convert between return types, and return. */
297  return returnTypeConvert(MSC_ErasePage((uint32_t *) pAddress));
298 }
299 
300 
301 /***************************************************************************/
324 void NVMHAL_Checksum(uint16_t *pChecksum, void *pMemory, uint16_t len)
325 {
326  uint8_t *pointer = (uint8_t *) pMemory;
327  uint16_t crc = *pChecksum;
328 
329  while(len--)
330  {
331  crc = (crc >> 8) | (crc << 8);
332  crc ^= *pointer++;
333  crc ^= (crc & 0xf0) >> 4;
334  crc ^= (crc & 0x0f) << 12;
335  crc ^= (crc & 0xff) << 5;
336  }
337  *pChecksum = crc;
338 }
void NVMHAL_Read(uint8_t *pAddress, void *pObject, uint16_t len)
Read data from NVM.
Definition: nvm_hal.c:168
void NVMHAL_Init(void)
Initialize NVM driver.
Definition: nvm_hal.c:127
Ecode_t NVMHAL_PageErase(uint8_t *pAddress)
Erase a page in the NVM.
Definition: nvm_hal.c:294
void MSC_Init(void)
Enables the flash controller for writing.
Definition: em_msc.c:157
void MSC_Deinit(void)
Disables the flash controller for writing.
Definition: em_msc.c:218
Non-Volatile Memory Wear-Leveling driver API.
Flash controller (MSC) peripheral API.
uint32_t Ecode_t
Typedef for API function error code return values.
Definition: ecode.h:51
void NVMHAL_Checksum(uint16_t *pChecksum, void *pMemory, uint16_t len)
Calculate checksum according to CCITT CRC16.
Definition: nvm_hal.c:324
#define ECODE_EMDRV_NVM_ERROR
General error.
Definition: nvm.h:52
MSC_RAMFUNC_DECLARATOR MSC_Status_TypeDef MSC_WriteWord(uint32_t *address, void const *data, uint32_t numBytes)
Writes data to flash memory. This function is interrupt safe, but slower than MSC_WriteWordFast(), which writes to flash with interrupts disabled. Write data must be aligned to words and contain a number of bytes that is divisable by four.
Definition: em_msc.c:839
void NVMHAL_DeInit(void)
De-initialize NVM .
Definition: nvm_hal.c:141
#define ECODE_EMDRV_NVM_ADDR_INVALID
Invalid address.
Definition: nvm.h:46
MSC_Status_TypeDef
Definition: em_msc.h:133
#define ECODE_EMDRV_NVM_OK
Success return value.
Definition: nvm.h:45
Non-Volatile Memory Wear-Leveling driver HAL.
#define ECODE_EMDRV_NVM_ALIGNMENT_INVALID
Invalid data alignment.
Definition: nvm.h:47
MSC_RAMFUNC_DECLARATOR MSC_Status_TypeDef MSC_ErasePage(uint32_t *startAddress)
Erases a page in flash memory.
Definition: em_msc.c:748
Ecode_t NVMHAL_Write(uint8_t *pAddress, void const *pObject, uint16_t len)
Write data to NVM.
Definition: nvm_hal.c:207