Improve XEBEC controller support
[SCSI2SD.git] / software / SCSI2SD / v4 / SCSI2SD.cydsn / Generated_Source / PSoC5 / cy_em_eeprom.c
1 /***************************************************************************//**
2 * \file cy_em_eeprom.c
3 * \version 2.0
4 *
5 * \brief
6 *  This file provides source code of the API for the Emulated EEPROM library.
7 *  The Emulated EEPROM API allows creating of an emulated EEPROM in flash that
8 *  has the ability to do wear leveling and restore corrupted data from a
9 *  redundant copy.
10 *
11 ********************************************************************************
12 * \copyright
13 * Copyright 2017, Cypress Semiconductor Corporation.  All rights reserved.
14 * You may use this file only in accordance with the license, terms, conditions,
15 * disclaimers, and limitations in the end user license agreement accompanying
16 * the software package with which this file was provided.
17 *******************************************************************************/
18
19
20 #include "cytypes.h"
21 #include <string.h>
22
23 #if (CYDEV_CHIP_FAMILY_USED == CYDEV_CHIP_FAMILY_PSOC6)
24     #include "em_eeprom/cy_em_eeprom.h"
25 #else
26     #include "cy_em_eeprom.h"
27 #endif /* (CYDEV_CHIP_FAMILY_USED == CYDEV_CHIP_FAMILY_PSOC6) */
28
29
30 #if defined(__cplusplus)
31 extern "C" {
32 #endif
33
34
35 /***************************************
36 * Private Function Prototypes
37 ***************************************/
38 static void FindLastWrittenRow(uint32 * lastWrRowPtr, cy_stc_eeprom_context_t * context);
39 static uint32 GetRowAddrBySeqNum(uint32 seqNum, cy_stc_eeprom_context_t * context);
40 static uint8 CalcChecksum(uint8 rowData[], uint32 len);
41 static void GetNextRowToWrite(uint32 seqNum,
42                             uint32 * rowToWrPtr,
43                             uint32 * rowToRdPtr,
44                             cy_stc_eeprom_context_t * context);
45 static cy_en_em_eeprom_status_t CheckRanges(cy_stc_eeprom_config_t* config);
46 static cy_en_em_eeprom_status_t WriteRow(uint32 rowAddr, uint32 *rowData, cy_stc_eeprom_context_t * context);
47 static cy_en_em_eeprom_status_t EraseRow(uint32 rowAddr, uint32 ramBuffAddr, cy_stc_eeprom_context_t * context);
48 static cy_en_em_eeprom_status_t CheckCrcAndCopy(uint32 startAddr,
49                                                 uint32 dstAddr,
50                                                 uint32 rowOffset,
51                                                 uint32 numBytes,
52                                                 cy_stc_eeprom_context_t * context);
53 static uint32 GetAddresses(uint32 *startAddr, uint32 *endAddr, uint32 *offset, uint32 rowNum, uint32 addr, uint32 len);
54 static cy_en_em_eeprom_status_t FillChecksum(cy_stc_eeprom_context_t * context);
55
56 /**
57 * \addtogroup group_em_eeprom_functions
58 * \{
59 */
60
61 /*******************************************************************************
62 * Function Name: Cy_Em_EEPROM_Init
63 ****************************************************************************//**
64 *
65 * Initializes the Emulated EEPROM library by filling the context structure. 
66 *
67 * \param config
68 * The pointer to a configuration structure. See \ref cy_stc_eeprom_config_t.
69 *
70 * \param context
71 * The pointer to the EEPROM context structure to be filled by the function.
72 * \ref cy_stc_eeprom_context_t.
73 *
74 * \return
75 * error / status code. See \ref cy_en_em_eeprom_status_t.
76 *
77 * \note
78 * The context structure should not be modified by the user after it is filled
79 * with this function. Modification of context structure may cause the 
80 * unexpected behavior of the Cy_Em_EEPROM API functions which rely on it.
81 *
82 * \note
83 * This function uses a buffer of the flash row size to perform read
84 * operation. For the size of the row refer to the specific PSoC device
85 * datasheet.
86 *
87 * \sideeffect 
88 * If the "Redundant Copy" option is used, the function performs a number of 
89 * write operations to the EEPROM to initialize flash rows checksums. Therefore,
90 * Cy_Em_EEPROM_NumWrites(), when it is called right after Cy_Em_EEPROM_Init(), 
91 * will return a non-zero value that identifies the number of writes performed 
92 * by Cy_Em_EEPROM_Init().
93 *
94 *******************************************************************************/
95 cy_en_em_eeprom_status_t Cy_Em_EEPROM_Init(cy_stc_eeprom_config_t* config, cy_stc_eeprom_context_t * context)
96 {
97     cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_PARAM;
98
99     if((NULL != context) && (NULL != config) && (NULL != ((uint32 *)config->userFlashStartAddr)) &&
100         (config->wearLevelingFactor <= CY_EM_EEPROM_MAX_WEAR_LEVELING_FACTOR) && (config->eepromSize != 0u))
101     {
102         ret = CheckRanges(config);
103
104         if(CY_EM_EEPROM_SUCCESS == ret)
105         {
106             /* Copy the user config structure fields into context */
107             context->eepromSize = config->eepromSize;
108             context->wearLevelingFactor = config->wearLevelingFactor;
109             context->redundantCopy = config->redundantCopy;
110             context->blockingWrite = config->blockingWrite;
111             context->userFlashStartAddr = config->userFlashStartAddr;
112             /* Store frequently used data for internal use */
113             context->numberOfRows = CY_EM_EEPROM_GET_NUM_ROWS_IN_EEPROM(config->eepromSize);
114             context->wlEndAddr = ((CY_EM_EEPROM_GET_EEPROM_SIZE(context->numberOfRows) * config->wearLevelingFactor) +
115                     config->userFlashStartAddr);
116             /* Find last written EEPROM row and store it for quick access */
117             FindLastWrittenRow(&context->lastWrRowAddr, context);
118
119             if((0u == CY_EM_EEPROM_GET_SEQ_NUM(context->lastWrRowAddr)) && (0u != context->redundantCopy))
120             {
121                 /* Call the function only after device reprogramming in case
122                 * if redundant copy is enabled.
123                 */
124                 ret = FillChecksum(context);
125                 
126                 /* Update the last written EEPROM row for Cy_Em_EEPROM_NumWrites() */
127                 FindLastWrittenRow(&context->lastWrRowAddr, context);
128             }
129         }
130     }
131
132     return(ret);
133 }
134
135
136 /*******************************************************************************
137 * Function Name: Cy_Em_EEPROM_Read
138 ****************************************************************************//**
139 *
140 * This function takes the logical EEPROM address, converts it to the actual
141 * physical address where the data is stored and returns the data to the user.
142 *
143 * \param addr
144 * The logical start address in EEPROM to start reading data from.
145 *
146 * \param eepromData
147 * The pointer to a user array to write data to.
148 *
149 * \param size
150 * The amount of data to read.
151 *
152 * \param context
153 * The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t.
154 *
155 * \return
156 * This function returns \ref cy_en_em_eeprom_status_t.
157 *
158 * \note
159 * This function uses a buffer of the flash row size to perform read
160 * operation. For the size of the row refer to the specific PSoC device
161 * datasheet.
162 *
163 * \note
164 * In case if redundant copy option is enabled the function may perform writes
165 * to EEPROM. This is done in case if the data in the EEPPROM is corrupted and
166 * the data in redundant copy is valid based on CRC-8 data integrity check.
167 *
168 *******************************************************************************/
169 cy_en_em_eeprom_status_t Cy_Em_EEPROM_Read(uint32 addr, 
170                                         void * eepromData, 
171                                         uint32 size,
172                                         cy_stc_eeprom_context_t * context)
173 {
174     cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_PARAM;
175     uint32 i;
176     uint32 numBytesToRead;
177     uint32 curEepromBaseAddr;
178     uint32 curRowOffset;
179     uint32 startRowAddr;
180     uint32 actEepromRowNum;
181     uint32 curRdEepromRowNum = 0u;
182     uint32 dataStartEepromRowNum = 0u;
183     uint32 eeData = (uint32) eepromData; /* To avoid the pointer arithmetic with void */
184
185     /* Validate input parameters */
186     if((0u != size) && ((addr + size) <= (context->eepromSize)) && (NULL != eepromData))
187     {
188         uint32 rdAddr = addr;
189         uint32 rdSize = size;
190         /* Get the sequence number of the last written row */
191         uint32 seqNum = CY_EM_EEPROM_GET_SEQ_NUM(context->lastWrRowAddr);
192         uint32 updateAddrFlag = 0u;
193
194         /* Calculate the number of the row read operations. Currently this only concerns
195         * the reads from the EEPROM data locations.
196         */
197         uint32 numRowReads = ((((rdAddr + rdSize) - 1u) / CY_EM_EEPROM_EEPROM_DATA_LEN) -
198                               (rdAddr / CY_EM_EEPROM_EEPROM_DATA_LEN)) + 1u;
199
200         /* Get the address of the first row of the currently active EEPROM sector. If
201         * no wear leveling is used - the EEPROM has only one sector, so use the base
202         * addr stored in "context->userFlashStartAddr".
203         */
204         curEepromBaseAddr = (((context->lastWrRowAddr - context->userFlashStartAddr) /
205                               (CY_EM_EEPROM_FLASH_SIZEOF_ROW * context->numberOfRows)) *
206                               (CY_EM_EEPROM_FLASH_SIZEOF_ROW * context->numberOfRows)) +
207                                context->userFlashStartAddr;
208
209         /* Find the number of the row that contains the start address of the data */
210         for(i = 0u; i < context->numberOfRows; i++)
211         {
212             if(0u != CY_EM_EEPROM_IS_ADDR_IN_ROW_RANGE(rdAddr, i))
213             {
214                 dataStartEepromRowNum = i;
215                 curRdEepromRowNum = dataStartEepromRowNum;
216                 break;
217             }
218         }
219
220         /* Find the row number of the last written row */
221         actEepromRowNum = (context->lastWrRowAddr - curEepromBaseAddr) / CY_EM_EEPROM_FLASH_SIZEOF_ROW;
222
223         /* Check if wear leveling is used */
224         if(context->wearLevelingFactor > 1u)
225         {
226             uint32 dataEndEepromRowNum = dataStartEepromRowNum + (numRowReads - 1u);
227
228             /* Check if the future validation of the read address is required. */
229             updateAddrFlag = (dataStartEepromRowNum > actEepromRowNum) ? 1u :
230                               ((dataEndEepromRowNum > actEepromRowNum) ? 1u : 0u);
231         }
232
233         /* Copy data from the EEPROM data locations to the user buffer */
234         for(i = 0u; i < numRowReads; i++)
235         {
236             startRowAddr = curEepromBaseAddr + (curRdEepromRowNum * CY_EM_EEPROM_FLASH_SIZEOF_ROW);
237             curRowOffset = CY_EM_EEPROM_EEPROM_DATA_LEN + (rdAddr % CY_EM_EEPROM_EEPROM_DATA_LEN);
238
239             /* Check if there are more reads pending and update the number of the
240             * remaining bytes to read respectively.
241             */
242             if((i + 1u) < numRowReads)
243             {
244                 numBytesToRead = CY_EM_EEPROM_EEPROM_DATA_LEN - (rdAddr % CY_EM_EEPROM_EEPROM_DATA_LEN);
245             }
246             else
247             {
248                 numBytesToRead = rdSize;
249             }
250
251             /* Check if the read address needs to be updated to point to the correct
252             * EEPROM sector.
253             */
254             if((0u != updateAddrFlag) && (curRdEepromRowNum > actEepromRowNum))
255             {
256                 startRowAddr -= context->numberOfRows * CY_EM_EEPROM_FLASH_SIZEOF_ROW;
257
258                 if(startRowAddr < context->userFlashStartAddr)
259                 {
260                     startRowAddr = context->wlEndAddr -
261                         ((context->numberOfRows - curRdEepromRowNum) * CY_EM_EEPROM_FLASH_SIZEOF_ROW);
262                 }
263             }
264
265             if(0u != context->redundantCopy)
266             {
267                 /* Check a checksum of the EEPROM row and if it is bad, check a checksum in
268                 * the corresponding row in redundant copy, otherwise return failure.
269                 */
270                 ret = CheckCrcAndCopy(startRowAddr, eeData, curRowOffset, numBytesToRead, context);
271
272                 if(CY_EM_EEPROM_SUCCESS != ret)
273                 {
274                     break;
275                 }
276             }
277             else
278             {
279                 /* Copy the data to the user buffer */
280                 (void)memcpy((void *)(eeData),
281                              (void *)(startRowAddr + curRowOffset),
282                              numBytesToRead);
283
284                 /* Indicate success to be able to execute next code block */
285                 ret = CY_EM_EEPROM_SUCCESS;
286             }
287
288             /* Update variables anticipated in the read operation */
289             rdAddr += numBytesToRead;
290             rdSize -= numBytesToRead;
291             eeData += numBytesToRead;
292             curRdEepromRowNum++;
293         }
294
295         /* This code block will copy the latest data from the EEPROM headers into the
296         * user buffer. The data previously copied into the user buffer may be updated
297         * as the EEPROM headers contain more recent data. 
298         * The code block is executed when two following conditions are true:
299         *  1) The reads from "historic" data locations were successful;
300         *  2) The user performed at least one write operation to Em_EEPROM (0u !=
301         *     seqNum).        
302         */
303         if((CY_EM_EEPROM_SUCCESS == ret) && (0u != seqNum))
304         {
305             numRowReads = (context->numberOfRows <= seqNum) ? (context->numberOfRows) : (seqNum);
306             numRowReads--;
307
308             for(i = (seqNum - numRowReads); i <= seqNum; i++)
309             {
310                 startRowAddr = GetRowAddrBySeqNum(i, context);
311
312                 if (0u != startRowAddr)
313                 {
314                     /* The following variables are introduced to increase code readability. */
315                     uint32 startAddr  = *(uint32 *)(startRowAddr + CY_EM_EEPROM_HEADER_ADDR_OFFSET);
316                     uint32 endAddr    = startAddr + (*(uint32 *)(startRowAddr + CY_EM_EEPROM_HEADER_LEN_OFFSET));
317
318                     /* Check if the current row EEPROM header contains the data requested for read */
319                     if(0u != CY_EM_EEPROM_IS_ADDRESES_CROSSING(startAddr, endAddr, addr, addr + size))
320                     {
321                         uint32 srcOffset = (startAddr > addr) ? (0u) : (addr - startAddr);
322                         uint32 dstOffset = (startAddr > addr) ? (startAddr - addr): (0u);
323                         rdAddr = (startAddr > addr) ? (startAddr) : (addr);
324
325                         srcOffset += CY_EM_EEPROM_HEADER_DATA_OFFSET;
326
327                         /* Calculate the number of bytes to be read from the current row's EEPROM header */
328                         numBytesToRead = ((endAddr < (addr + size)) ? endAddr : (addr + size)) - rdAddr;
329
330                         /* Calculate the offset in the user buffer from which the data will be updated. */
331                         eeData = ((uint32)eepromData) + dstOffset;
332
333                         /* Check a checksum of the EEPROM row and if it is bad, check a checksum in the
334                         * corresponding row in redundant copy, otherwise return failure. Copy the data
335                         * from the recent EEPROM headers to the user buffer. This will overwrite the
336                         * data copied form EEPROM data locations as the data in EEPROM headers is newer.
337                         */
338                         if(0u != context->redundantCopy)
339                         {
340                             ret = CheckCrcAndCopy(startRowAddr, eeData, srcOffset, numBytesToRead, context);
341
342                             if(CY_EM_EEPROM_SUCCESS != ret)
343                             {
344                                 break;
345                             }
346                         }
347                         else
348                         {
349                             (void)memcpy((void *)(eeData), (void *)(startRowAddr + srcOffset), numBytesToRead);
350                         }
351                     }
352                 }
353             }
354         }
355     }
356
357     return(ret);
358 }
359
360
361 /*******************************************************************************
362 * Function Name: Cy_Em_EEPROM_Write
363 ****************************************************************************//**
364 *
365 * This function takes the logical EEPROM address and converts it to the actual
366 * physical address and writes data there. If wear leveling is implemented, the
367 * writing process will use the wear leveling techniques. This is a blocking
368 * function and it does not return until the write operation is completed. The
369 * user firmware should not enter Hibernate mode until write is completed. The
370 * write operation is allowed in Sleep and Deep-Sleep modes. During the flash
371 * operation, the device should not be reset, including the XRES pin, a software
372 * reset, and watchdog reset sources. Also, low-voltage detect circuits should
373 * be configured to generate an interrupt instead of a reset. Otherwise, portions
374 * of flash may undergo unexpected changes.
375 *
376 * \param addr
377 * The logical start address in EEPROM to start writing data from.
378 *
379 * \param eepromData
380 * Data to write to EEPROM.
381 *
382 * \param size
383 * The amount of data to write to EEPROM.
384 *
385 * \param context
386 * The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t.
387 *
388 * \return
389 * This function returns \ref cy_en_em_eeprom_status_t.
390 *
391 * \note
392 * This function uses a buffer of the flash row size to perform write
393 * operation. For the size of the row refer to the specific PSoC device
394 * datasheet.
395 *
396 * \sideeffect
397 * In case when blocking write option is used, if this function is called by
398 * the CM4 the user code on CM0P and the user code on CM4 are blocked until erase
399 * flash row operation is finished. If this function is called by the CM0P the
400 * user code on CM4 is not blocked and the user code on CM0P is blocked until
401 * erase flash row operation is finished. Plan your task allocation accordingly.
402 *
403 * \sideeffect
404 * In case if non-blocking write option is used and when user flash is used as
405 * an EEPROM storage care should be taken to prevent the read while write (RWW)
406 * exception. To prevent the RWW exception the user flash macro that includes
407 * the EEPROM storage should not be read while the EEPROM write is not completed.
408 * The read also means the user code execution from the respective flash macro.
409 *
410 *******************************************************************************/
411 cy_en_em_eeprom_status_t Cy_Em_EEPROM_Write(uint32 addr,
412                                             void * eepromData,
413                                             uint32 size,
414                                             cy_stc_eeprom_context_t * context)
415 {
416     cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_PARAM;
417     uint32 i;
418     uint32 wrCnt;
419     uint32 actEmEepromRowNum;
420     uint32 writeRamBuffer[CY_EM_EEPROM_FLASH_SIZEOF_ROW / CY_EM_EEPROM_U32_DIV];
421     uint32 startAddr = 0u;
422     uint32 endAddr = 0u;
423     uint32 tmpRowAddr;
424     uint32 emEepromRowAddr = context->lastWrRowAddr;
425     uint32 emEepromRowRdAddr;
426     void * tmpData;
427     uint32 eeData = (uint32) eepromData; /* To avoid the pointer arithmetic with void */
428
429     /* Check if the EEPROM data does not exceed the EEPROM capacity */
430     if((0u != size) && ((addr + size) <= (context->eepromSize)) && (NULL != eepromData))
431     {
432         uint32 numWrites = ((size - 1u) / CY_EM_EEPROM_HEADER_DATA_LEN) + 1u;
433         uint32 eeHeaderDataOffset = 0u;
434
435         for(wrCnt = 0u; wrCnt < numWrites; wrCnt++)
436         {
437             uint32 skipOperation = 0u;
438             /* Get the sequence number of the last written row */
439             uint32 seqNum = CY_EM_EEPROM_GET_SEQ_NUM(emEepromRowAddr);
440
441             /* Get the address of the row to be written. The "emEepromRowAddr" may be
442             * updated with the proper address (if wear leveling is used). The
443             * "emEepromRowRdAddr" will point to the row address from which the historic
444             * data will be read into the RAM buffer.
445             */
446             GetNextRowToWrite(seqNum, &emEepromRowAddr, &emEepromRowRdAddr, context);
447
448             /* Clear the RAM buffer so to not put junk into flash */
449             (void)memset(writeRamBuffer, 0, CY_EM_EEPROM_FLASH_SIZEOF_ROW);
450
451             /* Fill the EM_EEPROM header info for the row in the RAM buffer */
452             seqNum++;
453             writeRamBuffer[CY_EM_EEPROM_HEADER_SEQ_NUM_OFFSET_U32] = seqNum;
454             writeRamBuffer[CY_EM_EEPROM_HEADER_ADDR_OFFSET_U32] = addr;
455             tmpData = (void *) eeData;
456
457             /* Check if this is the last row to write */
458             if(wrCnt == (numWrites - 1u))
459             {
460                 /* Fill in the remaining size value to the EEPROM header. */
461                 writeRamBuffer[CY_EM_EEPROM_HEADER_LEN_OFFSET_U32] = size;
462             }
463             else
464             {
465                 /* This is not the last row to write in the current EEPROM write operation.
466                 * Write the maximum possible data size to the EEPROM header. Update the
467                 * size, eeData and addr respectively.
468                 */
469                 writeRamBuffer[CY_EM_EEPROM_HEADER_LEN_OFFSET_U32] = CY_EM_EEPROM_HEADER_DATA_LEN;
470                 size -= CY_EM_EEPROM_HEADER_DATA_LEN;
471                 addr += CY_EM_EEPROM_HEADER_DATA_LEN;
472                 eeData += CY_EM_EEPROM_HEADER_DATA_LEN;
473             }
474
475             /* Write the data to the EEPROM header */
476             (void)memcpy((void *)&writeRamBuffer[CY_EM_EEPROM_HEADER_DATA_OFFSET_U32],
477                          tmpData,
478                          writeRamBuffer[CY_EM_EEPROM_HEADER_LEN_OFFSET_U32]);
479
480             if(emEepromRowRdAddr != 0UL)
481             {
482                 /* Copy the EEPROM historic data for this row from flash to RAM */
483                 (void)memcpy((void *)&writeRamBuffer[CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32],
484                              (void *)(emEepromRowRdAddr + CY_EM_EEPROM_EEPROM_DATA_LEN),
485                              CY_EM_EEPROM_EEPROM_DATA_LEN);
486             }
487
488             /* Check if there is data for this location in other EEPROM headers:
489             * find out the row with the lowest possible sequence number which
490             * may contain the data for the current row.
491             */
492             i = (seqNum > context->numberOfRows) ? ((seqNum - (context->numberOfRows)) + 1u) : 1u;
493
494             for(; i <= seqNum; i++)
495             {
496                 if(i == seqNum)
497                 {
498                     /* The code reached the row that is about to be written. Analyze the recently
499                     * created EEPROM header (stored in the RAM buffer currently): if it contains
500                     * the data for EEPROM data locations in the row that is about to be written.
501                     */
502                     tmpRowAddr = (uint32) writeRamBuffer;
503                 }
504                 else
505                 {
506                     /* Retrieve the address of the previously written row by its sequence number.
507                     * The pointer will be used to get data from the respective EEPROM header.
508                     */
509                     tmpRowAddr = GetRowAddrBySeqNum(i, context);
510                 }
511
512                 actEmEepromRowNum = CY_EM_EEPROM_GET_ACT_ROW_NUM_FROM_ADDR(emEepromRowAddr,
513                                                                            context->numberOfRows,
514                                                                            context->userFlashStartAddr);
515                 if(0UL != tmpRowAddr)
516                 {
517                     /* Calculate the required addressed for the later EEPROM historic data update */
518                     skipOperation = GetAddresses(
519                                               &startAddr,
520                                               &endAddr,
521                                               &eeHeaderDataOffset,
522                                               actEmEepromRowNum,
523                                               *(uint32 *)(tmpRowAddr + CY_EM_EEPROM_HEADER_ADDR_OFFSET),
524                                               *(uint32 *)(tmpRowAddr + CY_EM_EEPROM_HEADER_LEN_OFFSET));
525                 }
526                 else
527                 {
528                     /* Skip writes to the RAM buffer */
529                     skipOperation++;
530                 }
531
532                 /* Write data to the RAM buffer */
533                 if(0u == skipOperation)
534                 {
535                     uint32 dataAddr = ((uint32)((uint8 *)&writeRamBuffer)) + startAddr;
536
537                     /* Update the address to point to the EEPROM header data and not to
538                     * the start of the row.
539                     */
540                     tmpRowAddr = tmpRowAddr + CY_EM_EEPROM_HEADER_DATA_OFFSET + eeHeaderDataOffset;
541                     (void)memcpy((void *)(dataAddr), (void *)(tmpRowAddr), endAddr - startAddr);
542                 }
543
544                 /* Calculate the checksum if redundant copy is enabled */
545                 if(0u != context->redundantCopy)
546                 {
547                     writeRamBuffer[CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET_U32] = (uint32)
548                         CalcChecksum((uint8 *) &writeRamBuffer[CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32],
549                                                   CY_EM_EEPROM_EEPROM_DATA_LEN);
550                 }
551             }
552
553             /* Write the data to the specified flash row */
554             ret = WriteRow(emEepromRowAddr, writeRamBuffer, context);
555             tmpRowAddr = emEepromRowAddr;
556
557             /* Check if redundant copy is used */
558             if((0u != context->redundantCopy) && (CY_EM_EEPROM_SUCCESS == ret))
559             {
560                 /* Update the row address to point to the row in the redundant EEPROM's copy */
561                 tmpRowAddr = (emEepromRowAddr - context->userFlashStartAddr) + context->wlEndAddr;
562
563                 /* Write the data to the specified flash row */
564                 ret = WriteRow(tmpRowAddr, writeRamBuffer, context);
565             }
566
567             if(CY_EM_EEPROM_SUCCESS == ret)
568             {
569                 /* Store last written row address only when EEPROM and redundant
570                 * copy writes were successful.
571                 */
572                 context->lastWrRowAddr = emEepromRowAddr;
573             }
574             else
575             {
576                 break;
577             }
578         }
579     }
580     return(ret);
581 }
582
583
584 /*******************************************************************************
585 * Function Name: Cy_Em_EEPROM_Erase
586 ****************************************************************************//**
587 *
588 * This function erases the entire contents of the EEPROM. Erased values are all
589 * zeros. This is a blocking function and it does not return until the write
590 * operation is completed. The user firmware should not enter Hibernate mode until
591 * erase is completed. The erase operation is allowed in Sleep and Deep-Sleep modes.
592 * During the flash operation, the device should not be reset, including the
593 * XRES pin, a software reset, and watchdog reset sources. Also, low-voltage
594 * detect circuits should be configured to generate an interrupt instead of a
595 * reset. Otherwise, portions of flash may undergo unexpected changes.
596 *
597 * \param context
598 * The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t.
599 *
600 * \return
601 * This function returns \ref cy_en_em_eeprom_status_t.
602 *
603 * \note
604 * For all non PSoC 6 devices the erase operation is performed by clearing
605 * the EEPROM data using flash write. This affects the flash durability.
606 * So it is recommended to use this function in utmost case to prolongate
607 * flash life.
608 *
609 * \note
610 * This function uses a buffer of the flash row size to perform erase
611 * operation. For the size of the row refer to the specific PSoC device
612 * datasheet.
613 *
614 * \sideeffect
615 * In case when blocking write option is used, if this function is called by
616 * the CM4 the user code on CM0P and the user code on CM4 are blocked until erase
617 * flash row operation is finished. If this function is called by the CM0P the
618 * user code on CM4 is not blocked and the user code on CM0P is blocked until
619 * erase flash row operation is finished. Plan your task allocation accordingly.
620 *
621 * \sideeffect
622 * In case if non-blocking write option is used and when user flash is used as
623 * an EEPROM storage care should be taken to prevent the read while write (RWW)
624 * exception. To prevent the RWW exception the user flash macro that includes
625 * the EEPROM storage should not be read while the EEPROM erase is not completed.
626 * The read also means the user code execution from the respective flash macro.
627 *
628 *******************************************************************************/
629 cy_en_em_eeprom_status_t Cy_Em_EEPROM_Erase(cy_stc_eeprom_context_t * context)
630 {
631     uint32 i;
632     uint32 seqNum;
633     uint32 emEepromRowAddr = context->lastWrRowAddr;
634     uint32 emEepromRowRdAddr;
635     cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_WRITE_FAIL;
636     uint32 writeRamBuffer[CY_EM_EEPROM_FLASH_SIZEOF_ROW / CY_EM_EEPROM_U32_DIV] = {0u};
637 #if (CY_PSOC6)
638     uint32 emEepromStoredRowAddr = context->lastWrRowAddr;
639     uint32 storedSeqNum;
640 #endif /* (!CY_PSOC6) */
641
642     /* Get the sequence number of the last written row */
643     seqNum = CY_EM_EEPROM_GET_SEQ_NUM(emEepromRowAddr);
644
645     /* If there were no writes to EEPROM - nothing to erase */
646     if(0u != seqNum)
647     {
648         /* Calculate the number of row erase operations required */
649         uint32 numWrites = context->numberOfRows * context->wearLevelingFactor;
650
651     #if (CY_PSOC6)
652         GetNextRowToWrite(seqNum, &emEepromStoredRowAddr, &emEepromRowRdAddr, context);
653         storedSeqNum = seqNum + 1u;
654     #endif /* (CY_PSOC6) */
655
656         if(0u != context->redundantCopy)
657         {
658             writeRamBuffer[CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET_U32] = (uint32)
659                         CalcChecksum((uint8 *) &writeRamBuffer[CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32],
660                                                   CY_EM_EEPROM_EEPROM_DATA_LEN);
661         }
662
663         for(i = 0u; i < numWrites; i++)
664         {
665         #if (CY_PSOC6)
666             /* For PSoC 6 the erase operation moves backwards. From last written row
667             * identified by "seqNum" down to "seqNum" - "numWrites". If "emEepromRowAddr"
668             * is zero this means that the row identified by "seqNum" was previously 
669             * erased.
670             */
671             if(0u != emEepromRowAddr)
672             {
673                 ret = EraseRow(emEepromRowAddr, (uint32)writeRamBuffer, context);
674             }
675
676             seqNum--;
677
678             if(0u == seqNum)
679             {
680                 /* Exit the loop as there is no more row is EEPROM to be erased */
681                 break;
682             }
683             emEepromRowAddr = GetRowAddrBySeqNum(seqNum, context);
684         #else
685             seqNum = CY_EM_EEPROM_GET_SEQ_NUM(emEepromRowAddr);
686             /* Get the address of the row to be erased. "emEepromRowAddr" may be updated
687             * with the proper address (if wear leveling is used).
688             */
689             GetNextRowToWrite(seqNum, &emEepromRowAddr, &emEepromRowRdAddr, context);
690             seqNum++;
691             writeRamBuffer[0u] = seqNum;
692             ret = EraseRow(emEepromRowAddr, (uint32)writeRamBuffer, context);
693         #endif /* (CY_PSOC6) */
694         }
695
696     #if (CY_PSOC6)
697         if(CY_EM_EEPROM_SUCCESS == ret)
698         {
699             writeRamBuffer[0u] = storedSeqNum;
700
701             /* Write the previously stored sequence number to the flash row which would be
702             * written next if the erase wouldn't happen. In this case the write to
703             * redundant copy can be skipped as it does not add any value.
704             */
705             ret = WriteRow(emEepromStoredRowAddr, writeRamBuffer, context);
706
707             if(CY_EM_EEPROM_SUCCESS == ret)
708             {
709                 context->lastWrRowAddr = emEepromStoredRowAddr;
710             }
711         }
712     #endif /* (CY_PSOC6) */
713
714     }
715     return(ret);
716 }
717
718
719 /*******************************************************************************
720 * Function Name: Cy_Em_EEPROM_NumWrites
721 ****************************************************************************//**
722 *
723 * Returns the number of the EEPROM writes completed so far.
724 *
725 * \param context
726 * The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t.
727 *
728 * \return
729 * The number of writes performed to the EEPROM.
730 *
731 *******************************************************************************/
732 uint32 Cy_Em_EEPROM_NumWrites(cy_stc_eeprom_context_t * context)
733 {
734     return(CY_EM_EEPROM_GET_SEQ_NUM(context->lastWrRowAddr));
735 }
736
737 /** \} */
738
739 /** \cond INTERNAL */
740
741
742 /*******************************************************************************
743 * Function Name: FindLastWrittenRow
744 ****************************************************************************//**
745 *
746 * Performs a search of the last written row address of the EEPROM associated
747 * with the context structure. If there were no writes to the EEPROM the
748 * function returns the start address of the EEPROM. The row address is returned
749 * in the input parameter.
750 *
751 * \param lastWrRowPtr
752 * The pointer to a memory where the last written row will be returned.
753 *
754 * \param context
755 * The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t.
756 *
757 *******************************************************************************/
758 static void FindLastWrittenRow(uint32 * lastWrRowPtr, cy_stc_eeprom_context_t * context)
759 {
760     uint32 seqNum = 0u;
761     uint32 prevSeqNum = 0u;
762     uint32 numRows;
763     uint32 emEepromAddr = context->userFlashStartAddr;
764
765     *lastWrRowPtr = emEepromAddr;
766
767     for(numRows = 0u; numRows < (context->numberOfRows * context->wearLevelingFactor); numRows++)
768     {
769         seqNum = CY_EM_EEPROM_GET_SEQ_NUM(emEepromAddr);
770         if((0u != seqNum) && (seqNum > prevSeqNum))
771         {
772             /* Some record in EEPROM was found. Store found sequence
773             * number and row address.
774             */
775             prevSeqNum = seqNum;
776             *lastWrRowPtr = emEepromAddr;
777         }
778
779         /* Switch to the next row */
780         emEepromAddr = emEepromAddr + CY_EM_EEPROM_FLASH_SIZEOF_ROW;
781     }
782 }
783
784
785 /*******************************************************************************
786 * Function Name: GetRowAddrBySeqNum
787 ****************************************************************************//**
788 *
789 * Returns the address of the row in EEPROM using its sequence number.
790 *
791 * \param seqNum
792 * The sequence number of the row.
793 *
794 * \param context
795 * The pointer to the EEPROM context structure.
796 *
797 * \return
798 * The address of the row or zero if the row with the sequence number was not
799 * found.
800 *
801 *******************************************************************************/
802 static uint32 GetRowAddrBySeqNum(uint32 seqNum, cy_stc_eeprom_context_t * context)
803 {
804     uint32 emEepromAddr = context->userFlashStartAddr;
805
806     while(CY_EM_EEPROM_GET_SEQ_NUM(emEepromAddr) != seqNum)
807     {
808         /* Switch to the next row */
809         emEepromAddr = emEepromAddr + CY_EM_EEPROM_FLASH_SIZEOF_ROW;
810
811         if (CY_EM_EEPROM_ADDR_IN_RANGE !=
812             CY_EM_EEPROM_IS_ADDR_EXCEED_RANGE(emEepromAddr, context->wlEndAddr))
813         {
814             emEepromAddr = 0u;
815             /* Exit the loop as we reached the end of EEPROM */
816             break;
817         }
818     }
819
820     return (emEepromAddr);
821 }
822
823
824 /*******************************************************************************
825 * Function Name: GetNextRowToWrite
826 ****************************************************************************//**
827 *
828 * Performs a range check of the row that should be written and updates the
829 * address to the row respectively. The similar actions are done for the read
830 * address.
831 *
832 * \param seqNum
833 * The sequence number of the last written row.
834 *
835 * \param rowToWrPtr
836 * The address of the last written row (input). The address of the row to be 
837 * written (output).
838 *
839 * \param rowToRdPtr
840 * The address of the row from which the data should be read into the RAM buffer
841 * in a later write operation. Out parameter.
842 *
843 * \param context
844 * The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t.
845 *
846 *******************************************************************************/
847 static void GetNextRowToWrite(uint32 seqNum,
848                             uint32 * rowToWrPtr,
849                             uint32 * rowToRdPtr,
850                             cy_stc_eeprom_context_t * context)
851 {
852     /* Switch to the next row to be written if the current sequence number is
853     * not zero.
854     */
855     if(0u != seqNum)
856     {
857         *rowToWrPtr = (*rowToWrPtr + CY_EM_EEPROM_FLASH_SIZEOF_ROW);
858     }
859
860     /* If the resulting row address is out of EEPROM, then switch to the base
861     * EEPROM address (Row#0).
862     */
863     if(CY_EM_EEPROM_ADDR_IN_RANGE !=
864             CY_EM_EEPROM_IS_ADDR_EXCEED_RANGE(*rowToWrPtr, context->wlEndAddr))
865     {
866         *rowToWrPtr = context->userFlashStartAddr;
867     }
868
869     *rowToRdPtr = 0u;
870
871     /* Check if the sequence number is larger than the number of rows in the EEPROM.
872     * If not, do not update the row read address because there is no historic
873     * data to be read.
874     */
875     if(context->numberOfRows <= seqNum)
876     {
877         /* Check if wear leveling is used in EEPROM */
878         if(context->wearLevelingFactor > 1u)
879         {
880             /* The read row address should be taken from an EEPROM copy that became
881             * inactive recently. This condition check handles that.
882             */
883             if((*rowToWrPtr - (context->numberOfRows * CY_EM_EEPROM_FLASH_SIZEOF_ROW)) <
884                 context->userFlashStartAddr)
885             {
886                 *rowToRdPtr = context->userFlashStartAddr +
887                     (context->numberOfRows * (context->wearLevelingFactor - 1u) *
888                         CY_EM_EEPROM_FLASH_SIZEOF_ROW) + (*rowToWrPtr - context->userFlashStartAddr);
889             }
890             else
891             {
892                 *rowToRdPtr = *rowToWrPtr - (context->numberOfRows * CY_EM_EEPROM_FLASH_SIZEOF_ROW);
893             }
894         }
895         else
896         {
897             /* If no wear leveling, always read from the same flash row that
898             * should be written.
899             */
900             *rowToRdPtr = *rowToWrPtr;
901         }
902     }
903 }
904
905
906 /*******************************************************************************
907 * Function Name: CalcChecksum
908 ****************************************************************************//**
909 *
910 * Implements CRC-8 that is used in checksum calculation for the redundant copy
911 * algorithm.
912 *
913 * \param rowData
914 * The row data to be used to calculate the checksum.
915 *
916 * \param len
917 * The length of rowData.
918 *
919 * \return
920 * The calculated value of CRC-8.
921 *
922 *******************************************************************************/
923 static uint8 CalcChecksum(uint8 rowData[], uint32 len)
924 {
925     uint8 crc = CY_EM_EEPROM_CRC8_SEED;
926     uint8 i;
927     uint16 cnt = 0u;
928
929     while(cnt != len)
930     {
931         crc ^= rowData[cnt];
932         for (i = 0u; i < CY_EM_EEPROM_CRC8_POLYNOM_LEN; i++)
933         {
934             crc = CY_EM_EEPROM_CALCULATE_CRC8(crc);
935         }
936         cnt++;
937     }
938
939     return (crc);
940 }
941
942
943 /*******************************************************************************
944 * Function Name: CheckRanges
945 ****************************************************************************//**
946 *
947 * Checks if the EEPROM of the requested size can be placed in flash.
948 *
949 * \param config
950 * The pointer to a configuration structure. See \ref cy_stc_eeprom_config_t.
951 *
952 * \return
953 * error / status code. See \ref cy_en_em_eeprom_status_t.
954 *
955 *******************************************************************************/
956 static cy_en_em_eeprom_status_t CheckRanges(cy_stc_eeprom_config_t* config)
957 {
958     cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_DATA;
959     uint32 startAddr = config->userFlashStartAddr;
960     uint32 endAddr = startAddr + CY_EM_EEPROM_GET_PHYSICAL_SIZE(config->eepromSize,
961             config->wearLevelingFactor, config->redundantCopy);
962
963     /* Range check if there is enough flash for EEPROM */
964     if (CY_EM_EEPROM_IS_IN_FLASH_RANGE(startAddr, endAddr))
965     {
966         ret = CY_EM_EEPROM_SUCCESS;
967     }
968     return (ret);
969 }
970
971
972 /*******************************************************************************
973 * Function Name: WriteRow
974 ****************************************************************************//**
975 *
976 * Writes one flash row starting from the specified row address.
977 *
978 * \param rowAdd
979 * The address of the flash row.
980 *
981 * \param rowData
982 * The pointer to the data to be written to the row.
983 *
984 * \param context
985 * The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t.
986 *
987 * \return
988 * error / status code. See \ref cy_en_em_eeprom_status_t.
989 *
990 *******************************************************************************/
991 static cy_en_em_eeprom_status_t WriteRow(uint32 rowAddr,
992                                         uint32 *rowData,
993                                         cy_stc_eeprom_context_t * context)
994 {
995     cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_WRITE_FAIL;
996 #if (!CY_PSOC6)
997     cystatus rc;
998     uint32 rowId;
999     #if ((CY_PSOC3) || (CY_PSOC5))
1000         uint32 arrayId;
1001     #endif /* (CY_PSOC3) */
1002     
1003     #if (CY_PSOC3)
1004         rowAddr &= CY_EM_EEPROM_CODE_ADDR_MASK;
1005         context = context;      /* To avoid compiler warning generation */
1006     #else
1007         (void)context;          /* To avoid compiler warning generation */
1008     #endif /* ((CY_PSOC3) */
1009     
1010     /* For non-PSoC 6 devices, the Array ID and Row ID needed to write the row */
1011     rowId = (rowAddr / CY_EM_EEPROM_FLASH_SIZEOF_ROW) % CY_EM_EEPROM_ROWS_IN_ARRAY;
1012
1013     /* Write the flash row */
1014     #if (CY_PSOC4)
1015         rc = CySysFlashWriteRow(rowId, (uint8 *)rowData);
1016     #else
1017
1018         #ifndef CY_EM_EEPROM_SKIP_TEMP_MEASUREMENT
1019             (void)CySetTemp();
1020         #endif /* (CY_EM_EEPROM_SKIP_TEMP_MEASUREMENT) */
1021
1022         arrayId = rowAddr / CY_FLASH_SIZEOF_ARRAY;
1023         rc = CyWriteRowData((uint8)arrayId, (uint16)rowId, (uint8 *)rowData);
1024         
1025         #if (CY_PSOC5)
1026             CyFlushCache();
1027         #endif /* (CY_PSOC5) */
1028     #endif /* (CY_PSOC4) */
1029
1030     if(CYRET_SUCCESS == rc)
1031     {
1032         ret = CY_EM_EEPROM_SUCCESS;
1033     }
1034 #else /* PSoC 6 */
1035     if(0u != context->blockingWrite)
1036     {
1037         /* Do blocking write */
1038         if(CY_FLASH_DRV_SUCCESS == Cy_Flash_WriteRow(rowAddr, (const uint32 *)rowData))
1039         {
1040             ret = CY_EM_EEPROM_SUCCESS;
1041         }
1042     }
1043     else
1044     {
1045         /* Initiate write */
1046         if(CY_FLASH_DRV_OPERATION_STARTED == Cy_Flash_StartWrite(rowAddr, (const uint32 *)rowData))
1047         {
1048             uint32 countMs = CY_EM_EEPROM_MAX_WRITE_DURATION_MS;
1049             cy_en_flashdrv_status_t rc;
1050
1051             do
1052             {
1053                 CyDelay(1u);                         /* Wait 1ms */
1054                 rc = Cy_Flash_IsWriteComplete();     /* Check if write completed */
1055                 countMs--;
1056             }
1057             while ((rc == CY_FLASH_DRV_OPCODE_BUSY) && (0u != countMs));
1058
1059             if(CY_FLASH_DRV_SUCCESS == rc)
1060             {
1061                 ret = CY_EM_EEPROM_SUCCESS;
1062             }
1063         }
1064     }
1065 #endif /* (CY_PSOC6) */
1066
1067     return (ret);
1068 }
1069
1070
1071 /*******************************************************************************
1072 * Function Name: EraseRow
1073 ****************************************************************************//**
1074 *
1075 * Erases one flash row starting from the specified row address. If the redundant
1076 * copy option is enabled the corresponding row in the redundant copy will also
1077 * be erased.
1078 *
1079 * \param rowAdd
1080 * The address of the flash row.
1081 *
1082 * \param ramBuffAddr
1083 * The address of the RAM buffer that contains zeroed data (used only for
1084 * non-PSoC 6 devices).
1085 *
1086 * \param context
1087 * The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t.
1088 *
1089 * \return
1090 * error / status code. See \ref cy_en_em_eeprom_status_t.
1091 *
1092 *******************************************************************************/
1093 static cy_en_em_eeprom_status_t EraseRow(uint32 rowAddr,
1094                                         uint32 ramBuffAddr,
1095                                         cy_stc_eeprom_context_t * context)
1096 {
1097     uint32 emEepromRowAddr = rowAddr;
1098     cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_WRITE_FAIL;
1099 #if (CY_PSOC6)
1100     uint32 i = 1u;
1101
1102     (void)ramBuffAddr; /* To avoid compiler warning */
1103
1104     if(0u != context->redundantCopy)
1105     {
1106         i++;
1107     }
1108
1109     do
1110     {
1111         if(0u != context->blockingWrite)
1112         {
1113             /* Erase the flash row */
1114             if(CY_FLASH_DRV_SUCCESS == Cy_Flash_EraseRow(emEepromRowAddr))
1115             {
1116                 ret = CY_EM_EEPROM_SUCCESS;
1117             }
1118         }
1119         else
1120         {
1121             /* Initiate erase */
1122             if(CY_FLASH_DRV_OPERATION_STARTED == Cy_Flash_StartErase(emEepromRowAddr))
1123             {
1124                 uint32 countMs = CY_EM_EEPROM_MAX_WRITE_DURATION_MS;
1125                 cy_en_flashdrv_status_t rc;
1126
1127                 do
1128                 {
1129                     CyDelay(1u);                         /* Wait 1ms */
1130                     rc = Cy_Flash_IsWriteComplete();     /* Check if erase completed */
1131                     countMs--;
1132                 }
1133                 while ((rc == CY_FLASH_DRV_OPCODE_BUSY) && (0u != countMs));
1134
1135                 if(CY_FLASH_DRV_SUCCESS == rc)
1136                 {
1137                     ret = CY_EM_EEPROM_SUCCESS;
1138                 }
1139             }
1140         }
1141
1142         if(CY_EM_EEPROM_SUCCESS == ret)
1143         {
1144             /* Update the address to point to the redundant copy row */
1145             emEepromRowAddr = (emEepromRowAddr - context->userFlashStartAddr) + context->wlEndAddr;
1146         }
1147         else
1148         {
1149             break;
1150         }
1151         i--;
1152     } while (0u != i);
1153 #else
1154     /* Write the data to the specified flash row */
1155     ret = WriteRow(emEepromRowAddr, (uint32 *)ramBuffAddr, context);
1156
1157     if((CY_EM_EEPROM_SUCCESS == ret) && (0u != context->redundantCopy))
1158     {
1159         /* Update the address to point to the redundant copy row */
1160         emEepromRowAddr = (emEepromRowAddr - context->userFlashStartAddr) + context->wlEndAddr;
1161         ret = WriteRow(emEepromRowAddr, (uint32 *)ramBuffAddr, context);
1162     }
1163
1164     if(CY_EM_EEPROM_SUCCESS == ret)
1165     {
1166         context->lastWrRowAddr = rowAddr;
1167     }
1168 #endif /* (CY_PSOC6) */
1169
1170     return(ret);
1171 }
1172
1173
1174 /*******************************************************************************
1175 * Function Name: CheckCrcAndCopy
1176 ****************************************************************************//**
1177 *
1178 * Checks the checksum of the specific row in EEPROM. If the CRC matches - copies
1179 * the data to the "datAddr" from EEPROM. f the CRC does not match checks the
1180 * CRC of the corresponding row in the EEPROM's redundant copy. If the CRC
1181 * matches - copies the data to the "datAddr" from EEPROM redundant copy. If the
1182 * CRC of the redundant copy does not match - returns bad checksum.
1183 *
1184 * \param startAddr
1185 * The address that points to the start of the specified row.
1186 *
1187 * \param datAddr
1188 * The start address of where the row data will be copied if the CRC check
1189 * will succeed.
1190 *
1191 * \param rowOffset
1192 * The offset in the row from which the data should be copied.
1193 *
1194 * \param numBytes
1195 * The number of bytes to be copied.
1196 *
1197 * \param context
1198 * The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t.
1199 *
1200 * \return
1201 * error / status code. See \ref cy_en_em_eeprom_status_t.
1202 *
1203 * \note
1204 * This function uses a buffer of the flash row size to perform read
1205 * operation. For the size of the row refer to the specific PSoC device
1206 * datasheet.
1207 *
1208 *******************************************************************************/
1209 static cy_en_em_eeprom_status_t CheckCrcAndCopy(uint32 startAddr,
1210                                                 uint32 dstAddr,
1211                                                 uint32 rowOffset,
1212                                                 uint32 numBytes,
1213                                                 cy_stc_eeprom_context_t * context)
1214 {
1215     cy_en_em_eeprom_status_t ret;
1216     uint32 writeRamBuffer[CY_EM_EEPROM_FLASH_SIZEOF_ROW / CY_EM_EEPROM_U32_DIV];
1217
1218     /* Calculate the row address in the EEPROM's redundant copy */
1219     uint32 rcStartRowAddr = (startAddr - context->userFlashStartAddr) + context->wlEndAddr;
1220
1221     /* Check the row data CRC in the EEPROM */
1222     if((*(uint32 *)(startAddr + CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET)) ==
1223         ((uint32) CalcChecksum((uint8 *)(startAddr + CY_EM_EEPROM_EEPROM_DATA_OFFSET),
1224             CY_EM_EEPROM_EEPROM_DATA_LEN)))
1225     {
1226         (void)memcpy((void *)(dstAddr), (void *)(startAddr + rowOffset), numBytes);
1227
1228         ret = CY_EM_EEPROM_SUCCESS;
1229     }
1230     /* Check the row data CRC in the EEPROM's redundant copy */
1231     else if((*(uint32 *)(rcStartRowAddr + CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET)) ==
1232             ((uint32) CalcChecksum((uint8 *)(rcStartRowAddr + CY_EM_EEPROM_EEPROM_DATA_OFFSET),
1233                 CY_EM_EEPROM_EEPROM_DATA_LEN)))
1234     {
1235         /* Copy the redundant copy row to RAM buffer to avoid read while write (RWW)
1236         * flash exception. The RWW occurs while trying to write and read the data from
1237         * same flash macro.
1238         */
1239         (void)memcpy((void *)(writeRamBuffer), (void *)(rcStartRowAddr), CY_EM_EEPROM_FLASH_SIZEOF_ROW);
1240
1241         /* Restore bad row data from the RAM buffer */
1242         ret = WriteRow(startAddr, (uint32 *)writeRamBuffer, context);
1243
1244         if(CY_EM_EEPROM_SUCCESS == ret)
1245         {
1246             (void)memcpy((void *)(dstAddr), (void *)(writeRamBuffer + rowOffset), numBytes);
1247         }
1248     }
1249     else
1250     {
1251         ret = CY_EM_EEPROM_BAD_CHECKSUM;
1252     }
1253
1254     return(ret);
1255 }
1256
1257
1258 /*******************************************************************************
1259 * Function Name: GetAddresses
1260 ****************************************************************************//**
1261 *
1262 * Calculates the start and end address of the row's EEPROM data to be updated.
1263 * The start and end are not absolute addresses but a relative addresses in a
1264 * flash row.
1265 *
1266 * \param startAddr
1267 * The pointer the address where the EEPROM data start address will be returned.
1268 *
1269 * \param endAddr
1270 * The pointer the address where the EEPROM data end address will be returned.
1271 *
1272 * \param offset
1273 * The pointer the address where the calculated offset of the EEPROM header data
1274 * will be returned.
1275 *
1276 * \param rowNum
1277 * The row number that is about to be written.
1278 *
1279 * \param addr
1280 * The address of the EEPROM header data in the currently analyzed row that may
1281 * concern to the row about to be written.
1282 *
1283 * \param len
1284 * The length of the EEPROM header data in the currently analyzed row that may
1285 * concern to the row about to be written.
1286 *
1287 * \return
1288 * Zero indicates that the currently analyzed row has the data to be written to
1289 * the active EEPROM row data locations. Non zero value indicates that there is
1290 * no data to be written
1291 *
1292 *******************************************************************************/
1293 static uint32 GetAddresses(uint32 *startAddr, 
1294                         uint32 *endAddr, 
1295                         uint32 *offset,
1296                         uint32 rowNum,
1297                         uint32 addr,
1298                         uint32 len)
1299 {
1300     uint32 skip = 0u;
1301
1302     *offset =0u;
1303
1304     if(0u != CY_EM_EEPROM_IS_ADDR_IN_ROW_RANGE(addr, rowNum))
1305     {
1306         *startAddr = CY_EM_EEPROM_EEPROM_DATA_LEN + (addr % CY_EM_EEPROM_EEPROM_DATA_LEN);
1307
1308         if(0u != CY_EM_EEPROM_IS_ADDR_IN_ROW_RANGE(addr + len, rowNum))
1309         {
1310             *endAddr = *startAddr + len;
1311         }
1312         else
1313         {
1314             *endAddr = CY_EM_EEPROM_FLASH_SIZEOF_ROW;
1315         }
1316     }
1317     else
1318     {
1319
1320         if(0u != CY_EM_EEPROM_IS_ADDR_IN_ROW_RANGE(addr + len, rowNum))
1321         {
1322             *startAddr = CY_EM_EEPROM_EEPROM_DATA_LEN;
1323             *endAddr = (*startAddr + len) - (*startAddr - (addr % CY_EM_EEPROM_EEPROM_DATA_LEN));
1324             *offset = len - (*endAddr - *startAddr);
1325         }
1326         else
1327         {
1328             skip++;
1329         }
1330     }
1331
1332     return (skip);
1333 }
1334
1335
1336 /*******************************************************************************
1337 * Function Name: FillChecksum
1338 ****************************************************************************//**
1339 *
1340 * Performs calculation of the checksum on each row in the Em_EEPROM and fills
1341 * the Em_EEPROM headers checksum field with the calculated checksums.
1342 *
1343 * \param context
1344 * The pointer to the EEPROM context structure.
1345 *
1346 * \return
1347 * error / status code. See \ref cy_en_em_eeprom_status_t.
1348 *
1349 * \theory 
1350 * In case if redundant copy option is used the Em_EEPROM would return bad 
1351 * checksum while trying to read the EEPROM rows which were not yet written by 
1352 * the user. E.g. any read after device reprogramming without previous Write() 
1353 * operation to the EEPROM would fail. This would happen because the Em_EEPROM 
1354 * headers checksum field values (which is zero at the moment) would not be 
1355 * equal to the actual data checksum. This function allows to avoid read failure
1356 * after device reprogramming. 
1357 *
1358 * \note
1359 * This function uses a buffer of the flash row size to perform read
1360 * operation. For the size of the row refer to the specific PSoC device
1361 * datasheet.
1362 *
1363 *******************************************************************************/
1364 static cy_en_em_eeprom_status_t FillChecksum(cy_stc_eeprom_context_t * context)
1365 {
1366     uint32 i;
1367     uint32 rdAddr;
1368     uint32 writeRamBuffer[CY_EM_EEPROM_FLASH_SIZEOF_ROW / CY_EM_EEPROM_U32_DIV];
1369     uint32 wrAddr = context->lastWrRowAddr;
1370     uint32 tmpRowAddr;
1371     /* Get the sequence number (number of writes) */
1372     uint32 seqNum = CY_EM_EEPROM_GET_SEQ_NUM(wrAddr);
1373     cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_PARAM;
1374
1375     for(i = 0u; i < (context->numberOfRows * context->wearLevelingFactor); i++)
1376     {
1377         /* Copy the EEPROM row from Flash to RAM */
1378         (void)memcpy((void *)&writeRamBuffer[0u], (void *)(wrAddr), CY_EM_EEPROM_FLASH_SIZEOF_ROW);
1379
1380         /* Increment the sequence number */
1381         seqNum++;
1382         writeRamBuffer[CY_EM_EEPROM_HEADER_SEQ_NUM_OFFSET_U32] = seqNum;
1383
1384         /* Calculate and fill the checksum to the Em_EEPROM header */
1385         writeRamBuffer[CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET_U32] = (uint32)
1386                     CalcChecksum((uint8 *) &writeRamBuffer[CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32],
1387                                               CY_EM_EEPROM_EEPROM_DATA_LEN);
1388
1389         /* Write the data to the specified flash row */
1390         ret = WriteRow(wrAddr, writeRamBuffer, context);
1391
1392         /* Update the row address to point to the relevant row in the redundant 
1393         * EEPROM's copy.
1394         */
1395         tmpRowAddr = (wrAddr - context->userFlashStartAddr) + context->wlEndAddr;
1396
1397         /* Write the data to the specified flash row */
1398         ret = WriteRow(tmpRowAddr, writeRamBuffer, context);
1399
1400         /* Get the address of the next row to be written. 
1401         * "rdAddr" is not used in this function but provided to prevent NULL 
1402         * pointer exception in GetNextRowToWrite().
1403         */
1404         GetNextRowToWrite(seqNum, &wrAddr, &rdAddr, context);
1405     }
1406     
1407     return(ret);
1408 }
1409
1410 /** \endcond */
1411
1412 #if defined(__cplusplus)
1413 }
1414 #endif
1415
1416 /* [] END OF FILE */