|
@@ -26,35 +26,967 @@
|
26
|
26
|
|
27
|
27
|
#if ENABLED(FLASH_EEPROM_EMULATION)
|
28
|
28
|
|
29
|
|
-#include "../../inc/MarlinConfig.h"
|
|
29
|
+#ifndef E2END
|
|
30
|
+ #define E2END 0xFFF // Default to Flash emulated EEPROM size (eeprom_emul.cpp)
|
|
31
|
+#endif
|
30
|
32
|
|
31
|
|
-#include "../shared/eeprom_if.h"
|
32
|
|
-#include "../shared/eeprom_api.h"
|
|
33
|
+/* EEPROM emulation over flash with reduced wear
|
|
34
|
+ *
|
|
35
|
+ * We will use 2 contiguous groups of pages as main and alternate.
|
|
36
|
+ * We want an structure that allows to read as fast as possible,
|
|
37
|
+ * without the need of scanning the whole FLASH memory.
|
|
38
|
+ *
|
|
39
|
+ * FLASH bits default erased state is 1, and can be set to 0
|
|
40
|
+ * on a per bit basis. To reset them to 1, a full page erase
|
|
41
|
+ * is needed.
|
|
42
|
+ *
|
|
43
|
+ * Values are stored as differences that should be applied to a
|
|
44
|
+ * completely erased EEPROM (filled with 0xFFs). We just encode
|
|
45
|
+ * the starting address of the values to change, the length of
|
|
46
|
+ * the block of new values, and the values themselves. All diffs
|
|
47
|
+ * are accumulated into a RAM buffer, compacted into the least
|
|
48
|
+ * amount of non overlapping diffs possible and sorted by starting
|
|
49
|
+ * address before being saved into the next available page of FLASH
|
|
50
|
+ * of the current group.
|
|
51
|
+ * Once the current group is completely full, we compact it and save
|
|
52
|
+ * it into the other group, then erase the current group and switch
|
|
53
|
+ * to that new group and set it as current.
|
|
54
|
+ *
|
|
55
|
+ * The FLASH endurance is about 1/10 ... 1/100 of an EEPROM
|
|
56
|
+ * endurance, but EEPROM endurance is specified per byte, not
|
|
57
|
+ * per page. We can't emulate EE endurance with FLASH for all
|
|
58
|
+ * bytes, but we can emulate endurance for a given percent of
|
|
59
|
+ * bytes.
|
|
60
|
+ *
|
|
61
|
+ */
|
|
62
|
+
|
|
63
|
+//#define EE_EMU_DEBUG
|
|
64
|
+
|
|
65
|
+#define EEPROMSize 4096
|
|
66
|
+#define PagesPerGroup 128
|
|
67
|
+#define GroupCount 2
|
|
68
|
+#define PageSize 256u
|
|
69
|
+
|
|
70
|
+ /* Flash storage */
|
|
71
|
+typedef struct FLASH_SECTOR {
|
|
72
|
+ uint8_t page[PageSize];
|
|
73
|
+} FLASH_SECTOR_T;
|
|
74
|
+
|
|
75
|
+#define PAGE_FILL \
|
|
76
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
77
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
78
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
79
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
80
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
81
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
82
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
83
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
84
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
85
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
86
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
87
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
88
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
89
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
90
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, \
|
|
91
|
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
|
|
92
|
+
|
|
93
|
+#define FLASH_INIT_FILL \
|
|
94
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
95
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
96
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
97
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
98
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
99
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
100
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
101
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
102
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
103
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
104
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
105
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
106
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
107
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
108
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
109
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
110
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
111
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
112
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
113
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
114
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
115
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
116
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
117
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
118
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
119
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
120
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
121
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
122
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
123
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
124
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL, \
|
|
125
|
+ PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL,PAGE_FILL
|
|
126
|
+
|
|
127
|
+/* This is the FLASH area used to emulate a 2Kbyte EEPROM -- We need this buffer aligned
|
|
128
|
+ to a 256 byte boundary. */
|
|
129
|
+static const uint8_t flashStorage[PagesPerGroup * GroupCount * PageSize] __attribute__ ((aligned (PageSize))) = { FLASH_INIT_FILL };
|
|
130
|
+
|
|
131
|
+/* Get the address of an specific page */
|
|
132
|
+static const FLASH_SECTOR_T* getFlashStorage(int page) {
|
|
133
|
+ return (const FLASH_SECTOR_T*)&flashStorage[page*PageSize];
|
|
134
|
+}
|
|
135
|
+
|
|
136
|
+static uint8_t buffer[256] = {0}, // The RAM buffer to accumulate writes
|
|
137
|
+ curPage = 0, // Current FLASH page inside the group
|
|
138
|
+ curGroup = 0xFF; // Current FLASH group
|
|
139
|
+
|
|
140
|
+#define DEBUG_OUT ENABLED(EE_EMU_DEBUG)
|
|
141
|
+#include "../../core/debug_out.h"
|
|
142
|
+
|
|
143
|
+static void ee_Dump(const int page, const void* data) {
|
|
144
|
+
|
|
145
|
+ #ifdef EE_EMU_DEBUG
|
|
146
|
+
|
|
147
|
+ const uint8_t* c = (const uint8_t*) data;
|
|
148
|
+ char buffer[80];
|
|
149
|
+
|
|
150
|
+ sprintf_P(buffer, PSTR("Page: %d (0x%04x)\n"), page, page);
|
|
151
|
+ DEBUG_ECHO(buffer);
|
|
152
|
+
|
|
153
|
+ char* p = &buffer[0];
|
|
154
|
+ for (int i = 0; i< PageSize; ++i) {
|
|
155
|
+ if ((i & 0xF) == 0) p += sprintf_P(p, PSTR("%04x] "), i);
|
33
|
156
|
|
34
|
|
-#if !defined(E2END)
|
35
|
|
- #define E2END 0xFFF // Default to Flash emulated EEPROM size (EepromEmulation_Due.cpp)
|
|
157
|
+ p += sprintf_P(p, PSTR(" %02x"), c[i]);
|
|
158
|
+ if ((i & 0xF) == 0xF) {
|
|
159
|
+ *p++ = '\n';
|
|
160
|
+ *p = 0;
|
|
161
|
+ DEBUG_ECHO(buffer);
|
|
162
|
+ p = &buffer[0];
|
|
163
|
+ }
|
|
164
|
+ }
|
|
165
|
+
|
|
166
|
+ #else
|
|
167
|
+ UNUSED(page);
|
|
168
|
+ UNUSED(data);
|
|
169
|
+ #endif
|
|
170
|
+}
|
|
171
|
+
|
|
172
|
+/* Flash Writing Protection Key */
|
|
173
|
+#define FWP_KEY 0x5Au
|
|
174
|
+
|
|
175
|
+#if SAM4S_SERIES
|
|
176
|
+ #define EEFC_FCR_FCMD(value) \
|
|
177
|
+ ((EEFC_FCR_FCMD_Msk & ((value) << EEFC_FCR_FCMD_Pos)))
|
|
178
|
+ #define EEFC_ERROR_FLAGS (EEFC_FSR_FLOCKE | EEFC_FSR_FCMDE | EEFC_FSR_FLERR)
|
|
179
|
+#else
|
|
180
|
+ #define EEFC_ERROR_FLAGS (EEFC_FSR_FLOCKE | EEFC_FSR_FCMDE)
|
36
|
181
|
#endif
|
37
|
182
|
|
38
|
|
-extern void eeprom_flush();
|
|
183
|
+/**
|
|
184
|
+ * Writes the contents of the specified page (no previous erase)
|
|
185
|
+ * @param page (page #)
|
|
186
|
+ * @param data (pointer to the data buffer)
|
|
187
|
+ */
|
|
188
|
+__attribute__ ((long_call, section (".ramfunc")))
|
|
189
|
+static bool ee_PageWrite(uint16_t page, const void* data) {
|
|
190
|
+
|
|
191
|
+ uint16_t i;
|
|
192
|
+ uint32_t addrflash = uint32_t(getFlashStorage(page));
|
39
|
193
|
|
40
|
|
-size_t PersistentStore::capacity() { return E2END + 1; }
|
41
|
|
-bool PersistentStore::access_start() { return true; }
|
|
194
|
+ // Read the flash contents
|
|
195
|
+ uint32_t pageContents[PageSize>>2];
|
|
196
|
+ memcpy(pageContents, (void*)addrflash, PageSize);
|
|
197
|
+
|
|
198
|
+ // We ONLY want to toggle bits that have changed, and that have changed to 0.
|
|
199
|
+ // SAM3X8E tends to destroy contiguous bits if reprogrammed without erasing, so
|
|
200
|
+ // we try by all means to avoid this. That is why it says: "The Partial
|
|
201
|
+ // Programming mode works only with 128-bit (or higher) boundaries. It cannot
|
|
202
|
+ // be used with boundaries lower than 128 bits (8, 16 or 32-bit for example)."
|
|
203
|
+ // All bits that did not change, set them to 1.
|
|
204
|
+ for (i = 0; i <PageSize >> 2; i++)
|
|
205
|
+ pageContents[i] = (((uint32_t*)data)[i]) | (~(pageContents[i] ^ ((uint32_t*)data)[i]));
|
|
206
|
+
|
|
207
|
+ DEBUG_ECHO_START();
|
|
208
|
+ DEBUG_ECHOLNPAIR("EEPROM PageWrite ", page);
|
|
209
|
+ DEBUG_ECHOLNPAIR(" in FLASH address ", (uint32_t)addrflash);
|
|
210
|
+ DEBUG_ECHOLNPAIR(" base address ", (uint32_t)getFlashStorage(0));
|
|
211
|
+ DEBUG_FLUSH();
|
|
212
|
+
|
|
213
|
+ // Get the page relative to the start of the EFC controller, and the EFC controller to use
|
|
214
|
+ Efc *efc;
|
|
215
|
+ uint16_t fpage;
|
|
216
|
+ if (addrflash >= IFLASH1_ADDR) {
|
|
217
|
+ efc = EFC1;
|
|
218
|
+ fpage = (addrflash - IFLASH1_ADDR) / IFLASH1_PAGE_SIZE;
|
|
219
|
+ }
|
|
220
|
+ else {
|
|
221
|
+ efc = EFC0;
|
|
222
|
+ fpage = (addrflash - IFLASH0_ADDR) / IFLASH0_PAGE_SIZE;
|
|
223
|
+ }
|
|
224
|
+
|
|
225
|
+ // Get the page that must be unlocked, then locked
|
|
226
|
+ uint16_t lpage = fpage & (~((IFLASH0_LOCK_REGION_SIZE / IFLASH0_PAGE_SIZE) - 1));
|
|
227
|
+
|
|
228
|
+ // Disable all interrupts
|
|
229
|
+ __disable_irq();
|
|
230
|
+
|
|
231
|
+ // Get the FLASH wait states
|
|
232
|
+ uint32_t orgWS = (efc->EEFC_FMR & EEFC_FMR_FWS_Msk) >> EEFC_FMR_FWS_Pos;
|
|
233
|
+
|
|
234
|
+ // Set wait states to 6 (SAM errata)
|
|
235
|
+ efc->EEFC_FMR = (efc->EEFC_FMR & (~EEFC_FMR_FWS_Msk)) | EEFC_FMR_FWS(6);
|
|
236
|
+
|
|
237
|
+ // Unlock the flash page
|
|
238
|
+ uint32_t status;
|
|
239
|
+ efc->EEFC_FCR = EEFC_FCR_FKEY(FWP_KEY) | EEFC_FCR_FARG(lpage) | EEFC_FCR_FCMD(EFC_FCMD_CLB);
|
|
240
|
+ while (((status = efc->EEFC_FSR) & EEFC_FSR_FRDY) != EEFC_FSR_FRDY) {
|
|
241
|
+ // force compiler to not optimize this -- NOPs don't work!
|
|
242
|
+ __asm__ __volatile__("");
|
|
243
|
+ };
|
|
244
|
+
|
|
245
|
+ if ((status & EEFC_ERROR_FLAGS) != 0) {
|
|
246
|
+
|
|
247
|
+ // Restore original wait states
|
|
248
|
+ efc->EEFC_FMR = (efc->EEFC_FMR & (~EEFC_FMR_FWS_Msk)) | EEFC_FMR_FWS(orgWS);
|
|
249
|
+
|
|
250
|
+ // Reenable interrupts
|
|
251
|
+ __enable_irq();
|
|
252
|
+
|
|
253
|
+ DEBUG_ECHO_START();
|
|
254
|
+ DEBUG_ECHOLNPAIR("EEPROM Unlock failure for page ", page);
|
|
255
|
+ return false;
|
|
256
|
+ }
|
|
257
|
+
|
|
258
|
+ // Write page and lock: Writing 8-bit and 16-bit data is not allowed and may lead to unpredictable data corruption.
|
|
259
|
+ const uint32_t * aligned_src = (const uint32_t *) &pageContents[0]; /*data;*/
|
|
260
|
+ uint32_t * p_aligned_dest = (uint32_t *) addrflash;
|
|
261
|
+ for (i = 0; i < (IFLASH0_PAGE_SIZE / sizeof(uint32_t)); ++i) {
|
|
262
|
+ *p_aligned_dest++ = *aligned_src++;
|
|
263
|
+ }
|
|
264
|
+ efc->EEFC_FCR = EEFC_FCR_FKEY(FWP_KEY) | EEFC_FCR_FARG(fpage) | EEFC_FCR_FCMD(EFC_FCMD_WPL);
|
|
265
|
+ while (((status = efc->EEFC_FSR) & EEFC_FSR_FRDY) != EEFC_FSR_FRDY) {
|
|
266
|
+ // force compiler to not optimize this -- NOPs don't work!
|
|
267
|
+ __asm__ __volatile__("");
|
|
268
|
+ };
|
|
269
|
+
|
|
270
|
+ if ((status & EEFC_ERROR_FLAGS) != 0) {
|
|
271
|
+
|
|
272
|
+ // Restore original wait states
|
|
273
|
+ efc->EEFC_FMR = (efc->EEFC_FMR & (~EEFC_FMR_FWS_Msk)) | EEFC_FMR_FWS(orgWS);
|
|
274
|
+
|
|
275
|
+ // Reenable interrupts
|
|
276
|
+ __enable_irq();
|
|
277
|
+
|
|
278
|
+ DEBUG_ECHO_START();
|
|
279
|
+ DEBUG_ECHOLNPAIR("EEPROM Write failure for page ", page);
|
|
280
|
+
|
|
281
|
+ return false;
|
|
282
|
+ }
|
|
283
|
+
|
|
284
|
+ // Restore original wait states
|
|
285
|
+ efc->EEFC_FMR = (efc->EEFC_FMR & (~EEFC_FMR_FWS_Msk)) | EEFC_FMR_FWS(orgWS);
|
|
286
|
+
|
|
287
|
+ // Reenable interrupts
|
|
288
|
+ __enable_irq();
|
|
289
|
+
|
|
290
|
+ // Compare contents
|
|
291
|
+ if (memcmp(getFlashStorage(page),data,PageSize)) {
|
|
292
|
+
|
|
293
|
+ #ifdef EE_EMU_DEBUG
|
|
294
|
+ DEBUG_ECHO_START();
|
|
295
|
+ DEBUG_ECHOLNPAIR("EEPROM Verify Write failure for page ", page);
|
|
296
|
+
|
|
297
|
+ ee_Dump( page, (uint32_t *)addrflash);
|
|
298
|
+ ee_Dump(-page, data);
|
|
299
|
+
|
|
300
|
+ // Calculate count of changed bits
|
|
301
|
+ uint32_t* p1 = (uint32_t*)addrflash;
|
|
302
|
+ uint32_t* p2 = (uint32_t*)data;
|
|
303
|
+ int count = 0;
|
|
304
|
+ for (i =0; i<PageSize >> 2; i++) {
|
|
305
|
+ if (p1[i] != p2[i]) {
|
|
306
|
+ uint32_t delta = p1[i] ^ p2[i];
|
|
307
|
+ while (delta) {
|
|
308
|
+ if ((delta&1) != 0)
|
|
309
|
+ count++;
|
|
310
|
+ delta >>= 1;
|
|
311
|
+ }
|
|
312
|
+ }
|
|
313
|
+ }
|
|
314
|
+ DEBUG_ECHOLNPAIR("--> Differing bits: ", count);
|
|
315
|
+ #endif
|
|
316
|
+
|
|
317
|
+ return false;
|
|
318
|
+ }
|
|
319
|
+
|
|
320
|
+ return true;
|
|
321
|
+}
|
|
322
|
+
|
|
323
|
+/**
|
|
324
|
+ * Erases the contents of the specified page
|
|
325
|
+ * @param page (page #)
|
|
326
|
+ */
|
|
327
|
+__attribute__ ((long_call, section (".ramfunc")))
|
|
328
|
+static bool ee_PageErase(uint16_t page) {
|
|
329
|
+
|
|
330
|
+ uint16_t i;
|
|
331
|
+ uint32_t addrflash = uint32_t(getFlashStorage(page));
|
|
332
|
+
|
|
333
|
+ DEBUG_ECHO_START();
|
|
334
|
+ DEBUG_ECHOLNPAIR("EEPROM PageErase ", page);
|
|
335
|
+ DEBUG_ECHOLNPAIR(" in FLASH address ", (uint32_t)addrflash);
|
|
336
|
+ DEBUG_ECHOLNPAIR(" base address ", (uint32_t)getFlashStorage(0));
|
|
337
|
+ DEBUG_FLUSH();
|
|
338
|
+
|
|
339
|
+ // Get the page relative to the start of the EFC controller, and the EFC controller to use
|
|
340
|
+ Efc *efc;
|
|
341
|
+ uint16_t fpage;
|
|
342
|
+ if (addrflash >= IFLASH1_ADDR) {
|
|
343
|
+ efc = EFC1;
|
|
344
|
+ fpage = (addrflash - IFLASH1_ADDR) / IFLASH1_PAGE_SIZE;
|
|
345
|
+ }
|
|
346
|
+ else {
|
|
347
|
+ efc = EFC0;
|
|
348
|
+ fpage = (addrflash - IFLASH0_ADDR) / IFLASH0_PAGE_SIZE;
|
|
349
|
+ }
|
|
350
|
+
|
|
351
|
+ // Get the page that must be unlocked, then locked
|
|
352
|
+ uint16_t lpage = fpage & (~((IFLASH0_LOCK_REGION_SIZE / IFLASH0_PAGE_SIZE) - 1));
|
|
353
|
+
|
|
354
|
+ // Disable all interrupts
|
|
355
|
+ __disable_irq();
|
|
356
|
+
|
|
357
|
+ // Get the FLASH wait states
|
|
358
|
+ uint32_t orgWS = (efc->EEFC_FMR & EEFC_FMR_FWS_Msk) >> EEFC_FMR_FWS_Pos;
|
|
359
|
+
|
|
360
|
+ // Set wait states to 6 (SAM errata)
|
|
361
|
+ efc->EEFC_FMR = (efc->EEFC_FMR & (~EEFC_FMR_FWS_Msk)) | EEFC_FMR_FWS(6);
|
|
362
|
+
|
|
363
|
+ // Unlock the flash page
|
|
364
|
+ uint32_t status;
|
|
365
|
+ efc->EEFC_FCR = EEFC_FCR_FKEY(FWP_KEY) | EEFC_FCR_FARG(lpage) | EEFC_FCR_FCMD(EFC_FCMD_CLB);
|
|
366
|
+ while (((status = efc->EEFC_FSR) & EEFC_FSR_FRDY) != EEFC_FSR_FRDY) {
|
|
367
|
+ // force compiler to not optimize this -- NOPs don't work!
|
|
368
|
+ __asm__ __volatile__("");
|
|
369
|
+ };
|
|
370
|
+ if ((status & EEFC_ERROR_FLAGS) != 0) {
|
|
371
|
+
|
|
372
|
+ // Restore original wait states
|
|
373
|
+ efc->EEFC_FMR = (efc->EEFC_FMR & (~EEFC_FMR_FWS_Msk)) | EEFC_FMR_FWS(orgWS);
|
|
374
|
+
|
|
375
|
+ // Reenable interrupts
|
|
376
|
+ __enable_irq();
|
|
377
|
+
|
|
378
|
+ DEBUG_ECHO_START();
|
|
379
|
+ DEBUG_ECHOLNPAIR("EEPROM Unlock failure for page ",page);
|
|
380
|
+
|
|
381
|
+ return false;
|
|
382
|
+ }
|
|
383
|
+
|
|
384
|
+ // Erase Write page and lock: Writing 8-bit and 16-bit data is not allowed and may lead to unpredictable data corruption.
|
|
385
|
+ uint32_t * p_aligned_dest = (uint32_t *) addrflash;
|
|
386
|
+ for (i = 0; i < (IFLASH0_PAGE_SIZE / sizeof(uint32_t)); ++i) {
|
|
387
|
+ *p_aligned_dest++ = 0xFFFFFFFF;
|
|
388
|
+ }
|
|
389
|
+ efc->EEFC_FCR = EEFC_FCR_FKEY(FWP_KEY) | EEFC_FCR_FARG(fpage) | EEFC_FCR_FCMD(EFC_FCMD_EWPL);
|
|
390
|
+ while (((status = efc->EEFC_FSR) & EEFC_FSR_FRDY) != EEFC_FSR_FRDY) {
|
|
391
|
+ // force compiler to not optimize this -- NOPs don't work!
|
|
392
|
+ __asm__ __volatile__("");
|
|
393
|
+ };
|
|
394
|
+ if ((status & EEFC_ERROR_FLAGS) != 0) {
|
|
395
|
+
|
|
396
|
+ // Restore original wait states
|
|
397
|
+ efc->EEFC_FMR = (efc->EEFC_FMR & (~EEFC_FMR_FWS_Msk)) | EEFC_FMR_FWS(orgWS);
|
|
398
|
+
|
|
399
|
+ // Reenable interrupts
|
|
400
|
+ __enable_irq();
|
|
401
|
+
|
|
402
|
+ DEBUG_ECHO_START();
|
|
403
|
+ DEBUG_ECHOLNPAIR("EEPROM Erase failure for page ",page);
|
|
404
|
+
|
|
405
|
+ return false;
|
|
406
|
+ }
|
|
407
|
+
|
|
408
|
+ // Restore original wait states
|
|
409
|
+ efc->EEFC_FMR = (efc->EEFC_FMR & (~EEFC_FMR_FWS_Msk)) | EEFC_FMR_FWS(orgWS);
|
|
410
|
+
|
|
411
|
+ // Reenable interrupts
|
|
412
|
+ __enable_irq();
|
|
413
|
+
|
|
414
|
+ // Check erase
|
|
415
|
+ uint32_t * aligned_src = (uint32_t *) addrflash;
|
|
416
|
+ for (i = 0; i < PageSize >> 2; i++) {
|
|
417
|
+ if (*aligned_src++ != 0xFFFFFFFF) {
|
|
418
|
+ DEBUG_ECHO_START();
|
|
419
|
+ DEBUG_ECHOLNPAIR("EEPROM Verify Erase failure for page ",page);
|
|
420
|
+ ee_Dump(page, (uint32_t *)addrflash);
|
|
421
|
+ return false;
|
|
422
|
+ }
|
|
423
|
+ }
|
42
|
424
|
|
43
|
|
-bool PersistentStore::access_finish() {
|
44
|
|
- eeprom_flush();
|
45
|
425
|
return true;
|
46
|
426
|
}
|
47
|
427
|
|
|
428
|
+static uint8_t ee_Read(uint32_t address, bool excludeRAMBuffer=false) {
|
|
429
|
+
|
|
430
|
+ uint32_t baddr;
|
|
431
|
+ uint32_t blen;
|
|
432
|
+
|
|
433
|
+ // If we were requested an address outside of the emulated range, fail now
|
|
434
|
+ if (address >= EEPROMSize)
|
|
435
|
+ return false;
|
|
436
|
+
|
|
437
|
+ // Check that the value is not contained in the RAM buffer
|
|
438
|
+ if (!excludeRAMBuffer) {
|
|
439
|
+ uint16_t i = 0;
|
|
440
|
+ while (i <= (PageSize - 4)) { /* (PageSize - 4) because otherwise, there is not enough room for data and headers */
|
|
441
|
+
|
|
442
|
+ // Get the address of the block
|
|
443
|
+ baddr = buffer[i] | (buffer[i + 1] << 8);
|
|
444
|
+
|
|
445
|
+ // Get the length of the block
|
|
446
|
+ blen = buffer[i + 2];
|
|
447
|
+
|
|
448
|
+ // If we reach the end of the list, break loop
|
|
449
|
+ if (blen == 0xFF)
|
|
450
|
+ break;
|
|
451
|
+
|
|
452
|
+ // Check if data is contained in this block
|
|
453
|
+ if (address >= baddr &&
|
|
454
|
+ address < (baddr + blen)) {
|
|
455
|
+
|
|
456
|
+ // Yes, it is contained. Return it!
|
|
457
|
+ return buffer[i + 3 + address - baddr];
|
|
458
|
+ }
|
|
459
|
+
|
|
460
|
+ // As blocks are always sorted, if the starting address of this block is higher
|
|
461
|
+ // than the address we are looking for, break loop now - We wont find the value
|
|
462
|
+ // associated to the address
|
|
463
|
+ if (baddr > address)
|
|
464
|
+ break;
|
|
465
|
+
|
|
466
|
+ // Jump to the next block
|
|
467
|
+ i += 3 + blen;
|
|
468
|
+ }
|
|
469
|
+ }
|
|
470
|
+
|
|
471
|
+ // It is NOT on the RAM buffer. It could be stored in FLASH. We are
|
|
472
|
+ // ensured on a given FLASH page, address contents are never repeated
|
|
473
|
+ // but on different pages, there is no such warranty, so we must go
|
|
474
|
+ // backwards from the last written FLASH page to the first one.
|
|
475
|
+ for (int page = curPage - 1; page >= 0; --page) {
|
|
476
|
+
|
|
477
|
+ // Get a pointer to the flash page
|
|
478
|
+ uint8_t* pflash = (uint8_t*)getFlashStorage(page + curGroup * PagesPerGroup);
|
|
479
|
+
|
|
480
|
+ uint16_t i = 0;
|
|
481
|
+ while (i <= (PageSize - 4)) { /* (PageSize - 4) because otherwise, there is not enough room for data and headers */
|
|
482
|
+
|
|
483
|
+ // Get the address of the block
|
|
484
|
+ baddr = pflash[i] | (pflash[i + 1] << 8);
|
|
485
|
+
|
|
486
|
+ // Get the length of the block
|
|
487
|
+ blen = pflash[i + 2];
|
|
488
|
+
|
|
489
|
+ // If we reach the end of the list, break loop
|
|
490
|
+ if (blen == 0xFF)
|
|
491
|
+ break;
|
|
492
|
+
|
|
493
|
+ // Check if data is contained in this block
|
|
494
|
+ if (address >= baddr && address < (baddr + blen))
|
|
495
|
+ return pflash[i + 3 + address - baddr]; // Yes, it is contained. Return it!
|
|
496
|
+
|
|
497
|
+ // As blocks are always sorted, if the starting address of this block is higher
|
|
498
|
+ // than the address we are looking for, break loop now - We wont find the value
|
|
499
|
+ // associated to the address
|
|
500
|
+ if (baddr > address) break;
|
|
501
|
+
|
|
502
|
+ // Jump to the next block
|
|
503
|
+ i += 3 + blen;
|
|
504
|
+ }
|
|
505
|
+ }
|
|
506
|
+
|
|
507
|
+ // If reached here, value is not stored, so return its default value
|
|
508
|
+ return 0xFF;
|
|
509
|
+}
|
|
510
|
+
|
|
511
|
+static uint32_t ee_GetAddrRange(uint32_t address, bool excludeRAMBuffer=false) {
|
|
512
|
+ uint32_t baddr,
|
|
513
|
+ blen,
|
|
514
|
+ nextAddr = 0xFFFF,
|
|
515
|
+ nextRange = 0;
|
|
516
|
+
|
|
517
|
+ // Check that the value is not contained in the RAM buffer
|
|
518
|
+ if (!excludeRAMBuffer) {
|
|
519
|
+ uint16_t i = 0;
|
|
520
|
+ while (i <= (PageSize - 4)) { /* (PageSize - 4) because otherwise, there is not enough room for data and headers */
|
|
521
|
+
|
|
522
|
+ // Get the address of the block
|
|
523
|
+ baddr = buffer[i] | (buffer[i + 1] << 8);
|
|
524
|
+
|
|
525
|
+ // Get the length of the block
|
|
526
|
+ blen = buffer[i + 2];
|
|
527
|
+
|
|
528
|
+ // If we reach the end of the list, break loop
|
|
529
|
+ if (blen == 0xFF) break;
|
|
530
|
+
|
|
531
|
+ // Check if address and address + 1 is contained in this block
|
|
532
|
+ if (address >= baddr && address < (baddr + blen))
|
|
533
|
+ return address | ((blen - address + baddr) << 16); // Yes, it is contained. Return it!
|
|
534
|
+
|
|
535
|
+ // Otherwise, check if we can use it as a limit
|
|
536
|
+ if (baddr > address && baddr < nextAddr) {
|
|
537
|
+ nextAddr = baddr;
|
|
538
|
+ nextRange = blen;
|
|
539
|
+ }
|
|
540
|
+
|
|
541
|
+ // As blocks are always sorted, if the starting address of this block is higher
|
|
542
|
+ // than the address we are looking for, break loop now - We wont find the value
|
|
543
|
+ // associated to the address
|
|
544
|
+ if (baddr > address) break;
|
|
545
|
+
|
|
546
|
+ // Jump to the next block
|
|
547
|
+ i += 3 + blen;
|
|
548
|
+ }
|
|
549
|
+ }
|
|
550
|
+
|
|
551
|
+ // It is NOT on the RAM buffer. It could be stored in FLASH. We are
|
|
552
|
+ // ensured on a given FLASH page, address contents are never repeated
|
|
553
|
+ // but on different pages, there is no such warranty, so we must go
|
|
554
|
+ // backwards from the last written FLASH page to the first one.
|
|
555
|
+ for (int page = curPage - 1; page >= 0; --page) {
|
|
556
|
+
|
|
557
|
+ // Get a pointer to the flash page
|
|
558
|
+ uint8_t* pflash = (uint8_t*)getFlashStorage(page + curGroup * PagesPerGroup);
|
|
559
|
+
|
|
560
|
+ uint16_t i = 0;
|
|
561
|
+ while (i <= (PageSize - 4)) { /* (PageSize - 4) because otherwise, there is not enough room for data and headers */
|
|
562
|
+
|
|
563
|
+ // Get the address of the block
|
|
564
|
+ baddr = pflash[i] | (pflash[i + 1] << 8);
|
|
565
|
+
|
|
566
|
+ // Get the length of the block
|
|
567
|
+ blen = pflash[i + 2];
|
|
568
|
+
|
|
569
|
+ // If we reach the end of the list, break loop
|
|
570
|
+ if (blen == 0xFF) break;
|
|
571
|
+
|
|
572
|
+ // Check if data is contained in this block
|
|
573
|
+ if (address >= baddr && address < (baddr + blen))
|
|
574
|
+ return address | ((blen - address + baddr) << 16); // Yes, it is contained. Return it!
|
|
575
|
+
|
|
576
|
+ // Otherwise, check if we can use it as a limit
|
|
577
|
+ if (baddr > address && baddr < nextAddr) {
|
|
578
|
+ nextAddr = baddr;
|
|
579
|
+ nextRange = blen;
|
|
580
|
+ }
|
|
581
|
+
|
|
582
|
+ // As blocks are always sorted, if the starting address of this block is higher
|
|
583
|
+ // than the address we are looking for, break loop now - We wont find the value
|
|
584
|
+ // associated to the address
|
|
585
|
+ if (baddr > address) break;
|
|
586
|
+
|
|
587
|
+ // Jump to the next block
|
|
588
|
+ i += 3 + blen;
|
|
589
|
+ }
|
|
590
|
+ }
|
|
591
|
+
|
|
592
|
+ // If reached here, we will return the next valid address
|
|
593
|
+ return nextAddr | (nextRange << 16);
|
|
594
|
+}
|
|
595
|
+
|
|
596
|
+static bool ee_IsPageClean(int page) {
|
|
597
|
+ uint32_t* pflash = (uint32_t*) getFlashStorage(page);
|
|
598
|
+ for (uint16_t i = 0; i < (PageSize >> 2); ++i)
|
|
599
|
+ if (*pflash++ != 0xFFFFFFFF) return false;
|
|
600
|
+ return true;
|
|
601
|
+}
|
|
602
|
+
|
|
603
|
+static bool ee_Flush(uint32_t overrideAddress = 0xFFFFFFFF, uint8_t overrideData=0xFF) {
|
|
604
|
+
|
|
605
|
+ // Check if RAM buffer has something to be written
|
|
606
|
+ bool isEmpty = true;
|
|
607
|
+ uint32_t* p = (uint32_t*) &buffer[0];
|
|
608
|
+ for (uint16_t j = 0; j < (PageSize >> 2); j++) {
|
|
609
|
+ if (*p++ != 0xFFFFFFFF) {
|
|
610
|
+ isEmpty = false;
|
|
611
|
+ break;
|
|
612
|
+ }
|
|
613
|
+ }
|
|
614
|
+
|
|
615
|
+ // If something has to be written, do so!
|
|
616
|
+ if (!isEmpty) {
|
|
617
|
+
|
|
618
|
+ // Write the current ram buffer into FLASH
|
|
619
|
+ ee_PageWrite(curPage + curGroup * PagesPerGroup, buffer);
|
|
620
|
+
|
|
621
|
+ // Clear the RAM buffer
|
|
622
|
+ memset(buffer, 0xFF, sizeof(buffer));
|
|
623
|
+
|
|
624
|
+ // Increment the page to use the next time
|
|
625
|
+ ++curPage;
|
|
626
|
+ }
|
|
627
|
+
|
|
628
|
+ // Did we reach the maximum count of available pages per group for storage ?
|
|
629
|
+ if (curPage < PagesPerGroup) {
|
|
630
|
+
|
|
631
|
+ // Do we have an override address ?
|
|
632
|
+ if (overrideAddress < EEPROMSize) {
|
|
633
|
+
|
|
634
|
+ // Yes, just store the value into the RAM buffer
|
|
635
|
+ buffer[0] = overrideAddress & 0xFF;
|
|
636
|
+ buffer[0 + 1] = (overrideAddress >> 8) & 0xFF;
|
|
637
|
+ buffer[0 + 2] = 1;
|
|
638
|
+ buffer[0 + 3] = overrideData;
|
|
639
|
+ }
|
|
640
|
+
|
|
641
|
+ // Done!
|
|
642
|
+ return true;
|
|
643
|
+ }
|
|
644
|
+
|
|
645
|
+ // We have no space left on the current group - We must compact the values
|
|
646
|
+ uint16_t i = 0;
|
|
647
|
+
|
|
648
|
+ // Compute the next group to use
|
|
649
|
+ int curwPage = 0, curwGroup = curGroup + 1;
|
|
650
|
+ if (curwGroup >= GroupCount) curwGroup = 0;
|
|
651
|
+
|
|
652
|
+ uint32_t rdAddr = 0;
|
|
653
|
+ do {
|
|
654
|
+
|
|
655
|
+ // Get the next valid range
|
|
656
|
+ uint32_t addrRange = ee_GetAddrRange(rdAddr, true);
|
|
657
|
+
|
|
658
|
+ // Make sure not to skip the override address, if specified
|
|
659
|
+ int rdRange;
|
|
660
|
+ if (overrideAddress < EEPROMSize &&
|
|
661
|
+ rdAddr <= overrideAddress &&
|
|
662
|
+ (addrRange & 0xFFFF) > overrideAddress) {
|
|
663
|
+
|
|
664
|
+ rdAddr = overrideAddress;
|
|
665
|
+ rdRange = 1;
|
|
666
|
+ }
|
|
667
|
+ else {
|
|
668
|
+ rdAddr = addrRange & 0xFFFF;
|
|
669
|
+ rdRange = addrRange >> 16;
|
|
670
|
+ }
|
|
671
|
+
|
|
672
|
+ // If no range, break loop
|
|
673
|
+ if (rdRange == 0)
|
|
674
|
+ break;
|
|
675
|
+
|
|
676
|
+ do {
|
|
677
|
+
|
|
678
|
+ // Get the value
|
|
679
|
+ uint8_t rdValue = overrideAddress == rdAddr ? overrideData : ee_Read(rdAddr, true);
|
|
680
|
+
|
|
681
|
+ // Do not bother storing default values
|
|
682
|
+ if (rdValue != 0xFF) {
|
|
683
|
+
|
|
684
|
+ // If we have room, add it to the buffer
|
|
685
|
+ if (buffer[i + 2] == 0xFF) {
|
|
686
|
+
|
|
687
|
+ // Uninitialized buffer, just add it!
|
|
688
|
+ buffer[i] = rdAddr & 0xFF;
|
|
689
|
+ buffer[i + 1] = (rdAddr >> 8) & 0xFF;
|
|
690
|
+ buffer[i + 2] = 1;
|
|
691
|
+ buffer[i + 3] = rdValue;
|
|
692
|
+
|
|
693
|
+ }
|
|
694
|
+ else {
|
|
695
|
+ // Buffer already has contents. Check if we can extend it
|
|
696
|
+
|
|
697
|
+ // Get the address of the block
|
|
698
|
+ uint32_t baddr = buffer[i] | (buffer[i + 1] << 8);
|
|
699
|
+
|
|
700
|
+ // Get the length of the block
|
|
701
|
+ uint32_t blen = buffer[i + 2];
|
|
702
|
+
|
|
703
|
+ // Can we expand it ?
|
|
704
|
+ if (rdAddr == (baddr + blen) &&
|
|
705
|
+ i < (PageSize - 4) && /* This block has a chance to contain data AND */
|
|
706
|
+ buffer[i + 2] < (PageSize - i - 3)) {/* There is room for this block to be expanded */
|
|
707
|
+
|
|
708
|
+ // Yes, do it
|
|
709
|
+ ++buffer[i + 2];
|
|
710
|
+
|
|
711
|
+ // And store the value
|
|
712
|
+ buffer[i + 3 + rdAddr - baddr] = rdValue;
|
|
713
|
+
|
|
714
|
+ }
|
|
715
|
+ else {
|
|
716
|
+
|
|
717
|
+ // No, we can't expand it - Skip the existing block
|
|
718
|
+ i += 3 + blen;
|
|
719
|
+
|
|
720
|
+ // Can we create a new slot ?
|
|
721
|
+ if (i > (PageSize - 4)) {
|
|
722
|
+
|
|
723
|
+ // Not enough space - Write the current buffer to FLASH
|
|
724
|
+ ee_PageWrite(curwPage + curwGroup * PagesPerGroup, buffer);
|
|
725
|
+
|
|
726
|
+ // Advance write page (as we are compacting, should never overflow!)
|
|
727
|
+ ++curwPage;
|
|
728
|
+
|
|
729
|
+ // Clear RAM buffer
|
|
730
|
+ memset(buffer, 0xFF, sizeof(buffer));
|
|
731
|
+
|
|
732
|
+ // Start fresh */
|
|
733
|
+ i = 0;
|
|
734
|
+ }
|
|
735
|
+
|
|
736
|
+ // Enough space, add the new block
|
|
737
|
+ buffer[i] = rdAddr & 0xFF;
|
|
738
|
+ buffer[i + 1] = (rdAddr >> 8) & 0xFF;
|
|
739
|
+ buffer[i + 2] = 1;
|
|
740
|
+ buffer[i + 3] = rdValue;
|
|
741
|
+ }
|
|
742
|
+ }
|
|
743
|
+ }
|
|
744
|
+
|
|
745
|
+ // Go to the next address
|
|
746
|
+ ++rdAddr;
|
|
747
|
+
|
|
748
|
+ // Repeat for bytes of this range
|
|
749
|
+ } while (--rdRange);
|
|
750
|
+
|
|
751
|
+ // Repeat until we run out of ranges
|
|
752
|
+ } while (rdAddr < EEPROMSize);
|
|
753
|
+
|
|
754
|
+ // We must erase the previous group, in preparation for the next swap
|
|
755
|
+ for (int page = 0; page < curPage; page++) {
|
|
756
|
+ ee_PageErase(page + curGroup * PagesPerGroup);
|
|
757
|
+ }
|
|
758
|
+
|
|
759
|
+ // Finally, Now the active group is the created new group
|
|
760
|
+ curGroup = curwGroup;
|
|
761
|
+ curPage = curwPage;
|
|
762
|
+
|
|
763
|
+ // Done!
|
|
764
|
+ return true;
|
|
765
|
+}
|
|
766
|
+
|
|
767
|
+static bool ee_Write(uint32_t address, uint8_t data) {
|
|
768
|
+
|
|
769
|
+ // If we were requested an address outside of the emulated range, fail now
|
|
770
|
+ if (address >= EEPROMSize) return false;
|
|
771
|
+
|
|
772
|
+ // Lets check if we have a block with that data previously defined. Block
|
|
773
|
+ // start addresses are always sorted in ascending order
|
|
774
|
+ uint16_t i = 0;
|
|
775
|
+ while (i <= (PageSize - 4)) { /* (PageSize - 4) because otherwise, there is not enough room for data and headers */
|
|
776
|
+
|
|
777
|
+ // Get the address of the block
|
|
778
|
+ uint32_t baddr = buffer[i] | (buffer[i + 1] << 8);
|
|
779
|
+
|
|
780
|
+ // Get the length of the block
|
|
781
|
+ uint32_t blen = buffer[i + 2];
|
|
782
|
+
|
|
783
|
+ // If we reach the end of the list, break loop
|
|
784
|
+ if (blen == 0xFF)
|
|
785
|
+ break;
|
|
786
|
+
|
|
787
|
+ // Check if data is contained in this block
|
|
788
|
+ if (address >= baddr &&
|
|
789
|
+ address < (baddr + blen)) {
|
|
790
|
+
|
|
791
|
+ // Yes, it is contained. Just modify it
|
|
792
|
+ buffer[i + 3 + address - baddr] = data;
|
|
793
|
+
|
|
794
|
+ // Done!
|
|
795
|
+ return true;
|
|
796
|
+ }
|
|
797
|
+
|
|
798
|
+ // Maybe we could add it to the front or to the back
|
|
799
|
+ // of this block ?
|
|
800
|
+ if ((address + 1) == baddr || address == (baddr + blen)) {
|
|
801
|
+
|
|
802
|
+ // Potentially, it could be done. But we must ensure there is room
|
|
803
|
+ // so we can expand the block. Lets find how much free space remains
|
|
804
|
+ uint32_t iend = i;
|
|
805
|
+ do {
|
|
806
|
+ uint32_t ln = buffer[iend + 2];
|
|
807
|
+ if (ln == 0xFF) break;
|
|
808
|
+ iend += 3 + ln;
|
|
809
|
+ } while (iend <= (PageSize - 4)); /* (PageSize - 4) because otherwise, there is not enough room for data and headers */
|
|
810
|
+
|
|
811
|
+ // Here, inxt points to the first free address in the buffer. Do we have room ?
|
|
812
|
+ if (iend < PageSize) {
|
|
813
|
+ // Yes, at least a byte is free - We can expand the block
|
|
814
|
+
|
|
815
|
+ // Do we have to insert at the beginning ?
|
|
816
|
+ if ((address + 1) == baddr) {
|
|
817
|
+
|
|
818
|
+ // Insert at the beginning
|
|
819
|
+
|
|
820
|
+ // Make room at the beginning for our byte
|
|
821
|
+ memmove(&buffer[i + 3 + 1], &buffer[i + 3], iend - i - 3);
|
|
822
|
+
|
|
823
|
+ // Adjust the header and store the data
|
|
824
|
+ buffer[i] = address & 0xFF;
|
|
825
|
+ buffer[i + 1] = (address >> 8) & 0xFF;
|
|
826
|
+ buffer[i + 2]++;
|
|
827
|
+ buffer[i + 3] = data;
|
|
828
|
+
|
|
829
|
+ }
|
|
830
|
+ else {
|
|
831
|
+
|
|
832
|
+ // Insert at the end - There is a very interesting thing that could happen here:
|
|
833
|
+ // Maybe we could coalesce the next block with this block. Let's try to do it!
|
|
834
|
+ uint16_t inext = i + 3 + blen;
|
|
835
|
+ if (inext <= (PageSize - 4) &&
|
|
836
|
+ (buffer[inext] | uint16_t(buffer[inext + 1] << 8)) == (baddr + blen + 1)) {
|
|
837
|
+ // YES! ... we can coalesce blocks! . Do it!
|
|
838
|
+
|
|
839
|
+ // Adjust this block header to include the next one
|
|
840
|
+ buffer[i + 2] += buffer[inext + 2] + 1;
|
|
841
|
+
|
|
842
|
+ // Store data at the right place
|
|
843
|
+ buffer[i + 3 + blen] = data;
|
|
844
|
+
|
|
845
|
+ // Remove the next block header and append its data
|
|
846
|
+ memmove(&buffer[inext + 1], &buffer[inext + 3], iend - inext - 3);
|
|
847
|
+
|
|
848
|
+ // Finally, as we have saved 2 bytes at the end, make sure to clean them
|
|
849
|
+ buffer[iend - 2] = 0xFF;
|
|
850
|
+ buffer[iend - 1] = 0xFF;
|
|
851
|
+
|
|
852
|
+ }
|
|
853
|
+ else {
|
|
854
|
+ // NO ... No coalescing possible yet
|
|
855
|
+
|
|
856
|
+ // Make room at the end for our byte
|
|
857
|
+ memmove(&buffer[i + 3 + blen + 1], &buffer[i + 3 + blen], iend - i - 3 - blen);
|
|
858
|
+
|
|
859
|
+ // And add the data to the block
|
|
860
|
+ buffer[i + 2]++;
|
|
861
|
+ buffer[i + 3 + blen] = data;
|
|
862
|
+ }
|
|
863
|
+ }
|
|
864
|
+
|
|
865
|
+ // Done!
|
|
866
|
+ return true;
|
|
867
|
+ }
|
|
868
|
+ }
|
|
869
|
+
|
|
870
|
+ // As blocks are always sorted, if the starting address of this block is higher
|
|
871
|
+ // than the address we are looking for, break loop now - We wont find the value
|
|
872
|
+ // associated to the address
|
|
873
|
+ if (baddr > address) break;
|
|
874
|
+
|
|
875
|
+ // Jump to the next block
|
|
876
|
+ i += 3 + blen;
|
|
877
|
+ }
|
|
878
|
+
|
|
879
|
+ // Value is not stored AND we can't expand previous block to contain it. We must create a new block
|
|
880
|
+
|
|
881
|
+ // First, lets find how much free space remains
|
|
882
|
+ uint32_t iend = i;
|
|
883
|
+ while (iend <= (PageSize - 4)) { /* (PageSize - 4) because otherwise, there is not enough room for data and headers */
|
|
884
|
+ uint32_t ln = buffer[iend + 2];
|
|
885
|
+ if (ln == 0xFF) break;
|
|
886
|
+ iend += 3 + ln;
|
|
887
|
+ }
|
|
888
|
+
|
|
889
|
+ // If there is room for a new block, insert it at the proper place
|
|
890
|
+ if (iend <= (PageSize - 4)) {
|
|
891
|
+
|
|
892
|
+ // We have room to create a new block. Do so --- But add
|
|
893
|
+ // the block at the proper position, sorted by starting
|
|
894
|
+ // address, so it will be possible to compact it with other blocks.
|
|
895
|
+
|
|
896
|
+ // Make space
|
|
897
|
+ memmove(&buffer[i + 4], &buffer[i], iend - i);
|
|
898
|
+
|
|
899
|
+ // And add the block
|
|
900
|
+ buffer[i] = address & 0xFF;
|
|
901
|
+ buffer[i + 1] = (address >> 8) & 0xFF;
|
|
902
|
+ buffer[i + 2] = 1;
|
|
903
|
+ buffer[i + 3] = data;
|
|
904
|
+
|
|
905
|
+ // Done!
|
|
906
|
+ return true;
|
|
907
|
+ }
|
|
908
|
+
|
|
909
|
+ // Not enough room to store this information on this FLASH page - Perform a
|
|
910
|
+ // flush and override the address with the specified contents
|
|
911
|
+ return ee_Flush(address, data);
|
|
912
|
+}
|
|
913
|
+
|
|
914
|
+static void ee_Init() {
|
|
915
|
+
|
|
916
|
+ // Just init once!
|
|
917
|
+ if (curGroup != 0xFF) return;
|
|
918
|
+
|
|
919
|
+ // Clean up the SRAM buffer
|
|
920
|
+ memset(buffer, 0xFF, sizeof(buffer));
|
|
921
|
+
|
|
922
|
+ // Now, we must find out the group where settings are stored
|
|
923
|
+ for (curGroup = 0; curGroup < GroupCount; curGroup++)
|
|
924
|
+ if (!ee_IsPageClean(curGroup * PagesPerGroup)) break;
|
|
925
|
+
|
|
926
|
+ // If all groups seem to be used, default to first group
|
|
927
|
+ if (curGroup >= GroupCount) curGroup = 0;
|
|
928
|
+
|
|
929
|
+ DEBUG_ECHO_START();
|
|
930
|
+ DEBUG_ECHOLNPAIR("EEPROM Current Group: ",curGroup);
|
|
931
|
+ DEBUG_FLUSH();
|
|
932
|
+
|
|
933
|
+ // Now, validate that all the other group pages are empty
|
|
934
|
+ for (int grp = 0; grp < GroupCount; grp++) {
|
|
935
|
+ if (grp == curGroup) continue;
|
|
936
|
+
|
|
937
|
+ for (int page = 0; page < PagesPerGroup; page++) {
|
|
938
|
+ if (!ee_IsPageClean(grp * PagesPerGroup + page)) {
|
|
939
|
+ DEBUG_ECHO_START();
|
|
940
|
+ DEBUG_ECHOLNPAIR("EEPROM Page ", page, " not clean on group ", grp);
|
|
941
|
+ DEBUG_FLUSH();
|
|
942
|
+ ee_PageErase(grp * PagesPerGroup + page);
|
|
943
|
+ }
|
|
944
|
+ }
|
|
945
|
+ }
|
|
946
|
+
|
|
947
|
+ // Finally, for the active group, determine the first unused page
|
|
948
|
+ // and also validate that all the other ones are clean
|
|
949
|
+ for (curPage = 0; curPage < PagesPerGroup; curPage++) {
|
|
950
|
+ if (ee_IsPageClean(curGroup * PagesPerGroup + curPage)) {
|
|
951
|
+ ee_Dump(curGroup * PagesPerGroup + curPage, getFlashStorage(curGroup * PagesPerGroup + curPage));
|
|
952
|
+ break;
|
|
953
|
+ }
|
|
954
|
+ }
|
|
955
|
+
|
|
956
|
+ DEBUG_ECHO_START();
|
|
957
|
+ DEBUG_ECHOLNPAIR("EEPROM Active page: ", curPage);
|
|
958
|
+ DEBUG_FLUSH();
|
|
959
|
+
|
|
960
|
+ // Make sure the pages following the first clean one are also clean
|
|
961
|
+ for (int page = curPage + 1; page < PagesPerGroup; page++) {
|
|
962
|
+ if (!ee_IsPageClean(curGroup * PagesPerGroup + page)) {
|
|
963
|
+ DEBUG_ECHO_START();
|
|
964
|
+ DEBUG_ECHOLNPAIR("EEPROM Page ", page, " not clean on active group ", curGroup);
|
|
965
|
+ DEBUG_FLUSH();
|
|
966
|
+ ee_Dump(curGroup * PagesPerGroup + page, getFlashStorage(curGroup * PagesPerGroup + page));
|
|
967
|
+ ee_PageErase(curGroup * PagesPerGroup + page);
|
|
968
|
+ }
|
|
969
|
+ }
|
|
970
|
+}
|
|
971
|
+
|
|
972
|
+/* PersistentStore -----------------------------------------------------------*/
|
|
973
|
+
|
|
974
|
+#include "../shared/eeprom_api.h"
|
|
975
|
+
|
|
976
|
+size_t PersistentStore::capacity() { return E2END + 1; }
|
|
977
|
+bool PersistentStore::access_start() { ee_Init(); return true; }
|
|
978
|
+bool PersistentStore::access_finish() { ee_Flush(); return true; }
|
|
979
|
+
|
48
|
980
|
bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) {
|
49
|
981
|
while (size--) {
|
50
|
982
|
uint8_t * const p = (uint8_t * const)pos;
|
51
|
983
|
uint8_t v = *value;
|
52
|
984
|
// EEPROM has only ~100,000 write cycles,
|
53
|
985
|
// so only write bytes that have changed!
|
54
|
|
- if (v != eeprom_read_byte(p)) {
|
55
|
|
- eeprom_write_byte(p, v);
|
|
986
|
+ if (v != ee_Read(uint32_t(p))) {
|
|
987
|
+ ee_Write(uint32_t(p), v);
|
56
|
988
|
delay(2);
|
57
|
|
- if (eeprom_read_byte(p) != v) {
|
|
989
|
+ if (ee_Read(uint32_t(p)) != v) {
|
58
|
990
|
SERIAL_ECHO_MSG(STR_ERR_EEPROM_WRITE);
|
59
|
991
|
return true;
|
60
|
992
|
}
|
|
@@ -68,7 +1000,7 @@ bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, ui
|
68
|
1000
|
|
69
|
1001
|
bool PersistentStore::read_data(int &pos, uint8_t* value, size_t size, uint16_t *crc, const bool writing/*=true*/) {
|
70
|
1002
|
do {
|
71
|
|
- uint8_t c = eeprom_read_byte((uint8_t*)pos);
|
|
1003
|
+ uint8_t c = ee_Read(uint32_t(pos));
|
72
|
1004
|
if (writing) *value = c;
|
73
|
1005
|
crc16(crc, &c, 1);
|
74
|
1006
|
pos++;
|
|
@@ -77,5 +1009,5 @@ bool PersistentStore::read_data(int &pos, uint8_t* value, size_t size, uint16_t
|
77
|
1009
|
return false;
|
78
|
1010
|
}
|
79
|
1011
|
|
80
|
|
-#endif // EEPROM_SETTINGS
|
|
1012
|
+#endif // FLASH_EEPROM_EMULATION
|
81
|
1013
|
#endif // ARDUINO_ARCH_SAM
|