NHS31xx storage - Maximizing storage for equisized samples
storage.c
Go to the documentation of this file.
1 /*
2  * Copyright 2016-2020 NXP
3  * This software is owned or controlled by NXP and may only be used strictly
4  * in accordance with the applicable license terms. By expressly accepting
5  * such terms or by downloading, installing, activating and/or otherwise using
6  * the software, you are agreeing that you have read, and that you agree to
7  * comply with and are bound by, such license terms. If you do not agree to
8  * be bound by the applicable license terms, then you may not retain, install,
9  * activate or otherwise use the software.
10  */
11 
12 #include <string.h>
13 #include "storage.h"
14 
15 /**
16  * @file
17  *
18  * @par Maintaining state
19  * It is a crucial feature of this module to be able to resume operation at all times. Between de-initialization
20  * and re-initialization anything can happen, and no volatile memory can be trusted. Still, speed of operation is
21  * necessary. To be able to restore the exact state - done in #Storage_Init - three different recovery structures
22  * are used:
23  * - #RecoverInfo_t - stored in the general purpose registers
24  * - #Hint_t - stored on a fixed location in EEPROM
25  * - #Marker_t - stored on a variable location, immediately after the last written sample in EEPROM
26  * .
27  * After recovery, state is maintained in SRAM using the structure #Storage_Instance_t, and in #Storage_DeInit at
28  * least two of the three recovery structures are updated.
29  * @n The recovery structures are linked to each other as explained below.
30  *
31  * - #RecoverInfo_t
32  * This data is stored in the designated ALON register #STORAGE_FIRST_ALON_REGISTER and is by far the fastest
33  * and easiest way to recover the current state. It is always tried first. If its data is non-zero, the
34  * information is trusted and used.
35  * - #Hint_t
36  * This data is stored in the last page of the assigned EEPROM region, and also enables recovery of the
37  * current state in a fast way. However, this structure may be out of date as it may not be updated at every
38  * change.
39  * The reason for not updating is to ensure the maximum number of writes (endurance) is never reached.
40  * However, this data will always accurately tell
41  * - whether samples are stored or not
42  * - how much flash is occupied by data storage
43  * .
44  * It also provides a possible location to #Marker_t, which may be outdated.
45  * - #Marker_t
46  * This data is always stored just after the last sample written in EEPROM. Its precise location is not known,
47  * as it is progressing together with the data. #RecoverInfo_t points to the start of this structure in the
48  * assigned EEPROM region; #Hint_t may point to it; if both are failing a full slow search is performed in the
49  * assigned EEPROM region.
50  * .
51  *
52  * When a full backward search is performed during data recovery, we rely on the length of the marker to eliminate
53  * false positives, i.e. to ensure no bit sequence exists that looks like a valid marker but are in reality one or
54  * more samples stored in EEPROM. The comments near the code in #FindMarker explain the length and value of the
55  * marker avoid finding false positives. Yet, although extremely improbable, there is still a very tiny possibility
56  * that real-life data being stored in EEPROM results in such a false positive.
57  * Even then, most applications will never have the need to perform a full backward search, evading this problem.
58  * If your application is more likely to hit this, due to the use case being sufficiently different, and due to the
59  * samples being sufficiently similar, this module can be adapted to erase the full EEPROM after moving all data to
60  * FLASH - see #MoveSamplesFromEepromToFlash. During our continuous tests using randomized data of different
61  * lengths, we have never encountered this problem and therefore decided not to suffer the extra cost (time and
62  * power consumption) of implementing this.
63  *
64  * @par Recovering
65  * In #Storage_DeInit, at least the ALON register (#RecoverInfo_t) and the EEPROM marker (#Marker_t) are updated.
66  * Assuming only one sample was added, the EEPROM contents would then change as visually depicted below:
67  *
68  * @note the pictures below use this legend:
69  * - @c aaa, @c bbb, ... @c fff: samples, with bitsize #STORAGE_BITSIZE
70  * - @c AA: ALON register, formatted according to #RecoverInfo_t
71  * - @c HH: hint, formatted according to #Hint_t
72  * - @c MMMMMMM: marker, formatted according to #Marker_t
73  * - @c BB: block info, of size #FLASH_DATA_HEADER_SIZE
74  * - @c xxxx: block bytes, the (compressed) data bytes moved from EEPROM and now stored in FLASH
75  * .
76  *
77  * @code
78  * EEPROM EEPROM
79  * with samples a..d with samples a..e
80  * +---------+ +---------+
81  * | aaabbbc | | aaabbbc |
82  * | ccdddMM | After calling | ccdddee |
83  * | MMMMM | Storage_Write | eMMMMMM |
84  * | | with n=1: | MM |
85  * | | ------------------> | |
86  * | HH | | HH |
87  * +---------+ +---------+
88  * @endcode
89  *
90  * The ALON register will then point to the new location of the marker; while the hint will not be updated and will
91  * for sure contain faulty information.
92  *
93  * @dot
94  * digraph "Alon, Marker OK" {
95  * node [shape=box];
96  * subgraph a {
97  * rank=same
98  * AA [label="ALON register", URL="\ref STORAGE_FIRST_ALON_REGISTER"]
99  * }
100  * subgraph b {
101  * rank=same
102  * MMMMMMM [label="Moving marker in EEPROM", URL="\ref Marker_t"]
103  * HH [label="Hint information on a\nfixed EEPROM location", URL="\ref HINT_ABSOLUTE_BYTE_OFFSET"]
104  * }
105  * subgraph c {
106  * rank=same
107  * flash [label="flashByteCursor", URL="\ref Storage_Instance_t.flashByteCursor"]
108  * random [label="Wrong location", shape=point]
109  * }
110  *
111  * AA -> MMMMMMM
112  * HH -> random
113  * MMMMMMM -> flash
114  * }
115  * @enddot
116  *
117  * When power gets lost, the ALON register contents is reset to @c 0, and will then point to a faulty location as
118  * well.
119  *
120  * @dot
121  * digraph "Marker OK" {
122  * node [shape=box];
123  * subgraph a {
124  * rank=same
125  * AA [label="ALON register", URL="\ref STORAGE_FIRST_ALON_REGISTER"]
126  * }
127  * subgraph b {
128  * rank=same
129  * MMMMMMM [label="Moving marker in EEPROM", URL="\ref Marker_t"]
130  * HH [label="Hint information on a\nfixed EEPROM location", URL="\ref HINT_ABSOLUTE_BYTE_OFFSET"]
131  * NULL [label="0"]
132  * }
133  * subgraph c {
134  * rank=same
135  * flash [label="flashByteCursor", URL="\ref Storage_Instance_t.flashByteCursor"]
136  * random [shape=point]
137  * }
138  *
139  * AA -> NULL
140  * HH -> random
141  * MMMMMMM -> flash
142  * }
143  * @enddot
144  *
145  * Only the marker will then still be correct, which can be recovered only through a full backward search. When
146  * that is done, after leaving #Storage_DeInit, all three recovery structures will be correctly linked.
147  *
148  * @dot
149  * digraph "Alon, Hint, Marker OK" {
150  * node [shape=box];
151  * subgraph a {
152  * rank=same
153  * AA [label="ALON register", URL="\ref STORAGE_FIRST_ALON_REGISTER"]
154  * }
155  * subgraph b {
156  * rank=same
157  * MMMMMMM [label="Moving marker in EEPROM", URL="\ref Marker_t"]
158  * HH [label="Hint information on a\nfixed EEPROM location", URL="\ref HINT_ABSOLUTE_BYTE_OFFSET"]
159  * }
160  * subgraph c {
161  * rank=same
162  * flash [label="flashByteCursor", URL="\ref Storage_Instance_t.flashByteCursor"]
163  * }
164  *
165  * AA -> MMMMMMM
166  * HH -> MMMMMMM
167  * MMMMMMM -> flash
168  * }
169  * @enddot
170  *
171  * @par Data corruption
172  * When the battery is degrading, two scenarios can cause a corruption:
173  * - the supplied voltage drops too much during a flash operation, hanging the IC or causing a reset. This can
174  * corrupt the FLASH portion being written to.
175  * - the supplied voltage drops too much during an EEPROM operation, hanging the IC or causing a reset. This can
176  * corrupt the EEPROM row being written to.
177  * .
178  * The storage module can be fully resilient against this: no data present in the non-volatile memories EEPROM and
179  * FLASH is lost and all samples can still be read out, with one exception: the samples written after the last
180  * update to the hint structure cannot be recovered, as no duplicate information is yet available.
181  *
182  * After a hard reset, the general purpose registers are reset to 0, and the samples temporarily stored there are
183  * also lost. This brings the total number of minimum lost samples to
184  * #STORAGE_WRITE_RECOVERY_EVERY_X_SAMPLES + #STORAGE_SAMPLE_ALON_CACHE_COUNT - both are diversity settings the
185  * application can set.
186  *
187  * @par Using EEPROM @b and FLASH
188  * Both EEPROM and FLASH are used to store data - in the form of equisized samples. Since writing to EEPROM is
189  * cheaper, faster and less complicated than writing to FLASH, it is the preferred storage medium. Whenever the
190  * size of all the samples stored in EEPROM is large enough - see #STORAGE_BLOCK_SIZE_IN_SAMPLES - all that data is
191  * moved in one operation to FLASH. EEPROM is then completely empty, and new samples are then in EEPROM again.
192  *
193  * The memory content changes are visually depicted below. If writing a new sample would increase the size to equal
194  * to or higher than #STORAGE_BLOCK_SIZE_IN_SAMPLES, first the EEPROM contents are moved, then the new sample is
195  * written.
196  *
197  * @code
198  * EEPROM EEPROM
199  * with samples a..e with sample f only
200  * +---------+ +---------+
201  * | aaabbbc | | fffMMMM |
202  * | cc..... | | MMM |
203  * | ....... | | |
204  * | ....ddd | ----------------------> | |
205  * | eeeMMMM | | |
206  * | MMM HH | | HH |
207  * +---------+ +---------+
208  *
209  * FLASH FLASH
210  * with one block moved from EEPROM with two blocks moved from EEPROM
211  * +---------+ +---------+
212  * | BBxxxxx | | BBxxxxx |
213  * | xxxxxxx | | xxxxxxx |
214  * | x | ----------------------> | xBBxxxx |
215  * | | | xxxxx |
216  * | | | |
217  * | | | |
218  * +---------+ +---------+
219  * @endcode
220  *
221  * @par Caching data
222  * To reduce the number of EEPROM flushes, data is initially @b not stored in non-volatile memories EEPROM and
223  * FLASH. Instead, the reserved general purpose registers are used to cache the data. The value is preserved
224  * when entering and leaving a deep power down, but any inadvertent reset clears the registers, losing the data.
225  * This is not a disadvantage, since the cached data will be moved to non-volatile memory before writing the hint
226  * The benefit is a reduction of the required EEPROM program operations. This helps in avoiding the write
227  * endurance limit even when storing massive amounts of data, and also reduces the stress on the battery when it
228  * nears end-of-life.
229  *
230  * @anchor storage_initializing_par
231  * @par Initializing
232  * The implementation of #Storage_Init tries to recover the complete state as fast as possible. This can be done
233  * when either the recovery info or the hint information points to a valid marker structure. When a slow search
234  * is required, the initialization function make sure a next call will be fast.
235  * @n #Storage_Init implements this flowchart:
236  *
237  * @dot
238  * digraph "Storage_Init" {
239  * start [label="", shape=circle];
240  *
241  * subgraph cluster_rhf {
242  * rank=same
243  * label="Find Marker"
244  *
245  * subgraph r {
246  * node_rv [label="Recovery info\nfrom ALON register\nvalid?", shape=diamond]
247  * node_rg [label="Read marker location\nin EEPROM pointed to\nby recovery info" shape=rectangle]
248  * node_rmv [label="Marker\nvalid?", shape=diamond]
249  * }
250  * subgraph h {
251  * node_hv [label="Hint info\nfrom fixed EEPROM\nlocation valid?", shape=diamond]
252  * node_hg [label="Read marker location\nin EEPROM pointed to\nby hint info" shape=rectangle]
253  * node_hmv [label="Marker\nvalid?", shape=diamond]
254  * }
255  * subgraph f {
256  * node_none [label="----------------------------------", shape=none, fontcolor=white]
257  * node_f [label="Start a slow search\nRead the entire EEPROM\nto find a valid marker", shape=rectangle]
258  * node_fmv [label="Marker\nfound?", shape=diamond]
259  * }
260  * }
261  *
262  * subgraph c {
263  * node_c1 [label="Hint info\nvalid?", shape=diamond]
264  * node_c2 [label="Hint info\nvalid?", shape=diamond]
265  * }
266  *
267  * subgraph cluster_i {
268  * label=" Initialize instance"
269  *
270  * node_inito [label="No data present or\nno data can be recovered.\nWrite a new marker and a new hint", shape=rectangle]
271  * node_inith [label="Recover data from last row and\naccept potential loss of data.\nWrite a new marker", shape=rectangle]
272  * node_initm1 [label="All data is retained.\nWrite a new hint", shape=rectangle]
273  * node_initm2 [label="All data is retained", shape=rectangle]
274  * }
275  *
276  * end [label="", shape=circle, style=filled, color=black];
277  *
278  * start -> node_rv
279  * node_rv -> node_rg [label="yes"]
280  * node_rv -> node_hv [label=" no"]
281  * node_rg -> node_rmv
282  * node_rmv -> node_c2 [label=" yes"]
283  * node_rmv -> node_hv [label="no"]
284  * node_hv -> node_hg [label="yes"]
285  * node_hv -> node_f [label="no"]
286  * node_hg -> node_hmv
287  * node_hmv -> node_c2 [label=" yes"]
288  * node_hmv -> node_f [label="no"]
289  * node_f -> node_fmv
290  * node_fmv -> node_c2 [label="yes"]
291  * node_fmv -> node_c1 [label="no"]
292  * node_c1 -> node_inith [label=" yes"]
293  * node_c1 -> node_inito [label="no"]
294  * node_c2 -> node_initm1 [label=" no"]
295  * node_c2 -> node_initm2 [label=" yes"]
296  * node_inito -> end
297  * node_inith -> end
298  * node_initm1 -> end
299  * edge [weight=2]
300  * node_initm2 -> end
301  * }
302  * @enddot
303  */
304 
305 /**
306  * The absolute offset to the very first byte of the assigned EEPROM region.
307  * @note Unless explicitly specified, all bit and byte offsets referring to the EEPROM region in the code are relative
308  * to this value.
309  */
310 #define EEPROM_ABSOLUTE_FIRST_BYTE_OFFSET (STORAGE_EEPROM_FIRST_ROW * EEPROM_ROW_SIZE)
311 
312 /**
313  * The absolute offset to the very last byte of the assigned EEPROM region. */
314 #define EEPROM_ABSOLUTE_LAST_BYTE_OFFSET (((STORAGE_EEPROM_LAST_ROW + 1) * EEPROM_ROW_SIZE) - 1)
315 
316 /** Translates a flash byte cursor relative to assigned FLASH region to a byte address. */
317 #define FLASH_CURSOR_TO_BYTE_ADDRESS(flashByteCursor) \
318  ((uint8_t *)(FLASH_START + (STORAGE_FLASH_FIRST_PAGE * FLASH_PAGE_SIZE) + (flashByteCursor)))
319 
320 /** Translates a FLASH byte cursor relative to assigned FLASH region to the number of the page the cursor refers to. */
321 #define FLASH_CURSOR_TO_PAGE(flashByteCursor) (STORAGE_FLASH_FIRST_PAGE + ((flashByteCursor) / FLASH_PAGE_SIZE))
322 
323 /** Translates a FLASH page number to the start address of that page. */
324 #define FLASH_PAGE_TO_ADDRESS(type, flashPage) ((type)(FLASH_START + ((flashPage) * FLASH_PAGE_SIZE)))
325 
326 /** The very first byte address of the assigned FLASH region. */
327 #define FLASH_FIRST_BYTE_ADDRESS FLASH_PAGE_TO_ADDRESS(uint8_t *, STORAGE_FLASH_FIRST_PAGE)
328 
329 /** The very last byte address of the assigned FLASH region. */
330 #define FLASH_LAST_BYTE_ADDRESS (FLASH_PAGE_TO_ADDRESS(uint8_t *, STORAGE_FLASH_LAST_PAGE + 1) - 1)
331 
332 /** The very first 32-bit word address of the assigned FLASH region. */
333 #define FLASH_FIRST_WORD_ADDRESS FLASH_PAGE_TO_ADDRESS(uint32_t *, STORAGE_FLASH_FIRST_PAGE)
334 
335 /** The very last WORD address of the assigned FLASH region. */
336 #define FLASH_LAST_WORD_ADDRESS (FLASH_PAGE_TO_ADDRESS(uint32_t *, STORAGE_FLASH_LAST_PAGE + 1) - 1)
337 
338 /**
339  * The size in bytes of the meta data stored just in front of the (compressed) data block in FLASH. The LSBit of the
340  * first byte after these header size contains the start of the (compressed) data block.
341  * This header is used to give the decompress callback the correct arguments, and to deduce where to find the next
342  * block.
343  * @note Either this size is a value greater than 0 but less than #STORAGE_UNCOMPRESSED_BLOCK_SIZE_IN_BITS, which
344  * indicates the contents have been compressed; either this size is equal to #STORAGE_UNCOMPRESSED_BLOCK_SIZE_IN_BITS
345  * which indicates no compression/decompression algorithm was set or compression yielded bad results on this block and
346  * therefore the block was stored uncompressed.
347  * @see FLASH_BLOCK_SIZE
348  */
349 #define FLASH_DATA_HEADER_SIZE STORAGE_BLOCK_HEADER_SIZE
350 
351 /**
352  * Given the header value of a (compressed) data block in FLASH, calculates the size in bytes of the complete block,
353  * @b including #FLASH_DATA_HEADER_SIZE.
354  * The start of a new block @b must @c always be stored on a 32-bit word boundary: this allows to continue writing the
355  * same page without erasing. As a consequence, up to 31 padding bits after the end of previous (compressed) data
356  * block are lost.
357  * @param bitCount The size in bits of the (compressed) data block, @b excluding #FLASH_DATA_HEADER_SIZE.
358  * @note
359  * - From the FLASH specification:
360  * <em>It is possible to write only a sub-set of the page (Y words), but the maximum number of
361  * times that a page can be written to, before an erase must be performed, is 16.
362  * WARNING: Writing a page more than 16 times without erasing may result in corrupting
363  * the page’s contents, due to write disturb (an intrinsic mechanism to the programming
364  * mechanism of the C14EFLASH)</em>
365  * - From the NVMC datasheet:
366  * <em>Due to the presence of ECC, it is not allowed to modify additional bits inside a memory
367  * word where some bits have already been programmed: the resulting ECC code in
368  * memory would then be the AND of the codes for the previous and new value written, and
369  * will most likely be inconsistent with the resulting data, potentially resulting in unwanted or
370  * missing bit corrections, or spurious error conditions. [C-NODPG]</em>
371  * .
372  */
373 #define FLASH_BLOCK_SIZE(bitCount) (4 * STORAGE_IDIVUP((bitCount) + FLASH_DATA_HEADER_SIZE * 8, 32))
374 
375 /**
376  * The first of two special values that are used in #Marker_t to be able to reconstruct the EEPROM and FLASH bit cursor
377  * in case the battery has died - and thus the register value has been reset to zero.
378  * @note These values are not chosen randomly: the code using them - #FindMarker - is depending on their specific bit
379  * values.
380  */
381 #define MARKER_HEADER ((int)0x0000FFFF)
382 #define MARKER_FOOTER ((int)0x7FFFFFFF) /**< The second special value. @see MARKER_HEADER */
383 
384 /** The mask to use to zero out all possible 1 bits in #Marker_t.flashByteCursor */
385 #define MARKER_CURSOR_ZERO_MASK 0xFFFF8003
386 
387 /* ------------------------------------------------------------------------- */
388 
389 #if !STORAGE_FLASH_FIRST_PAGE
390 extern const int _etext; /**< Generated by the linker, used to calculate #STORAGE_FLASH_FIRST_PAGE */
391 extern const int _data; /**< Generated by the linker, used to calculate #STORAGE_FLASH_FIRST_PAGE */
392 extern const int _edata; /**< Generated by the linker, used to calculate #STORAGE_FLASH_FIRST_PAGE */
393 __attribute__ ((section(".noinit")))
394 static int sStorageFlashFirstPage; /* Initialized in #Storage_Init, RO afterwards. */
395  #undef STORAGE_FLASH_FIRST_PAGE
396  #define STORAGE_FLASH_FIRST_PAGE sStorageFlashFirstPage
397 #endif
398 
399 /* ------------------------------------------------------------------------- */
400 
401 /** @see Storage_Instance_t.readLocation */
402 typedef enum LOCATION {
403  LOCATION_UNKNOWN, /**< A successful call to #Storage_Seek is required. */
404 #if STORAGE_SAMPLE_ALON_CACHE_COUNT > 0
405  LOCATION_CACHE, /**< Memory in the assigned general purpose registers is targeted. */
406 #endif
407  LOCATION_EEPROM, /**< Memory in the assigned EEPROM region is targeted. */
408  LOCATION_FLASH /**< Memory in the assigned FLASH region is targeted. */
409 } LOCATION_T;
410 
411 /** The size of the first bits of the cache, in the same general purpose register where #RecoverInfo_t is stored. */
412 #define FIRST_BITS_OF_CACHE_SIZE 13
413 
414 /**
415  * An instance of this structure is stored in the designated register of the ALON domain, and points to where to find
416  * #Marker_t. This structure is stored in ALON and allows for the fastest initialization.
417  * If all these values are 0, either no logging is ongoing, or the battery has gone empty. To discover which case
418  * holds true, first #Hint_t is checked; if that fails too, the EEPROM is checked backwards for the location of a stored
419  * instance of type #Marker_t (which is time consuming). Once found, the correct values for this structure can be
420  * reconstructed.
421  */
422 typedef struct RecoverInfo_s {
423  unsigned int eepromBitCursor : 15; /**< @see Storage_Instance_t.eepromBitCursor */
424 
425  /**
426  * The number of samples that are cached in the general purpose registers.
427  * These are stored starting from @c firstBitsOfCache up to the last general purpose register.
428  * A value of 0 indicates no samples are cached.
429  */
430  unsigned int sampleCacheCount : 4;
431 
432  /** A filler. It allows to perform a sanity check at compile time on the size of RecoverInfo_s. */
434 } RecoverInfo_t;
435 
436 /** Byte size of #Hint_t. Checked at compile time using #checkSizeOfHint. */
437 #define SIZE_OF_HINT 4
438 
439 /** The absolute offset to a copy of the hint structure, to validate its contents. */
440 #define INVERSE_HINT_ABSOLUTE_BYTE_OFFSET ((EEPROM_ABSOLUTE_LAST_BYTE_OFFSET + 1 - SIZE_OF_HINT))
441 
442 /** The absolute offset to #Hint_t at the end of the assigned EEPROM region. */
443 #define HINT_ABSOLUTE_BYTE_OFFSET (INVERSE_HINT_ABSOLUTE_BYTE_OFFSET - SIZE_OF_HINT)
444 
445  /** Byte size of the duplicate information. */
446 #if STORAGE_REDUCE_RECOVERY_WRITES
447  #define SIZE_OF_DUPLICATE_DATA (EEPROM_ROW_SIZE - (2 * SIZE_OF_HINT))
448  #if SIZE_OF_DUPLICATE_DATA != 56
449  #error Unexpected value for SIZE_OF_DUPLICATE_DATA. Check the comments for magic values 56 and 8 that are related to this.
450  #endif
451 #else
452  #define SIZE_OF_DUPLICATE_DATA 64
453 #endif
454 
455  /** The absolute offset to the recovery information at the end of the assigned EEPROM region. */
456 #if STORAGE_REDUCE_RECOVERY_WRITES
457  #define DUPLICATE_DATA_ABSOLUTE_BYTE_OFFSET (EEPROM_ABSOLUTE_LAST_BYTE_OFFSET + 1 - EEPROM_ROW_SIZE)
458 #else
459  #define DUPLICATE_DATA_ABSOLUTE_BYTE_OFFSET (EEPROM_ABSOLUTE_LAST_BYTE_OFFSET + 1 - (2 * EEPROM_ROW_SIZE))
460 #endif
461 
462 /**
463  * An instance of this structure is stored at the fixed location #HINT_ABSOLUTE_BYTE_OFFSET, and points to where to
464  * find #Marker_t. If all the information contained is correct (which may not be the case) it still allows for a fast
465  * initialization.
466  * - @c eepromBitCursor @b may contain correct information. If not, the EEPROM is checked backwards for the location of
467  * a stored instance of type #Marker_t (which is time consuming) and this hint structure is adapted (so a next
468  * initialization under the same circumstances is done quickly).
469  * - The Hint_t instance also contains a duplicate reference to flashByteCursor. This will be used when the marker is no
470  * longer available or is corrupt.
471  * .
472  */
473 typedef struct Hint_s {
474  /** @see Storage_Instance_t.eepromBitCursor */
475  uint16_t eepromBitCursor;
476 
477  /** @see Storage_Instance_t.flashByteCursor */
478  uint16_t flashByteCursor;
479 } Hint_t;
480 
481 /**
482  * An instance of this structure is stored in EEPROM, right after the end of the last sample in EEPROM.
483  * It is used to re-create #sInstance upon initialization.
484  * Since a number of false candidates may be proposed, it contains a @c header and a @c footer to have a near 100%
485  * chance of detecting erroneous information.
486  */
487 typedef struct Marker_s {
488  const int header; /**< Must equal #MARKER_HEADER, or @c flashByteCursor is not valid. */
489 
490  /**
491  * @see Storage_Instance_t.flashByteCursor
492  * @note Usage
493  * - RO in #Storage_Init,
494  * - WO to set the value equal to #Storage_Instance_t.flashByteCursor in #Storage_DeInit, or to clear the
495  * marker from EEPROM in #Storage_Reset,
496  * - not to be read, not to be written to at all other times.
497  * .
498  */
500 
501  const int footer; /**< Must equal #MARKER_FOOTER, or @c flashByteCursor is not valid. */
502 } Marker_t;
503 
504 /**
505  * The total number of bytes in EEPROM consumed by meta-data, to be able to keep track of what is stored in FLASH and
506  * EEPROM, even after a power-off.
507  * Apart from #Marker_t, there is an additional bit that marks whether the storage in FLASH and EEPROM is empty or at
508  * least one sample is stored in EEPROM - @see #Hint_t
509  */
510 #define SIZE_OF_MARKER 12
511 
512 /**
513  * Stores all meta data to perform the requested reads and writes in FLASH and EEPROM. Upon de-initialization, an
514  * updated instance of #Marker_t is updated and stored in EEPROM, and an updated instance of #RecoverInfo_t is stored
515  * in the ALON domain, using the information in this structure.
516  */
517 typedef struct Storage_Instance_s {
518  /**
519  * The position of the first bit where new data can be stored in EEPROM, relative to #STORAGE_EEPROM_FIRST_ROW.
520  * This doubles up as the start position of a stored instance of type #Marker_t in EEPROM.
521  * There are at most @c EEPROM_NR_OF_RW_ROWS rows of EEPROM that can be used for storage, thus an EEPROM bit cursor
522  * always has values less than 2**15: 15 bits are required to store this information.
523  */
525 
526  /**
527  * The position of the first byte where new data can be stored in FLASH, relative to #STORAGE_FLASH_FIRST_PAGE.
528  * @pre Must be 32-bit word-aligned: multiple FLASH writes in the same page must occur at word boundaries. See
529  * #FLASH_BLOCK_SIZE
530  * @note There are at most (FLASH_NR_OF_RW_SECTORS * FLASH_PAGES_PER_SECTOR - program size) pages of FLASH that
531  * can be used for storage, thus a FLASH byte cursor always has values less than 2**15; and due to the boundary
532  * requirement, the 2 LSBits must be 0 as well: 13 bits are thus required to store this information.
533  * Masking with #MARKER_CURSOR_ZERO_MASK must thus always yield a @c 0 value.
534  */
536 
537  /* ------------------------------------------------------------------------- */
538 
539  /**
540  * Indicates the type of memory to @c readCursor targets. It also defines the type of @c readCursor:
541  * a byte cursor or a bit cursor.
542  */
544 
545  /**
546  * - If @c readLocation equals #LOCATION_FLASH: the offset in bytes relative to
547  * #FLASH_FIRST_BYTE_ADDRESS where to read next in FLASH. This points to the two-byte header that precedes
548  * a (compressed) data block, stored in FLASH.
549  * - If @c readLocation equals #LOCATION_EEPROM: the offset in bits relative to
550  * #EEPROM_ABSOLUTE_FIRST_BYTE_OFFSET where to read next in EEPROM. This points to the position of the LSBit of
551  * the next sample to read.
552  * - If @c readLocation equals #LOCATION_CACHE: a relative index. The index of the sample that is stored
553  * in the cache. @c 0 indicates the oldest sample still present in the cache.
554  * @note A negative value indicates nothing can be read. A call to #Storage_Seek is then required.
555  */
557 
558  /**
559  * A sequence number to help identify where the next sample, which must be read out, is located.
560  * - In case of reading from FLASH, where (compressed) blocks of samples are stored, this is the absolute sequence
561  * number of the first sample in that block after decompressing.
562  * - In case of reading from EEPROM, this is the absolute sequence number of the sample @c readCursor points to.
563  * - In case of reading from sCache, this is the absolute sequence number of the sample @c readCursor points to.
564  * The very first sample written via #Storage_Write has sequence number 0.
565  * @note A negative value indicates nothing can be read. A call to #Storage_Seek is then required.
566  */
568 
569  /**
570  * The sequence number that was requested in the last call to #Storage_Seek.
571  * - This value equals @c readSequence in case @c readLocation equals #LOCATION_EEPROM or #LOCATION_CACHE
572  * - This value may be equal - but is unlikely to - in case @c readLocation equals #LOCATION_FLASH
573  * .
574  */
576 
577  /* ------------------------------------------------------------------------- */
578 
579  /**
580  * Determines what contents are available in #STORAGE_WORKAREA.
581  * - The flash byte offset relative to #FLASH_FIRST_BYTE_ADDRESS where the header preceding the (compressed) data
582  * block can be found; these contents are decompressed and the samples, packed without padding bits, are available
583  * from the start of #STORAGE_WORKAREA.
584  * - @c -1 if #STORAGE_WORKAREA does not contain samples after decompressing a data block stored in FLASH, or
585  * if #STORAGE_WORKAREA is used to compress data.
586  * .
587  */
590 
591 /**
592  * The assigned EEPROM region cannot be used fully: there must be always room for #Marker_t and the last couple of
593  * bytes are used to #Hint_t. To avoid corruption in both the marker and the hint, the hint is placed in a separate
594  * row.
595  * The total overhead is summed up here.
596  */
597 #if STORAGE_REDUCE_RECOVERY_WRITES
598  #define EEPROM_OVERHEAD_IN_BITS ((SIZE_OF_MARKER + EEPROM_ROW_SIZE) * 8)
599 #else
600  #define EEPROM_OVERHEAD_IN_BITS ((SIZE_OF_MARKER + 2 * EEPROM_ROW_SIZE) * 8)
601 #endif
602 
603 #if STORAGE_MAX_BLOCK_SIZE_IN_SAMPLES != (((STORAGE_EEPROM_ROW_COUNT * EEPROM_ROW_SIZE * 8) - EEPROM_OVERHEAD_IN_BITS) / STORAGE_BITSIZE)
604  #error Internal memory storage model has changed - likely Marker_t or Hint_t - and no longer matches the define STORAGE_MAX_BLOCK_SIZE_IN_SAMPLES
605 #endif
606 
607 #pragma GCC diagnostic push
608 #pragma GCC diagnostic ignored "-Wunused-parameter"
609 
610 /** If this construct doesn't compile, #STORAGE_TYPE can not hold #STORAGE_BITSIZE bits. */
611 int checkSizeOfSampleType[(sizeof(STORAGE_TYPE) * 8 < STORAGE_BITSIZE) ? -1 : 1]; /* Dummy variable since we can't use sizeof during precompilation. */
612 
613 /** If this construct doesn't compile, the define #SIZE_OF_MARKER is no longer equal to @c sizeof(#Hint_t) */
614 int checkSizeOfHint[(SIZE_OF_HINT == sizeof(Hint_t)) ? 1 : -1]; /* Dummy variable since we can't use sizeof during precompilation. */
615 
616 /** If this construct doesn't compile, the define #SIZE_OF_MARKER is no longer equal to @c sizeof(#Marker_t) */
617 int checkSizeOfMarker[(SIZE_OF_MARKER == sizeof(Marker_t)) ? 1 : -1]; /* Dummy variable since we can't use sizeof during precompilation. */
618 
619 /** If this construct doesn't compile, adjust #FIRST_BITS_OF_CACHE_SIZE. RecoverInfo_t must be exactly 32 bits in size */
620 int checkSizeOfRecoverInfo[sizeof(RecoverInfo_t) == 4 ? 1 : -1]; /* Dummy variable since we can't use sizeof during precompilation. */
621 
622 #if STORAGE_MAX_SAMPLE_ALON_CACHE_COUNT != (((5 - STORAGE_FIRST_ALON_REGISTER) * 32 - (32 - FIRST_BITS_OF_CACHE_SIZE)) / STORAGE_BITSIZE)
623  /* 5: there are 5 general purpose registers */
624  /* 32: one word == 32 bits */
625  #error Internal memory storage model has changed - likely RecoverInfo_t - and no longer matches the define STORAGE_MAX_SAMPLE_ALON_CACHE_COUNT
626 #endif
627 
628 #pragma GCC diagnostic pop
629 
630 /* ------------------------------------------------------------------------- */
631 
632 /**
633  * The module's instance information, where recovered information is copied to, and where all updates are stored.
634  * In #Storage_DeInit, this information is copied to more permanent storage.
635  */
636 __attribute__ ((section(".noinit")))
638 
639 /**
640  * To reduce the call count to @c Chip_PMU_GetRetainedData and @c Chip_PMU_SetRetainedData, data is copied to SRAM in
641  * #Storage_Init once, and copied back to GPREG in #Storage_DeInit once.
642  */
643 __attribute__ ((section(".noinit"))) __attribute__((aligned (4)))
644 static uint8_t sCache[4 * (5 - STORAGE_FIRST_ALON_REGISTER)];
645 
646 /**
647  * Initialized in #Storage_Init to points to the recovery structure in #sCache.
648  */
649 __attribute__ ((section(".noinit")))
651 
652 /* ------------------------------------------------------------------------- */
653 
654 /**
655  * Set to @c true after writing a new sample in EEPROM, used in #Storage_DeInit to know when to write the marker.
656  * Not set when resetting the module; as there is no need for a marker in that case.
657  */
658 static bool sEepromBitCursorChanged = false;
659 
660 #if STORAGE_WORKAREA_SELF_DEFINED == 1
661 __attribute__ ((section(".noinit"))) __attribute__((aligned (4)))
663 #endif
665 
666 extern int STORAGE_COMPRESS_CB(int eepromByteOffset, int bitCount, void * pOut);
667 extern int STORAGE_DECOMPRESS_CB(const uint8_t * pData, int bitCount, void * pOut);
668 
669 #pragma GCC diagnostic push
670 #pragma GCC diagnostic ignored "-Wunused-parameter"
671 /**
672  * This is a dummy implementation to provoke fallback behavior: a plain copy from EEPROM to FLASH.
673  * It is only used when #STORAGE_COMPRESS_CB is not overridden in an application specific @c app_sel.h header file
674  * @see pStorage_CompressCb_t
675  * @see STORAGE_COMPRESS_CB
676  * @param eepromByteOffset unused
677  * @param bitCount unused
678  * @param pOut unused
679  * @return @c 0
680  */
681 int Storage_DummyCompressCb(int eepromByteOffset, int bitCount, void * pOut)
682 {
683  return 0;
684 }
685 /**
686  * This is a dummy implementation to provoke a failure; it should normally never be called, since the dummy compression
687  * implementation will provoke the fallback behavior: a plain copy from EEPROM to FLASH, and vice-versa while reading.
688  * It is only used when #STORAGE_COMPRESS_CB is not overridden in an application specific @c app_sel.h header file
689  * @see pStorage_DecompressCb_t
690  * @see STORAGE_DECOMPRESS_CB
691  * @param pData unused
692  * @param bitCount unused
693  * @param pOut unused
694  * @return @c 0
695  */
696 int Storage_DummyDecompressCb(const uint8_t * pData, int bitCount, void * pOut)
697 {
698  return 0;
699 }
700 #pragma GCC diagnostic pop
701 
702 /* ------------------------------------------------------------------------- */
703 
704 static void ResetInstance(void);
705 static void ShiftAlignedData(uint8_t * pTo, const uint8_t * pFrom, const int bitAlignment, const int bitCount);
706 static void ShiftUnalignedData(uint8_t * pTo, const uint8_t * pFrom, const int bitAlignment, const int bitCount);
707 #if STORAGE_SAMPLE_ALON_CACHE_COUNT > 0
708 static bool CacheSample(const STORAGE_TYPE * pSample);
709 static bool GetCachedSample(const int n, void * pData);
710 #endif
711 static void WriteToEeprom(const int bitCursor, const void * pData, const int bitCount);
712 static void ReadFromEeprom(const unsigned int bitCursor, void * pData, const int bitCount);
713 static unsigned int FindMarker(Marker_t * pMarker);
714 static int GetEepromCount(void);
715 static int GetFlashCount(void);
716 #if STORAGE_FLASH_FIRST_PAGE <= STORAGE_FLASH_LAST_PAGE
717 static bool WriteToFlash(const int pageCursor, const uint8_t * pData, const int pageCount);
718 #endif
719 static int StoreSamplesInEeprom(const STORAGE_TYPE * pSamples, int n);
720 static bool MoveSamplesFromEepromToFlash(void);
721 static int ReadAndCacheSamplesFromFlash(int readCursor);
722 static bool ValidateRecoverInfo(void);
723 static bool ValidateMarker(const Marker_t * pMarker, int expectedFlashByteCursor);
724 static bool ValidateHint(const Hint_t * pHint);
725 static void WriteHint(void);
726 static void WriteMarker(void);
727 
728 /* ------------------------------------------------------------------------- */
729 
730 /**
731  * Re-initializes #sInstance with default values.
732  * @note No EEPROM reads or writes, no FLASH reads or writes are done.
733  * @post sBitCursorChanged is set when samples have become inaccessible.
734  */
735 static void ResetInstance(void)
736 {
740  sInstance.readSequence = -1;
741  sInstance.readCursor = -1;
744 }
745 
746 /**
747  * Copies a number of bits of byte aligned data to a buffer, non-byte aligned.
748  * @param pTo The location to copy to. A number of LSBits of the first byte are not touched.
749  * @param pFrom The location to copy from. The first bit to copy will become the LSBit of the first byte in this buffer.
750  * @param bitAlignment The number of bits to disregard in @c to. Must be less than @c 8.
751  * @param bitCount The number of bits to copy.
752  * @pre @code STORAGE_IDIVUP(bitCount, 8) @endcode bytes must be available in @c from.
753  * @post @code STORAGE_IDIVUP(bitCount + bitAlignment, 8) @endcode bytes will be written to @c to.
754  * @note The first @c bitAlignment bits of the first byte written to are not touched.
755  * @note For example, use this function to move bits in this way:
756  * - With @c - indicating not touched or don't care,
757  * - @c bitAlignment equal to @c 5, and
758  * - @c bitCount equal to @c 14:
759  * .
760  * @code 8765 4321 --fe dcba is copied as 321- ---- cba8 7654 0000 0fed @endcode
761  */
762 static void ShiftAlignedData(uint8_t * pTo, const uint8_t * pFrom, const int bitAlignment, const int bitCount)
763 {
764  const uint8_t mask = (uint8_t)(0xFF & ((1 << bitAlignment) - 1)); /* Covering the existing bits in the last byte. */
765 
766  ASSERT((bitAlignment >= 0) && (bitAlignment < 8));
767  ASSERT(bitCount > 0);
768 
769  /* 8 bits at a time, data is moved from @c from to @c to.
770  *
771  * 8 bits from @c from are copied to two consecutive bytes in @c to: 321- ---- ---8 7654 (-: don't care)
772  * - Step 1: write the lsbits of 87654321 such that the ongoing byte is filled
773  * ... xxxxxxxx 321xxxxx -------- ...
774  * - Step 2: write the msbits of 87654321 in the lsbit positions of the next byte
775  * ... xxxxxxxx 321xxxxx ---87654 ...
776  * (in the above example, @c bitAlignment equals 5)
777  */
778  int n = 0;
779  do {
780  /* Step 1: copy x lsbits of the current byte, with x = 8-bitAlignment so left shift bitAlignment places */
781  *pTo = (uint8_t)((pFrom[n] << bitAlignment) | ((*pTo) & mask));
782 
783  if ((n * 8) + 8 - bitAlignment < bitCount) {
784  /* Advance to the next location to copy bits to. */
785  pTo++;
786 
787  /* Step 2: copy x msbits of the new byte, with x = bitAlignment so right shift 8-bitAlignment places */
788  *pTo = (uint8_t)(pFrom[n] >> (8 - bitAlignment));
789  }
790 
791  n++;
792  } while (n * 8 < bitCount);
793 
794  /* Clear the bits that were copied in excess. */
795  uint8_t finalMask = (uint8_t)~(0xFF << ((bitCount + bitAlignment) % 8));
796  if (finalMask != 0) { /* finalMask equals zero when the last bit to retain is the MSBit of the last byte written. */
797  *pTo = *pTo & finalMask;
798  }
799 }
800 
801 /**
802  * Copies a number of bits of non-byte aligned data to a buffer, byte aligned.
803  * @param pTo The location to copy to. The first bit to copy will become the LSBit of the first byte in this buffer.
804  * @param pFrom The location to copy from. A number of LSBits are disregarded.
805  * @param bitAlignment The number of bits to disregard in @c from. Must be less than @c 8.
806  * @param bitCount The number of bits to copy.
807  * @pre @code STORAGE_IDIVUP(bitCount + bitAlignment, 8) @endcode bytes must be available in @c from.
808  * @post @code STORAGE_IDIVUP(bitCount, 8) @endcode bytes will be written to @c to.
809  * @note The remainder bits in @c to after @c bitCount bits is set to @c 0.
810  * @note For example, use this function to move bits in this way:
811  * - With @c - indicating not touched or don't care,
812  * - @c bitAlignment equal to @c 5, and
813  * - @c bitCount equal to @c 14:
814  * .
815  * @code 321- ---- cba8 7654 ---- -fed is copied as 8765 4321 00fe dcba @endcode
816  */
817 static void ShiftUnalignedData(uint8_t * pTo, const uint8_t * pFrom, const int bitAlignment, const int bitCount)
818 {
819  const uint8_t mask = 0xFF & (uint8_t)((1 << (8 - bitAlignment)) - 1); /* Covering the lsbits to retain in the copied byte. */
820 
821  ASSERT((bitAlignment >= 0) && (bitAlignment < 8));
822  ASSERT(bitCount > 0);
823 
824  /* 8 bits at a time, data is moved from @c from to @c to.
825  *
826  * 8 bits from two consecutive bytes in @c from: 321- ---- ---8 7654 (-: don't care) are copied to one byte in
827  * @c to:
828  * - Step 1: read the msbits of 321- ----
829  * byte = 0b00000321
830  * - Step 2: read the lsbits of ---8 7654
831  * byte = 0b87654321
832  * (in the above example, @c bitAlignment equals 5)
833  */
834  int n = 0;
835  do {
836  /* Step 1: copy x msbits of the current byte, with x = 8-bitAlignment so right shift bitAlignment places. */
837  *pTo = (uint8_t)((pFrom[n]) >> bitAlignment);
838 
839  /* Advance to the next location to copy bits from. */
840  n++;
841 
842  if (((n - 1) * 8) + 8 - bitAlignment < bitCount) {
843 
844  /* Step 2: copy x lsbits of the next byte, with x = bitAlignment so left shift 8-bitAlignment places. */
845  *pTo = (uint8_t)((pFrom[n] << (8 - bitAlignment)) | ((*pTo) & mask));
846 
847  if (n * 8 < bitCount) {
848  pTo++; /* Do not increment @c to unconditionally: @c finalmask still may have to be applied. */
849  }
850  }
851  } while (n * 8 < bitCount);
852 
853  /* Clear the bits that were copied in excess. */
854  uint8_t finalMask = (uint8_t)~(0xFF << (bitCount % 8));
855  if (finalMask != 0) { /* finalMask equals zero when the last bit to retain is the MSBit of the last byte written. */
856  *pTo = *pTo & finalMask;
857  }
858 }
859 
860 /* ------------------------------------------------------------------------- */
861 
862 #if STORAGE_SAMPLE_ALON_CACHE_COUNT > 0
863 /**
864  * Stores data in #sCache.
865  * @param pSample May not be @c NULL. The data to cache.
866  * @return @c true when the sample was copied; @c false when it did not fit and no changes were made.
867  */
868 static bool CacheSample(const STORAGE_TYPE * pSample)
869 {
870  bool success = false;
871 
874  int byteOffset = bitCursor / 8;
875  int bitAlignment = bitCursor % 8;
876  ShiftAlignedData(&sCache[byteOffset], (uint8_t *)pSample, bitAlignment, STORAGE_BITSIZE);
878  success = true;
879  }
880 
881  return success;
882 }
883 
884 /**
885  * Retrieves a sample cached in an earlier call to #CacheSample.
886  * @param n A relative index. Determines the sample to copy. A value of 0 indicates the oldest sample that is present in #sCache.
887  * @param pData May not be @c NULL. The sample is copied to the array pointed to.
888  * @return @c true if a sample was copied; @c false if less than @c n samples are stored in #sCache
889  */
890 static bool GetCachedSample(const int n, void * pData)
891 {
892  if (n < spRecoverInfo->sampleCacheCount) {
893  int bitCursor = (32 - FIRST_BITS_OF_CACHE_SIZE) + (n * STORAGE_BITSIZE);
894  int byteOffset = bitCursor / 8;
895  int bitAlignment = bitCursor % 8;
896  ShiftUnalignedData((uint8_t *)pData, &sCache[byteOffset], bitAlignment, STORAGE_BITSIZE);
897  }
898  return n < spRecoverInfo->sampleCacheCount;
899 }
900 #endif
901 
902 /* ------------------------------------------------------------------------- */
903 
904 /**
905  * @pre EEPROM is initialized
906  * @pre Enough free space must be available in EEPROM starting from @c bitCursor
907  * @param bitCursor Must be positive. The first bit where to start writing.
908  * @param pData May not be @c NULL.
909  * @param bitCount Must be strict positive.
910  * @post #sInstance is not touched.
911  */
912 static void WriteToEeprom(const int bitCursor, const void * pData, const int bitCount)
913 {
914  const int bitAlignment = bitCursor % 8; /* The number of lsbits written in the last byte. */
915  int byteOffset = EEPROM_ABSOLUTE_FIRST_BYTE_OFFSET + bitCursor / 8; /* The index to the last byte written. */
916 
917  ASSERT(bitCursor >= 0);
918  ASSERT(pData != NULL);
919  ASSERT(bitCount > 0);
920 
921  int byteCount = STORAGE_IDIVUP(bitCount + bitAlignment, 8);
922  uint8_t bytes[byteCount];
923 
924  /* Read the first byte we will write to, to preserve a number of LSBits in that byte which were written
925  * previously.
926  */
927  Chip_EEPROM_Read(NSS_EEPROM, byteOffset, &bytes, 1);
928  ShiftAlignedData(bytes, (uint8_t *)pData, bitAlignment, bitCount);
929  Chip_EEPROM_Write(NSS_EEPROM, byteOffset, bytes, byteCount);
930 }
931 
932 /**
933  * @pre EEPROM is initialized
934  * @pre Enough bits to read are available in EEPROM starting from @c bitCursor
935  * @param bitCursor Must be strict positive. The bit position in EEPROM to start reading: @c 0 means to start reading
936  * from the very first EEPROM bit assigned for bit storage, and so on.
937  * @param pData May not be @c NULL. All bits are copied in the array pointed to.
938  * @param bitCount Must be strict positive. When not a multiple of 8, the remainder MSBits of the last byte are set to
939  * @c 0.
940  * @post #sInstance is not touched.
941  */
942 static void ReadFromEeprom(const unsigned int bitCursor, void * pData, const int bitCount)
943 {
944  const int bitAlignment = bitCursor % 8; /* The number of lsbits written in the last byte. */
945  unsigned int byteOffset = EEPROM_ABSOLUTE_FIRST_BYTE_OFFSET + bitCursor / 8; /* The index to the last byte written. */
946 
947  ASSERT(pData != NULL);
948  ASSERT(bitCount > 0);
949 
950  int byteCount = STORAGE_IDIVUP(bitCount + bitAlignment, 8);
951  uint8_t bytes[byteCount];
952 
953  Chip_EEPROM_Read(NSS_EEPROM, (int)byteOffset, bytes, byteCount);
954  ShiftUnalignedData((uint8_t *)pData, bytes, bitAlignment, bitCount);
955 }
956 
957 /* ------------------------------------------------------------------------- */
958 
959 /**
960  * Search backwards in the assigned EEPROM region, looking for a valid marker.
961  * @param [out] pMarker : Where to copy the found marker data to. If @c 0 is returned, this will contain an invalid
962  * marker.
963  * @return The position of the first bit of a valid stored instance of type #Marker_t in EEPROM, relative to
964  * #STORAGE_EEPROM_FIRST_ROW. @c 0 if the marker could not be found.
965  */
966 static unsigned int FindMarker(Marker_t * pMarker)
967 {
968  unsigned int eepromBitCursor;
969  bool found = false;
970 
971  /* Start a slow search, checking the full EEPROM contents assigned to the storage module. */
972  unsigned int byteOffset = EEPROM_ABSOLUTE_LAST_BYTE_OFFSET - (EEPROM_OVERHEAD_IN_BITS / 8); /* Search backwards. */
973  do {
974  /* The marker consists of a header, a value, and a footer.
975  * By reading 16-bit words one at a time (step a) from high offset to low, we must find a value equal to
976  * @c 0xFFFF that is part of the FOOTER: see ---------------- below.
977  * If found (step b), we can read the word value @c h with relative byte offset @c -8 (step c) that is part
978  * of the HEADER: see ++++++++++++++++ below.
979  * A validation check is to verify whether @c h+1 is a power of 2 (step d): if so, from the number of bits
980  * set in @c h (step e) we know the extra bit offset to subtract to find the complete marker:
981  * -7 for Example 1, -13 for Example 2, -1 for Example 3.
982  * The full 96 bits can then be read out (step f) and validated (step g): #MARKER_HEADER and #MARKER_FOOTER
983  * values must be found, and the size of the value must be acceptable.
984  *
985  * Header Value Footer
986  * 0x00 0x00 0xFF 0xFF 0x00 0x00 0x?? 0x?? 0x7F 0xFF 0xFF 0xFF
987  * 00000000000000001111111111111111 00000000000000000vvvvvvvvvvvvv00 01111111111111111111111111111111
988  * ++++++++++++++++ ---------------- Example 1
989  * ++++++++++++++++ ---------------- Example 2
990  * ++++++++++++++++ ---------------- Example 3
991  * 0 1 2 3 4 5 6 7 8 9
992  * 01234567890123456789012345678901 23456789012345678901234567890123 45678901234567890123456789012345
993  *
994  * Magic values used:
995  * 2: (step a) the portion in number of bytes of the FOOTER value to search for.
996  * 0xFFFF: (step b) part of the FOOTER value.
997  * 8, -8: (while, step c) The distance in bytes between the start of the footer and the start of the header.
998  */
999  uint16_t word;
1000  Chip_EEPROM_Read(NSS_EEPROM, (int)byteOffset, &word, 2); /* step a */
1001  if (word == 0xFFFF) { /* step b */
1002  Chip_EEPROM_Read(NSS_EEPROM, (int)byteOffset - 8, &word, 2); /* step c */
1003  if (((word + 1) & word) == 0) { /* step d */
1004  unsigned int bits;
1005  for (bits = 0; word; bits++) { /* step e */
1006  word &= (uint16_t)(word - 1); /* Counting bits set, Brian Kernighan's way */
1007  }
1008  eepromBitCursor = (byteOffset - EEPROM_ABSOLUTE_FIRST_BYTE_OFFSET - 8) * 8 - (16 - bits);
1009  if ((eepromBitCursor % STORAGE_BITSIZE) == 0) {
1010  ReadFromEeprom(eepromBitCursor, pMarker, sizeof(Marker_t) * 8); /* step f */
1011  found = ValidateMarker(pMarker, -1);
1012  }
1013  }
1014  }
1015  byteOffset -= 2;
1016 
1017  } while ((!found) && (byteOffset >= EEPROM_ABSOLUTE_FIRST_BYTE_OFFSET + 8));
1018 
1019  if (!found) {
1020  eepromBitCursor = 0;
1021  memset(pMarker, 0, sizeof(Marker_t));
1022  }
1023  return eepromBitCursor;
1024 }
1025 
1026 /** @return The number of samples stored in EEPROM. */
1027 static int GetEepromCount(void)
1028 {
1029  ASSERT((sInstance.eepromBitCursor % STORAGE_BITSIZE) == 0); /* The integer division has no remainder. */
1031 }
1032 
1033 /** @return The number of samples stored in FLASH. */
1034 static int GetFlashCount(void)
1035 {
1036  int sequenceCount = 0;
1037  /* Loop over all the (compressed) data blocks in FLASH - each is storing the same amount of samples. */
1038  int readCursor = 0;
1039  while (readCursor < sInstance.flashByteCursor) {
1040  uint8_t * header = FLASH_CURSOR_TO_BYTE_ADDRESS(readCursor);
1041  int bitCount = (int)(header[0] | (header[1] << 8));
1042  sequenceCount += STORAGE_BLOCK_SIZE_IN_SAMPLES;
1043  readCursor += FLASH_BLOCK_SIZE(bitCount);
1044  }
1045  return sequenceCount;
1046 }
1047 
1048 /* ------------------------------------------------------------------------- */
1049 
1050 #if STORAGE_FLASH_FIRST_PAGE <= STORAGE_FLASH_LAST_PAGE
1051 /**
1052  * Writes a block of (compressed) samples to FLASH. After the operation, the complete FLASH contents are verified.
1053  * @param pageCursor Must be strict positive. The absolute page number: a value of @c 0 indicates the first page of
1054  * sector 0. Defines the first byte of the page where to start writing.
1055  * @param pData May not be @c NULL. Must be word (32 bits) aligned.
1056  * @param pageCount The number of pages to write. @c data must provide this multiple of #FLASH_PAGE_SIZE bytes of data.
1057  * @warning All interrupts are disabled at the beginning of this function and restored before leaving this function.
1058  * @warning This function will take 100+ milliseconds to complete.
1059  * @pre FLASH pages must have been erased beforehand - this is not checked.
1060  * @post #sInstance is not touched.
1061  * @return the result of the FLASH program action: @c true for success.
1062  */
1063 static bool WriteToFlash(const int pageCursor, const uint8_t * pData, const int pageCount)
1064 {
1065  uint8_t * pDest;
1066  uint32_t size;
1067  IAP_STATUS_T status;
1068  uint32_t sectorStart = (uint32_t)(pageCursor / FLASH_PAGES_PER_SECTOR);
1069  uint32_t sectorEnd = (uint32_t)((pageCursor + pageCount - 1) / FLASH_PAGES_PER_SECTOR);
1070 
1071  ASSERT(pageCursor > 0);
1072  ASSERT(pData != NULL);
1073  ASSERT(pageCount > 0);
1074  ASSERT(sectorEnd < FLASH_NR_OF_RW_SECTORS);
1075 
1076  pDest = (uint8_t *)FLASH_START + (pageCursor * FLASH_PAGE_SIZE);
1077  size = (uint32_t)pageCount * FLASH_PAGE_SIZE;
1078 
1079  status = Chip_IAP_Flash_PrepareSector(sectorStart, sectorEnd);
1080  if (status == IAP_STATUS_CMD_SUCCESS) {
1081  __disable_irq();
1082  status = Chip_IAP_Flash_Program(pData, pDest, size, 0);
1083  __enable_irq();
1084  }
1085  if (status == IAP_STATUS_CMD_SUCCESS) {
1086  /* To compare, only compare the new data, i.e. skip the initial 0xFF values, as they overlap with the last
1087  * portion of the previously written data ("o" in MoveSamplesFromEepromToFlash).
1088  */
1089  uint32_t fillWord = *(uint32_t *)pData;
1090  while ((fillWord == 0xFFFFFFFF) && (size > 0)) {
1091  pData += 4;
1092  pDest += 4;
1093  size -= 4;
1094  fillWord = *(uint32_t *)pData;
1095  }
1096  __disable_irq();
1097  status = Chip_IAP_Compare(pData, pDest, size, NULL);
1098  __enable_irq();
1099  }
1100  return status == IAP_STATUS_CMD_SUCCESS;
1101 }
1102 #endif
1103 
1104 /**
1105  * Stores @c n samples in EEPROM. If necessary, the oldest data is moved to FLASH.
1106  * @param pSamples May not be @c NULL. Pointer to the start of the array where to copy the samples from.
1107  * @param n The size of the array
1108  * @return The number of samples that were stored. A value less than @c n indicates insufficient storage capacity.
1109  */
1110 static int StoreSamplesInEeprom(const STORAGE_TYPE * pSamples, int n)
1111 {
1112  int count = 0;
1113  while (count < n) {
1115  /* There are enough samples stored in EEPROM to warrant a compression and a move to FLASH.
1116  * Clear the EEPROM by moving them to FLASH. When that is done, the EEPROM is fully empty again.
1117  * We do this just before writing a new sample in EEPROM, to ensure at least one sample is present in
1118  * EEPROM whenever at least sample has been given to the storage module. There is no code relying on this:
1119  * it just feels more natural to only move when absolutely necessary.
1120  */
1122  /* Even if it fails, we can still continue and try writing in the rest of the EEPROM.
1123  * The if test above only tests for equality, so we will not needlessly try again on each added sample.
1124  */
1125  }
1129  count++;
1130  }
1131  else {
1132  /* The EEPROM is fully filled with samples, and an earlier call to move data from EEPROM to FLASH failed
1133  * - if it succeeded, this branch would not be chosen.
1134  * Storage is full, both EEPROM and FLASH, and no data can be written any more.
1135  */
1136  break;
1137  }
1138  }
1139 
1140  sEepromBitCursorChanged |= (count > 0);
1141  return count;
1142 }
1143 
1144 /**
1145  * Clear the assigned EEPROM region, by moving all data to assigned FLASH region. Before moving the data, the
1146  * application is given the opportunity to compress the data. The (compressed) data block is then appended to the
1147  * existing data in FLASH.
1148  * Steps:
1149  * - Prepare #STORAGE_WORKAREA for FLASH write
1150  * - Copy data from EEPROM to #STORAGE_WORKAREA
1151  * - Call #STORAGE_COMPRESS_CB
1152  * - Flash
1153  * - Update pointers
1154  * @return @c true when the compression was successful and the data has been moved to FLASH, @c false when the
1155  * compression callback function returned @c false or when the FLASH storage is full: nothing has been changed in
1156  * FLASH or EEPROM in that case.
1157  * @post #sInstance is fully updated when this function returns.
1158  * @note Uses #STORAGE_WORKAREA. Not in use anymore when this function returns.
1159  */
1161 {
1162 #if STORAGE_FLASH_FIRST_PAGE > STORAGE_FLASH_LAST_PAGE
1163  /* There is no flash assigned for storage. Cannot move to flash. */
1164  return false;
1165 #else
1167  /* There is no flash available for storage. Cannot move to flash. */
1168  return false;
1169  }
1170  else {
1171  bool success;
1172  uint8_t * pOut = STORAGE_WORKAREA;
1173 
1175 
1176  /* Prepare a number of pages to FLASH:
1177  * Pages: |----------------|---...-------------|
1178  * Data: oooooohhcccccccccccc...ccfffffffffff
1179  * with:
1180  * - o: the last portion of the previously written (compressed) data block.
1181  * - h: the two-byte header indicating the size in bits of the (compressed) data block that follows.
1182  * - c: the (compressed) data block to write.
1183  * - f: the yet-unused trailing bytes of the last page where the new (compressed) data block is written to.
1184  * By adding 1-bits, we can later write without the need for a costly FLASH page erase cycle.
1185  */
1186 
1187  /* Determine the first page to flash in this call. It is likely that during the previous call to this
1188  * function the then-last page flashed was not completely filled; that last page in the previous call gets now
1189  * completely filled first and becomes the first page to flash in this call.
1190  */
1191  int firstFlashPage = FLASH_CURSOR_TO_PAGE(sInstance.flashByteCursor);
1192  int flashByteOffsetInPage = sInstance.flashByteCursor % FLASH_PAGE_SIZE;
1193 
1194  /* o: Ensure the part of the last page that was already written to in a previous move from EEPROM to FLASH
1195  * remains untouched.
1196  */
1197  memset(pOut, 0xFF, (size_t)flashByteOffsetInPage);
1198  pOut += flashByteOffsetInPage;
1199 
1200  /* c: The compress algorithm is to store the new (compressed) data block output just after the just copied data.
1201  * Skip the meta data header for now: that is filled in when the compression completed.
1202  */
1204  pOut + FLASH_DATA_HEADER_SIZE);
1205  if ((bitCount <= 0) || (bitCount >= STORAGE_UNCOMPRESSED_BLOCK_SIZE_IN_BITS)) {
1206  /* Compression failed, or resulted in a larger block size. In this case we still have a fallback:
1207  * copy the data from EEPROM to FLASH unaltered.
1208  */
1209  Chip_EEPROM_Read(NSS_EEPROM, EEPROM_ABSOLUTE_FIRST_BYTE_OFFSET, pOut + FLASH_DATA_HEADER_SIZE,
1212  }
1213  int compressedDataSizeInBytes = STORAGE_IDIVUP(bitCount, 8);
1214 
1215  /* h: */
1216  pOut[0] = (uint8_t)(bitCount & 0xFF);
1217  pOut[1] = (uint8_t)((bitCount >> 8) & 0xFF);
1218  pOut += FLASH_DATA_HEADER_SIZE;
1219 
1220  /* c: */
1221  pOut += compressedDataSizeInBytes;
1222 
1223  /* f: */
1224  while (((int)(pOut - STORAGE_WORKAREA) % FLASH_PAGE_SIZE) != 0) {
1225  *pOut = 0xFF;
1226  pOut++;
1227  }
1228 
1229  int newFlashByteCursor = sInstance.flashByteCursor + FLASH_BLOCK_SIZE(bitCount);
1230  ASSERT((newFlashByteCursor & 0x3) == 0); /* Must be 32-bit word-aligned. */
1231  if (FLASH_CURSOR_TO_BYTE_ADDRESS(newFlashByteCursor) - 1 > FLASH_LAST_BYTE_ADDRESS) {
1232  /* There is not enough space left in the assigned FLASH region to store the (compressed) data block. */
1233  success = false;
1234  }
1235  else {
1236  /* Only now we can write the full oooooohhcccccccccccc...ccfffffffffff sequence to FLASH. */
1237  int pageCountToFlash = (pOut - STORAGE_WORKAREA + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE;
1238  ASSERT(pageCountToFlash > 0); /* Must be at least 1. */
1239  success = WriteToFlash(firstFlashPage, STORAGE_WORKAREA, pageCountToFlash);
1240  }
1241  if (success) { /* All that is left now is to do some housekeeping: in EEPROM and in sInstance. */
1242  int oldEepromBitCursor = sInstance.eepromBitCursor; /* Used after updating the hint information. */
1243 
1245  /* Update variables used when reading samples. */
1251  }
1252  else {
1254  //sInstance.readLocation remains the same
1255  sInstance.readCursor = 0;
1256  //sInstance.readSequence remains the same
1257  }
1258  //sInstance.targetSequence remains the same
1259  }
1260  /* Only update flashByteCursor after updating readCursor & readSequence */
1261  sInstance.flashByteCursor = newFlashByteCursor;
1262 
1263  /* Now that everything has been copied from EEPROM to FLASH, the new marker is to be written at the
1264  * beginning of the assigned EEPROM space.
1265  * If that fails, we still want to have access to the just moved data.
1266  * Thus: update the hint before changing the marker.
1267  */
1268  WriteHint();
1269 
1270  /* Clear the marker in EEPROM - after a power-off we don't want to find this information any more.
1271  * sBitCursorChanged is set in Storage_Write() - which is the sole caller of this function - and the new
1272  * correct marker on the new correct location will be written in Storage_DeInit().
1273  */
1274  uint8_t zeroMarker[sizeof(Marker_t)] = {0};
1275  WriteToEeprom(oldEepromBitCursor, zeroMarker, sizeof(Marker_t) * 8);
1276  }
1277 
1278  return success;
1279  }
1280 #endif
1281 }
1282 
1283 /**
1284  * Reads a block of compressed data containing #STORAGE_BLOCK_SIZE_IN_SAMPLES samples, decompresses the data
1285  * and stores the result in the workspace given by the application.
1286  * @param readCursor The offset in bytes relative to FLASH_FIRST_BYTE_ADDRESS to the header preceding the
1287  * (compressed) data block that must be read.
1288  * @return
1289  * - When the samples are available in #STORAGE_WORKAREA (either when the previous decompression was still valid and
1290  * the decompression callback was not called; or when decompression was successful as indicated by the returnvalue
1291  * of the called decompression callback): the size of the (compressed) data block including the header. This is
1292  * equal to the number of bytes to advance the read cursor to the header of the next (compressed) data block.
1293  * - @c 0 when the FLASH contents were invalid, or when the decompression callback function returned @c false:
1294  * nothing has been changed in that case.
1295  * .
1296  * @post Only #Storage_Instance_t.cachedBlockOffset is fully updated when this function returns.
1297  * @note Uses #STORAGE_WORKAREA. Only the first #STORAGE_UNCOMPRESSED_BLOCK_SIZE_IN_BYTES bytes are in use
1298  * when this function returns.
1299  */
1300 static int ReadAndCacheSamplesFromFlash(int readCursor)
1301 {
1302  uint8_t * pHeader = FLASH_CURSOR_TO_BYTE_ADDRESS(readCursor);
1303  int bitCount = (int)(pHeader[0] | (pHeader[1] << 8));
1304  int blockSize;
1305  /* if header[0:1] == 0xFFFF, the flash was emptied.
1306  * This indicates a discrepancy between the instance information and the FLASH contents.
1307  * This may happen during development when re-flashing with the same image and erasing the non-used pages.
1308  */
1309  if (bitCount == 0x0000FFFF) {
1310  blockSize = 0;
1311  }
1312  else if (sInstance.cachedBlockOffset == readCursor) {
1313  /* The output from the previous call to STORAGE_DECOMPRESS_CB is still valid. */
1314  blockSize = FLASH_BLOCK_SIZE(bitCount);
1315  }
1316  else if (bitCount == STORAGE_UNCOMPRESSED_BLOCK_SIZE_IN_BITS) {
1319  } else {
1320  int decompressedBitCount = STORAGE_DECOMPRESS_CB(pHeader + FLASH_DATA_HEADER_SIZE, bitCount, STORAGE_WORKAREA);
1321  if (decompressedBitCount == STORAGE_UNCOMPRESSED_BLOCK_SIZE_IN_BITS) {
1322  sInstance.cachedBlockOffset = readCursor;
1323  blockSize = FLASH_BLOCK_SIZE(bitCount);
1324  }
1325  else {
1326  blockSize = 0;
1327  }
1328  }
1329  return blockSize;
1330 }
1331 
1332 /**
1333  * Checks whether #spRecoverInfo contains possible correct information.
1334  * @return @c true when the information contained in the structure #spRecoverInfo points to can be used.
1335  */
1336 static bool ValidateRecoverInfo(void)
1337 {
1338  return ((spRecoverInfo->eepromBitCursor > 0) || (spRecoverInfo->sampleCacheCount > 0)) /* Situation after Storage_Reset or power-off: 0 */
1339  && (spRecoverInfo->sampleCacheCount <= STORAGE_SAMPLE_ALON_CACHE_COUNT) /* Validity check */
1340  && ((spRecoverInfo->eepromBitCursor % STORAGE_BITSIZE) == 0) /* Validity check */
1342 }
1343 
1344 /**
1345  * Checks whether the given structure contains possible correct information.
1346  * @param pMarker : May not be NULL. Points to the structure to check.
1347  * @param expectedFlashByteCursor : Ignored when negative. When zero or positive, provides an additional constraint on
1348  * the marker structure.
1349  * @return @c true when the information contained in the structure can be used.
1350  */
1351 static bool ValidateMarker(const Marker_t * pMarker, int expectedFlashByteCursor)
1352 {
1353  bool ok = (pMarker->header == MARKER_HEADER)
1354  && (pMarker->footer == MARKER_FOOTER)
1355  && (((unsigned int)pMarker->flashByteCursor & MARKER_CURSOR_ZERO_MASK) == 0)
1356  && ((pMarker->flashByteCursor == 0) /* if no flash memory is available, FLASH_CURSOR_TO_BYTE_ADDRESS gives 0x7800 */
1358  if (ok && (expectedFlashByteCursor >= 0)) {
1359  ok = pMarker->flashByteCursor == expectedFlashByteCursor;
1360  }
1361  return ok;
1362 }
1363 
1364 /**
1365  * Checks whether the given structure contains possible correct information.
1366  * @param pHint : May not be NULL. Points to the structure to check.
1367  * @return @c true when the information contained in the structure can be used.
1368  */
1369 static bool ValidateHint(const Hint_t * pHint)
1370 {
1371  bool ok = ((pHint->eepromBitCursor % STORAGE_BITSIZE) == 0) /* Validity check */
1372  && (pHint->eepromBitCursor <= STORAGE_MAX_UNCOMPRESSED_BLOCK_SIZE_IN_BITS); /* Validity check */
1373  if (ok) {
1374  Hint_t inverseHint;
1375  Chip_EEPROM_Read(NSS_EEPROM, INVERSE_HINT_ABSOLUTE_BYTE_OFFSET, &inverseHint, sizeof(Hint_t));
1376  inverseHint.eepromBitCursor = (uint16_t)~inverseHint.eepromBitCursor;
1377  inverseHint.flashByteCursor = (uint16_t)~inverseHint.flashByteCursor;
1378  /* Normal situation: */
1379  ok = (inverseHint.eepromBitCursor == pHint->eepromBitCursor)
1380  && (inverseHint.flashByteCursor == pHint->flashByteCursor);
1381  if (!ok) {
1382  /* Initial situation: no data has been written, all EEPROM is in blank state */
1383  uint32_t data;
1384  Chip_EEPROM_Read(NSS_EEPROM, EEPROM_ABSOLUTE_FIRST_BYTE_OFFSET, &data, sizeof(data));
1385  ok = (data == 0)
1386  && (inverseHint.eepromBitCursor == 0xFFFF) /* The inverse from the memory content was taken above. */
1387  && (inverseHint.flashByteCursor == 0xFFFF) /* The inverse from the memory content was taken above. */
1388  && (pHint->eepromBitCursor == 0)
1389  && (pHint->flashByteCursor == 0);
1390  }
1391  }
1392  /* if !ok -> data corruption */
1393  return ok;
1394 }
1395 
1396 /** Uses the information of sInstance to create a #Hint_t structure, and writes it to EEPROM. */
1397 static void WriteHint(void)
1398 {
1399  Hint_t hint = {.eepromBitCursor = (uint16_t)sInstance.eepromBitCursor,
1400  .flashByteCursor = (uint16_t)sInstance.flashByteCursor};
1401  Chip_EEPROM_Write(NSS_EEPROM, HINT_ABSOLUTE_BYTE_OFFSET, &hint, sizeof(Hint_t));
1402 
1403  Hint_t inverseHint = {.eepromBitCursor = (uint16_t)~hint.eepromBitCursor,
1404  .flashByteCursor = (uint16_t)~hint.flashByteCursor};
1405  Chip_EEPROM_Write(NSS_EEPROM, INVERSE_HINT_ABSOLUTE_BYTE_OFFSET, &inverseHint, sizeof(Hint_t));
1406 
1407  /* Store extra recovery information together with the hint by copying as many of the last bytes of the page where
1408  * the marker starts to the duplicate data area.
1409  */
1410  int byteCursor = ((sInstance.eepromBitCursor / 8) / EEPROM_ROW_SIZE) * EEPROM_ROW_SIZE;
1411  uint8_t duplicate[SIZE_OF_DUPLICATE_DATA];
1412  Chip_EEPROM_Read(NSS_EEPROM, EEPROM_ABSOLUTE_FIRST_BYTE_OFFSET + byteCursor, duplicate, SIZE_OF_DUPLICATE_DATA);
1413  Chip_EEPROM_Write(NSS_EEPROM, DUPLICATE_DATA_ABSOLUTE_BYTE_OFFSET, duplicate, SIZE_OF_DUPLICATE_DATA);
1414 }
1415 
1416 /** Uses the information of sInstance to create a #Marker_t structure, and writes it to EEPROM. */
1417 static void WriteMarker(void)
1418 {
1419  Marker_t marker = {.header = MARKER_HEADER, .flashByteCursor = sInstance.flashByteCursor, .footer = MARKER_FOOTER};
1420  WriteToEeprom(sInstance.eepromBitCursor, &marker, sizeof(marker) * 8);
1421 }
1422 
1423 /* ------------------------------------------------------------------------- */
1424 
1425 void Storage_Init(void)
1426 {
1427 #if !STORAGE_FLASH_FIRST_PAGE
1428  sStorageFlashFirstPage = ((int)&_etext + (int)&_edata - (int)&_data + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE;
1429 #endif
1430  ResetInstance();
1431 
1432  /** Be sure to check out the @ref storage_initializing_par "initialization flowchart" */
1433 
1434  Chip_PMU_GetRetainedData((uint32_t *)sCache, STORAGE_FIRST_ALON_REGISTER, 5 - STORAGE_FIRST_ALON_REGISTER);
1436  bool recoverInfoIsValid = ValidateRecoverInfo();
1437 
1438  Hint_t hint;
1439  Chip_EEPROM_Read(NSS_EEPROM, HINT_ABSOLUTE_BYTE_OFFSET, &hint, sizeof(Hint_t));
1440  bool hintIsValid = ValidateHint(&hint);
1441 
1442  Marker_t marker;
1443  bool markerIsValid = false;
1444 
1445  if (recoverInfoIsValid) {
1446  ReadFromEeprom(spRecoverInfo->eepromBitCursor, &marker, sizeof(marker) * 8);
1447  markerIsValid = ValidateMarker(&marker, hintIsValid ? hint.flashByteCursor : -1);
1448  }
1449 
1450  if (!markerIsValid) {
1451  if (hintIsValid) {
1452  spRecoverInfo->eepromBitCursor = (unsigned int)hint.eepromBitCursor & 0x7FFF;
1453  ReadFromEeprom(spRecoverInfo->eepromBitCursor, &marker, sizeof(marker) * 8);
1454  markerIsValid = ValidateMarker(&marker, hint.flashByteCursor);
1455  }
1456  }
1457 
1458  if (!markerIsValid) {
1459  spRecoverInfo->eepromBitCursor = (unsigned int)FindMarker(&marker) & 0x7FFF;
1460  //markerIsValid = ValidateMarker(&marker, hint.flashByteCursor);
1461  markerIsValid = spRecoverInfo->eepromBitCursor != 0; /* Equivalent to commented out line above. */
1462  }
1463 
1464  if (markerIsValid) {
1467  }
1468  else if (hintIsValid) {
1470  /* The bytes in the last written page - where the marker should start if there was no corruption - can not be
1471  * trusted. Recover most of them by using the duplicate data area.
1472  */
1473  int byteCursor = ((sInstance.eepromBitCursor / 8) / EEPROM_ROW_SIZE) * EEPROM_ROW_SIZE;
1474  uint8_t duplicate[SIZE_OF_DUPLICATE_DATA];
1475  Chip_EEPROM_Read(NSS_EEPROM, DUPLICATE_DATA_ABSOLUTE_BYTE_OFFSET, duplicate, SIZE_OF_DUPLICATE_DATA);
1476  Chip_EEPROM_Write(NSS_EEPROM, EEPROM_ABSOLUTE_FIRST_BYTE_OFFSET + byteCursor, duplicate, SIZE_OF_DUPLICATE_DATA);
1477  /* Remove the bits we couldn't recover. */
1478  int lastRecoveredBit = (byteCursor + SIZE_OF_DUPLICATE_DATA) * 8;
1479  while (sInstance.eepromBitCursor > lastRecoveredBit) {
1481  }
1482 
1485  }
1486  else {
1490  }
1491 
1492  if (!markerIsValid) {
1493  WriteMarker();
1494  }
1495  if (!hintIsValid) {
1496  WriteHint();
1497  }
1498  Chip_EEPROM_Flush(NSS_EEPROM, true);
1499  /* After this point and before adding new samples, a new initialization of the storage module after a power failure
1500  * will always have a fast initialization, avoiding a slow search by calling FindMarker. Or, case (RH) can not occur
1501  * for as long as new samples have not been added.
1502  */
1503 }
1504 
1505 void Storage_DeInit(void)
1506 {
1507  /* Write the marker, but only when samples have been added or when the module has been reset - to avoid hitting
1508  * the max write cycles of the EEPROM.
1509  */
1511  WriteMarker();
1512  }
1513 
1514  /* Write the hint every X samples. */
1515  Hint_t hint;
1516  Chip_EEPROM_Read(NSS_EEPROM, HINT_ABSOLUTE_BYTE_OFFSET, &hint, sizeof(Hint_t));
1517  bool hintIsValid = ValidateHint(&hint);
1518  int difference = sInstance.eepromBitCursor - hint.eepromBitCursor;
1519  if ((!hintIsValid) || (difference < 0)
1521  WriteHint();
1522  }
1523 
1524  Chip_EEPROM_Flush(NSS_EEPROM, true);
1525 
1526  spRecoverInfo->eepromBitCursor = (unsigned int)sInstance.eepromBitCursor & 0x7FFF;
1527  Chip_PMU_SetRetainedData((uint32_t *)sCache, STORAGE_FIRST_ALON_REGISTER, 5 - STORAGE_FIRST_ALON_REGISTER);
1528 }
1529 
1531 {
1533 }
1534 
1535 void Storage_Reset(bool checkFlash)
1536 {
1538 
1539  /* The contents in EEPROM do not need to be fully erased, as they can be overwritten. Just ensure the EEPROM is
1540  * marked as 'empty'.
1541  * Clear the marker in EEPROM - after a power-off we don't want to find this information any more.
1542  */
1543  uint8_t zeroMarker[sizeof(Marker_t)] = {0};
1544  WriteToEeprom(sInstance.eepromBitCursor, zeroMarker, sizeof(Marker_t) * 8);
1545 
1546  /* Also invalidate the hint information */
1547  Chip_EEPROM_Memset(NSS_EEPROM, HINT_ABSOLUTE_BYTE_OFFSET, 0, sizeof(Hint_t));
1548 
1549 #if STORAGE_FLASH_FIRST_PAGE > STORAGE_FLASH_LAST_PAGE
1550  (void)checkFlash; /* suppress [-Wunused-parameter]: There is no flash assigned for storage, nothing to check. */
1551 #else
1552  if ((!checkFlash) || (STORAGE_FLASH_FIRST_PAGE > STORAGE_FLASH_LAST_PAGE)) {
1553  /* Caller explicitly indicates there is no need to check the flash, or,
1554  * there is no flash available for storage, so nothing to check.
1555  */
1556  }
1557  else {
1558  /* The contents in FLASH must be erased, as they can only be overwritten after erasing.
1559  * It is costly to erase the FLASH: it is taxing the battery and taking a long time. The FLASH is therefore
1560  * not erased unconditionally.
1561  * The only fail-safe way - also catching memory corruption; a direct EEPROM erase without a corresponding
1562  * direct FLASH erase; or a firmware image update - is to check the FLASH memory itself.
1563  * Check words from first to last, as the first words are more likely to be in use already. This is not done
1564  * using Chip_IAP_Flash_SectorBlankCheck, as this operates on full sectors only.
1565  * At 500 kHz, this adds +- 10 ms execution time per kb of assigned FLASH storage (worst case is
1566  * checking all words to find out an erase is not necessary).
1567  */
1568  uint32_t * cursor = FLASH_FIRST_WORD_ADDRESS;
1569  uint32_t * last = FLASH_LAST_WORD_ADDRESS;
1570  while ((cursor <= last) && (*cursor == 0xFFFFFFFF)) {
1571  cursor++;
1572  }
1573  if (cursor <= last) { /* If not above the last word to check, erase the FLASH memory. */
1574  IAP_STATUS_T status;
1575  const uint32_t sectorStart = (uint32_t)STORAGE_FLASH_FIRST_PAGE / FLASH_PAGES_PER_SECTOR;
1576  const uint32_t sectorEnd = STORAGE_FLASH_LAST_PAGE / FLASH_PAGES_PER_SECTOR;
1577  status = Chip_IAP_Flash_PrepareSector(sectorStart, sectorEnd);
1578  if (status == IAP_STATUS_CMD_SUCCESS) {
1579  /* - First erase pages up to (not including) the first page @c b of a sector @c s.
1580  * - Next erase as many sectors as possible: from @c s till @c t.
1581  * - Finally erase pages from the first page of @c sectorEnd till #STORAGE_FLASH_LAST_PAGE.
1582  * Since each call involves different sectors, one prepare call (done above) is sufficient - only
1583  * involved sectors are locked again in each IAP call below.
1584  */
1585  uint32_t s = (uint32_t)(STORAGE_FLASH_FIRST_PAGE + FLASH_PAGES_PER_SECTOR - 1) / FLASH_PAGES_PER_SECTOR;
1586  uint32_t b = s * FLASH_PAGES_PER_SECTOR;
1587  uint32_t t = sectorEnd;
1588  if ((STORAGE_FLASH_LAST_PAGE % FLASH_PAGES_PER_SECTOR) < FLASH_PAGES_PER_SECTOR - 1) {
1589  t--; /* The last sector involved is not fully assigned for sample storage. */
1590  }
1591  if ((uint32_t)STORAGE_FLASH_FIRST_PAGE < b) {
1592  __disable_irq();
1593  status = Chip_IAP_Flash_ErasePage((uint32_t)STORAGE_FLASH_FIRST_PAGE, b - 1, 0);
1594  __enable_irq();
1595  ASSERT(status == IAP_STATUS_CMD_SUCCESS);
1596  }
1597  if (s <= t) {
1598  __disable_irq();
1599  status = Chip_IAP_Flash_EraseSector(s, t, 0);
1600  __enable_irq();
1601  ASSERT(status == IAP_STATUS_CMD_SUCCESS);
1602  }
1603  if (t < sectorEnd) {
1604  __disable_irq();
1605  status = Chip_IAP_Flash_ErasePage(sectorEnd * FLASH_PAGES_PER_SECTOR, STORAGE_FLASH_LAST_PAGE, 0);
1606  __enable_irq();
1607  ASSERT(status == IAP_STATUS_CMD_SUCCESS);
1608  }
1609  }
1610  ASSERT(status == IAP_STATUS_CMD_SUCCESS);
1611  }
1612  }
1613 #endif
1614  ResetInstance();
1615  /* A new marker will be written in EEPROM in a later call to Storage_DeInit() */
1616 }
1617 
1618 #if STORAGE_SAMPLE_ALON_CACHE_COUNT > 0
1619 int Storage_Write(STORAGE_TYPE * samples, int n)
1620 {
1621  ASSERT(samples != NULL);
1622 
1623  int count = 0;
1624  while (count < n) {
1625  if (CacheSample(samples + count)) {
1626  count++;
1627  }
1628  else {
1629  STORAGE_TYPE cachedSamples[STORAGE_SAMPLE_ALON_CACHE_COUNT + 1];
1630  int cachedCount = 0;
1631  while (GetCachedSample(cachedCount, cachedSamples + cachedCount)) {
1632  cachedCount++;
1633  }
1634  ASSERT(cachedCount == STORAGE_SAMPLE_ALON_CACHE_COUNT);
1635  cachedSamples[cachedCount] = samples[count];
1636  cachedCount++;
1637 
1638  int stored = StoreSamplesInEeprom(cachedSamples, cachedCount);
1639  if (stored) {
1640  count++; /* At least one sample was stored in EEPROM: there is room now in cache if needs be. */
1641  /* Clear cache, then place back on the cache what could not be moved to EEPROM. */
1643  for (int i = stored; i < cachedCount; i++) {
1644  CacheSample(cachedSamples + i);
1645  }
1646 
1647  /* Update variables used when reading samples. */
1649  if (sInstance.readCursor >= stored) { /* Possibly true if not all samples could be moved. */
1650  // sInstance.readLocation does not change
1651  sInstance.readCursor -= stored;
1652  // sInstance.readSequence does not change
1653  }
1654  else {
1657  - ((stored - sInstance.readCursor) * STORAGE_BITSIZE);
1658  // sInstance.readSequence does not change
1659  }
1660  }
1661 
1662  /* Loop again, cache has been updated. */
1663  }
1664  else {
1665  /* Leave the cache intact. */
1666  break; /* Stop the while loop: storage is full. */
1667  }
1668  }
1669  }
1670 
1671  return count;
1672 }
1673 #else
1674 int Storage_Write(STORAGE_TYPE * samples, int n)
1675 {
1676  ASSERT(samples != NULL);
1677  return StoreSamplesInEeprom(samples, n);
1678 }
1679 #endif
1680 
1681 bool Storage_Seek(int n)
1682 {
1684  sInstance.readSequence = -1;
1685  sInstance.readCursor = -1;
1686 
1687  if (n < 0) { return false; }
1688 
1689  int currentSequence = -1;
1690  int currentCursor = -1;
1691  int nextSequence = 0;
1692  int nextCursor = 0;
1693 
1694  /* Step through the (compressed) blocks in FLASH, counting the number of samples stored in there.
1695  * If the count surpasses n we have found a block where the requested sample is stored in.
1696  * - The cursor variables below indicate a byte offset in FLASH.
1697  * - The sequence variables below indicate a sequence count.
1698  */
1699  while (nextCursor < sInstance.flashByteCursor) {
1700  currentSequence = nextSequence;
1701  currentCursor = nextCursor;
1702  uint8_t * header = FLASH_CURSOR_TO_BYTE_ADDRESS(nextCursor);
1703 
1704  int bitCount = (int)(header[0] | (header[1] << 8));
1705  nextSequence += STORAGE_BLOCK_SIZE_IN_SAMPLES;
1706  nextCursor += FLASH_BLOCK_SIZE(bitCount);
1707  ASSERT((nextCursor & 0x3) == 0); /* Must be 32-bit word-aligned. */
1708  if ((currentSequence <= n) && (n < nextSequence)) {
1709  break;
1710  }
1711  }
1712  if ((currentSequence <= n) && (n < nextSequence)) {
1714  sInstance.readSequence = currentSequence;
1715  sInstance.readCursor = currentCursor;
1716  }
1717  else {
1718  /* Try to find the sequence number sought for in EEPROM.
1719  * If the jump location surpasses sInstance.eepromBitCursor then the n-th sample is not yet committed in NVM
1720  * - The cursor variable below indicates a bit offset in EEPROM.
1721  */
1722  nextCursor = (n - nextSequence) * STORAGE_BITSIZE;
1723  if (nextCursor < sInstance.eepromBitCursor) {
1725  sInstance.readSequence = n;
1726  sInstance.readCursor = nextCursor;
1727  }
1728  else {
1729 #if STORAGE_SAMPLE_ALON_CACHE_COUNT > 0
1730  /* Try to find the sequence number sought for in sCache.
1731  * - The sequence variable below indicates a sequence count.
1732  * - The cursor variable below indicates a sample offset in sCache.
1733  */
1734  nextSequence += GetEepromCount();
1735  nextCursor = n - nextSequence;
1736  if (nextCursor < spRecoverInfo->sampleCacheCount) {
1738  sInstance.readSequence = n;
1739  sInstance.readCursor = nextCursor;
1740  }
1741 #endif
1742  /* else: can not comply as there are not yet this many samples stored. */
1743  }
1744  }
1745 
1747  return sInstance.readSequence >= 0;
1748 }
1749 
1750 int Storage_Read(STORAGE_TYPE * samples, int n)
1751 {
1752  int count = 0;
1753  ASSERT(samples != NULL);
1754 
1755  if (sInstance.readSequence < 0) {
1756  /* A prior successful call to Storage_Seek is required. */
1757  }
1758  else {
1761  while (blockSize && (count < n) && (sInstance.readCursor < sInstance.flashByteCursor)) {
1763  /* Determine the offset in bytes and the initial number of LSBits to ignore. */
1765  int byteOffset = bitOffset / 8;
1766  int bitAlignment = bitOffset % 8;
1767 
1768  ShiftUnalignedData((uint8_t *)(samples + count), STORAGE_WORKAREA + byteOffset, bitAlignment,
1769  STORAGE_BITSIZE);
1770  bitOffset += STORAGE_BITSIZE;
1771  count++;
1773  }
1775  /* A next sample is available in EEPROM or in the next (compressed) block of data in FLASH. */
1776  sInstance.readCursor += blockSize;
1777  ASSERT((sInstance.readCursor & 0x3) == 0); /* Must be 32-bit word-aligned. */
1779  }
1781  }
1782 
1784  /* All is read from FLASH, ensure the next read will pick the samples from EEPROM. */
1786  sInstance.readCursor = 0;
1787  }
1788  }
1789 
1791  while ((count < n) && (sInstance.readCursor < sInstance.eepromBitCursor)) {
1792  ReadFromEeprom((unsigned int)sInstance.readCursor, samples + count, STORAGE_BITSIZE);
1793  count++;
1797  };
1798 
1799 #if STORAGE_SAMPLE_ALON_CACHE_COUNT > 0
1801  /* All is read from EEPROM, ensure the next read will pick the samples from cache. */
1803  sInstance.readCursor = 0;
1804  }
1805 #endif
1806  }
1807 
1808 #if STORAGE_SAMPLE_ALON_CACHE_COUNT > 0
1810  while ((count < n) && GetCachedSample(sInstance.readCursor, samples + count)) {
1811  count++;
1815  }
1816  }
1817 #endif
1818  }
1819 
1820 #if STORAGE_SIGNED
1821  /* STORAGE_TYPE is signed: propagate the bit at position STORAGE_BITSIZE to the left */
1822  int msbits = sizeof(STORAGE_TYPE) * 8 - STORAGE_BITSIZE;
1823  for (int i = 0; i < count; i++) {
1824  samples[i] = (STORAGE_TYPE)((STORAGE_TYPE)(samples[i] << msbits) >> msbits);
1825  }
1826 #endif
1827  return count;
1828 }
static Storage_Instance_t sInstance
Definition: storage.c:637
int Storage_DummyDecompressCb(const uint8_t *pData, int bitCount, void *pOut)
Definition: storage.c:696
#define STORAGE_FLASH_LAST_PAGE
Definition: storage_dft.h:246
#define STORAGE_SAMPLE_ALON_CACHE_COUNT
Definition: storage_dft.h:288
const int _data
static void ShiftUnalignedData(uint8_t *pTo, const uint8_t *pFrom, const int bitAlignment, const int bitCount)
Definition: storage.c:817
#define STORAGE_MAX_BLOCK_SIZE_IN_SAMPLES
Definition: storage_dft.h:372
int STORAGE_DECOMPRESS_CB(const uint8_t *pData, int bitCount, void *pOut)
#define EEPROM_ABSOLUTE_FIRST_BYTE_OFFSET
Definition: storage.c:310
#define STORAGE_WORKAREA_SIZE
Definition: storage_dft.h:410
#define STORAGE_FIRST_ALON_REGISTER
Definition: storage_dft.h:176
int Storage_Read(STORAGE_TYPE *samples, int n)
Definition: storage.c:1750
int checkSizeOfHint[(SIZE_OF_HINT==sizeof(Hint_t)) ? 1 :-1]
Definition: storage.c:614
const int _etext
#define STORAGE_BLOCK_SIZE_IN_SAMPLES
Definition: storage_dft.h:396
static bool ValidateHint(const Hint_t *pHint)
Definition: storage.c:1369
#define FLASH_DATA_HEADER_SIZE
Definition: storage.c:349
uint8_t STORAGE_WORKAREA[STORAGE_WORKAREA_SIZE]
static bool ValidateRecoverInfo(void)
Definition: storage.c:1336
#define STORAGE_UNCOMPRESSED_BLOCK_SIZE_IN_BITS
Definition: storage_dft.h:404
#define FLASH_CURSOR_TO_BYTE_ADDRESS(flashByteCursor)
Definition: storage.c:317
#define HINT_ABSOLUTE_BYTE_OFFSET
Definition: storage.c:443
static int GetFlashCount(void)
Definition: storage.c:1034
#define EEPROM_ABSOLUTE_LAST_BYTE_OFFSET
Definition: storage.c:314
static int ReadAndCacheSamplesFromFlash(int readCursor)
Definition: storage.c:1300
#define FLASH_LAST_BYTE_ADDRESS
Definition: storage.c:330
#define MARKER_CURSOR_ZERO_MASK
Definition: storage.c:385
#define FLASH_FIRST_WORD_ADDRESS
Definition: storage.c:333
const int header
Definition: storage.c:488
#define FLASH_CURSOR_TO_PAGE(flashByteCursor)
Definition: storage.c:321
static bool sEepromBitCursorChanged
Definition: storage.c:658
static bool ValidateMarker(const Marker_t *pMarker, int expectedFlashByteCursor)
Definition: storage.c:1351
static uint8_t sCache[4 *(5 - STORAGE_FIRST_ALON_REGISTER)]
Definition: storage.c:644
static void WriteToEeprom(const int bitCursor, const void *pData, const int bitCount)
Definition: storage.c:912
uint16_t flashByteCursor
Definition: storage.c:478
uint16_t eepromBitCursor
Definition: storage.c:475
bool Storage_Seek(int n)
Definition: storage.c:1681
int checkSizeOfMarker[(SIZE_OF_MARKER==sizeof(Marker_t)) ? 1 :-1]
Definition: storage.c:617
#define STORAGE_BITSIZE
Definition: storage_dft.h:272
#define STORAGE_UNCOMPRESSED_BLOCK_SIZE_IN_BYTES
Definition: storage_dft.h:407
static int StoreSamplesInEeprom(const STORAGE_TYPE *pSamples, int n)
Definition: storage.c:1110
void Storage_DeInit(void)
Definition: storage.c:1505
const int footer
Definition: storage.c:501
LOCATION_T
Definition: storage.c:402
#define FIRST_BITS_OF_CACHE_SIZE
Definition: storage.c:412
void Storage_Init(void)
Definition: storage.c:1425
#define STORAGE_MAX_UNCOMPRESSED_BLOCK_SIZE_IN_BITS
Definition: storage_dft.h:377
#define STORAGE_WRITE_RECOVERY_EVERY_X_SAMPLES
Definition: storage_dft.h:323
#define EEPROM_OVERHEAD_IN_BITS
Definition: storage.c:600
unsigned int eepromBitCursor
Definition: storage.c:423
#define DUPLICATE_DATA_ABSOLUTE_BYTE_OFFSET
Definition: storage.c:459
#define SIZE_OF_MARKER
Definition: storage.c:510
#define MARKER_FOOTER
Definition: storage.c:382
int flashByteCursor
Definition: storage.c:499
static bool GetCachedSample(const int n, void *pData)
Definition: storage.c:890
int Storage_DummyCompressCb(int eepromByteOffset, int bitCount, void *pOut)
Definition: storage.c:681
LOCATION_T readLocation
Definition: storage.c:543
unsigned int sampleCacheCount
Definition: storage.c:430
static unsigned int FindMarker(Marker_t *pMarker)
Definition: storage.c:966
static bool MoveSamplesFromEepromToFlash(void)
Definition: storage.c:1160
static int GetEepromCount(void)
Definition: storage.c:1027
static int sStorageFlashFirstPage
Definition: storage.c:394
int Storage_GetCount(void)
Definition: storage.c:1530
void Storage_Reset(bool checkFlash)
Definition: storage.c:1535
#define FLASH_LAST_WORD_ADDRESS
Definition: storage.c:336
static RecoverInfo_t * spRecoverInfo
Definition: storage.c:650
#define STORAGE_IDIVUP(n, d)
Definition: storage_dft.h:158
static void WriteHint(void)
Definition: storage.c:1397
const int _edata
int STORAGE_COMPRESS_CB(int eepromByteOffset, int bitCount, void *pOut)
int Storage_Write(STORAGE_TYPE *samples, int n)
Definition: storage.c:1619
static void WriteMarker(void)
Definition: storage.c:1417
uint8_t sStorage_Workarea[STORAGE_WORKAREA_SIZE]
Definition: storage.c:662
#define INVERSE_HINT_ABSOLUTE_BYTE_OFFSET
Definition: storage.c:440
#define FLASH_BLOCK_SIZE(bitCount)
Definition: storage.c:373
#define SIZE_OF_DUPLICATE_DATA
Definition: storage.c:452
static void ReadFromEeprom(const unsigned int bitCursor, void *pData, const int bitCount)
Definition: storage.c:942
static bool CacheSample(const STORAGE_TYPE *pSample)
Definition: storage.c:868
#define STORAGE_FLASH_FIRST_PAGE
Definition: storage.c:396
#define SIZE_OF_HINT
Definition: storage.c:437
int checkSizeOfSampleType[(sizeof(STORAGE_TYPE) *8< STORAGE_BITSIZE) ? -1 :1]
Definition: storage.c:611
unsigned int firstBitsOfCache
Definition: storage.c:433
#define STORAGE_TYPE
Definition: storage_dft.h:264
static void ResetInstance(void)
Definition: storage.c:735
static void ShiftAlignedData(uint8_t *pTo, const uint8_t *pFrom, const int bitAlignment, const int bitCount)
Definition: storage.c:762
#define MARKER_HEADER
Definition: storage.c:381
int checkSizeOfRecoverInfo[sizeof(RecoverInfo_t)==4 ? 1 :-1]
Definition: storage.c:620