From: Michael McMaster Date: Sat, 2 Mar 2019 05:08:12 +0000 (+1000) Subject: Improve XEBEC controller support X-Git-Tag: v4.8.3~7 X-Git-Url: http://git.codesrc.com/gitweb.cgi?a=commitdiff_plain;h=efca14f6b5e9bb6b9656f8d6f501475dc7828efb;p=SCSI2SD.git Improve XEBEC controller support --- diff --git a/software/SCSI2SD/src/diagnostic.c b/software/SCSI2SD/src/diagnostic.c index 50bdb78..d8f6477 100755 --- a/software/SCSI2SD/src/diagnostic.c +++ b/software/SCSI2SD/src/diagnostic.c @@ -214,4 +214,15 @@ void scsiWriteBuffer() } } +// XEBEC specific command. See +// http://www.bitsavers.org/pdf/westernDigital/WD100x/79-000004_WD1002-SHD_OEM_Manual_Aug1984.pdf +// Section 4.3.14 +void scsiWriteSectorBuffer() +{ + scsiDev.dataLen = scsiDev.target->liveCfg.bytesPerSector; + scsiDev.phase = DATA_OUT; + scsiDev.postDataOutHook = doWriteBuffer; +} + + diff --git a/software/SCSI2SD/src/diagnostic.h b/software/SCSI2SD/src/diagnostic.h index 98e90d2..977058f 100755 --- a/software/SCSI2SD/src/diagnostic.h +++ b/software/SCSI2SD/src/diagnostic.h @@ -20,6 +20,7 @@ void scsiSendDiagnostic(void); void scsiReceiveDiagnostic(void); void scsiWriteBuffer(void); +void scsiWriteSectorBuffer(void); void scsiReadBuffer(void); #endif diff --git a/software/SCSI2SD/src/scsi.c b/software/SCSI2SD/src/scsi.c index db37854..dc39968 100755 --- a/software/SCSI2SD/src/scsi.c +++ b/software/SCSI2SD/src/scsi.c @@ -472,6 +472,11 @@ static void process_Command() { scsiWriteBuffer(); } + else if (command == 0x0f && + scsiDev.target->cfg->quirks == CONFIG_QUIRKS_XEBEC) + { + scsiWriteSectorBuffer(); + } else if (command == 0x3C) { scsiReadBuffer(); @@ -586,7 +591,16 @@ static void scsiReset() // in which case TERMPWR cannot be supplied, and reset will ALWAYS // be true. Therefore, the sleep here must be slow to avoid slowing // USB comms - CyDelay(1); // 1ms. + // Also, need to exit quickly for XEBEC controllers which may + // assert RST immediately before pulsing a SEL. + uint32_t rstTimerBegin = getTime_ms(); + while (SCSI_ReadFilt(SCSI_Filt_RST)) + { + if (elapsedTime_ms(rstTimerBegin) >= 1) + { + break; + } + } } static void enter_SelectionPhase() @@ -735,6 +749,13 @@ static void process_SelectionPhase() { break; } + else if (elapsedTime_ms(selTimerBegin) >= 10 && + scsiDev.target->cfg->quirks == CONFIG_QUIRKS_XEBEC) + { + // XEBEC hosts may not bother releasing SEL at all until + // just before the command ends. + break; + } else if (elapsedTime_ms(selTimerBegin) >= 250) { SCSI_ClearPin(SCSI_Out_BSY); diff --git a/software/SCSI2SD/src/scsiPhy.c b/software/SCSI2SD/src/scsiPhy.c index 84cbd88..275cc71 100755 --- a/software/SCSI2SD/src/scsiPhy.c +++ b/software/SCSI2SD/src/scsiPhy.c @@ -457,7 +457,6 @@ void scsiPhyReset() #ifdef SCSI_Out_ACK SCSI_ClearPin(SCSI_Out_ACK); #endif - SCSI_ClearPin(SCSI_Out_RST); SCSI_ClearPin(SCSI_Out_SEL); SCSI_ClearPin(SCSI_Out_REQ); diff --git a/software/SCSI2SD/v4/SCSI2SD.cydsn/Generated_Source/PSoC5/cy_em_eeprom.c b/software/SCSI2SD/v4/SCSI2SD.cydsn/Generated_Source/PSoC5/cy_em_eeprom.c new file mode 100755 index 0000000..ce94d9c --- /dev/null +++ b/software/SCSI2SD/v4/SCSI2SD.cydsn/Generated_Source/PSoC5/cy_em_eeprom.c @@ -0,0 +1,1416 @@ +/***************************************************************************//** +* \file cy_em_eeprom.c +* \version 2.0 +* +* \brief +* This file provides source code of the API for the Emulated EEPROM library. +* The Emulated EEPROM API allows creating of an emulated EEPROM in flash that +* has the ability to do wear leveling and restore corrupted data from a +* redundant copy. +* +******************************************************************************** +* \copyright +* Copyright 2017, Cypress Semiconductor Corporation. All rights reserved. +* You may use this file only in accordance with the license, terms, conditions, +* disclaimers, and limitations in the end user license agreement accompanying +* the software package with which this file was provided. +*******************************************************************************/ + + +#include "cytypes.h" +#include + +#if (CYDEV_CHIP_FAMILY_USED == CYDEV_CHIP_FAMILY_PSOC6) + #include "em_eeprom/cy_em_eeprom.h" +#else + #include "cy_em_eeprom.h" +#endif /* (CYDEV_CHIP_FAMILY_USED == CYDEV_CHIP_FAMILY_PSOC6) */ + + +#if defined(__cplusplus) +extern "C" { +#endif + + +/*************************************** +* Private Function Prototypes +***************************************/ +static void FindLastWrittenRow(uint32 * lastWrRowPtr, cy_stc_eeprom_context_t * context); +static uint32 GetRowAddrBySeqNum(uint32 seqNum, cy_stc_eeprom_context_t * context); +static uint8 CalcChecksum(uint8 rowData[], uint32 len); +static void GetNextRowToWrite(uint32 seqNum, + uint32 * rowToWrPtr, + uint32 * rowToRdPtr, + cy_stc_eeprom_context_t * context); +static cy_en_em_eeprom_status_t CheckRanges(cy_stc_eeprom_config_t* config); +static cy_en_em_eeprom_status_t WriteRow(uint32 rowAddr, uint32 *rowData, cy_stc_eeprom_context_t * context); +static cy_en_em_eeprom_status_t EraseRow(uint32 rowAddr, uint32 ramBuffAddr, cy_stc_eeprom_context_t * context); +static cy_en_em_eeprom_status_t CheckCrcAndCopy(uint32 startAddr, + uint32 dstAddr, + uint32 rowOffset, + uint32 numBytes, + cy_stc_eeprom_context_t * context); +static uint32 GetAddresses(uint32 *startAddr, uint32 *endAddr, uint32 *offset, uint32 rowNum, uint32 addr, uint32 len); +static cy_en_em_eeprom_status_t FillChecksum(cy_stc_eeprom_context_t * context); + +/** +* \addtogroup group_em_eeprom_functions +* \{ +*/ + +/******************************************************************************* +* Function Name: Cy_Em_EEPROM_Init +****************************************************************************//** +* +* Initializes the Emulated EEPROM library by filling the context structure. +* +* \param config +* The pointer to a configuration structure. See \ref cy_stc_eeprom_config_t. +* +* \param context +* The pointer to the EEPROM context structure to be filled by the function. +* \ref cy_stc_eeprom_context_t. +* +* \return +* error / status code. See \ref cy_en_em_eeprom_status_t. +* +* \note +* The context structure should not be modified by the user after it is filled +* with this function. Modification of context structure may cause the +* unexpected behavior of the Cy_Em_EEPROM API functions which rely on it. +* +* \note +* This function uses a buffer of the flash row size to perform read +* operation. For the size of the row refer to the specific PSoC device +* datasheet. +* +* \sideeffect +* If the "Redundant Copy" option is used, the function performs a number of +* write operations to the EEPROM to initialize flash rows checksums. Therefore, +* Cy_Em_EEPROM_NumWrites(), when it is called right after Cy_Em_EEPROM_Init(), +* will return a non-zero value that identifies the number of writes performed +* by Cy_Em_EEPROM_Init(). +* +*******************************************************************************/ +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Init(cy_stc_eeprom_config_t* config, cy_stc_eeprom_context_t * context) +{ + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_PARAM; + + if((NULL != context) && (NULL != config) && (NULL != ((uint32 *)config->userFlashStartAddr)) && + (config->wearLevelingFactor <= CY_EM_EEPROM_MAX_WEAR_LEVELING_FACTOR) && (config->eepromSize != 0u)) + { + ret = CheckRanges(config); + + if(CY_EM_EEPROM_SUCCESS == ret) + { + /* Copy the user config structure fields into context */ + context->eepromSize = config->eepromSize; + context->wearLevelingFactor = config->wearLevelingFactor; + context->redundantCopy = config->redundantCopy; + context->blockingWrite = config->blockingWrite; + context->userFlashStartAddr = config->userFlashStartAddr; + /* Store frequently used data for internal use */ + context->numberOfRows = CY_EM_EEPROM_GET_NUM_ROWS_IN_EEPROM(config->eepromSize); + context->wlEndAddr = ((CY_EM_EEPROM_GET_EEPROM_SIZE(context->numberOfRows) * config->wearLevelingFactor) + + config->userFlashStartAddr); + /* Find last written EEPROM row and store it for quick access */ + FindLastWrittenRow(&context->lastWrRowAddr, context); + + if((0u == CY_EM_EEPROM_GET_SEQ_NUM(context->lastWrRowAddr)) && (0u != context->redundantCopy)) + { + /* Call the function only after device reprogramming in case + * if redundant copy is enabled. + */ + ret = FillChecksum(context); + + /* Update the last written EEPROM row for Cy_Em_EEPROM_NumWrites() */ + FindLastWrittenRow(&context->lastWrRowAddr, context); + } + } + } + + return(ret); +} + + +/******************************************************************************* +* Function Name: Cy_Em_EEPROM_Read +****************************************************************************//** +* +* This function takes the logical EEPROM address, converts it to the actual +* physical address where the data is stored and returns the data to the user. +* +* \param addr +* The logical start address in EEPROM to start reading data from. +* +* \param eepromData +* The pointer to a user array to write data to. +* +* \param size +* The amount of data to read. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +* \return +* This function returns \ref cy_en_em_eeprom_status_t. +* +* \note +* This function uses a buffer of the flash row size to perform read +* operation. For the size of the row refer to the specific PSoC device +* datasheet. +* +* \note +* In case if redundant copy option is enabled the function may perform writes +* to EEPROM. This is done in case if the data in the EEPPROM is corrupted and +* the data in redundant copy is valid based on CRC-8 data integrity check. +* +*******************************************************************************/ +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Read(uint32 addr, + void * eepromData, + uint32 size, + cy_stc_eeprom_context_t * context) +{ + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_PARAM; + uint32 i; + uint32 numBytesToRead; + uint32 curEepromBaseAddr; + uint32 curRowOffset; + uint32 startRowAddr; + uint32 actEepromRowNum; + uint32 curRdEepromRowNum = 0u; + uint32 dataStartEepromRowNum = 0u; + uint32 eeData = (uint32) eepromData; /* To avoid the pointer arithmetic with void */ + + /* Validate input parameters */ + if((0u != size) && ((addr + size) <= (context->eepromSize)) && (NULL != eepromData)) + { + uint32 rdAddr = addr; + uint32 rdSize = size; + /* Get the sequence number of the last written row */ + uint32 seqNum = CY_EM_EEPROM_GET_SEQ_NUM(context->lastWrRowAddr); + uint32 updateAddrFlag = 0u; + + /* Calculate the number of the row read operations. Currently this only concerns + * the reads from the EEPROM data locations. + */ + uint32 numRowReads = ((((rdAddr + rdSize) - 1u) / CY_EM_EEPROM_EEPROM_DATA_LEN) - + (rdAddr / CY_EM_EEPROM_EEPROM_DATA_LEN)) + 1u; + + /* Get the address of the first row of the currently active EEPROM sector. If + * no wear leveling is used - the EEPROM has only one sector, so use the base + * addr stored in "context->userFlashStartAddr". + */ + curEepromBaseAddr = (((context->lastWrRowAddr - context->userFlashStartAddr) / + (CY_EM_EEPROM_FLASH_SIZEOF_ROW * context->numberOfRows)) * + (CY_EM_EEPROM_FLASH_SIZEOF_ROW * context->numberOfRows)) + + context->userFlashStartAddr; + + /* Find the number of the row that contains the start address of the data */ + for(i = 0u; i < context->numberOfRows; i++) + { + if(0u != CY_EM_EEPROM_IS_ADDR_IN_ROW_RANGE(rdAddr, i)) + { + dataStartEepromRowNum = i; + curRdEepromRowNum = dataStartEepromRowNum; + break; + } + } + + /* Find the row number of the last written row */ + actEepromRowNum = (context->lastWrRowAddr - curEepromBaseAddr) / CY_EM_EEPROM_FLASH_SIZEOF_ROW; + + /* Check if wear leveling is used */ + if(context->wearLevelingFactor > 1u) + { + uint32 dataEndEepromRowNum = dataStartEepromRowNum + (numRowReads - 1u); + + /* Check if the future validation of the read address is required. */ + updateAddrFlag = (dataStartEepromRowNum > actEepromRowNum) ? 1u : + ((dataEndEepromRowNum > actEepromRowNum) ? 1u : 0u); + } + + /* Copy data from the EEPROM data locations to the user buffer */ + for(i = 0u; i < numRowReads; i++) + { + startRowAddr = curEepromBaseAddr + (curRdEepromRowNum * CY_EM_EEPROM_FLASH_SIZEOF_ROW); + curRowOffset = CY_EM_EEPROM_EEPROM_DATA_LEN + (rdAddr % CY_EM_EEPROM_EEPROM_DATA_LEN); + + /* Check if there are more reads pending and update the number of the + * remaining bytes to read respectively. + */ + if((i + 1u) < numRowReads) + { + numBytesToRead = CY_EM_EEPROM_EEPROM_DATA_LEN - (rdAddr % CY_EM_EEPROM_EEPROM_DATA_LEN); + } + else + { + numBytesToRead = rdSize; + } + + /* Check if the read address needs to be updated to point to the correct + * EEPROM sector. + */ + if((0u != updateAddrFlag) && (curRdEepromRowNum > actEepromRowNum)) + { + startRowAddr -= context->numberOfRows * CY_EM_EEPROM_FLASH_SIZEOF_ROW; + + if(startRowAddr < context->userFlashStartAddr) + { + startRowAddr = context->wlEndAddr - + ((context->numberOfRows - curRdEepromRowNum) * CY_EM_EEPROM_FLASH_SIZEOF_ROW); + } + } + + if(0u != context->redundantCopy) + { + /* Check a checksum of the EEPROM row and if it is bad, check a checksum in + * the corresponding row in redundant copy, otherwise return failure. + */ + ret = CheckCrcAndCopy(startRowAddr, eeData, curRowOffset, numBytesToRead, context); + + if(CY_EM_EEPROM_SUCCESS != ret) + { + break; + } + } + else + { + /* Copy the data to the user buffer */ + (void)memcpy((void *)(eeData), + (void *)(startRowAddr + curRowOffset), + numBytesToRead); + + /* Indicate success to be able to execute next code block */ + ret = CY_EM_EEPROM_SUCCESS; + } + + /* Update variables anticipated in the read operation */ + rdAddr += numBytesToRead; + rdSize -= numBytesToRead; + eeData += numBytesToRead; + curRdEepromRowNum++; + } + + /* This code block will copy the latest data from the EEPROM headers into the + * user buffer. The data previously copied into the user buffer may be updated + * as the EEPROM headers contain more recent data. + * The code block is executed when two following conditions are true: + * 1) The reads from "historic" data locations were successful; + * 2) The user performed at least one write operation to Em_EEPROM (0u != + * seqNum). + */ + if((CY_EM_EEPROM_SUCCESS == ret) && (0u != seqNum)) + { + numRowReads = (context->numberOfRows <= seqNum) ? (context->numberOfRows) : (seqNum); + numRowReads--; + + for(i = (seqNum - numRowReads); i <= seqNum; i++) + { + startRowAddr = GetRowAddrBySeqNum(i, context); + + if (0u != startRowAddr) + { + /* The following variables are introduced to increase code readability. */ + uint32 startAddr = *(uint32 *)(startRowAddr + CY_EM_EEPROM_HEADER_ADDR_OFFSET); + uint32 endAddr = startAddr + (*(uint32 *)(startRowAddr + CY_EM_EEPROM_HEADER_LEN_OFFSET)); + + /* Check if the current row EEPROM header contains the data requested for read */ + if(0u != CY_EM_EEPROM_IS_ADDRESES_CROSSING(startAddr, endAddr, addr, addr + size)) + { + uint32 srcOffset = (startAddr > addr) ? (0u) : (addr - startAddr); + uint32 dstOffset = (startAddr > addr) ? (startAddr - addr): (0u); + rdAddr = (startAddr > addr) ? (startAddr) : (addr); + + srcOffset += CY_EM_EEPROM_HEADER_DATA_OFFSET; + + /* Calculate the number of bytes to be read from the current row's EEPROM header */ + numBytesToRead = ((endAddr < (addr + size)) ? endAddr : (addr + size)) - rdAddr; + + /* Calculate the offset in the user buffer from which the data will be updated. */ + eeData = ((uint32)eepromData) + dstOffset; + + /* Check a checksum of the EEPROM row and if it is bad, check a checksum in the + * corresponding row in redundant copy, otherwise return failure. Copy the data + * from the recent EEPROM headers to the user buffer. This will overwrite the + * data copied form EEPROM data locations as the data in EEPROM headers is newer. + */ + if(0u != context->redundantCopy) + { + ret = CheckCrcAndCopy(startRowAddr, eeData, srcOffset, numBytesToRead, context); + + if(CY_EM_EEPROM_SUCCESS != ret) + { + break; + } + } + else + { + (void)memcpy((void *)(eeData), (void *)(startRowAddr + srcOffset), numBytesToRead); + } + } + } + } + } + } + + return(ret); +} + + +/******************************************************************************* +* Function Name: Cy_Em_EEPROM_Write +****************************************************************************//** +* +* This function takes the logical EEPROM address and converts it to the actual +* physical address and writes data there. If wear leveling is implemented, the +* writing process will use the wear leveling techniques. This is a blocking +* function and it does not return until the write operation is completed. The +* user firmware should not enter Hibernate mode until write is completed. The +* write operation is allowed in Sleep and Deep-Sleep modes. During the flash +* operation, the device should not be reset, including the XRES pin, a software +* reset, and watchdog reset sources. Also, low-voltage detect circuits should +* be configured to generate an interrupt instead of a reset. Otherwise, portions +* of flash may undergo unexpected changes. +* +* \param addr +* The logical start address in EEPROM to start writing data from. +* +* \param eepromData +* Data to write to EEPROM. +* +* \param size +* The amount of data to write to EEPROM. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +* \return +* This function returns \ref cy_en_em_eeprom_status_t. +* +* \note +* This function uses a buffer of the flash row size to perform write +* operation. For the size of the row refer to the specific PSoC device +* datasheet. +* +* \sideeffect +* In case when blocking write option is used, if this function is called by +* the CM4 the user code on CM0P and the user code on CM4 are blocked until erase +* flash row operation is finished. If this function is called by the CM0P the +* user code on CM4 is not blocked and the user code on CM0P is blocked until +* erase flash row operation is finished. Plan your task allocation accordingly. +* +* \sideeffect +* In case if non-blocking write option is used and when user flash is used as +* an EEPROM storage care should be taken to prevent the read while write (RWW) +* exception. To prevent the RWW exception the user flash macro that includes +* the EEPROM storage should not be read while the EEPROM write is not completed. +* The read also means the user code execution from the respective flash macro. +* +*******************************************************************************/ +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Write(uint32 addr, + void * eepromData, + uint32 size, + cy_stc_eeprom_context_t * context) +{ + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_PARAM; + uint32 i; + uint32 wrCnt; + uint32 actEmEepromRowNum; + uint32 writeRamBuffer[CY_EM_EEPROM_FLASH_SIZEOF_ROW / CY_EM_EEPROM_U32_DIV]; + uint32 startAddr = 0u; + uint32 endAddr = 0u; + uint32 tmpRowAddr; + uint32 emEepromRowAddr = context->lastWrRowAddr; + uint32 emEepromRowRdAddr; + void * tmpData; + uint32 eeData = (uint32) eepromData; /* To avoid the pointer arithmetic with void */ + + /* Check if the EEPROM data does not exceed the EEPROM capacity */ + if((0u != size) && ((addr + size) <= (context->eepromSize)) && (NULL != eepromData)) + { + uint32 numWrites = ((size - 1u) / CY_EM_EEPROM_HEADER_DATA_LEN) + 1u; + uint32 eeHeaderDataOffset = 0u; + + for(wrCnt = 0u; wrCnt < numWrites; wrCnt++) + { + uint32 skipOperation = 0u; + /* Get the sequence number of the last written row */ + uint32 seqNum = CY_EM_EEPROM_GET_SEQ_NUM(emEepromRowAddr); + + /* Get the address of the row to be written. The "emEepromRowAddr" may be + * updated with the proper address (if wear leveling is used). The + * "emEepromRowRdAddr" will point to the row address from which the historic + * data will be read into the RAM buffer. + */ + GetNextRowToWrite(seqNum, &emEepromRowAddr, &emEepromRowRdAddr, context); + + /* Clear the RAM buffer so to not put junk into flash */ + (void)memset(writeRamBuffer, 0, CY_EM_EEPROM_FLASH_SIZEOF_ROW); + + /* Fill the EM_EEPROM header info for the row in the RAM buffer */ + seqNum++; + writeRamBuffer[CY_EM_EEPROM_HEADER_SEQ_NUM_OFFSET_U32] = seqNum; + writeRamBuffer[CY_EM_EEPROM_HEADER_ADDR_OFFSET_U32] = addr; + tmpData = (void *) eeData; + + /* Check if this is the last row to write */ + if(wrCnt == (numWrites - 1u)) + { + /* Fill in the remaining size value to the EEPROM header. */ + writeRamBuffer[CY_EM_EEPROM_HEADER_LEN_OFFSET_U32] = size; + } + else + { + /* This is not the last row to write in the current EEPROM write operation. + * Write the maximum possible data size to the EEPROM header. Update the + * size, eeData and addr respectively. + */ + writeRamBuffer[CY_EM_EEPROM_HEADER_LEN_OFFSET_U32] = CY_EM_EEPROM_HEADER_DATA_LEN; + size -= CY_EM_EEPROM_HEADER_DATA_LEN; + addr += CY_EM_EEPROM_HEADER_DATA_LEN; + eeData += CY_EM_EEPROM_HEADER_DATA_LEN; + } + + /* Write the data to the EEPROM header */ + (void)memcpy((void *)&writeRamBuffer[CY_EM_EEPROM_HEADER_DATA_OFFSET_U32], + tmpData, + writeRamBuffer[CY_EM_EEPROM_HEADER_LEN_OFFSET_U32]); + + if(emEepromRowRdAddr != 0UL) + { + /* Copy the EEPROM historic data for this row from flash to RAM */ + (void)memcpy((void *)&writeRamBuffer[CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32], + (void *)(emEepromRowRdAddr + CY_EM_EEPROM_EEPROM_DATA_LEN), + CY_EM_EEPROM_EEPROM_DATA_LEN); + } + + /* Check if there is data for this location in other EEPROM headers: + * find out the row with the lowest possible sequence number which + * may contain the data for the current row. + */ + i = (seqNum > context->numberOfRows) ? ((seqNum - (context->numberOfRows)) + 1u) : 1u; + + for(; i <= seqNum; i++) + { + if(i == seqNum) + { + /* The code reached the row that is about to be written. Analyze the recently + * created EEPROM header (stored in the RAM buffer currently): if it contains + * the data for EEPROM data locations in the row that is about to be written. + */ + tmpRowAddr = (uint32) writeRamBuffer; + } + else + { + /* Retrieve the address of the previously written row by its sequence number. + * The pointer will be used to get data from the respective EEPROM header. + */ + tmpRowAddr = GetRowAddrBySeqNum(i, context); + } + + actEmEepromRowNum = CY_EM_EEPROM_GET_ACT_ROW_NUM_FROM_ADDR(emEepromRowAddr, + context->numberOfRows, + context->userFlashStartAddr); + if(0UL != tmpRowAddr) + { + /* Calculate the required addressed for the later EEPROM historic data update */ + skipOperation = GetAddresses( + &startAddr, + &endAddr, + &eeHeaderDataOffset, + actEmEepromRowNum, + *(uint32 *)(tmpRowAddr + CY_EM_EEPROM_HEADER_ADDR_OFFSET), + *(uint32 *)(tmpRowAddr + CY_EM_EEPROM_HEADER_LEN_OFFSET)); + } + else + { + /* Skip writes to the RAM buffer */ + skipOperation++; + } + + /* Write data to the RAM buffer */ + if(0u == skipOperation) + { + uint32 dataAddr = ((uint32)((uint8 *)&writeRamBuffer)) + startAddr; + + /* Update the address to point to the EEPROM header data and not to + * the start of the row. + */ + tmpRowAddr = tmpRowAddr + CY_EM_EEPROM_HEADER_DATA_OFFSET + eeHeaderDataOffset; + (void)memcpy((void *)(dataAddr), (void *)(tmpRowAddr), endAddr - startAddr); + } + + /* Calculate the checksum if redundant copy is enabled */ + if(0u != context->redundantCopy) + { + writeRamBuffer[CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET_U32] = (uint32) + CalcChecksum((uint8 *) &writeRamBuffer[CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32], + CY_EM_EEPROM_EEPROM_DATA_LEN); + } + } + + /* Write the data to the specified flash row */ + ret = WriteRow(emEepromRowAddr, writeRamBuffer, context); + tmpRowAddr = emEepromRowAddr; + + /* Check if redundant copy is used */ + if((0u != context->redundantCopy) && (CY_EM_EEPROM_SUCCESS == ret)) + { + /* Update the row address to point to the row in the redundant EEPROM's copy */ + tmpRowAddr = (emEepromRowAddr - context->userFlashStartAddr) + context->wlEndAddr; + + /* Write the data to the specified flash row */ + ret = WriteRow(tmpRowAddr, writeRamBuffer, context); + } + + if(CY_EM_EEPROM_SUCCESS == ret) + { + /* Store last written row address only when EEPROM and redundant + * copy writes were successful. + */ + context->lastWrRowAddr = emEepromRowAddr; + } + else + { + break; + } + } + } + return(ret); +} + + +/******************************************************************************* +* Function Name: Cy_Em_EEPROM_Erase +****************************************************************************//** +* +* This function erases the entire contents of the EEPROM. Erased values are all +* zeros. This is a blocking function and it does not return until the write +* operation is completed. The user firmware should not enter Hibernate mode until +* erase is completed. The erase operation is allowed in Sleep and Deep-Sleep modes. +* During the flash operation, the device should not be reset, including the +* XRES pin, a software reset, and watchdog reset sources. Also, low-voltage +* detect circuits should be configured to generate an interrupt instead of a +* reset. Otherwise, portions of flash may undergo unexpected changes. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +* \return +* This function returns \ref cy_en_em_eeprom_status_t. +* +* \note +* For all non PSoC 6 devices the erase operation is performed by clearing +* the EEPROM data using flash write. This affects the flash durability. +* So it is recommended to use this function in utmost case to prolongate +* flash life. +* +* \note +* This function uses a buffer of the flash row size to perform erase +* operation. For the size of the row refer to the specific PSoC device +* datasheet. +* +* \sideeffect +* In case when blocking write option is used, if this function is called by +* the CM4 the user code on CM0P and the user code on CM4 are blocked until erase +* flash row operation is finished. If this function is called by the CM0P the +* user code on CM4 is not blocked and the user code on CM0P is blocked until +* erase flash row operation is finished. Plan your task allocation accordingly. +* +* \sideeffect +* In case if non-blocking write option is used and when user flash is used as +* an EEPROM storage care should be taken to prevent the read while write (RWW) +* exception. To prevent the RWW exception the user flash macro that includes +* the EEPROM storage should not be read while the EEPROM erase is not completed. +* The read also means the user code execution from the respective flash macro. +* +*******************************************************************************/ +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Erase(cy_stc_eeprom_context_t * context) +{ + uint32 i; + uint32 seqNum; + uint32 emEepromRowAddr = context->lastWrRowAddr; + uint32 emEepromRowRdAddr; + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_WRITE_FAIL; + uint32 writeRamBuffer[CY_EM_EEPROM_FLASH_SIZEOF_ROW / CY_EM_EEPROM_U32_DIV] = {0u}; +#if (CY_PSOC6) + uint32 emEepromStoredRowAddr = context->lastWrRowAddr; + uint32 storedSeqNum; +#endif /* (!CY_PSOC6) */ + + /* Get the sequence number of the last written row */ + seqNum = CY_EM_EEPROM_GET_SEQ_NUM(emEepromRowAddr); + + /* If there were no writes to EEPROM - nothing to erase */ + if(0u != seqNum) + { + /* Calculate the number of row erase operations required */ + uint32 numWrites = context->numberOfRows * context->wearLevelingFactor; + + #if (CY_PSOC6) + GetNextRowToWrite(seqNum, &emEepromStoredRowAddr, &emEepromRowRdAddr, context); + storedSeqNum = seqNum + 1u; + #endif /* (CY_PSOC6) */ + + if(0u != context->redundantCopy) + { + writeRamBuffer[CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET_U32] = (uint32) + CalcChecksum((uint8 *) &writeRamBuffer[CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32], + CY_EM_EEPROM_EEPROM_DATA_LEN); + } + + for(i = 0u; i < numWrites; i++) + { + #if (CY_PSOC6) + /* For PSoC 6 the erase operation moves backwards. From last written row + * identified by "seqNum" down to "seqNum" - "numWrites". If "emEepromRowAddr" + * is zero this means that the row identified by "seqNum" was previously + * erased. + */ + if(0u != emEepromRowAddr) + { + ret = EraseRow(emEepromRowAddr, (uint32)writeRamBuffer, context); + } + + seqNum--; + + if(0u == seqNum) + { + /* Exit the loop as there is no more row is EEPROM to be erased */ + break; + } + emEepromRowAddr = GetRowAddrBySeqNum(seqNum, context); + #else + seqNum = CY_EM_EEPROM_GET_SEQ_NUM(emEepromRowAddr); + /* Get the address of the row to be erased. "emEepromRowAddr" may be updated + * with the proper address (if wear leveling is used). + */ + GetNextRowToWrite(seqNum, &emEepromRowAddr, &emEepromRowRdAddr, context); + seqNum++; + writeRamBuffer[0u] = seqNum; + ret = EraseRow(emEepromRowAddr, (uint32)writeRamBuffer, context); + #endif /* (CY_PSOC6) */ + } + + #if (CY_PSOC6) + if(CY_EM_EEPROM_SUCCESS == ret) + { + writeRamBuffer[0u] = storedSeqNum; + + /* Write the previously stored sequence number to the flash row which would be + * written next if the erase wouldn't happen. In this case the write to + * redundant copy can be skipped as it does not add any value. + */ + ret = WriteRow(emEepromStoredRowAddr, writeRamBuffer, context); + + if(CY_EM_EEPROM_SUCCESS == ret) + { + context->lastWrRowAddr = emEepromStoredRowAddr; + } + } + #endif /* (CY_PSOC6) */ + + } + return(ret); +} + + +/******************************************************************************* +* Function Name: Cy_Em_EEPROM_NumWrites +****************************************************************************//** +* +* Returns the number of the EEPROM writes completed so far. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +* \return +* The number of writes performed to the EEPROM. +* +*******************************************************************************/ +uint32 Cy_Em_EEPROM_NumWrites(cy_stc_eeprom_context_t * context) +{ + return(CY_EM_EEPROM_GET_SEQ_NUM(context->lastWrRowAddr)); +} + +/** \} */ + +/** \cond INTERNAL */ + + +/******************************************************************************* +* Function Name: FindLastWrittenRow +****************************************************************************//** +* +* Performs a search of the last written row address of the EEPROM associated +* with the context structure. If there were no writes to the EEPROM the +* function returns the start address of the EEPROM. The row address is returned +* in the input parameter. +* +* \param lastWrRowPtr +* The pointer to a memory where the last written row will be returned. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +*******************************************************************************/ +static void FindLastWrittenRow(uint32 * lastWrRowPtr, cy_stc_eeprom_context_t * context) +{ + uint32 seqNum = 0u; + uint32 prevSeqNum = 0u; + uint32 numRows; + uint32 emEepromAddr = context->userFlashStartAddr; + + *lastWrRowPtr = emEepromAddr; + + for(numRows = 0u; numRows < (context->numberOfRows * context->wearLevelingFactor); numRows++) + { + seqNum = CY_EM_EEPROM_GET_SEQ_NUM(emEepromAddr); + if((0u != seqNum) && (seqNum > prevSeqNum)) + { + /* Some record in EEPROM was found. Store found sequence + * number and row address. + */ + prevSeqNum = seqNum; + *lastWrRowPtr = emEepromAddr; + } + + /* Switch to the next row */ + emEepromAddr = emEepromAddr + CY_EM_EEPROM_FLASH_SIZEOF_ROW; + } +} + + +/******************************************************************************* +* Function Name: GetRowAddrBySeqNum +****************************************************************************//** +* +* Returns the address of the row in EEPROM using its sequence number. +* +* \param seqNum +* The sequence number of the row. +* +* \param context +* The pointer to the EEPROM context structure. +* +* \return +* The address of the row or zero if the row with the sequence number was not +* found. +* +*******************************************************************************/ +static uint32 GetRowAddrBySeqNum(uint32 seqNum, cy_stc_eeprom_context_t * context) +{ + uint32 emEepromAddr = context->userFlashStartAddr; + + while(CY_EM_EEPROM_GET_SEQ_NUM(emEepromAddr) != seqNum) + { + /* Switch to the next row */ + emEepromAddr = emEepromAddr + CY_EM_EEPROM_FLASH_SIZEOF_ROW; + + if (CY_EM_EEPROM_ADDR_IN_RANGE != + CY_EM_EEPROM_IS_ADDR_EXCEED_RANGE(emEepromAddr, context->wlEndAddr)) + { + emEepromAddr = 0u; + /* Exit the loop as we reached the end of EEPROM */ + break; + } + } + + return (emEepromAddr); +} + + +/******************************************************************************* +* Function Name: GetNextRowToWrite +****************************************************************************//** +* +* Performs a range check of the row that should be written and updates the +* address to the row respectively. The similar actions are done for the read +* address. +* +* \param seqNum +* The sequence number of the last written row. +* +* \param rowToWrPtr +* The address of the last written row (input). The address of the row to be +* written (output). +* +* \param rowToRdPtr +* The address of the row from which the data should be read into the RAM buffer +* in a later write operation. Out parameter. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +*******************************************************************************/ +static void GetNextRowToWrite(uint32 seqNum, + uint32 * rowToWrPtr, + uint32 * rowToRdPtr, + cy_stc_eeprom_context_t * context) +{ + /* Switch to the next row to be written if the current sequence number is + * not zero. + */ + if(0u != seqNum) + { + *rowToWrPtr = (*rowToWrPtr + CY_EM_EEPROM_FLASH_SIZEOF_ROW); + } + + /* If the resulting row address is out of EEPROM, then switch to the base + * EEPROM address (Row#0). + */ + if(CY_EM_EEPROM_ADDR_IN_RANGE != + CY_EM_EEPROM_IS_ADDR_EXCEED_RANGE(*rowToWrPtr, context->wlEndAddr)) + { + *rowToWrPtr = context->userFlashStartAddr; + } + + *rowToRdPtr = 0u; + + /* Check if the sequence number is larger than the number of rows in the EEPROM. + * If not, do not update the row read address because there is no historic + * data to be read. + */ + if(context->numberOfRows <= seqNum) + { + /* Check if wear leveling is used in EEPROM */ + if(context->wearLevelingFactor > 1u) + { + /* The read row address should be taken from an EEPROM copy that became + * inactive recently. This condition check handles that. + */ + if((*rowToWrPtr - (context->numberOfRows * CY_EM_EEPROM_FLASH_SIZEOF_ROW)) < + context->userFlashStartAddr) + { + *rowToRdPtr = context->userFlashStartAddr + + (context->numberOfRows * (context->wearLevelingFactor - 1u) * + CY_EM_EEPROM_FLASH_SIZEOF_ROW) + (*rowToWrPtr - context->userFlashStartAddr); + } + else + { + *rowToRdPtr = *rowToWrPtr - (context->numberOfRows * CY_EM_EEPROM_FLASH_SIZEOF_ROW); + } + } + else + { + /* If no wear leveling, always read from the same flash row that + * should be written. + */ + *rowToRdPtr = *rowToWrPtr; + } + } +} + + +/******************************************************************************* +* Function Name: CalcChecksum +****************************************************************************//** +* +* Implements CRC-8 that is used in checksum calculation for the redundant copy +* algorithm. +* +* \param rowData +* The row data to be used to calculate the checksum. +* +* \param len +* The length of rowData. +* +* \return +* The calculated value of CRC-8. +* +*******************************************************************************/ +static uint8 CalcChecksum(uint8 rowData[], uint32 len) +{ + uint8 crc = CY_EM_EEPROM_CRC8_SEED; + uint8 i; + uint16 cnt = 0u; + + while(cnt != len) + { + crc ^= rowData[cnt]; + for (i = 0u; i < CY_EM_EEPROM_CRC8_POLYNOM_LEN; i++) + { + crc = CY_EM_EEPROM_CALCULATE_CRC8(crc); + } + cnt++; + } + + return (crc); +} + + +/******************************************************************************* +* Function Name: CheckRanges +****************************************************************************//** +* +* Checks if the EEPROM of the requested size can be placed in flash. +* +* \param config +* The pointer to a configuration structure. See \ref cy_stc_eeprom_config_t. +* +* \return +* error / status code. See \ref cy_en_em_eeprom_status_t. +* +*******************************************************************************/ +static cy_en_em_eeprom_status_t CheckRanges(cy_stc_eeprom_config_t* config) +{ + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_DATA; + uint32 startAddr = config->userFlashStartAddr; + uint32 endAddr = startAddr + CY_EM_EEPROM_GET_PHYSICAL_SIZE(config->eepromSize, + config->wearLevelingFactor, config->redundantCopy); + + /* Range check if there is enough flash for EEPROM */ + if (CY_EM_EEPROM_IS_IN_FLASH_RANGE(startAddr, endAddr)) + { + ret = CY_EM_EEPROM_SUCCESS; + } + return (ret); +} + + +/******************************************************************************* +* Function Name: WriteRow +****************************************************************************//** +* +* Writes one flash row starting from the specified row address. +* +* \param rowAdd +* The address of the flash row. +* +* \param rowData +* The pointer to the data to be written to the row. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +* \return +* error / status code. See \ref cy_en_em_eeprom_status_t. +* +*******************************************************************************/ +static cy_en_em_eeprom_status_t WriteRow(uint32 rowAddr, + uint32 *rowData, + cy_stc_eeprom_context_t * context) +{ + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_WRITE_FAIL; +#if (!CY_PSOC6) + cystatus rc; + uint32 rowId; + #if ((CY_PSOC3) || (CY_PSOC5)) + uint32 arrayId; + #endif /* (CY_PSOC3) */ + + #if (CY_PSOC3) + rowAddr &= CY_EM_EEPROM_CODE_ADDR_MASK; + context = context; /* To avoid compiler warning generation */ + #else + (void)context; /* To avoid compiler warning generation */ + #endif /* ((CY_PSOC3) */ + + /* For non-PSoC 6 devices, the Array ID and Row ID needed to write the row */ + rowId = (rowAddr / CY_EM_EEPROM_FLASH_SIZEOF_ROW) % CY_EM_EEPROM_ROWS_IN_ARRAY; + + /* Write the flash row */ + #if (CY_PSOC4) + rc = CySysFlashWriteRow(rowId, (uint8 *)rowData); + #else + + #ifndef CY_EM_EEPROM_SKIP_TEMP_MEASUREMENT + (void)CySetTemp(); + #endif /* (CY_EM_EEPROM_SKIP_TEMP_MEASUREMENT) */ + + arrayId = rowAddr / CY_FLASH_SIZEOF_ARRAY; + rc = CyWriteRowData((uint8)arrayId, (uint16)rowId, (uint8 *)rowData); + + #if (CY_PSOC5) + CyFlushCache(); + #endif /* (CY_PSOC5) */ + #endif /* (CY_PSOC4) */ + + if(CYRET_SUCCESS == rc) + { + ret = CY_EM_EEPROM_SUCCESS; + } +#else /* PSoC 6 */ + if(0u != context->blockingWrite) + { + /* Do blocking write */ + if(CY_FLASH_DRV_SUCCESS == Cy_Flash_WriteRow(rowAddr, (const uint32 *)rowData)) + { + ret = CY_EM_EEPROM_SUCCESS; + } + } + else + { + /* Initiate write */ + if(CY_FLASH_DRV_OPERATION_STARTED == Cy_Flash_StartWrite(rowAddr, (const uint32 *)rowData)) + { + uint32 countMs = CY_EM_EEPROM_MAX_WRITE_DURATION_MS; + cy_en_flashdrv_status_t rc; + + do + { + CyDelay(1u); /* Wait 1ms */ + rc = Cy_Flash_IsWriteComplete(); /* Check if write completed */ + countMs--; + } + while ((rc == CY_FLASH_DRV_OPCODE_BUSY) && (0u != countMs)); + + if(CY_FLASH_DRV_SUCCESS == rc) + { + ret = CY_EM_EEPROM_SUCCESS; + } + } + } +#endif /* (CY_PSOC6) */ + + return (ret); +} + + +/******************************************************************************* +* Function Name: EraseRow +****************************************************************************//** +* +* Erases one flash row starting from the specified row address. If the redundant +* copy option is enabled the corresponding row in the redundant copy will also +* be erased. +* +* \param rowAdd +* The address of the flash row. +* +* \param ramBuffAddr +* The address of the RAM buffer that contains zeroed data (used only for +* non-PSoC 6 devices). +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +* \return +* error / status code. See \ref cy_en_em_eeprom_status_t. +* +*******************************************************************************/ +static cy_en_em_eeprom_status_t EraseRow(uint32 rowAddr, + uint32 ramBuffAddr, + cy_stc_eeprom_context_t * context) +{ + uint32 emEepromRowAddr = rowAddr; + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_WRITE_FAIL; +#if (CY_PSOC6) + uint32 i = 1u; + + (void)ramBuffAddr; /* To avoid compiler warning */ + + if(0u != context->redundantCopy) + { + i++; + } + + do + { + if(0u != context->blockingWrite) + { + /* Erase the flash row */ + if(CY_FLASH_DRV_SUCCESS == Cy_Flash_EraseRow(emEepromRowAddr)) + { + ret = CY_EM_EEPROM_SUCCESS; + } + } + else + { + /* Initiate erase */ + if(CY_FLASH_DRV_OPERATION_STARTED == Cy_Flash_StartErase(emEepromRowAddr)) + { + uint32 countMs = CY_EM_EEPROM_MAX_WRITE_DURATION_MS; + cy_en_flashdrv_status_t rc; + + do + { + CyDelay(1u); /* Wait 1ms */ + rc = Cy_Flash_IsWriteComplete(); /* Check if erase completed */ + countMs--; + } + while ((rc == CY_FLASH_DRV_OPCODE_BUSY) && (0u != countMs)); + + if(CY_FLASH_DRV_SUCCESS == rc) + { + ret = CY_EM_EEPROM_SUCCESS; + } + } + } + + if(CY_EM_EEPROM_SUCCESS == ret) + { + /* Update the address to point to the redundant copy row */ + emEepromRowAddr = (emEepromRowAddr - context->userFlashStartAddr) + context->wlEndAddr; + } + else + { + break; + } + i--; + } while (0u != i); +#else + /* Write the data to the specified flash row */ + ret = WriteRow(emEepromRowAddr, (uint32 *)ramBuffAddr, context); + + if((CY_EM_EEPROM_SUCCESS == ret) && (0u != context->redundantCopy)) + { + /* Update the address to point to the redundant copy row */ + emEepromRowAddr = (emEepromRowAddr - context->userFlashStartAddr) + context->wlEndAddr; + ret = WriteRow(emEepromRowAddr, (uint32 *)ramBuffAddr, context); + } + + if(CY_EM_EEPROM_SUCCESS == ret) + { + context->lastWrRowAddr = rowAddr; + } +#endif /* (CY_PSOC6) */ + + return(ret); +} + + +/******************************************************************************* +* Function Name: CheckCrcAndCopy +****************************************************************************//** +* +* Checks the checksum of the specific row in EEPROM. If the CRC matches - copies +* the data to the "datAddr" from EEPROM. f the CRC does not match checks the +* CRC of the corresponding row in the EEPROM's redundant copy. If the CRC +* matches - copies the data to the "datAddr" from EEPROM redundant copy. If the +* CRC of the redundant copy does not match - returns bad checksum. +* +* \param startAddr +* The address that points to the start of the specified row. +* +* \param datAddr +* The start address of where the row data will be copied if the CRC check +* will succeed. +* +* \param rowOffset +* The offset in the row from which the data should be copied. +* +* \param numBytes +* The number of bytes to be copied. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +* \return +* error / status code. See \ref cy_en_em_eeprom_status_t. +* +* \note +* This function uses a buffer of the flash row size to perform read +* operation. For the size of the row refer to the specific PSoC device +* datasheet. +* +*******************************************************************************/ +static cy_en_em_eeprom_status_t CheckCrcAndCopy(uint32 startAddr, + uint32 dstAddr, + uint32 rowOffset, + uint32 numBytes, + cy_stc_eeprom_context_t * context) +{ + cy_en_em_eeprom_status_t ret; + uint32 writeRamBuffer[CY_EM_EEPROM_FLASH_SIZEOF_ROW / CY_EM_EEPROM_U32_DIV]; + + /* Calculate the row address in the EEPROM's redundant copy */ + uint32 rcStartRowAddr = (startAddr - context->userFlashStartAddr) + context->wlEndAddr; + + /* Check the row data CRC in the EEPROM */ + if((*(uint32 *)(startAddr + CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET)) == + ((uint32) CalcChecksum((uint8 *)(startAddr + CY_EM_EEPROM_EEPROM_DATA_OFFSET), + CY_EM_EEPROM_EEPROM_DATA_LEN))) + { + (void)memcpy((void *)(dstAddr), (void *)(startAddr + rowOffset), numBytes); + + ret = CY_EM_EEPROM_SUCCESS; + } + /* Check the row data CRC in the EEPROM's redundant copy */ + else if((*(uint32 *)(rcStartRowAddr + CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET)) == + ((uint32) CalcChecksum((uint8 *)(rcStartRowAddr + CY_EM_EEPROM_EEPROM_DATA_OFFSET), + CY_EM_EEPROM_EEPROM_DATA_LEN))) + { + /* Copy the redundant copy row to RAM buffer to avoid read while write (RWW) + * flash exception. The RWW occurs while trying to write and read the data from + * same flash macro. + */ + (void)memcpy((void *)(writeRamBuffer), (void *)(rcStartRowAddr), CY_EM_EEPROM_FLASH_SIZEOF_ROW); + + /* Restore bad row data from the RAM buffer */ + ret = WriteRow(startAddr, (uint32 *)writeRamBuffer, context); + + if(CY_EM_EEPROM_SUCCESS == ret) + { + (void)memcpy((void *)(dstAddr), (void *)(writeRamBuffer + rowOffset), numBytes); + } + } + else + { + ret = CY_EM_EEPROM_BAD_CHECKSUM; + } + + return(ret); +} + + +/******************************************************************************* +* Function Name: GetAddresses +****************************************************************************//** +* +* Calculates the start and end address of the row's EEPROM data to be updated. +* The start and end are not absolute addresses but a relative addresses in a +* flash row. +* +* \param startAddr +* The pointer the address where the EEPROM data start address will be returned. +* +* \param endAddr +* The pointer the address where the EEPROM data end address will be returned. +* +* \param offset +* The pointer the address where the calculated offset of the EEPROM header data +* will be returned. +* +* \param rowNum +* The row number that is about to be written. +* +* \param addr +* The address of the EEPROM header data in the currently analyzed row that may +* concern to the row about to be written. +* +* \param len +* The length of the EEPROM header data in the currently analyzed row that may +* concern to the row about to be written. +* +* \return +* Zero indicates that the currently analyzed row has the data to be written to +* the active EEPROM row data locations. Non zero value indicates that there is +* no data to be written +* +*******************************************************************************/ +static uint32 GetAddresses(uint32 *startAddr, + uint32 *endAddr, + uint32 *offset, + uint32 rowNum, + uint32 addr, + uint32 len) +{ + uint32 skip = 0u; + + *offset =0u; + + if(0u != CY_EM_EEPROM_IS_ADDR_IN_ROW_RANGE(addr, rowNum)) + { + *startAddr = CY_EM_EEPROM_EEPROM_DATA_LEN + (addr % CY_EM_EEPROM_EEPROM_DATA_LEN); + + if(0u != CY_EM_EEPROM_IS_ADDR_IN_ROW_RANGE(addr + len, rowNum)) + { + *endAddr = *startAddr + len; + } + else + { + *endAddr = CY_EM_EEPROM_FLASH_SIZEOF_ROW; + } + } + else + { + + if(0u != CY_EM_EEPROM_IS_ADDR_IN_ROW_RANGE(addr + len, rowNum)) + { + *startAddr = CY_EM_EEPROM_EEPROM_DATA_LEN; + *endAddr = (*startAddr + len) - (*startAddr - (addr % CY_EM_EEPROM_EEPROM_DATA_LEN)); + *offset = len - (*endAddr - *startAddr); + } + else + { + skip++; + } + } + + return (skip); +} + + +/******************************************************************************* +* Function Name: FillChecksum +****************************************************************************//** +* +* Performs calculation of the checksum on each row in the Em_EEPROM and fills +* the Em_EEPROM headers checksum field with the calculated checksums. +* +* \param context +* The pointer to the EEPROM context structure. +* +* \return +* error / status code. See \ref cy_en_em_eeprom_status_t. +* +* \theory +* In case if redundant copy option is used the Em_EEPROM would return bad +* checksum while trying to read the EEPROM rows which were not yet written by +* the user. E.g. any read after device reprogramming without previous Write() +* operation to the EEPROM would fail. This would happen because the Em_EEPROM +* headers checksum field values (which is zero at the moment) would not be +* equal to the actual data checksum. This function allows to avoid read failure +* after device reprogramming. +* +* \note +* This function uses a buffer of the flash row size to perform read +* operation. For the size of the row refer to the specific PSoC device +* datasheet. +* +*******************************************************************************/ +static cy_en_em_eeprom_status_t FillChecksum(cy_stc_eeprom_context_t * context) +{ + uint32 i; + uint32 rdAddr; + uint32 writeRamBuffer[CY_EM_EEPROM_FLASH_SIZEOF_ROW / CY_EM_EEPROM_U32_DIV]; + uint32 wrAddr = context->lastWrRowAddr; + uint32 tmpRowAddr; + /* Get the sequence number (number of writes) */ + uint32 seqNum = CY_EM_EEPROM_GET_SEQ_NUM(wrAddr); + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_PARAM; + + for(i = 0u; i < (context->numberOfRows * context->wearLevelingFactor); i++) + { + /* Copy the EEPROM row from Flash to RAM */ + (void)memcpy((void *)&writeRamBuffer[0u], (void *)(wrAddr), CY_EM_EEPROM_FLASH_SIZEOF_ROW); + + /* Increment the sequence number */ + seqNum++; + writeRamBuffer[CY_EM_EEPROM_HEADER_SEQ_NUM_OFFSET_U32] = seqNum; + + /* Calculate and fill the checksum to the Em_EEPROM header */ + writeRamBuffer[CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET_U32] = (uint32) + CalcChecksum((uint8 *) &writeRamBuffer[CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32], + CY_EM_EEPROM_EEPROM_DATA_LEN); + + /* Write the data to the specified flash row */ + ret = WriteRow(wrAddr, writeRamBuffer, context); + + /* Update the row address to point to the relevant row in the redundant + * EEPROM's copy. + */ + tmpRowAddr = (wrAddr - context->userFlashStartAddr) + context->wlEndAddr; + + /* Write the data to the specified flash row */ + ret = WriteRow(tmpRowAddr, writeRamBuffer, context); + + /* Get the address of the next row to be written. + * "rdAddr" is not used in this function but provided to prevent NULL + * pointer exception in GetNextRowToWrite(). + */ + GetNextRowToWrite(seqNum, &wrAddr, &rdAddr, context); + } + + return(ret); +} + +/** \endcond */ + +#if defined(__cplusplus) +} +#endif + +/* [] END OF FILE */ diff --git a/software/SCSI2SD/v4/SCSI2SD.cydsn/Generated_Source/PSoC5/cy_em_eeprom.h b/software/SCSI2SD/v4/SCSI2SD.cydsn/Generated_Source/PSoC5/cy_em_eeprom.h new file mode 100755 index 0000000..4aef67b --- /dev/null +++ b/software/SCSI2SD/v4/SCSI2SD.cydsn/Generated_Source/PSoC5/cy_em_eeprom.h @@ -0,0 +1,556 @@ +/******************************************************************************* +* \file cy_em_eeprom.h +* \version 2.0 +* +* \brief +* This file provides the function prototypes and constants for the Emulated +* EEPROM middleware library. +* +******************************************************************************** +* Copyright 2017, Cypress Semiconductor Corporation. All rights reserved. +* You may use this file only in accordance with the license, terms, conditions, +* disclaimers, and limitations in the end user license agreement accompanying +* the software package with which this file was provided. +*******************************************************************************/ + +/** + * \mainpage Cypress Em_EEPROM Middleware Library + * + * The Emulated EEPROM provides an API that allows creating an emulated + * EEPROM in flash that has the ability to do wear leveling and restore + * corrupted data from a redundant copy. The Emulated EEPROM library is designed + * to be used with the Em_EEPROM component. + * + * The Cy_Em_EEPROM API is described in the following sections: + * - \ref group_em_eeprom_macros + * - \ref group_em_eeprom_data_structures + * - \ref group_em_eeprom_enums + * - \ref group_em_eeprom_functions + * + * Features: + * * EEPROM-Like Non-Volatile Storage + * * Easy to use Read and Write API + * * Optional Wear Leveling + * * Optional Redundant Data storage + * + * \section group_em_eeprom_configuration Configuration Considerations + * + * The Em_EEPROM operates on the top of the flash driver. The flash driver has + * some prerequisites for proper operation. Refer to the "Flash System + * Routine (Flash)" section of the PDL API Reference Manual. + * + * Initializing Emulated EEPROM in User flash + * + * To initialize an Emulated EEPROM in the User flash, the EEPROM storage should + * be declared by the user. For the proper operation, the EEPROM storage should + * be aligned to the size of the flash row. An example of the EEPROM storage + * declaration is below (applicable for GCC and MDK compilers): + * + * CY_ALIGN(CY_EM_EEPROM_FLASH_SIZEOF_ROW) + * const uint8 emEeprom[Em_EEPROM_PHYSICAL_SIZE] = {0u}; + * + * The same declaration for the IAR compiler: + * + * #pragma data_alignment = CY_EM_EEPROM_FLASH_SIZEOF_ROW + * const uint8 emEeprom[Em_EEPROM_PHYSICAL_SIZE] = {0u}; + * + * Note that the name "emEeprom" is shown for reference. Any other name can be + * used instead. Also, note that the Em_EEPROM_PHYSICAL_SIZE constant is + * generated by the PSoC Creator Em_EEPROM component and so it is instance name + * dependent and its prefix should be changed when the name of the component + * changes. If the The Cy_Em_EEPROM middleware library is used without the + * Em_EEPROM component, the user has to provide a proper size for the EEPROM + * storage instead of Em_EEPROM_PHYSICAL_SIZE. The size of the EEPROM storage + * can be calculated using the following equation: + * + * Physical size = EEPROM data size * 2 * wear leveling * (1 + redundant copy) + * + * where, + * "EEPROM data size" - the size of data the user wants to store in the + * EEPROM. The data size must divide evenly to the half of the flash row size. + * "wear leveling" - the wear leveling factor (1-10). + * "redundant copy" - "zero" if a redundant copy is not used, and "one" + * otherwise. + * + * The start address of the storage should be filled to the Emulated EEPROM + * configuration structure and then passed to the Cy_Em_EEPROM_Init(). + * If the Em_EEPROM component is used, the config (Em_EEPROM_config) and + * context structures (Em_EEPROM_context) are defined by the component, so the + * user may just use that structures otherwise both of the structures need to + * be provided by the user. Note that if the "Config Data in Flash" + * option is selected in the component, then the configuration structure should + * be copied to RAM to allow EEPROM storage start address update. The following + * code demonstrates utilization of "Em_EEPROM_config" and "Em_EEPROM_context" + * Em_EEPROM component structures for Cy_Em_EEPROM middleware library + * initialization: + * + * cy_en_em_eeprom_status_t retValue; + * cy_stc_eeprom_config_t config; + * + * memcpy((void *)&config, + (void *)&Em_EEPROM_config, + sizeof(cy_stc_eeprom_config_t)); + * config.userFlashStartAddr = (uint32)emEeprom; + * retValue = Cy_Em_EEPROM_Init(&config, &Em_EEPROM_context); + * + * Initializing EEPROM in Emulated EEPROM flash area + * + * Initializing of the EEPROM storage in the Emulated EEPROM flash area is + * identical to initializing of the EEPROM storage in the User flash with one + * difference. The location of the Emulated EEPROM storage should be specified + * somewhere in the EmulatedEEPROM flash area. If the Em_EEPROM component is + * utilized in the project, then the respective storage + * (Em_EEPROM_em_EepromStorage[]) is automatically declared by the component + * if the "Use Emulated EEPROM" option is set to "Yes". The user just needs to + * fill the start address of the storage to the config structure. If the + * Em_EEPROM component is not used, the user needs to declare the storage + * in the Emulated EEPROM flash area. An example of such declaration is + * following (applicable for GCC and MDK compilers): + * + * CY_SECTION(".cy_em_eeprom") CY_ALIGN(CY_EM_EEPROM_FLASH_SIZEOF_ROW) + * const uint8_t emEeprom[Em_EEPROM_PHYSICAL_SIZE] = {0u}; + * + * The same declaration for the IAR compiler: + * + * #pragma location = ".cy_em_eeprom" + * #pragma data_alignment = CY_EM_EEPROM_FLASH_SIZEOF_ROW + * const uint8 emEeprom[Em_EEPROM_PHYSICAL_SIZE] = {0u}; + * + * where, + * Em_EEPROM_PHYSICAL_SIZE - is a constant that is generated by the Em_EEPROM + * component when the component is utilized in the project or it should be + * provided by the user. The equation for the calculation of the constant is + * shown above. + * + * Note that the size of the Emulated EEPROM flash area is limited. Refer to the + * specific device datasheet for the value of the available EEPROM Emulation + * area. + * + * \section group_em_eeprom_more_information More Information + * See the Em_EEPROM Component datasheet. + * + * + * \section group_em_eeprom_MISRA MISRA-C Compliance + * + * The Cy_Em_EEPROM library has the following specific deviations: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
MISRA RuleRule Class (Required/Advisory)Rule DescriptionDescription of Deviation(s)
11.4AThe cast should not be performed between a pointer to the object type + * and a different pointer to the object type.The cast from the object type and a different pointer to the object + * was used intentionally because of the performance reasons.
14.2RAll non-null statements shall either have at least one side-effect, + * however executed, or cause control flow to change.To maintain common codebase, some variables, unused for a specific + * device, are casted to void to prevent generation of an unused variable + * compiler warning.
16.7AThe object addressed by the pointer parameter is not modified and so + * the pointer could be of type 'pointer to const'.The warning is generated because of the pointer dereferencing to + * address which makes the MISRA checker think the data is not + * modified.
17.4RThe array indexing shall be the only allowed form of pointer + * arithmetic.The pointer arithmetic used in several places on the Cy_Em_EEPROM + * implementation is safe and preferred because it increases the code + * flexibility.
19.7AA function shall be used in preference to a function-like macro.Macro is used because of performance reasons.
+ * + * \section group_em_eeprom_changelog Changelog + * + * + * + * + * + * + * + *
VersionChangesReason for Change
1.0Initial Version
+ * + * \defgroup group_em_eeprom_macros Macros + * \brief + * This section describes the Emulated EEPROM Macros. + * + * \defgroup group_em_eeprom_functions Functions + * \brief + * This section describes the Emulated EEPROM Function Prototypes. + * + * \defgroup group_em_eeprom_data_structures Data Structures + * \brief + * Describes the data structures defined by the Emulated EEPROM. + * + * \defgroup group_em_eeprom_enums Enumerated types + * \brief + * Describes the enumeration types defined by the Emulated EEPROM. + * + */ + + +#if !defined(CY_EM_EEPROM_H) +#define CY_EM_EEPROM_H + +#include "cytypes.h" +#include +#if (CYDEV_CHIP_FAMILY_USED == CYDEV_CHIP_FAMILY_PSOC6) + #include + #include "syslib/cy_syslib.h" + #include "flash/cy_flash.h" +#else + #include "CyFlash.h" + #include +#endif /* (CYDEV_CHIP_FAMILY_USED == CYDEV_CHIP_FAMILY_PSOC6) */ + +/* The C binding of definitions if building with the C++ compiler */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/*************************************** +* Conditional Compilation Parameters +***************************************/ +#define CY_PSOC6 (CYDEV_CHIP_FAMILY_USED == CYDEV_CHIP_FAMILY_PSOC6) + + +/*************************************** +* Data Structure definitions +***************************************/ +/** +* \addtogroup group_em_eeprom_data_structures +* \{ +*/ + +/** EEPROM configuration structure */ +typedef struct +{ + /** The number of bytes to store in EEPROM */ + uint32 eepromSize; + + /** The amount of wear leveling from 1 to 10. 1 means no wear leveling + * is used. + */ + uint32 wearLevelingFactor; + + /** If not zero, a redundant copy of the Em_EEPROM is included. */ + uint8 redundantCopy; + + /** If not zero, a blocking write to flash is used. Otherwise non-blocking + * write is used. This parameter is used only for PSoC 6. + */ + uint8 blockingWrite; + + /** The start address for the EEPROM memory in the user's flash. */ + uint32 userFlashStartAddr; +} cy_stc_eeprom_config_t; + +/** \} group_em_eeprom_data_structures */ + +/** The EEPROM context data structure. It is used to store the specific +* EEPROM context data. +*/ +typedef struct +{ + /** The pointer to the end address of EEPROM including wear leveling overhead + * and excluding redundant copy overhead. + */ + uint32 wlEndAddr; + + /** The number of flash rows allocated for the EEPROM excluding the number of + * rows allocated for wear leveling and redundant copy overhead. + */ + uint32 numberOfRows; + + /** The address of the last written EEPROM row */ + uint32 lastWrRowAddr; + + /** The number of bytes to store in EEPROM */ + uint32 eepromSize; + + /** The amount of wear leveling from 1 to 10. 1 means no wear leveling + * is used. + */ + uint32 wearLevelingFactor; + + /** If not zero, a redundant copy of the Em_EEPROM is included. */ + uint8 redundantCopy; + + /** If not zero, a blocking write to flash is used. Otherwise non-blocking + * write is used. This parameter is used only for PSoC 6. + */ + uint8 blockingWrite; + + /** The start address for the EEPROM memory in the user's flash. */ + uint32 userFlashStartAddr; +} cy_stc_eeprom_context_t; + +#if (CY_PSOC6) + + #define CY_EM_EEPROM_ID (CY_PDL_DRV_ID(0x1BuL)) /**< Em_EEPROM PDL ID */ + /** + * \addtogroup group_em_eeprom_enums + * \{ + * Specifies return values meaning. + */ + /** A prefix for EEPROM function error return-values */ + #define CY_EM_EEPROM_ID_ERROR (uint32_t)(CY_EM_EEPROM_ID | CY_PDL_STATUS_ERROR) + +#else + + /** A prefix for EEPROM function status codes. For non-PSoC6 devices, + * prefix is zero. + */ + #define CY_EM_EEPROM_ID_ERROR (0uL) + +#endif /* (CY_PSOC6) */ + + +/*************************************** +* Enumerated Types and Parameters +***************************************/ + +/** EEPROM return enumeration type */ +typedef enum +{ + CY_EM_EEPROM_SUCCESS = 0x00uL, /**< The function executed successfully */ + CY_EM_EEPROM_BAD_PARAM = (CY_EM_EEPROM_ID_ERROR + 1uL), /**< The input parameter is invalid */ + CY_EM_EEPROM_BAD_CHECKSUM = (CY_EM_EEPROM_ID_ERROR + 2uL), /**< The data in EEPROM is corrupted */ + CY_EM_EEPROM_BAD_DATA = (CY_EM_EEPROM_ID_ERROR + 3uL), /**< Failed to place the EEPROM in flash */ + CY_EM_EEPROM_WRITE_FAIL = (CY_EM_EEPROM_ID_ERROR + 4uL) /**< Write to EEPROM failed */ +} cy_en_em_eeprom_status_t; + +/** \} group_em_eeprom_enums */ + + +/*************************************** +* Function Prototypes +***************************************/ + +/** +* \addtogroup group_em_eeprom_functions +* \{ +*/ +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Init(cy_stc_eeprom_config_t* config, cy_stc_eeprom_context_t * context); +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Read(uint32 addr, + void * eepromData, + uint32 size, + cy_stc_eeprom_context_t * context); +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Write(uint32 addr, + void * eepromData, + uint32 size, + cy_stc_eeprom_context_t * context); +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Erase(cy_stc_eeprom_context_t * context); +uint32 Cy_Em_EEPROM_NumWrites(cy_stc_eeprom_context_t * context); +/** \} group_em_eeprom_functions */ + + +/*************************************** +* API Constants +***************************************/ +/** +* \addtogroup group_em_eeprom_macros +* \{ +*/ +/** Library major version */ +#define CY_EM_EEPROM_VERSION_MAJOR (2) + +/** Library minor version */ +#define CY_EM_EEPROM_VERSION_MINOR (0) + +/** Defines the maximum data length that can be stored in one flash row */ +#define CY_EM_EEPROM_EEPROM_DATA_LEN (CY_EM_EEPROM_FLASH_SIZEOF_ROW / 2u) + +/** \} group_em_eeprom_macros */ + + +/*************************************** +* Macro definitions +***************************************/ +/** \cond INTERNAL */ + +/* Defines the size of flash row */ +#define CY_EM_EEPROM_FLASH_SIZEOF_ROW (CY_FLASH_SIZEOF_ROW) + +/* Device specific flash constants */ +#if (!CY_PSOC6) + #define CY_EM_EEPROM_FLASH_BASE_ADDR (CYDEV_FLASH_BASE) + #define CY_EM_EEPROM_FLASH_SIZE (CYDEV_FLASH_SIZE) + #define CY_EM_EEPROM_ROWS_IN_ARRAY (CY_FLASH_SIZEOF_ARRAY / CY_EM_EEPROM_FLASH_SIZEOF_ROW) + #if (CY_PSOC3) + #define CY_EM_EEPROM_CODE_MEM_CLASS_PREFIX (0xff0000uL) + #define CY_EM_EEPROM_CODE_ADDR_END \ + (CY_EM_EEPROM_CODE_MEM_CLASS_PREFIX + (CY_EM_EEPROM_FLASH_SIZE - 1u)) + #define CY_EM_EEPROM_CODE_ADDR_MASK (0xffffu) + /* Checks if the EEPROM is in flash range */ + #define CY_EM_EEPROM_IS_IN_FLASH_RANGE(startAddr, endAddr) \ + (((startAddr) > CY_EM_EEPROM_CODE_MEM_CLASS_PREFIX) && \ + ((endAddr) <= CY_EM_EEPROM_CODE_ADDR_END)) + #else + /* Checks is the EEPROM is in flash range */ + #define CY_EM_EEPROM_IS_IN_FLASH_RANGE(startAddr, endAddr) \ + (((startAddr) > CY_EM_EEPROM_FLASH_BASE_ADDR) && ((endAddr) <= CY_EM_EEPROM_FLASH_END_ADDR)) + #endif /* (CY_PSOC3) */ +#else + #define CY_EM_EEPROM_FLASH_BASE_ADDR (CY_FLASH_BASE) + #define CY_EM_EEPROM_FLASH_SIZE (CY_FLASH_SIZE) + #define CY_EM_EEPROM_EM_EEPROM_BASE_ADDR (CY_EM_EEPROM_BASE) + #define CY_EM_EEPROM_EM_EEPROM_SIZE (CY_EM_EEPROM_SIZE) + #define CY_EM_EEPROM_EM_EEPROM_END_ADDR (CY_EM_EEPROM_EM_EEPROM_BASE_ADDR + CY_EM_EEPROM_EM_EEPROM_SIZE) + /* Checks is the EEPROM is in flash range */ + #define CY_EM_EEPROM_IS_IN_FLASH_RANGE(startAddr, endAddr) \ + (((((startAddr) > CY_EM_EEPROM_FLASH_BASE_ADDR) && ((endAddr) <= CY_EM_EEPROM_FLASH_END_ADDR)) || \ + (((startAddr) >= CY_EM_EEPROM_EM_EEPROM_BASE_ADDR) && \ + ((endAddr) <= CY_EM_EEPROM_EM_EEPROM_END_ADDR)))) +#endif /* (!CY_PSOC6) */ + +#define CY_EM_EEPROM_FLASH_END_ADDR (CY_EM_EEPROM_FLASH_BASE_ADDR + CY_EM_EEPROM_FLASH_SIZE) + +/* Defines the length of EEPROM data that can be stored in Em_EEPROM header */ +#define CY_EM_EEPROM_HEADER_DATA_LEN ((CY_EM_EEPROM_FLASH_SIZEOF_ROW / 2u) - 16u) + +#define CY_EM_EEPROM_ADDR_IN_RANGE (1u) + +/* Return CY_EM_EEPROM_ADDR_IN_RANGE if addr exceeded the upper range of +* EEPROM. The wear leveling overhead is included in the range but redundant copy +* is excluded. +*/ +#define CY_EM_EEPROM_IS_ADDR_EXCEED_RANGE(addr, endEepromAddr) \ + (((addr) >= (endEepromAddr)) ? (0u) : (CY_EM_EEPROM_ADDR_IN_RANGE)) + +/* Check to see if the specified address is present in the EEPROM */ +#define CY_EM_EEPROM_IS_ADDR_IN_RANGE(addr, startEepromAddr, endEepromAddr) \ + (((addr) > (startEepromAddr)) ? \ + (((addr) < (endEepromAddr)) ? (CY_EM_EEPROM_ADDR_IN_RANGE) : (0u)) : (0u)) + +/* Check if the EEPROM address locations from startAddr1 to endAddr1 +* are crossed with EEPROM address locations from startAddr2 to endAddr2. +*/ +#define CY_EM_EEPROM_IS_ADDRESES_CROSSING(startAddr1, endAddr1 , startAddr2, endAddr2) \ + (((startAddr1) > (startAddr2)) ? (((startAddr1) >= (endAddr2)) ? (0u) : (1u) ) : \ + (((startAddr2) >= (endAddr1)) ? (0u) : (1u))) + +/* Return the pointer to the start of the redundant copy of the EEPROM */ +#define CY_EM_EEPROM_GET_REDNT_COPY_ADDR_BASE(numRows, wearLeveling, eepromStartAddr) \ + ((((numRows) * CY_EM_EEPROM_FLASH_SIZEOF_ROW) * (wearLeveling)) + (eepromStartAddr)) + +/* Return the number of the row in EM_EEPROM which contains an address defined by +* rowAddr. + */ +#define CY_EM_EEPROM_GET_ACT_ROW_NUM_FROM_ADDR(rowAddr, maxRows, eepromStartAddr) \ + ((((rowAddr) - (eepromStartAddr)) / CY_EM_EEPROM_FLASH_SIZEOF_ROW) % (maxRows)) + + +/** Returns the size allocated for the EEPROM excluding wear leveling and +* redundant copy overhead. +*/ +#define CY_EM_EEPROM_GET_EEPROM_SIZE(numRows) ((numRows) * CY_EM_EEPROM_FLASH_SIZEOF_ROW) + +/* Check if the given address belongs to the EEPROM address of the row +* specified by rowNum. +*/ +#define CY_EM_EEPROM_IS_ADDR_IN_ROW_RANGE(addr, rowNum) \ + (((addr) < ((rowNum) * (CY_EM_EEPROM_FLASH_SIZEOF_ROW / 2u))) ? (0u) : \ + (((addr) > ((((rowNum) + 1u) * (CY_EM_EEPROM_FLASH_SIZEOF_ROW / 2u)) - 1u)) ? \ + (0u) : (1u))) + +/* CRC-8 constants */ +#define CY_EM_EEPROM_CRC8_POLYNOM ((uint8)(0x31u)) +#define CY_EM_EEPROM_CRC8_POLYNOM_LEN (8u) +#define CY_EM_EEPROM_CRC8_SEED (0xFFu) +#define CY_EM_EEPROM_CRC8_XOR_VAL ((uint8) (0x80u)) + +#define CY_EM_EEPROM_CALCULATE_CRC8(crc) \ + ((CY_EM_EEPROM_CRC8_XOR_VAL == ((crc) & CY_EM_EEPROM_CRC8_XOR_VAL)) ? \ + ((uint8)(((uint8)((uint8)((crc) << 1u))) ^ CY_EM_EEPROM_CRC8_POLYNOM)) : ((uint8)((crc) << 1u))) + +#define CY_EM_EEPROM_GET_SEQ_NUM(addr) (*(uint32*)(addr)) + +/** \endcond */ + +/** +* \addtogroup group_em_eeprom_macros +* \{ +*/ + +/** Calculate the number of flash rows required to create an Em_EEPROM of +* dataSize. +*/ +#define CY_EM_EEPROM_GET_NUM_ROWS_IN_EEPROM(dataSize) \ + (((dataSize) / (CY_EM_EEPROM_EEPROM_DATA_LEN)) + \ + ((((dataSize) % (CY_EM_EEPROM_EEPROM_DATA_LEN)) != 0u) ? 1U : 0U)) + +/** Returns the size of flash allocated for EEPROM including wear leveling and +* redundant copy overhead. +*/ +#define CY_EM_EEPROM_GET_PHYSICAL_SIZE(dataSize, wearLeveling, redundantCopy) \ + (((CY_EM_EEPROM_GET_NUM_ROWS_IN_EEPROM(dataSize) * \ + CY_EM_EEPROM_FLASH_SIZEOF_ROW) * \ + (wearLeveling)) * (1uL + (redundantCopy))) + +/** \} group_em_eeprom_macros */ + + +/****************************************************************************** +* Local definitions +*******************************************************************************/ +/** \cond INTERNAL */ + +/* Offsets for 32-bit RAM buffer addressing */ +#define CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32 ((CY_EM_EEPROM_FLASH_SIZEOF_ROW / 2u) / 4u) +#define CY_EM_EEPROM_HEADER_SEQ_NUM_OFFSET_U32 (0u) +#define CY_EM_EEPROM_HEADER_ADDR_OFFSET_U32 (1u) +#define CY_EM_EEPROM_HEADER_LEN_OFFSET_U32 (2u) +#define CY_EM_EEPROM_HEADER_DATA_OFFSET_U32 (3u) +#define CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET_U32 (CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32 - 1u) + +/* The same offsets as above used for direct memory addressing */ +#define CY_EM_EEPROM_EEPROM_DATA_OFFSET (CY_EM_EEPROM_FLASH_SIZEOF_ROW / 2u) +#define CY_EM_EEPROM_HEADER_ADDR_OFFSET (4u) +#define CY_EM_EEPROM_HEADER_LEN_OFFSET (8u) +#define CY_EM_EEPROM_HEADER_DATA_OFFSET (12u) +#define CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET (CY_EM_EEPROM_EEPROM_DATA_OFFSET - 4u) + +#define CY_EM_EEPROM_U32_DIV (4u) + +/* Maximum wear leveling value */ +#define CY_EM_EEPROM_MAX_WEAR_LEVELING_FACTOR (10u) + +/* Maximum allowed flash row write/erase operation duration */ +#define CY_EM_EEPROM_MAX_WRITE_DURATION_MS (50u) + +/** \endcond */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CY_EM_EEPROM_H */ + + +/* [] END OF FILE */ diff --git a/software/SCSI2SD/v5.1/SCSI2SD.cydsn/Generated_Source/PSoC5/cy_em_eeprom.c b/software/SCSI2SD/v5.1/SCSI2SD.cydsn/Generated_Source/PSoC5/cy_em_eeprom.c new file mode 100755 index 0000000..ce94d9c --- /dev/null +++ b/software/SCSI2SD/v5.1/SCSI2SD.cydsn/Generated_Source/PSoC5/cy_em_eeprom.c @@ -0,0 +1,1416 @@ +/***************************************************************************//** +* \file cy_em_eeprom.c +* \version 2.0 +* +* \brief +* This file provides source code of the API for the Emulated EEPROM library. +* The Emulated EEPROM API allows creating of an emulated EEPROM in flash that +* has the ability to do wear leveling and restore corrupted data from a +* redundant copy. +* +******************************************************************************** +* \copyright +* Copyright 2017, Cypress Semiconductor Corporation. All rights reserved. +* You may use this file only in accordance with the license, terms, conditions, +* disclaimers, and limitations in the end user license agreement accompanying +* the software package with which this file was provided. +*******************************************************************************/ + + +#include "cytypes.h" +#include + +#if (CYDEV_CHIP_FAMILY_USED == CYDEV_CHIP_FAMILY_PSOC6) + #include "em_eeprom/cy_em_eeprom.h" +#else + #include "cy_em_eeprom.h" +#endif /* (CYDEV_CHIP_FAMILY_USED == CYDEV_CHIP_FAMILY_PSOC6) */ + + +#if defined(__cplusplus) +extern "C" { +#endif + + +/*************************************** +* Private Function Prototypes +***************************************/ +static void FindLastWrittenRow(uint32 * lastWrRowPtr, cy_stc_eeprom_context_t * context); +static uint32 GetRowAddrBySeqNum(uint32 seqNum, cy_stc_eeprom_context_t * context); +static uint8 CalcChecksum(uint8 rowData[], uint32 len); +static void GetNextRowToWrite(uint32 seqNum, + uint32 * rowToWrPtr, + uint32 * rowToRdPtr, + cy_stc_eeprom_context_t * context); +static cy_en_em_eeprom_status_t CheckRanges(cy_stc_eeprom_config_t* config); +static cy_en_em_eeprom_status_t WriteRow(uint32 rowAddr, uint32 *rowData, cy_stc_eeprom_context_t * context); +static cy_en_em_eeprom_status_t EraseRow(uint32 rowAddr, uint32 ramBuffAddr, cy_stc_eeprom_context_t * context); +static cy_en_em_eeprom_status_t CheckCrcAndCopy(uint32 startAddr, + uint32 dstAddr, + uint32 rowOffset, + uint32 numBytes, + cy_stc_eeprom_context_t * context); +static uint32 GetAddresses(uint32 *startAddr, uint32 *endAddr, uint32 *offset, uint32 rowNum, uint32 addr, uint32 len); +static cy_en_em_eeprom_status_t FillChecksum(cy_stc_eeprom_context_t * context); + +/** +* \addtogroup group_em_eeprom_functions +* \{ +*/ + +/******************************************************************************* +* Function Name: Cy_Em_EEPROM_Init +****************************************************************************//** +* +* Initializes the Emulated EEPROM library by filling the context structure. +* +* \param config +* The pointer to a configuration structure. See \ref cy_stc_eeprom_config_t. +* +* \param context +* The pointer to the EEPROM context structure to be filled by the function. +* \ref cy_stc_eeprom_context_t. +* +* \return +* error / status code. See \ref cy_en_em_eeprom_status_t. +* +* \note +* The context structure should not be modified by the user after it is filled +* with this function. Modification of context structure may cause the +* unexpected behavior of the Cy_Em_EEPROM API functions which rely on it. +* +* \note +* This function uses a buffer of the flash row size to perform read +* operation. For the size of the row refer to the specific PSoC device +* datasheet. +* +* \sideeffect +* If the "Redundant Copy" option is used, the function performs a number of +* write operations to the EEPROM to initialize flash rows checksums. Therefore, +* Cy_Em_EEPROM_NumWrites(), when it is called right after Cy_Em_EEPROM_Init(), +* will return a non-zero value that identifies the number of writes performed +* by Cy_Em_EEPROM_Init(). +* +*******************************************************************************/ +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Init(cy_stc_eeprom_config_t* config, cy_stc_eeprom_context_t * context) +{ + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_PARAM; + + if((NULL != context) && (NULL != config) && (NULL != ((uint32 *)config->userFlashStartAddr)) && + (config->wearLevelingFactor <= CY_EM_EEPROM_MAX_WEAR_LEVELING_FACTOR) && (config->eepromSize != 0u)) + { + ret = CheckRanges(config); + + if(CY_EM_EEPROM_SUCCESS == ret) + { + /* Copy the user config structure fields into context */ + context->eepromSize = config->eepromSize; + context->wearLevelingFactor = config->wearLevelingFactor; + context->redundantCopy = config->redundantCopy; + context->blockingWrite = config->blockingWrite; + context->userFlashStartAddr = config->userFlashStartAddr; + /* Store frequently used data for internal use */ + context->numberOfRows = CY_EM_EEPROM_GET_NUM_ROWS_IN_EEPROM(config->eepromSize); + context->wlEndAddr = ((CY_EM_EEPROM_GET_EEPROM_SIZE(context->numberOfRows) * config->wearLevelingFactor) + + config->userFlashStartAddr); + /* Find last written EEPROM row and store it for quick access */ + FindLastWrittenRow(&context->lastWrRowAddr, context); + + if((0u == CY_EM_EEPROM_GET_SEQ_NUM(context->lastWrRowAddr)) && (0u != context->redundantCopy)) + { + /* Call the function only after device reprogramming in case + * if redundant copy is enabled. + */ + ret = FillChecksum(context); + + /* Update the last written EEPROM row for Cy_Em_EEPROM_NumWrites() */ + FindLastWrittenRow(&context->lastWrRowAddr, context); + } + } + } + + return(ret); +} + + +/******************************************************************************* +* Function Name: Cy_Em_EEPROM_Read +****************************************************************************//** +* +* This function takes the logical EEPROM address, converts it to the actual +* physical address where the data is stored and returns the data to the user. +* +* \param addr +* The logical start address in EEPROM to start reading data from. +* +* \param eepromData +* The pointer to a user array to write data to. +* +* \param size +* The amount of data to read. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +* \return +* This function returns \ref cy_en_em_eeprom_status_t. +* +* \note +* This function uses a buffer of the flash row size to perform read +* operation. For the size of the row refer to the specific PSoC device +* datasheet. +* +* \note +* In case if redundant copy option is enabled the function may perform writes +* to EEPROM. This is done in case if the data in the EEPPROM is corrupted and +* the data in redundant copy is valid based on CRC-8 data integrity check. +* +*******************************************************************************/ +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Read(uint32 addr, + void * eepromData, + uint32 size, + cy_stc_eeprom_context_t * context) +{ + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_PARAM; + uint32 i; + uint32 numBytesToRead; + uint32 curEepromBaseAddr; + uint32 curRowOffset; + uint32 startRowAddr; + uint32 actEepromRowNum; + uint32 curRdEepromRowNum = 0u; + uint32 dataStartEepromRowNum = 0u; + uint32 eeData = (uint32) eepromData; /* To avoid the pointer arithmetic with void */ + + /* Validate input parameters */ + if((0u != size) && ((addr + size) <= (context->eepromSize)) && (NULL != eepromData)) + { + uint32 rdAddr = addr; + uint32 rdSize = size; + /* Get the sequence number of the last written row */ + uint32 seqNum = CY_EM_EEPROM_GET_SEQ_NUM(context->lastWrRowAddr); + uint32 updateAddrFlag = 0u; + + /* Calculate the number of the row read operations. Currently this only concerns + * the reads from the EEPROM data locations. + */ + uint32 numRowReads = ((((rdAddr + rdSize) - 1u) / CY_EM_EEPROM_EEPROM_DATA_LEN) - + (rdAddr / CY_EM_EEPROM_EEPROM_DATA_LEN)) + 1u; + + /* Get the address of the first row of the currently active EEPROM sector. If + * no wear leveling is used - the EEPROM has only one sector, so use the base + * addr stored in "context->userFlashStartAddr". + */ + curEepromBaseAddr = (((context->lastWrRowAddr - context->userFlashStartAddr) / + (CY_EM_EEPROM_FLASH_SIZEOF_ROW * context->numberOfRows)) * + (CY_EM_EEPROM_FLASH_SIZEOF_ROW * context->numberOfRows)) + + context->userFlashStartAddr; + + /* Find the number of the row that contains the start address of the data */ + for(i = 0u; i < context->numberOfRows; i++) + { + if(0u != CY_EM_EEPROM_IS_ADDR_IN_ROW_RANGE(rdAddr, i)) + { + dataStartEepromRowNum = i; + curRdEepromRowNum = dataStartEepromRowNum; + break; + } + } + + /* Find the row number of the last written row */ + actEepromRowNum = (context->lastWrRowAddr - curEepromBaseAddr) / CY_EM_EEPROM_FLASH_SIZEOF_ROW; + + /* Check if wear leveling is used */ + if(context->wearLevelingFactor > 1u) + { + uint32 dataEndEepromRowNum = dataStartEepromRowNum + (numRowReads - 1u); + + /* Check if the future validation of the read address is required. */ + updateAddrFlag = (dataStartEepromRowNum > actEepromRowNum) ? 1u : + ((dataEndEepromRowNum > actEepromRowNum) ? 1u : 0u); + } + + /* Copy data from the EEPROM data locations to the user buffer */ + for(i = 0u; i < numRowReads; i++) + { + startRowAddr = curEepromBaseAddr + (curRdEepromRowNum * CY_EM_EEPROM_FLASH_SIZEOF_ROW); + curRowOffset = CY_EM_EEPROM_EEPROM_DATA_LEN + (rdAddr % CY_EM_EEPROM_EEPROM_DATA_LEN); + + /* Check if there are more reads pending and update the number of the + * remaining bytes to read respectively. + */ + if((i + 1u) < numRowReads) + { + numBytesToRead = CY_EM_EEPROM_EEPROM_DATA_LEN - (rdAddr % CY_EM_EEPROM_EEPROM_DATA_LEN); + } + else + { + numBytesToRead = rdSize; + } + + /* Check if the read address needs to be updated to point to the correct + * EEPROM sector. + */ + if((0u != updateAddrFlag) && (curRdEepromRowNum > actEepromRowNum)) + { + startRowAddr -= context->numberOfRows * CY_EM_EEPROM_FLASH_SIZEOF_ROW; + + if(startRowAddr < context->userFlashStartAddr) + { + startRowAddr = context->wlEndAddr - + ((context->numberOfRows - curRdEepromRowNum) * CY_EM_EEPROM_FLASH_SIZEOF_ROW); + } + } + + if(0u != context->redundantCopy) + { + /* Check a checksum of the EEPROM row and if it is bad, check a checksum in + * the corresponding row in redundant copy, otherwise return failure. + */ + ret = CheckCrcAndCopy(startRowAddr, eeData, curRowOffset, numBytesToRead, context); + + if(CY_EM_EEPROM_SUCCESS != ret) + { + break; + } + } + else + { + /* Copy the data to the user buffer */ + (void)memcpy((void *)(eeData), + (void *)(startRowAddr + curRowOffset), + numBytesToRead); + + /* Indicate success to be able to execute next code block */ + ret = CY_EM_EEPROM_SUCCESS; + } + + /* Update variables anticipated in the read operation */ + rdAddr += numBytesToRead; + rdSize -= numBytesToRead; + eeData += numBytesToRead; + curRdEepromRowNum++; + } + + /* This code block will copy the latest data from the EEPROM headers into the + * user buffer. The data previously copied into the user buffer may be updated + * as the EEPROM headers contain more recent data. + * The code block is executed when two following conditions are true: + * 1) The reads from "historic" data locations were successful; + * 2) The user performed at least one write operation to Em_EEPROM (0u != + * seqNum). + */ + if((CY_EM_EEPROM_SUCCESS == ret) && (0u != seqNum)) + { + numRowReads = (context->numberOfRows <= seqNum) ? (context->numberOfRows) : (seqNum); + numRowReads--; + + for(i = (seqNum - numRowReads); i <= seqNum; i++) + { + startRowAddr = GetRowAddrBySeqNum(i, context); + + if (0u != startRowAddr) + { + /* The following variables are introduced to increase code readability. */ + uint32 startAddr = *(uint32 *)(startRowAddr + CY_EM_EEPROM_HEADER_ADDR_OFFSET); + uint32 endAddr = startAddr + (*(uint32 *)(startRowAddr + CY_EM_EEPROM_HEADER_LEN_OFFSET)); + + /* Check if the current row EEPROM header contains the data requested for read */ + if(0u != CY_EM_EEPROM_IS_ADDRESES_CROSSING(startAddr, endAddr, addr, addr + size)) + { + uint32 srcOffset = (startAddr > addr) ? (0u) : (addr - startAddr); + uint32 dstOffset = (startAddr > addr) ? (startAddr - addr): (0u); + rdAddr = (startAddr > addr) ? (startAddr) : (addr); + + srcOffset += CY_EM_EEPROM_HEADER_DATA_OFFSET; + + /* Calculate the number of bytes to be read from the current row's EEPROM header */ + numBytesToRead = ((endAddr < (addr + size)) ? endAddr : (addr + size)) - rdAddr; + + /* Calculate the offset in the user buffer from which the data will be updated. */ + eeData = ((uint32)eepromData) + dstOffset; + + /* Check a checksum of the EEPROM row and if it is bad, check a checksum in the + * corresponding row in redundant copy, otherwise return failure. Copy the data + * from the recent EEPROM headers to the user buffer. This will overwrite the + * data copied form EEPROM data locations as the data in EEPROM headers is newer. + */ + if(0u != context->redundantCopy) + { + ret = CheckCrcAndCopy(startRowAddr, eeData, srcOffset, numBytesToRead, context); + + if(CY_EM_EEPROM_SUCCESS != ret) + { + break; + } + } + else + { + (void)memcpy((void *)(eeData), (void *)(startRowAddr + srcOffset), numBytesToRead); + } + } + } + } + } + } + + return(ret); +} + + +/******************************************************************************* +* Function Name: Cy_Em_EEPROM_Write +****************************************************************************//** +* +* This function takes the logical EEPROM address and converts it to the actual +* physical address and writes data there. If wear leveling is implemented, the +* writing process will use the wear leveling techniques. This is a blocking +* function and it does not return until the write operation is completed. The +* user firmware should not enter Hibernate mode until write is completed. The +* write operation is allowed in Sleep and Deep-Sleep modes. During the flash +* operation, the device should not be reset, including the XRES pin, a software +* reset, and watchdog reset sources. Also, low-voltage detect circuits should +* be configured to generate an interrupt instead of a reset. Otherwise, portions +* of flash may undergo unexpected changes. +* +* \param addr +* The logical start address in EEPROM to start writing data from. +* +* \param eepromData +* Data to write to EEPROM. +* +* \param size +* The amount of data to write to EEPROM. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +* \return +* This function returns \ref cy_en_em_eeprom_status_t. +* +* \note +* This function uses a buffer of the flash row size to perform write +* operation. For the size of the row refer to the specific PSoC device +* datasheet. +* +* \sideeffect +* In case when blocking write option is used, if this function is called by +* the CM4 the user code on CM0P and the user code on CM4 are blocked until erase +* flash row operation is finished. If this function is called by the CM0P the +* user code on CM4 is not blocked and the user code on CM0P is blocked until +* erase flash row operation is finished. Plan your task allocation accordingly. +* +* \sideeffect +* In case if non-blocking write option is used and when user flash is used as +* an EEPROM storage care should be taken to prevent the read while write (RWW) +* exception. To prevent the RWW exception the user flash macro that includes +* the EEPROM storage should not be read while the EEPROM write is not completed. +* The read also means the user code execution from the respective flash macro. +* +*******************************************************************************/ +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Write(uint32 addr, + void * eepromData, + uint32 size, + cy_stc_eeprom_context_t * context) +{ + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_PARAM; + uint32 i; + uint32 wrCnt; + uint32 actEmEepromRowNum; + uint32 writeRamBuffer[CY_EM_EEPROM_FLASH_SIZEOF_ROW / CY_EM_EEPROM_U32_DIV]; + uint32 startAddr = 0u; + uint32 endAddr = 0u; + uint32 tmpRowAddr; + uint32 emEepromRowAddr = context->lastWrRowAddr; + uint32 emEepromRowRdAddr; + void * tmpData; + uint32 eeData = (uint32) eepromData; /* To avoid the pointer arithmetic with void */ + + /* Check if the EEPROM data does not exceed the EEPROM capacity */ + if((0u != size) && ((addr + size) <= (context->eepromSize)) && (NULL != eepromData)) + { + uint32 numWrites = ((size - 1u) / CY_EM_EEPROM_HEADER_DATA_LEN) + 1u; + uint32 eeHeaderDataOffset = 0u; + + for(wrCnt = 0u; wrCnt < numWrites; wrCnt++) + { + uint32 skipOperation = 0u; + /* Get the sequence number of the last written row */ + uint32 seqNum = CY_EM_EEPROM_GET_SEQ_NUM(emEepromRowAddr); + + /* Get the address of the row to be written. The "emEepromRowAddr" may be + * updated with the proper address (if wear leveling is used). The + * "emEepromRowRdAddr" will point to the row address from which the historic + * data will be read into the RAM buffer. + */ + GetNextRowToWrite(seqNum, &emEepromRowAddr, &emEepromRowRdAddr, context); + + /* Clear the RAM buffer so to not put junk into flash */ + (void)memset(writeRamBuffer, 0, CY_EM_EEPROM_FLASH_SIZEOF_ROW); + + /* Fill the EM_EEPROM header info for the row in the RAM buffer */ + seqNum++; + writeRamBuffer[CY_EM_EEPROM_HEADER_SEQ_NUM_OFFSET_U32] = seqNum; + writeRamBuffer[CY_EM_EEPROM_HEADER_ADDR_OFFSET_U32] = addr; + tmpData = (void *) eeData; + + /* Check if this is the last row to write */ + if(wrCnt == (numWrites - 1u)) + { + /* Fill in the remaining size value to the EEPROM header. */ + writeRamBuffer[CY_EM_EEPROM_HEADER_LEN_OFFSET_U32] = size; + } + else + { + /* This is not the last row to write in the current EEPROM write operation. + * Write the maximum possible data size to the EEPROM header. Update the + * size, eeData and addr respectively. + */ + writeRamBuffer[CY_EM_EEPROM_HEADER_LEN_OFFSET_U32] = CY_EM_EEPROM_HEADER_DATA_LEN; + size -= CY_EM_EEPROM_HEADER_DATA_LEN; + addr += CY_EM_EEPROM_HEADER_DATA_LEN; + eeData += CY_EM_EEPROM_HEADER_DATA_LEN; + } + + /* Write the data to the EEPROM header */ + (void)memcpy((void *)&writeRamBuffer[CY_EM_EEPROM_HEADER_DATA_OFFSET_U32], + tmpData, + writeRamBuffer[CY_EM_EEPROM_HEADER_LEN_OFFSET_U32]); + + if(emEepromRowRdAddr != 0UL) + { + /* Copy the EEPROM historic data for this row from flash to RAM */ + (void)memcpy((void *)&writeRamBuffer[CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32], + (void *)(emEepromRowRdAddr + CY_EM_EEPROM_EEPROM_DATA_LEN), + CY_EM_EEPROM_EEPROM_DATA_LEN); + } + + /* Check if there is data for this location in other EEPROM headers: + * find out the row with the lowest possible sequence number which + * may contain the data for the current row. + */ + i = (seqNum > context->numberOfRows) ? ((seqNum - (context->numberOfRows)) + 1u) : 1u; + + for(; i <= seqNum; i++) + { + if(i == seqNum) + { + /* The code reached the row that is about to be written. Analyze the recently + * created EEPROM header (stored in the RAM buffer currently): if it contains + * the data for EEPROM data locations in the row that is about to be written. + */ + tmpRowAddr = (uint32) writeRamBuffer; + } + else + { + /* Retrieve the address of the previously written row by its sequence number. + * The pointer will be used to get data from the respective EEPROM header. + */ + tmpRowAddr = GetRowAddrBySeqNum(i, context); + } + + actEmEepromRowNum = CY_EM_EEPROM_GET_ACT_ROW_NUM_FROM_ADDR(emEepromRowAddr, + context->numberOfRows, + context->userFlashStartAddr); + if(0UL != tmpRowAddr) + { + /* Calculate the required addressed for the later EEPROM historic data update */ + skipOperation = GetAddresses( + &startAddr, + &endAddr, + &eeHeaderDataOffset, + actEmEepromRowNum, + *(uint32 *)(tmpRowAddr + CY_EM_EEPROM_HEADER_ADDR_OFFSET), + *(uint32 *)(tmpRowAddr + CY_EM_EEPROM_HEADER_LEN_OFFSET)); + } + else + { + /* Skip writes to the RAM buffer */ + skipOperation++; + } + + /* Write data to the RAM buffer */ + if(0u == skipOperation) + { + uint32 dataAddr = ((uint32)((uint8 *)&writeRamBuffer)) + startAddr; + + /* Update the address to point to the EEPROM header data and not to + * the start of the row. + */ + tmpRowAddr = tmpRowAddr + CY_EM_EEPROM_HEADER_DATA_OFFSET + eeHeaderDataOffset; + (void)memcpy((void *)(dataAddr), (void *)(tmpRowAddr), endAddr - startAddr); + } + + /* Calculate the checksum if redundant copy is enabled */ + if(0u != context->redundantCopy) + { + writeRamBuffer[CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET_U32] = (uint32) + CalcChecksum((uint8 *) &writeRamBuffer[CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32], + CY_EM_EEPROM_EEPROM_DATA_LEN); + } + } + + /* Write the data to the specified flash row */ + ret = WriteRow(emEepromRowAddr, writeRamBuffer, context); + tmpRowAddr = emEepromRowAddr; + + /* Check if redundant copy is used */ + if((0u != context->redundantCopy) && (CY_EM_EEPROM_SUCCESS == ret)) + { + /* Update the row address to point to the row in the redundant EEPROM's copy */ + tmpRowAddr = (emEepromRowAddr - context->userFlashStartAddr) + context->wlEndAddr; + + /* Write the data to the specified flash row */ + ret = WriteRow(tmpRowAddr, writeRamBuffer, context); + } + + if(CY_EM_EEPROM_SUCCESS == ret) + { + /* Store last written row address only when EEPROM and redundant + * copy writes were successful. + */ + context->lastWrRowAddr = emEepromRowAddr; + } + else + { + break; + } + } + } + return(ret); +} + + +/******************************************************************************* +* Function Name: Cy_Em_EEPROM_Erase +****************************************************************************//** +* +* This function erases the entire contents of the EEPROM. Erased values are all +* zeros. This is a blocking function and it does not return until the write +* operation is completed. The user firmware should not enter Hibernate mode until +* erase is completed. The erase operation is allowed in Sleep and Deep-Sleep modes. +* During the flash operation, the device should not be reset, including the +* XRES pin, a software reset, and watchdog reset sources. Also, low-voltage +* detect circuits should be configured to generate an interrupt instead of a +* reset. Otherwise, portions of flash may undergo unexpected changes. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +* \return +* This function returns \ref cy_en_em_eeprom_status_t. +* +* \note +* For all non PSoC 6 devices the erase operation is performed by clearing +* the EEPROM data using flash write. This affects the flash durability. +* So it is recommended to use this function in utmost case to prolongate +* flash life. +* +* \note +* This function uses a buffer of the flash row size to perform erase +* operation. For the size of the row refer to the specific PSoC device +* datasheet. +* +* \sideeffect +* In case when blocking write option is used, if this function is called by +* the CM4 the user code on CM0P and the user code on CM4 are blocked until erase +* flash row operation is finished. If this function is called by the CM0P the +* user code on CM4 is not blocked and the user code on CM0P is blocked until +* erase flash row operation is finished. Plan your task allocation accordingly. +* +* \sideeffect +* In case if non-blocking write option is used and when user flash is used as +* an EEPROM storage care should be taken to prevent the read while write (RWW) +* exception. To prevent the RWW exception the user flash macro that includes +* the EEPROM storage should not be read while the EEPROM erase is not completed. +* The read also means the user code execution from the respective flash macro. +* +*******************************************************************************/ +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Erase(cy_stc_eeprom_context_t * context) +{ + uint32 i; + uint32 seqNum; + uint32 emEepromRowAddr = context->lastWrRowAddr; + uint32 emEepromRowRdAddr; + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_WRITE_FAIL; + uint32 writeRamBuffer[CY_EM_EEPROM_FLASH_SIZEOF_ROW / CY_EM_EEPROM_U32_DIV] = {0u}; +#if (CY_PSOC6) + uint32 emEepromStoredRowAddr = context->lastWrRowAddr; + uint32 storedSeqNum; +#endif /* (!CY_PSOC6) */ + + /* Get the sequence number of the last written row */ + seqNum = CY_EM_EEPROM_GET_SEQ_NUM(emEepromRowAddr); + + /* If there were no writes to EEPROM - nothing to erase */ + if(0u != seqNum) + { + /* Calculate the number of row erase operations required */ + uint32 numWrites = context->numberOfRows * context->wearLevelingFactor; + + #if (CY_PSOC6) + GetNextRowToWrite(seqNum, &emEepromStoredRowAddr, &emEepromRowRdAddr, context); + storedSeqNum = seqNum + 1u; + #endif /* (CY_PSOC6) */ + + if(0u != context->redundantCopy) + { + writeRamBuffer[CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET_U32] = (uint32) + CalcChecksum((uint8 *) &writeRamBuffer[CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32], + CY_EM_EEPROM_EEPROM_DATA_LEN); + } + + for(i = 0u; i < numWrites; i++) + { + #if (CY_PSOC6) + /* For PSoC 6 the erase operation moves backwards. From last written row + * identified by "seqNum" down to "seqNum" - "numWrites". If "emEepromRowAddr" + * is zero this means that the row identified by "seqNum" was previously + * erased. + */ + if(0u != emEepromRowAddr) + { + ret = EraseRow(emEepromRowAddr, (uint32)writeRamBuffer, context); + } + + seqNum--; + + if(0u == seqNum) + { + /* Exit the loop as there is no more row is EEPROM to be erased */ + break; + } + emEepromRowAddr = GetRowAddrBySeqNum(seqNum, context); + #else + seqNum = CY_EM_EEPROM_GET_SEQ_NUM(emEepromRowAddr); + /* Get the address of the row to be erased. "emEepromRowAddr" may be updated + * with the proper address (if wear leveling is used). + */ + GetNextRowToWrite(seqNum, &emEepromRowAddr, &emEepromRowRdAddr, context); + seqNum++; + writeRamBuffer[0u] = seqNum; + ret = EraseRow(emEepromRowAddr, (uint32)writeRamBuffer, context); + #endif /* (CY_PSOC6) */ + } + + #if (CY_PSOC6) + if(CY_EM_EEPROM_SUCCESS == ret) + { + writeRamBuffer[0u] = storedSeqNum; + + /* Write the previously stored sequence number to the flash row which would be + * written next if the erase wouldn't happen. In this case the write to + * redundant copy can be skipped as it does not add any value. + */ + ret = WriteRow(emEepromStoredRowAddr, writeRamBuffer, context); + + if(CY_EM_EEPROM_SUCCESS == ret) + { + context->lastWrRowAddr = emEepromStoredRowAddr; + } + } + #endif /* (CY_PSOC6) */ + + } + return(ret); +} + + +/******************************************************************************* +* Function Name: Cy_Em_EEPROM_NumWrites +****************************************************************************//** +* +* Returns the number of the EEPROM writes completed so far. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +* \return +* The number of writes performed to the EEPROM. +* +*******************************************************************************/ +uint32 Cy_Em_EEPROM_NumWrites(cy_stc_eeprom_context_t * context) +{ + return(CY_EM_EEPROM_GET_SEQ_NUM(context->lastWrRowAddr)); +} + +/** \} */ + +/** \cond INTERNAL */ + + +/******************************************************************************* +* Function Name: FindLastWrittenRow +****************************************************************************//** +* +* Performs a search of the last written row address of the EEPROM associated +* with the context structure. If there were no writes to the EEPROM the +* function returns the start address of the EEPROM. The row address is returned +* in the input parameter. +* +* \param lastWrRowPtr +* The pointer to a memory where the last written row will be returned. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +*******************************************************************************/ +static void FindLastWrittenRow(uint32 * lastWrRowPtr, cy_stc_eeprom_context_t * context) +{ + uint32 seqNum = 0u; + uint32 prevSeqNum = 0u; + uint32 numRows; + uint32 emEepromAddr = context->userFlashStartAddr; + + *lastWrRowPtr = emEepromAddr; + + for(numRows = 0u; numRows < (context->numberOfRows * context->wearLevelingFactor); numRows++) + { + seqNum = CY_EM_EEPROM_GET_SEQ_NUM(emEepromAddr); + if((0u != seqNum) && (seqNum > prevSeqNum)) + { + /* Some record in EEPROM was found. Store found sequence + * number and row address. + */ + prevSeqNum = seqNum; + *lastWrRowPtr = emEepromAddr; + } + + /* Switch to the next row */ + emEepromAddr = emEepromAddr + CY_EM_EEPROM_FLASH_SIZEOF_ROW; + } +} + + +/******************************************************************************* +* Function Name: GetRowAddrBySeqNum +****************************************************************************//** +* +* Returns the address of the row in EEPROM using its sequence number. +* +* \param seqNum +* The sequence number of the row. +* +* \param context +* The pointer to the EEPROM context structure. +* +* \return +* The address of the row or zero if the row with the sequence number was not +* found. +* +*******************************************************************************/ +static uint32 GetRowAddrBySeqNum(uint32 seqNum, cy_stc_eeprom_context_t * context) +{ + uint32 emEepromAddr = context->userFlashStartAddr; + + while(CY_EM_EEPROM_GET_SEQ_NUM(emEepromAddr) != seqNum) + { + /* Switch to the next row */ + emEepromAddr = emEepromAddr + CY_EM_EEPROM_FLASH_SIZEOF_ROW; + + if (CY_EM_EEPROM_ADDR_IN_RANGE != + CY_EM_EEPROM_IS_ADDR_EXCEED_RANGE(emEepromAddr, context->wlEndAddr)) + { + emEepromAddr = 0u; + /* Exit the loop as we reached the end of EEPROM */ + break; + } + } + + return (emEepromAddr); +} + + +/******************************************************************************* +* Function Name: GetNextRowToWrite +****************************************************************************//** +* +* Performs a range check of the row that should be written and updates the +* address to the row respectively. The similar actions are done for the read +* address. +* +* \param seqNum +* The sequence number of the last written row. +* +* \param rowToWrPtr +* The address of the last written row (input). The address of the row to be +* written (output). +* +* \param rowToRdPtr +* The address of the row from which the data should be read into the RAM buffer +* in a later write operation. Out parameter. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +*******************************************************************************/ +static void GetNextRowToWrite(uint32 seqNum, + uint32 * rowToWrPtr, + uint32 * rowToRdPtr, + cy_stc_eeprom_context_t * context) +{ + /* Switch to the next row to be written if the current sequence number is + * not zero. + */ + if(0u != seqNum) + { + *rowToWrPtr = (*rowToWrPtr + CY_EM_EEPROM_FLASH_SIZEOF_ROW); + } + + /* If the resulting row address is out of EEPROM, then switch to the base + * EEPROM address (Row#0). + */ + if(CY_EM_EEPROM_ADDR_IN_RANGE != + CY_EM_EEPROM_IS_ADDR_EXCEED_RANGE(*rowToWrPtr, context->wlEndAddr)) + { + *rowToWrPtr = context->userFlashStartAddr; + } + + *rowToRdPtr = 0u; + + /* Check if the sequence number is larger than the number of rows in the EEPROM. + * If not, do not update the row read address because there is no historic + * data to be read. + */ + if(context->numberOfRows <= seqNum) + { + /* Check if wear leveling is used in EEPROM */ + if(context->wearLevelingFactor > 1u) + { + /* The read row address should be taken from an EEPROM copy that became + * inactive recently. This condition check handles that. + */ + if((*rowToWrPtr - (context->numberOfRows * CY_EM_EEPROM_FLASH_SIZEOF_ROW)) < + context->userFlashStartAddr) + { + *rowToRdPtr = context->userFlashStartAddr + + (context->numberOfRows * (context->wearLevelingFactor - 1u) * + CY_EM_EEPROM_FLASH_SIZEOF_ROW) + (*rowToWrPtr - context->userFlashStartAddr); + } + else + { + *rowToRdPtr = *rowToWrPtr - (context->numberOfRows * CY_EM_EEPROM_FLASH_SIZEOF_ROW); + } + } + else + { + /* If no wear leveling, always read from the same flash row that + * should be written. + */ + *rowToRdPtr = *rowToWrPtr; + } + } +} + + +/******************************************************************************* +* Function Name: CalcChecksum +****************************************************************************//** +* +* Implements CRC-8 that is used in checksum calculation for the redundant copy +* algorithm. +* +* \param rowData +* The row data to be used to calculate the checksum. +* +* \param len +* The length of rowData. +* +* \return +* The calculated value of CRC-8. +* +*******************************************************************************/ +static uint8 CalcChecksum(uint8 rowData[], uint32 len) +{ + uint8 crc = CY_EM_EEPROM_CRC8_SEED; + uint8 i; + uint16 cnt = 0u; + + while(cnt != len) + { + crc ^= rowData[cnt]; + for (i = 0u; i < CY_EM_EEPROM_CRC8_POLYNOM_LEN; i++) + { + crc = CY_EM_EEPROM_CALCULATE_CRC8(crc); + } + cnt++; + } + + return (crc); +} + + +/******************************************************************************* +* Function Name: CheckRanges +****************************************************************************//** +* +* Checks if the EEPROM of the requested size can be placed in flash. +* +* \param config +* The pointer to a configuration structure. See \ref cy_stc_eeprom_config_t. +* +* \return +* error / status code. See \ref cy_en_em_eeprom_status_t. +* +*******************************************************************************/ +static cy_en_em_eeprom_status_t CheckRanges(cy_stc_eeprom_config_t* config) +{ + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_DATA; + uint32 startAddr = config->userFlashStartAddr; + uint32 endAddr = startAddr + CY_EM_EEPROM_GET_PHYSICAL_SIZE(config->eepromSize, + config->wearLevelingFactor, config->redundantCopy); + + /* Range check if there is enough flash for EEPROM */ + if (CY_EM_EEPROM_IS_IN_FLASH_RANGE(startAddr, endAddr)) + { + ret = CY_EM_EEPROM_SUCCESS; + } + return (ret); +} + + +/******************************************************************************* +* Function Name: WriteRow +****************************************************************************//** +* +* Writes one flash row starting from the specified row address. +* +* \param rowAdd +* The address of the flash row. +* +* \param rowData +* The pointer to the data to be written to the row. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +* \return +* error / status code. See \ref cy_en_em_eeprom_status_t. +* +*******************************************************************************/ +static cy_en_em_eeprom_status_t WriteRow(uint32 rowAddr, + uint32 *rowData, + cy_stc_eeprom_context_t * context) +{ + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_WRITE_FAIL; +#if (!CY_PSOC6) + cystatus rc; + uint32 rowId; + #if ((CY_PSOC3) || (CY_PSOC5)) + uint32 arrayId; + #endif /* (CY_PSOC3) */ + + #if (CY_PSOC3) + rowAddr &= CY_EM_EEPROM_CODE_ADDR_MASK; + context = context; /* To avoid compiler warning generation */ + #else + (void)context; /* To avoid compiler warning generation */ + #endif /* ((CY_PSOC3) */ + + /* For non-PSoC 6 devices, the Array ID and Row ID needed to write the row */ + rowId = (rowAddr / CY_EM_EEPROM_FLASH_SIZEOF_ROW) % CY_EM_EEPROM_ROWS_IN_ARRAY; + + /* Write the flash row */ + #if (CY_PSOC4) + rc = CySysFlashWriteRow(rowId, (uint8 *)rowData); + #else + + #ifndef CY_EM_EEPROM_SKIP_TEMP_MEASUREMENT + (void)CySetTemp(); + #endif /* (CY_EM_EEPROM_SKIP_TEMP_MEASUREMENT) */ + + arrayId = rowAddr / CY_FLASH_SIZEOF_ARRAY; + rc = CyWriteRowData((uint8)arrayId, (uint16)rowId, (uint8 *)rowData); + + #if (CY_PSOC5) + CyFlushCache(); + #endif /* (CY_PSOC5) */ + #endif /* (CY_PSOC4) */ + + if(CYRET_SUCCESS == rc) + { + ret = CY_EM_EEPROM_SUCCESS; + } +#else /* PSoC 6 */ + if(0u != context->blockingWrite) + { + /* Do blocking write */ + if(CY_FLASH_DRV_SUCCESS == Cy_Flash_WriteRow(rowAddr, (const uint32 *)rowData)) + { + ret = CY_EM_EEPROM_SUCCESS; + } + } + else + { + /* Initiate write */ + if(CY_FLASH_DRV_OPERATION_STARTED == Cy_Flash_StartWrite(rowAddr, (const uint32 *)rowData)) + { + uint32 countMs = CY_EM_EEPROM_MAX_WRITE_DURATION_MS; + cy_en_flashdrv_status_t rc; + + do + { + CyDelay(1u); /* Wait 1ms */ + rc = Cy_Flash_IsWriteComplete(); /* Check if write completed */ + countMs--; + } + while ((rc == CY_FLASH_DRV_OPCODE_BUSY) && (0u != countMs)); + + if(CY_FLASH_DRV_SUCCESS == rc) + { + ret = CY_EM_EEPROM_SUCCESS; + } + } + } +#endif /* (CY_PSOC6) */ + + return (ret); +} + + +/******************************************************************************* +* Function Name: EraseRow +****************************************************************************//** +* +* Erases one flash row starting from the specified row address. If the redundant +* copy option is enabled the corresponding row in the redundant copy will also +* be erased. +* +* \param rowAdd +* The address of the flash row. +* +* \param ramBuffAddr +* The address of the RAM buffer that contains zeroed data (used only for +* non-PSoC 6 devices). +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +* \return +* error / status code. See \ref cy_en_em_eeprom_status_t. +* +*******************************************************************************/ +static cy_en_em_eeprom_status_t EraseRow(uint32 rowAddr, + uint32 ramBuffAddr, + cy_stc_eeprom_context_t * context) +{ + uint32 emEepromRowAddr = rowAddr; + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_WRITE_FAIL; +#if (CY_PSOC6) + uint32 i = 1u; + + (void)ramBuffAddr; /* To avoid compiler warning */ + + if(0u != context->redundantCopy) + { + i++; + } + + do + { + if(0u != context->blockingWrite) + { + /* Erase the flash row */ + if(CY_FLASH_DRV_SUCCESS == Cy_Flash_EraseRow(emEepromRowAddr)) + { + ret = CY_EM_EEPROM_SUCCESS; + } + } + else + { + /* Initiate erase */ + if(CY_FLASH_DRV_OPERATION_STARTED == Cy_Flash_StartErase(emEepromRowAddr)) + { + uint32 countMs = CY_EM_EEPROM_MAX_WRITE_DURATION_MS; + cy_en_flashdrv_status_t rc; + + do + { + CyDelay(1u); /* Wait 1ms */ + rc = Cy_Flash_IsWriteComplete(); /* Check if erase completed */ + countMs--; + } + while ((rc == CY_FLASH_DRV_OPCODE_BUSY) && (0u != countMs)); + + if(CY_FLASH_DRV_SUCCESS == rc) + { + ret = CY_EM_EEPROM_SUCCESS; + } + } + } + + if(CY_EM_EEPROM_SUCCESS == ret) + { + /* Update the address to point to the redundant copy row */ + emEepromRowAddr = (emEepromRowAddr - context->userFlashStartAddr) + context->wlEndAddr; + } + else + { + break; + } + i--; + } while (0u != i); +#else + /* Write the data to the specified flash row */ + ret = WriteRow(emEepromRowAddr, (uint32 *)ramBuffAddr, context); + + if((CY_EM_EEPROM_SUCCESS == ret) && (0u != context->redundantCopy)) + { + /* Update the address to point to the redundant copy row */ + emEepromRowAddr = (emEepromRowAddr - context->userFlashStartAddr) + context->wlEndAddr; + ret = WriteRow(emEepromRowAddr, (uint32 *)ramBuffAddr, context); + } + + if(CY_EM_EEPROM_SUCCESS == ret) + { + context->lastWrRowAddr = rowAddr; + } +#endif /* (CY_PSOC6) */ + + return(ret); +} + + +/******************************************************************************* +* Function Name: CheckCrcAndCopy +****************************************************************************//** +* +* Checks the checksum of the specific row in EEPROM. If the CRC matches - copies +* the data to the "datAddr" from EEPROM. f the CRC does not match checks the +* CRC of the corresponding row in the EEPROM's redundant copy. If the CRC +* matches - copies the data to the "datAddr" from EEPROM redundant copy. If the +* CRC of the redundant copy does not match - returns bad checksum. +* +* \param startAddr +* The address that points to the start of the specified row. +* +* \param datAddr +* The start address of where the row data will be copied if the CRC check +* will succeed. +* +* \param rowOffset +* The offset in the row from which the data should be copied. +* +* \param numBytes +* The number of bytes to be copied. +* +* \param context +* The pointer to the EEPROM context structure \ref cy_stc_eeprom_context_t. +* +* \return +* error / status code. See \ref cy_en_em_eeprom_status_t. +* +* \note +* This function uses a buffer of the flash row size to perform read +* operation. For the size of the row refer to the specific PSoC device +* datasheet. +* +*******************************************************************************/ +static cy_en_em_eeprom_status_t CheckCrcAndCopy(uint32 startAddr, + uint32 dstAddr, + uint32 rowOffset, + uint32 numBytes, + cy_stc_eeprom_context_t * context) +{ + cy_en_em_eeprom_status_t ret; + uint32 writeRamBuffer[CY_EM_EEPROM_FLASH_SIZEOF_ROW / CY_EM_EEPROM_U32_DIV]; + + /* Calculate the row address in the EEPROM's redundant copy */ + uint32 rcStartRowAddr = (startAddr - context->userFlashStartAddr) + context->wlEndAddr; + + /* Check the row data CRC in the EEPROM */ + if((*(uint32 *)(startAddr + CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET)) == + ((uint32) CalcChecksum((uint8 *)(startAddr + CY_EM_EEPROM_EEPROM_DATA_OFFSET), + CY_EM_EEPROM_EEPROM_DATA_LEN))) + { + (void)memcpy((void *)(dstAddr), (void *)(startAddr + rowOffset), numBytes); + + ret = CY_EM_EEPROM_SUCCESS; + } + /* Check the row data CRC in the EEPROM's redundant copy */ + else if((*(uint32 *)(rcStartRowAddr + CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET)) == + ((uint32) CalcChecksum((uint8 *)(rcStartRowAddr + CY_EM_EEPROM_EEPROM_DATA_OFFSET), + CY_EM_EEPROM_EEPROM_DATA_LEN))) + { + /* Copy the redundant copy row to RAM buffer to avoid read while write (RWW) + * flash exception. The RWW occurs while trying to write and read the data from + * same flash macro. + */ + (void)memcpy((void *)(writeRamBuffer), (void *)(rcStartRowAddr), CY_EM_EEPROM_FLASH_SIZEOF_ROW); + + /* Restore bad row data from the RAM buffer */ + ret = WriteRow(startAddr, (uint32 *)writeRamBuffer, context); + + if(CY_EM_EEPROM_SUCCESS == ret) + { + (void)memcpy((void *)(dstAddr), (void *)(writeRamBuffer + rowOffset), numBytes); + } + } + else + { + ret = CY_EM_EEPROM_BAD_CHECKSUM; + } + + return(ret); +} + + +/******************************************************************************* +* Function Name: GetAddresses +****************************************************************************//** +* +* Calculates the start and end address of the row's EEPROM data to be updated. +* The start and end are not absolute addresses but a relative addresses in a +* flash row. +* +* \param startAddr +* The pointer the address where the EEPROM data start address will be returned. +* +* \param endAddr +* The pointer the address where the EEPROM data end address will be returned. +* +* \param offset +* The pointer the address where the calculated offset of the EEPROM header data +* will be returned. +* +* \param rowNum +* The row number that is about to be written. +* +* \param addr +* The address of the EEPROM header data in the currently analyzed row that may +* concern to the row about to be written. +* +* \param len +* The length of the EEPROM header data in the currently analyzed row that may +* concern to the row about to be written. +* +* \return +* Zero indicates that the currently analyzed row has the data to be written to +* the active EEPROM row data locations. Non zero value indicates that there is +* no data to be written +* +*******************************************************************************/ +static uint32 GetAddresses(uint32 *startAddr, + uint32 *endAddr, + uint32 *offset, + uint32 rowNum, + uint32 addr, + uint32 len) +{ + uint32 skip = 0u; + + *offset =0u; + + if(0u != CY_EM_EEPROM_IS_ADDR_IN_ROW_RANGE(addr, rowNum)) + { + *startAddr = CY_EM_EEPROM_EEPROM_DATA_LEN + (addr % CY_EM_EEPROM_EEPROM_DATA_LEN); + + if(0u != CY_EM_EEPROM_IS_ADDR_IN_ROW_RANGE(addr + len, rowNum)) + { + *endAddr = *startAddr + len; + } + else + { + *endAddr = CY_EM_EEPROM_FLASH_SIZEOF_ROW; + } + } + else + { + + if(0u != CY_EM_EEPROM_IS_ADDR_IN_ROW_RANGE(addr + len, rowNum)) + { + *startAddr = CY_EM_EEPROM_EEPROM_DATA_LEN; + *endAddr = (*startAddr + len) - (*startAddr - (addr % CY_EM_EEPROM_EEPROM_DATA_LEN)); + *offset = len - (*endAddr - *startAddr); + } + else + { + skip++; + } + } + + return (skip); +} + + +/******************************************************************************* +* Function Name: FillChecksum +****************************************************************************//** +* +* Performs calculation of the checksum on each row in the Em_EEPROM and fills +* the Em_EEPROM headers checksum field with the calculated checksums. +* +* \param context +* The pointer to the EEPROM context structure. +* +* \return +* error / status code. See \ref cy_en_em_eeprom_status_t. +* +* \theory +* In case if redundant copy option is used the Em_EEPROM would return bad +* checksum while trying to read the EEPROM rows which were not yet written by +* the user. E.g. any read after device reprogramming without previous Write() +* operation to the EEPROM would fail. This would happen because the Em_EEPROM +* headers checksum field values (which is zero at the moment) would not be +* equal to the actual data checksum. This function allows to avoid read failure +* after device reprogramming. +* +* \note +* This function uses a buffer of the flash row size to perform read +* operation. For the size of the row refer to the specific PSoC device +* datasheet. +* +*******************************************************************************/ +static cy_en_em_eeprom_status_t FillChecksum(cy_stc_eeprom_context_t * context) +{ + uint32 i; + uint32 rdAddr; + uint32 writeRamBuffer[CY_EM_EEPROM_FLASH_SIZEOF_ROW / CY_EM_EEPROM_U32_DIV]; + uint32 wrAddr = context->lastWrRowAddr; + uint32 tmpRowAddr; + /* Get the sequence number (number of writes) */ + uint32 seqNum = CY_EM_EEPROM_GET_SEQ_NUM(wrAddr); + cy_en_em_eeprom_status_t ret = CY_EM_EEPROM_BAD_PARAM; + + for(i = 0u; i < (context->numberOfRows * context->wearLevelingFactor); i++) + { + /* Copy the EEPROM row from Flash to RAM */ + (void)memcpy((void *)&writeRamBuffer[0u], (void *)(wrAddr), CY_EM_EEPROM_FLASH_SIZEOF_ROW); + + /* Increment the sequence number */ + seqNum++; + writeRamBuffer[CY_EM_EEPROM_HEADER_SEQ_NUM_OFFSET_U32] = seqNum; + + /* Calculate and fill the checksum to the Em_EEPROM header */ + writeRamBuffer[CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET_U32] = (uint32) + CalcChecksum((uint8 *) &writeRamBuffer[CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32], + CY_EM_EEPROM_EEPROM_DATA_LEN); + + /* Write the data to the specified flash row */ + ret = WriteRow(wrAddr, writeRamBuffer, context); + + /* Update the row address to point to the relevant row in the redundant + * EEPROM's copy. + */ + tmpRowAddr = (wrAddr - context->userFlashStartAddr) + context->wlEndAddr; + + /* Write the data to the specified flash row */ + ret = WriteRow(tmpRowAddr, writeRamBuffer, context); + + /* Get the address of the next row to be written. + * "rdAddr" is not used in this function but provided to prevent NULL + * pointer exception in GetNextRowToWrite(). + */ + GetNextRowToWrite(seqNum, &wrAddr, &rdAddr, context); + } + + return(ret); +} + +/** \endcond */ + +#if defined(__cplusplus) +} +#endif + +/* [] END OF FILE */ diff --git a/software/SCSI2SD/v5.1/SCSI2SD.cydsn/Generated_Source/PSoC5/cy_em_eeprom.h b/software/SCSI2SD/v5.1/SCSI2SD.cydsn/Generated_Source/PSoC5/cy_em_eeprom.h new file mode 100755 index 0000000..4aef67b --- /dev/null +++ b/software/SCSI2SD/v5.1/SCSI2SD.cydsn/Generated_Source/PSoC5/cy_em_eeprom.h @@ -0,0 +1,556 @@ +/******************************************************************************* +* \file cy_em_eeprom.h +* \version 2.0 +* +* \brief +* This file provides the function prototypes and constants for the Emulated +* EEPROM middleware library. +* +******************************************************************************** +* Copyright 2017, Cypress Semiconductor Corporation. All rights reserved. +* You may use this file only in accordance with the license, terms, conditions, +* disclaimers, and limitations in the end user license agreement accompanying +* the software package with which this file was provided. +*******************************************************************************/ + +/** + * \mainpage Cypress Em_EEPROM Middleware Library + * + * The Emulated EEPROM provides an API that allows creating an emulated + * EEPROM in flash that has the ability to do wear leveling and restore + * corrupted data from a redundant copy. The Emulated EEPROM library is designed + * to be used with the Em_EEPROM component. + * + * The Cy_Em_EEPROM API is described in the following sections: + * - \ref group_em_eeprom_macros + * - \ref group_em_eeprom_data_structures + * - \ref group_em_eeprom_enums + * - \ref group_em_eeprom_functions + * + * Features: + * * EEPROM-Like Non-Volatile Storage + * * Easy to use Read and Write API + * * Optional Wear Leveling + * * Optional Redundant Data storage + * + * \section group_em_eeprom_configuration Configuration Considerations + * + * The Em_EEPROM operates on the top of the flash driver. The flash driver has + * some prerequisites for proper operation. Refer to the "Flash System + * Routine (Flash)" section of the PDL API Reference Manual. + * + * Initializing Emulated EEPROM in User flash + * + * To initialize an Emulated EEPROM in the User flash, the EEPROM storage should + * be declared by the user. For the proper operation, the EEPROM storage should + * be aligned to the size of the flash row. An example of the EEPROM storage + * declaration is below (applicable for GCC and MDK compilers): + * + * CY_ALIGN(CY_EM_EEPROM_FLASH_SIZEOF_ROW) + * const uint8 emEeprom[Em_EEPROM_PHYSICAL_SIZE] = {0u}; + * + * The same declaration for the IAR compiler: + * + * #pragma data_alignment = CY_EM_EEPROM_FLASH_SIZEOF_ROW + * const uint8 emEeprom[Em_EEPROM_PHYSICAL_SIZE] = {0u}; + * + * Note that the name "emEeprom" is shown for reference. Any other name can be + * used instead. Also, note that the Em_EEPROM_PHYSICAL_SIZE constant is + * generated by the PSoC Creator Em_EEPROM component and so it is instance name + * dependent and its prefix should be changed when the name of the component + * changes. If the The Cy_Em_EEPROM middleware library is used without the + * Em_EEPROM component, the user has to provide a proper size for the EEPROM + * storage instead of Em_EEPROM_PHYSICAL_SIZE. The size of the EEPROM storage + * can be calculated using the following equation: + * + * Physical size = EEPROM data size * 2 * wear leveling * (1 + redundant copy) + * + * where, + * "EEPROM data size" - the size of data the user wants to store in the + * EEPROM. The data size must divide evenly to the half of the flash row size. + * "wear leveling" - the wear leveling factor (1-10). + * "redundant copy" - "zero" if a redundant copy is not used, and "one" + * otherwise. + * + * The start address of the storage should be filled to the Emulated EEPROM + * configuration structure and then passed to the Cy_Em_EEPROM_Init(). + * If the Em_EEPROM component is used, the config (Em_EEPROM_config) and + * context structures (Em_EEPROM_context) are defined by the component, so the + * user may just use that structures otherwise both of the structures need to + * be provided by the user. Note that if the "Config Data in Flash" + * option is selected in the component, then the configuration structure should + * be copied to RAM to allow EEPROM storage start address update. The following + * code demonstrates utilization of "Em_EEPROM_config" and "Em_EEPROM_context" + * Em_EEPROM component structures for Cy_Em_EEPROM middleware library + * initialization: + * + * cy_en_em_eeprom_status_t retValue; + * cy_stc_eeprom_config_t config; + * + * memcpy((void *)&config, + (void *)&Em_EEPROM_config, + sizeof(cy_stc_eeprom_config_t)); + * config.userFlashStartAddr = (uint32)emEeprom; + * retValue = Cy_Em_EEPROM_Init(&config, &Em_EEPROM_context); + * + * Initializing EEPROM in Emulated EEPROM flash area + * + * Initializing of the EEPROM storage in the Emulated EEPROM flash area is + * identical to initializing of the EEPROM storage in the User flash with one + * difference. The location of the Emulated EEPROM storage should be specified + * somewhere in the EmulatedEEPROM flash area. If the Em_EEPROM component is + * utilized in the project, then the respective storage + * (Em_EEPROM_em_EepromStorage[]) is automatically declared by the component + * if the "Use Emulated EEPROM" option is set to "Yes". The user just needs to + * fill the start address of the storage to the config structure. If the + * Em_EEPROM component is not used, the user needs to declare the storage + * in the Emulated EEPROM flash area. An example of such declaration is + * following (applicable for GCC and MDK compilers): + * + * CY_SECTION(".cy_em_eeprom") CY_ALIGN(CY_EM_EEPROM_FLASH_SIZEOF_ROW) + * const uint8_t emEeprom[Em_EEPROM_PHYSICAL_SIZE] = {0u}; + * + * The same declaration for the IAR compiler: + * + * #pragma location = ".cy_em_eeprom" + * #pragma data_alignment = CY_EM_EEPROM_FLASH_SIZEOF_ROW + * const uint8 emEeprom[Em_EEPROM_PHYSICAL_SIZE] = {0u}; + * + * where, + * Em_EEPROM_PHYSICAL_SIZE - is a constant that is generated by the Em_EEPROM + * component when the component is utilized in the project or it should be + * provided by the user. The equation for the calculation of the constant is + * shown above. + * + * Note that the size of the Emulated EEPROM flash area is limited. Refer to the + * specific device datasheet for the value of the available EEPROM Emulation + * area. + * + * \section group_em_eeprom_more_information More Information + * See the Em_EEPROM Component datasheet. + * + * + * \section group_em_eeprom_MISRA MISRA-C Compliance + * + * The Cy_Em_EEPROM library has the following specific deviations: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
MISRA RuleRule Class (Required/Advisory)Rule DescriptionDescription of Deviation(s)
11.4AThe cast should not be performed between a pointer to the object type + * and a different pointer to the object type.The cast from the object type and a different pointer to the object + * was used intentionally because of the performance reasons.
14.2RAll non-null statements shall either have at least one side-effect, + * however executed, or cause control flow to change.To maintain common codebase, some variables, unused for a specific + * device, are casted to void to prevent generation of an unused variable + * compiler warning.
16.7AThe object addressed by the pointer parameter is not modified and so + * the pointer could be of type 'pointer to const'.The warning is generated because of the pointer dereferencing to + * address which makes the MISRA checker think the data is not + * modified.
17.4RThe array indexing shall be the only allowed form of pointer + * arithmetic.The pointer arithmetic used in several places on the Cy_Em_EEPROM + * implementation is safe and preferred because it increases the code + * flexibility.
19.7AA function shall be used in preference to a function-like macro.Macro is used because of performance reasons.
+ * + * \section group_em_eeprom_changelog Changelog + * + * + * + * + * + * + * + *
VersionChangesReason for Change
1.0Initial Version
+ * + * \defgroup group_em_eeprom_macros Macros + * \brief + * This section describes the Emulated EEPROM Macros. + * + * \defgroup group_em_eeprom_functions Functions + * \brief + * This section describes the Emulated EEPROM Function Prototypes. + * + * \defgroup group_em_eeprom_data_structures Data Structures + * \brief + * Describes the data structures defined by the Emulated EEPROM. + * + * \defgroup group_em_eeprom_enums Enumerated types + * \brief + * Describes the enumeration types defined by the Emulated EEPROM. + * + */ + + +#if !defined(CY_EM_EEPROM_H) +#define CY_EM_EEPROM_H + +#include "cytypes.h" +#include +#if (CYDEV_CHIP_FAMILY_USED == CYDEV_CHIP_FAMILY_PSOC6) + #include + #include "syslib/cy_syslib.h" + #include "flash/cy_flash.h" +#else + #include "CyFlash.h" + #include +#endif /* (CYDEV_CHIP_FAMILY_USED == CYDEV_CHIP_FAMILY_PSOC6) */ + +/* The C binding of definitions if building with the C++ compiler */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/*************************************** +* Conditional Compilation Parameters +***************************************/ +#define CY_PSOC6 (CYDEV_CHIP_FAMILY_USED == CYDEV_CHIP_FAMILY_PSOC6) + + +/*************************************** +* Data Structure definitions +***************************************/ +/** +* \addtogroup group_em_eeprom_data_structures +* \{ +*/ + +/** EEPROM configuration structure */ +typedef struct +{ + /** The number of bytes to store in EEPROM */ + uint32 eepromSize; + + /** The amount of wear leveling from 1 to 10. 1 means no wear leveling + * is used. + */ + uint32 wearLevelingFactor; + + /** If not zero, a redundant copy of the Em_EEPROM is included. */ + uint8 redundantCopy; + + /** If not zero, a blocking write to flash is used. Otherwise non-blocking + * write is used. This parameter is used only for PSoC 6. + */ + uint8 blockingWrite; + + /** The start address for the EEPROM memory in the user's flash. */ + uint32 userFlashStartAddr; +} cy_stc_eeprom_config_t; + +/** \} group_em_eeprom_data_structures */ + +/** The EEPROM context data structure. It is used to store the specific +* EEPROM context data. +*/ +typedef struct +{ + /** The pointer to the end address of EEPROM including wear leveling overhead + * and excluding redundant copy overhead. + */ + uint32 wlEndAddr; + + /** The number of flash rows allocated for the EEPROM excluding the number of + * rows allocated for wear leveling and redundant copy overhead. + */ + uint32 numberOfRows; + + /** The address of the last written EEPROM row */ + uint32 lastWrRowAddr; + + /** The number of bytes to store in EEPROM */ + uint32 eepromSize; + + /** The amount of wear leveling from 1 to 10. 1 means no wear leveling + * is used. + */ + uint32 wearLevelingFactor; + + /** If not zero, a redundant copy of the Em_EEPROM is included. */ + uint8 redundantCopy; + + /** If not zero, a blocking write to flash is used. Otherwise non-blocking + * write is used. This parameter is used only for PSoC 6. + */ + uint8 blockingWrite; + + /** The start address for the EEPROM memory in the user's flash. */ + uint32 userFlashStartAddr; +} cy_stc_eeprom_context_t; + +#if (CY_PSOC6) + + #define CY_EM_EEPROM_ID (CY_PDL_DRV_ID(0x1BuL)) /**< Em_EEPROM PDL ID */ + /** + * \addtogroup group_em_eeprom_enums + * \{ + * Specifies return values meaning. + */ + /** A prefix for EEPROM function error return-values */ + #define CY_EM_EEPROM_ID_ERROR (uint32_t)(CY_EM_EEPROM_ID | CY_PDL_STATUS_ERROR) + +#else + + /** A prefix for EEPROM function status codes. For non-PSoC6 devices, + * prefix is zero. + */ + #define CY_EM_EEPROM_ID_ERROR (0uL) + +#endif /* (CY_PSOC6) */ + + +/*************************************** +* Enumerated Types and Parameters +***************************************/ + +/** EEPROM return enumeration type */ +typedef enum +{ + CY_EM_EEPROM_SUCCESS = 0x00uL, /**< The function executed successfully */ + CY_EM_EEPROM_BAD_PARAM = (CY_EM_EEPROM_ID_ERROR + 1uL), /**< The input parameter is invalid */ + CY_EM_EEPROM_BAD_CHECKSUM = (CY_EM_EEPROM_ID_ERROR + 2uL), /**< The data in EEPROM is corrupted */ + CY_EM_EEPROM_BAD_DATA = (CY_EM_EEPROM_ID_ERROR + 3uL), /**< Failed to place the EEPROM in flash */ + CY_EM_EEPROM_WRITE_FAIL = (CY_EM_EEPROM_ID_ERROR + 4uL) /**< Write to EEPROM failed */ +} cy_en_em_eeprom_status_t; + +/** \} group_em_eeprom_enums */ + + +/*************************************** +* Function Prototypes +***************************************/ + +/** +* \addtogroup group_em_eeprom_functions +* \{ +*/ +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Init(cy_stc_eeprom_config_t* config, cy_stc_eeprom_context_t * context); +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Read(uint32 addr, + void * eepromData, + uint32 size, + cy_stc_eeprom_context_t * context); +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Write(uint32 addr, + void * eepromData, + uint32 size, + cy_stc_eeprom_context_t * context); +cy_en_em_eeprom_status_t Cy_Em_EEPROM_Erase(cy_stc_eeprom_context_t * context); +uint32 Cy_Em_EEPROM_NumWrites(cy_stc_eeprom_context_t * context); +/** \} group_em_eeprom_functions */ + + +/*************************************** +* API Constants +***************************************/ +/** +* \addtogroup group_em_eeprom_macros +* \{ +*/ +/** Library major version */ +#define CY_EM_EEPROM_VERSION_MAJOR (2) + +/** Library minor version */ +#define CY_EM_EEPROM_VERSION_MINOR (0) + +/** Defines the maximum data length that can be stored in one flash row */ +#define CY_EM_EEPROM_EEPROM_DATA_LEN (CY_EM_EEPROM_FLASH_SIZEOF_ROW / 2u) + +/** \} group_em_eeprom_macros */ + + +/*************************************** +* Macro definitions +***************************************/ +/** \cond INTERNAL */ + +/* Defines the size of flash row */ +#define CY_EM_EEPROM_FLASH_SIZEOF_ROW (CY_FLASH_SIZEOF_ROW) + +/* Device specific flash constants */ +#if (!CY_PSOC6) + #define CY_EM_EEPROM_FLASH_BASE_ADDR (CYDEV_FLASH_BASE) + #define CY_EM_EEPROM_FLASH_SIZE (CYDEV_FLASH_SIZE) + #define CY_EM_EEPROM_ROWS_IN_ARRAY (CY_FLASH_SIZEOF_ARRAY / CY_EM_EEPROM_FLASH_SIZEOF_ROW) + #if (CY_PSOC3) + #define CY_EM_EEPROM_CODE_MEM_CLASS_PREFIX (0xff0000uL) + #define CY_EM_EEPROM_CODE_ADDR_END \ + (CY_EM_EEPROM_CODE_MEM_CLASS_PREFIX + (CY_EM_EEPROM_FLASH_SIZE - 1u)) + #define CY_EM_EEPROM_CODE_ADDR_MASK (0xffffu) + /* Checks if the EEPROM is in flash range */ + #define CY_EM_EEPROM_IS_IN_FLASH_RANGE(startAddr, endAddr) \ + (((startAddr) > CY_EM_EEPROM_CODE_MEM_CLASS_PREFIX) && \ + ((endAddr) <= CY_EM_EEPROM_CODE_ADDR_END)) + #else + /* Checks is the EEPROM is in flash range */ + #define CY_EM_EEPROM_IS_IN_FLASH_RANGE(startAddr, endAddr) \ + (((startAddr) > CY_EM_EEPROM_FLASH_BASE_ADDR) && ((endAddr) <= CY_EM_EEPROM_FLASH_END_ADDR)) + #endif /* (CY_PSOC3) */ +#else + #define CY_EM_EEPROM_FLASH_BASE_ADDR (CY_FLASH_BASE) + #define CY_EM_EEPROM_FLASH_SIZE (CY_FLASH_SIZE) + #define CY_EM_EEPROM_EM_EEPROM_BASE_ADDR (CY_EM_EEPROM_BASE) + #define CY_EM_EEPROM_EM_EEPROM_SIZE (CY_EM_EEPROM_SIZE) + #define CY_EM_EEPROM_EM_EEPROM_END_ADDR (CY_EM_EEPROM_EM_EEPROM_BASE_ADDR + CY_EM_EEPROM_EM_EEPROM_SIZE) + /* Checks is the EEPROM is in flash range */ + #define CY_EM_EEPROM_IS_IN_FLASH_RANGE(startAddr, endAddr) \ + (((((startAddr) > CY_EM_EEPROM_FLASH_BASE_ADDR) && ((endAddr) <= CY_EM_EEPROM_FLASH_END_ADDR)) || \ + (((startAddr) >= CY_EM_EEPROM_EM_EEPROM_BASE_ADDR) && \ + ((endAddr) <= CY_EM_EEPROM_EM_EEPROM_END_ADDR)))) +#endif /* (!CY_PSOC6) */ + +#define CY_EM_EEPROM_FLASH_END_ADDR (CY_EM_EEPROM_FLASH_BASE_ADDR + CY_EM_EEPROM_FLASH_SIZE) + +/* Defines the length of EEPROM data that can be stored in Em_EEPROM header */ +#define CY_EM_EEPROM_HEADER_DATA_LEN ((CY_EM_EEPROM_FLASH_SIZEOF_ROW / 2u) - 16u) + +#define CY_EM_EEPROM_ADDR_IN_RANGE (1u) + +/* Return CY_EM_EEPROM_ADDR_IN_RANGE if addr exceeded the upper range of +* EEPROM. The wear leveling overhead is included in the range but redundant copy +* is excluded. +*/ +#define CY_EM_EEPROM_IS_ADDR_EXCEED_RANGE(addr, endEepromAddr) \ + (((addr) >= (endEepromAddr)) ? (0u) : (CY_EM_EEPROM_ADDR_IN_RANGE)) + +/* Check to see if the specified address is present in the EEPROM */ +#define CY_EM_EEPROM_IS_ADDR_IN_RANGE(addr, startEepromAddr, endEepromAddr) \ + (((addr) > (startEepromAddr)) ? \ + (((addr) < (endEepromAddr)) ? (CY_EM_EEPROM_ADDR_IN_RANGE) : (0u)) : (0u)) + +/* Check if the EEPROM address locations from startAddr1 to endAddr1 +* are crossed with EEPROM address locations from startAddr2 to endAddr2. +*/ +#define CY_EM_EEPROM_IS_ADDRESES_CROSSING(startAddr1, endAddr1 , startAddr2, endAddr2) \ + (((startAddr1) > (startAddr2)) ? (((startAddr1) >= (endAddr2)) ? (0u) : (1u) ) : \ + (((startAddr2) >= (endAddr1)) ? (0u) : (1u))) + +/* Return the pointer to the start of the redundant copy of the EEPROM */ +#define CY_EM_EEPROM_GET_REDNT_COPY_ADDR_BASE(numRows, wearLeveling, eepromStartAddr) \ + ((((numRows) * CY_EM_EEPROM_FLASH_SIZEOF_ROW) * (wearLeveling)) + (eepromStartAddr)) + +/* Return the number of the row in EM_EEPROM which contains an address defined by +* rowAddr. + */ +#define CY_EM_EEPROM_GET_ACT_ROW_NUM_FROM_ADDR(rowAddr, maxRows, eepromStartAddr) \ + ((((rowAddr) - (eepromStartAddr)) / CY_EM_EEPROM_FLASH_SIZEOF_ROW) % (maxRows)) + + +/** Returns the size allocated for the EEPROM excluding wear leveling and +* redundant copy overhead. +*/ +#define CY_EM_EEPROM_GET_EEPROM_SIZE(numRows) ((numRows) * CY_EM_EEPROM_FLASH_SIZEOF_ROW) + +/* Check if the given address belongs to the EEPROM address of the row +* specified by rowNum. +*/ +#define CY_EM_EEPROM_IS_ADDR_IN_ROW_RANGE(addr, rowNum) \ + (((addr) < ((rowNum) * (CY_EM_EEPROM_FLASH_SIZEOF_ROW / 2u))) ? (0u) : \ + (((addr) > ((((rowNum) + 1u) * (CY_EM_EEPROM_FLASH_SIZEOF_ROW / 2u)) - 1u)) ? \ + (0u) : (1u))) + +/* CRC-8 constants */ +#define CY_EM_EEPROM_CRC8_POLYNOM ((uint8)(0x31u)) +#define CY_EM_EEPROM_CRC8_POLYNOM_LEN (8u) +#define CY_EM_EEPROM_CRC8_SEED (0xFFu) +#define CY_EM_EEPROM_CRC8_XOR_VAL ((uint8) (0x80u)) + +#define CY_EM_EEPROM_CALCULATE_CRC8(crc) \ + ((CY_EM_EEPROM_CRC8_XOR_VAL == ((crc) & CY_EM_EEPROM_CRC8_XOR_VAL)) ? \ + ((uint8)(((uint8)((uint8)((crc) << 1u))) ^ CY_EM_EEPROM_CRC8_POLYNOM)) : ((uint8)((crc) << 1u))) + +#define CY_EM_EEPROM_GET_SEQ_NUM(addr) (*(uint32*)(addr)) + +/** \endcond */ + +/** +* \addtogroup group_em_eeprom_macros +* \{ +*/ + +/** Calculate the number of flash rows required to create an Em_EEPROM of +* dataSize. +*/ +#define CY_EM_EEPROM_GET_NUM_ROWS_IN_EEPROM(dataSize) \ + (((dataSize) / (CY_EM_EEPROM_EEPROM_DATA_LEN)) + \ + ((((dataSize) % (CY_EM_EEPROM_EEPROM_DATA_LEN)) != 0u) ? 1U : 0U)) + +/** Returns the size of flash allocated for EEPROM including wear leveling and +* redundant copy overhead. +*/ +#define CY_EM_EEPROM_GET_PHYSICAL_SIZE(dataSize, wearLeveling, redundantCopy) \ + (((CY_EM_EEPROM_GET_NUM_ROWS_IN_EEPROM(dataSize) * \ + CY_EM_EEPROM_FLASH_SIZEOF_ROW) * \ + (wearLeveling)) * (1uL + (redundantCopy))) + +/** \} group_em_eeprom_macros */ + + +/****************************************************************************** +* Local definitions +*******************************************************************************/ +/** \cond INTERNAL */ + +/* Offsets for 32-bit RAM buffer addressing */ +#define CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32 ((CY_EM_EEPROM_FLASH_SIZEOF_ROW / 2u) / 4u) +#define CY_EM_EEPROM_HEADER_SEQ_NUM_OFFSET_U32 (0u) +#define CY_EM_EEPROM_HEADER_ADDR_OFFSET_U32 (1u) +#define CY_EM_EEPROM_HEADER_LEN_OFFSET_U32 (2u) +#define CY_EM_EEPROM_HEADER_DATA_OFFSET_U32 (3u) +#define CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET_U32 (CY_EM_EEPROM_EEPROM_DATA_OFFSET_U32 - 1u) + +/* The same offsets as above used for direct memory addressing */ +#define CY_EM_EEPROM_EEPROM_DATA_OFFSET (CY_EM_EEPROM_FLASH_SIZEOF_ROW / 2u) +#define CY_EM_EEPROM_HEADER_ADDR_OFFSET (4u) +#define CY_EM_EEPROM_HEADER_LEN_OFFSET (8u) +#define CY_EM_EEPROM_HEADER_DATA_OFFSET (12u) +#define CY_EM_EEPROM_HEADER_CHECKSUM_OFFSET (CY_EM_EEPROM_EEPROM_DATA_OFFSET - 4u) + +#define CY_EM_EEPROM_U32_DIV (4u) + +/* Maximum wear leveling value */ +#define CY_EM_EEPROM_MAX_WEAR_LEVELING_FACTOR (10u) + +/* Maximum allowed flash row write/erase operation duration */ +#define CY_EM_EEPROM_MAX_WRITE_DURATION_MS (50u) + +/** \endcond */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CY_EM_EEPROM_H */ + + +/* [] END OF FILE */