Contiki 2.5
nvm.c
Go to the documentation of this file.
1 /** @file hal/micro/cortexm3/nvm.c
2  * @brief Cortex-M3 Non-Volatile Memory data storage system.
3  *
4  * This file implements the NVM data storage system. Refer to nvm.h for
5  * full documentation of how the NVM data storage system works, is configured,
6  * and is accessed.
7  *
8  * <!--(C) COPYRIGHT 2010 STMicroelectronics. All rights reserved. -->
9  */
10 
11 #include PLATFORM_HEADER
12 #include "error.h"
13 
14 #ifdef NVM_RAM_EMULATION
15 
16 static int16u calibrationData[32+2]={
17  0xFFFF, 0xFFFF,
18  0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
19  0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
20  0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
21  0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
22 };
23 
24 int8u halCommonReadFromNvm(void *data, int32u offset, int16u length)
25 {
26  halCommonMemCopy(data, ((int8u *) calibrationData) + offset, length);
27  return ST_SUCCESS;
28 }
29 int8u halCommonWriteToNvm(const void *data, int32u offset, int16u length)
30 {
31  halCommonMemCopy(((int8u *) calibrationData) + offset, data, length);
32  return ST_SUCCESS;
33 }
34 
35 #else
36 
37 //flash.h gives access to halInternalFlashErase and halInternalFlashWrite.
39 //nvm.h includes memmap.h. These two headers define the key parameters:
40 // MFB_PAGE_SIZE_B
41 // MFB_TOP
42 // NVM_LEFT_PAGE
43 // NVM_RIGHT_PAGE
44 // NVM_DATA_SIZE_B
45 // NVM_FLASH_PAGE_COUNT
46 // NVM_MGMT_SIZE_B
47 #include "hal/micro/cortexm3/nvm.h"
48 
49 //Define two variables that hold the actual NVM data storage. LEFT and RIGHT
50 //are not required to be continuous memory blocks so they can be define
51 //separately. The linker is responsible for placing these storage containers
52 //on flash page boundaries.
53 NO_STRIPPING __no_init VAR_AT_SEGMENT (const int8u nvmStorageLeft[NVM_DATA_SIZE_B], __NVM__);
54 NO_STRIPPING __no_init VAR_AT_SEGMENT (const int8u nvmStorageRight[NVM_DATA_SIZE_B], __NVM__);
55 
56 static int8u determineState(void)
57 {
58  int32u leftMgmt = *(int32u *)NVM_LEFT_PAGE;
59  int32u rightMgmt = *(int32u *)NVM_RIGHT_PAGE;
60  int8u state=0;
61 
62  if((leftMgmt==0xFFFF0000) && (rightMgmt==0xFFFFFFFF)) {
63  //State 1 and state 4 use identical mgmt words. The function
64  //determineState() is only called at the start of a NVM read
65  //or write. During a read, state 1 and 4 both read from the
66  //LEFT so there is no reason to make a distinction. During
67  //a write, the system will see the current page as LEFT and
68  //therefore be transitioning from LEFT to RIGHT so state 4 is
69  //correct. State 1 is only required to transition from 0 to 2.
70  state = 4;
71  } else if((leftMgmt==0xFFFF0000) && (rightMgmt==0xFF00FFFF)) {
72  state = 2;
73  } else if((leftMgmt==0xFFFF0000) && (rightMgmt==0xFF000000)) {
74  state = 3;
75  } else if((leftMgmt==0xFFFF0000) && (rightMgmt==0xFFFFFFFF)) {
76  state = 4;
77  } else if((leftMgmt==0xFFFF0000) && (rightMgmt==0xFFFFFF00)) {
78  state = 5;
79  } else if((leftMgmt==0xFF000000) && (rightMgmt==0xFFFFFF00)) {
80  state = 6;
81  } else if((leftMgmt==0xFF000000) && (rightMgmt==0xFFFF0000)) {
82  state = 7;
83  } else if((leftMgmt==0xFFFFFFFF) && (rightMgmt==0xFFFF0000)) {
84  state = 8;
85  } else if((leftMgmt==0xFFFFFF00) && (rightMgmt==0xFFFF0000)) {
86  state = 9;
87  } else if((leftMgmt==0xFFFFFF00) && (rightMgmt==0xFF000000)) {
88  state = 10;
89  } else {
90  //State 0 is used to indicate erased or invalid.
91  state = 0;
92  }
93 
94  return state;
95 }
96 
97 
98 int8u halCommonReadFromNvm(void *data, int32u offset, int16u length)
99 {
100  int16u i;
101  int16u *flash;
102  //Remember: all flash writes are 16bits.
103  int16u *ram = (int16u*)data;
104 
105  //The NVM data storage system cannot function if the LEFT and RIGHT
106  //storage are not aligned to physical flash pages.
107  assert((NVM_LEFT_PAGE%MFB_PAGE_SIZE_B)==0);
108  assert((NVM_RIGHT_PAGE%MFB_PAGE_SIZE_B)==0);
109  //The offset of the NVM data must be 16bit aligned.
110  assert((offset&0x1)==0);
111  //The length of the NVM data must be 16bit aligned.
112  assert((length&0x1)==0);
113 
114  assert(offset+length<NVM_DATA_SIZE_B);
115 
116  //Obtain the data from NVM storage.
117  switch(determineState()) {
118  case 1:
119  case 2:
120  case 3:
121  case 4:
122  case 9:
123  case 10:
124  flash = (int16u *)(NVM_LEFT_PAGE+offset);
125  for(i=0;i<(length/2);i++) {
126  ram[i] = flash[i];
127  }
128  break;
129  case 5:
130  case 6:
131  case 7:
132  case 8:
133  flash = (int16u *)(NVM_RIGHT_PAGE+offset);
134  for(i=0;i<(length/2);i++) {
135  ram[i] = flash[i];
136  }
137  break;
138  case 0:
139  default:
140  //Reading from NVM while the mgmt bytes are in an invalid state
141  //should not return any bytes actually found in flash. Instead,
142  //return nothing but 0xFF. This is legitimate because the next
143  //call to the write function will also find invalid mgmt bytes
144  //and trigger an erasure of NVM, after which the NVM really will
145  //contain just 0xFF for data (plus the new data supplied during
146  //the write call).
147  for(i=0;i<(length/2);i++) {
148  ram[i] = 0xFFFF;
149  }
150  //Inform the calling code. using ST_ERR_FATAL, that there were
151  //invalid mgmt bytes and 0xFF was forcefully returned.
152  return ST_ERR_FATAL;
153  }
154 
155  return ST_SUCCESS;
156 }
157 
158 int16u *halCommonGetAddressFromNvm(int32u offset)
159 {
160  int16u *flash;
161 
162  //The NVM data storage system cannot function if the LEFT and RIGHT
163  //storage are not aligned to physical flash pages.
164  assert((NVM_LEFT_PAGE%MFB_PAGE_SIZE_B)==0);
165  assert((NVM_RIGHT_PAGE%MFB_PAGE_SIZE_B)==0);
166  //The offset of the NVM data must be 16bit aligned.
167  assert((offset&0x1)==0);
168 
169  //Obtain the data from NVM storage.
170  switch(determineState()) {
171  case 1:
172  case 2:
173  case 3:
174  case 4:
175  case 9:
176  case 10:
177  flash = (int16u *)(NVM_LEFT_PAGE+offset);
178  break;
179  case 5:
180  case 6:
181  case 7:
182  case 8:
183  flash = (int16u *)(NVM_RIGHT_PAGE+offset);
184  break;
185  case 0:
186  default:
187  // Flash is in an invalid state
188  // Fix it with a dummy write and then return the flash page left
189  {
190  int16u dummy = 0xFFFF;
191  halCommonWriteToNvm(&dummy, 0, 2);
192  flash = (int16u *)(NVM_LEFT_PAGE+offset);
193  }
194  }
195 
196  return flash;
197 }
198 
199 
200 static int8u erasePage(int32u page)
201 {
202  StStatus status;
203  int32u i, k;
204  int32u address;
205  int8u *flash;
206 
207  //Erasing a LEFT or RIGHT page requires erasing all of the flash pages.
208  //Since the mgmt bytes are stored at the bottom of a page, the flash pages
209  //are erased from the top down ensuring that that mgmt words are the last
210  //data to be erased. This way, if a reset occurs while erasing, the mgmt
211  //words are still valid the next time determineState() is called.
212  for(i=NVM_FLASH_PAGE_COUNT;i>0;i--) {
213  address = (page+((i-1)*MFB_PAGE_SIZE_B));
214  flash = (int8u *)address;
215  //Scan the page to determine if it is fully erased already.
216  //If the flash is not erased, erase it. The purpose of scanning
217  //first is to save a little time if erasing is not required.
218  for(k=0;k<MFB_PAGE_SIZE_B;k++,flash++) {
219  if(*flash != 0xFF) {
220  status = halInternalFlashErase(MFB_PAGE_ERASE, address);
221  if(status != ST_SUCCESS) {
222  return status;
223  }
224  //Don't bother looking at the rest of this flash page and just
225  //move to the next.
226  k=MFB_PAGE_SIZE_B;
227  }
228  }
229  }
230  return ST_SUCCESS;
231 }
232 
233 
234 //This macro is responsible for erasing an NVM page (LEFT or RIGHT).
235 #define ERASE_PAGE(page) \
236  do { \
237  status = erasePage(page); \
238  if(status != ST_SUCCESS) { \
239  return status; \
240  } \
241  } while(0)
242 
243 
244 //This macro is responsible for writing the new data into the destination
245 //page and copying existing data from the source page to the
246 //destination page.
247 #define WRITE_DATA(destPage, srcPage, offset, length) \
248  do { \
249  /*Copy all data below the new data from the srcPage to the destPage*/ \
250  status = halInternalFlashWrite(destPage+NVM_MGMT_SIZE_B, \
251  (int16u *)(srcPage+NVM_MGMT_SIZE_B), \
252  (offset-NVM_MGMT_SIZE_B)/2); \
253  if(status != ST_SUCCESS) { return status; } \
254  /*Write the new data*/ \
255  status = halInternalFlashWrite(destPage+offset, \
256  ram, \
257  (length)/2); \
258  if(status != ST_SUCCESS) { return status; } \
259  /*Copy all data above the new data from the srcPage to the destPage*/ \
260  status = halInternalFlashWrite(destPage+offset+length, \
261  (int16u *)(srcPage+offset+length), \
262  (NVM_DATA_SIZE_B- \
263  length-offset- \
264  NVM_MGMT_SIZE_B)/2); \
265  if(status != ST_SUCCESS) { return status; } \
266  } while(0)
267 
268 //This macro is responsible for writing 16bits of management data to
269 //the proper management address.
270 #define WRITE_MGMT_16BITS(address, data) \
271  do{ \
272  int16u value = data; \
273  status = halInternalFlashWrite((address), &value, 1); \
274  if(status != ST_SUCCESS) { \
275  return status; \
276  } \
277  } while(0)
278 
279 
280 int8u halCommonWriteToNvm(const void *data, int32u offset, int16u length)
281 {
282  StStatus status;
283  int8u state, exitState;
284  int32u srcPage;
285  int32u destPage;
286  //Remember: NVM data storage works on 16bit quantities.
287  int16u *ram = (int16u*)data;
288 
289  //The NVM data storage system cannot function if the LEFT and RIGHT
290  //storage are not aligned to physical flash pages.
291  assert((NVM_LEFT_PAGE%MFB_PAGE_SIZE_B)==0);
292  assert((NVM_RIGHT_PAGE%MFB_PAGE_SIZE_B)==0);
293  //The offset of the NVM data must be 16bit aligned.
294  assert((offset&0x1)==0);
295  //The length of the NVM data must be 16bit aligned.
296  assert((length&0x1)==0);
297  //It is illegal to write to an offset outside of NVM storage.
298  assert(offset+length<NVM_DATA_SIZE_B);
299 
300 
301  state = determineState();
302 
303  switch(state) {
304  case 1:
305  case 2:
306  case 3:
307  case 4:
308  case 9:
309  case 10:
310  srcPage = NVM_LEFT_PAGE;
311  destPage = NVM_RIGHT_PAGE;
312  exitState = 7;
313  break;
314  case 5:
315  case 6:
316  case 7:
317  case 8:
318  srcPage = NVM_RIGHT_PAGE;
319  destPage = NVM_LEFT_PAGE;
320  exitState = 3;
321  break;
322  case 0:
323  default:
324  //Invalid state. Default to writing to the LEFT page. Defaulting to
325  //using RIGHT as the source page is valid since the RIGHT page
326  //will also be erased and therefore produce 0xFF for data values.
327  state = 0;
328  srcPage = NVM_RIGHT_PAGE;
329  destPage = NVM_LEFT_PAGE;
330  exitState = 3;
331  break;
332  }
333 
334  //Advance the state machine. Starting on state 3 requires state 7 to
335  //exit and starting on state 7 requires state 3 to exit. Starting on
336  //any other state requires either 3 or 7 to exit.
337  //NOTE: Refer to nvm.h for a description of the states and how the
338  // state transitions correspond to erasing, writing data, and
339  // writing mgmt values.
340  while(TRUE) {
341  switch(state) {
342  case 0:
343  //State 0 is the only state where the source page needs to be erased.
344  ERASE_PAGE(srcPage);
345  ERASE_PAGE(destPage);
346  WRITE_DATA(destPage, srcPage, offset, length);
347  WRITE_MGMT_16BITS(NVM_LEFT_PAGE+0, 0x0000);
348  state=1;
349  break;
350  case 1:
351  WRITE_MGMT_16BITS(NVM_RIGHT_PAGE+2, 0xFF00);
352  state=2;
353  break;
354  case 2:
355  WRITE_MGMT_16BITS(NVM_RIGHT_PAGE+0, 0x0000);
356  state=3;
357  break;
358  case 3:
359  if(exitState==3) {
360  return ST_SUCCESS;
361  }
362  ERASE_PAGE(destPage);
363  state=4;
364  break;
365  case 4:
366  WRITE_DATA(destPage, srcPage, offset, length);
367  WRITE_MGMT_16BITS(NVM_RIGHT_PAGE+0, 0xFF00);
368  state=5;
369  break;
370  case 5:
371  WRITE_MGMT_16BITS(NVM_LEFT_PAGE+2, 0xFF00);
372  state=6;
373  break;
374  case 6:
375  WRITE_MGMT_16BITS(NVM_RIGHT_PAGE+0, 0x0000);
376  state=7;
377  break;
378  case 7:
379  if(exitState==7) {
380  return ST_SUCCESS;
381  }
382  ERASE_PAGE(destPage);
383  state=8;
384  break;
385  case 8:
386  WRITE_DATA(destPage, srcPage, offset, length);
387  WRITE_MGMT_16BITS(NVM_LEFT_PAGE+0, 0xFF00);
388  state=9;
389  break;
390  case 9:
391  WRITE_MGMT_16BITS(NVM_RIGHT_PAGE+2, 0xFF00);
392  state=10;
393  break;
394  case 10:
395  WRITE_MGMT_16BITS(NVM_LEFT_PAGE+0, 0x0000);
396  state=3;
397  break;
398  }
399  }
400 }
401 
402 #endif // NVM_RAM_EMULATION