/************************************************************************** * * Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file NVDataMgmt.c * * @author (last) Sean Nash * @date (last) 12-Aug-2020 * * @author (original) Dara Navaei * @date (original) 12-Feb-2020 * ***************************************************************************/ // Includes #include // For memcpy #include "F021.h" #include "NVDataMgmt.h" #include "RTC.h" #include "system.h" #include "Timers.h" #include "Utilities.h" // Private defines #define QUEUE_MAX_SIZE 20U ///< Max queue size #define QUEUE_START_INDEX 0U ///< Queue start index #define MIN_JOBS_NEEDED_FOR_DATA_LOG 3U ///< Min queue required for data log (3) #define MIN_JOBS_NEEDED_FOR_SECTOR_0 4U ///< Min queue count needed to write all (4) records back in sector 0 // The clock frequency comes from HCLK_FREQ and it has to be rounded up to the // nearest number #define ROUNDED_HCLK_FREQ FLOAT_TO_INT_WITH_ROUND(HCLK_FREQ) ///< Rounded HCLK for EERPOM clock #define BANK7_SECTOR_0_31_ENABLE_BIT_MASK 0x0000000F ///< Bank7 sector 0 t0 31 enable mask #define BANK7_SECTOR_32_63_ENABLE_BIT_MASK 0x00000000 ///< Bank7 sector 32 to 63 enable mask #define BANK7_SECTOR0_START_ADDRESS 0xF0200000 ///< Bank7 sector 0 start address #define BANK7_SECTOR0_END_ADDRESS 0xF0203FFF ///< Bank7 sector 0 end address #define BANK7_SECTOR1_START_ADDRESS 0xF0204000 ///< Bank7 sector 1 start address #define BANK7_SECTOR1_END_ADDRESS 0xF0207FFF ///< Bank7 sector 1 end address #define BANK7_SECTOR2_START_ADDRESS 0xF0208000 ///< Bank7 sector 2 start address #define BANK7_SECTOR2_END_ADDRESS 0xF020BFFF ///< Bank7 sector 2 end address #define BANK7_SECTOR3_START_ADDRESS 0xF020C000 ///< Bank7 sector 3 start address #define BANK7_SECTOR3_END_ADDRESS 0xF020FFFF ///< Bank7 sector 3 end address #define MAX_EEPROM_WRITE_BUFFER_BYTES 16U ///< Max allowed bytes for an EEPROM write (16 bytes) #define MAX_JOB_DATA_SIZE_BYTES 32U ///< Max bytes per job (32 bytes) #define LOG_DATA_START_INDEX 0U ///< Log data start index #define MAX_NUM_OF_SECTORS_FOR_LOG_DATA 3U ///< Max number of sector (3 sectors) #define MAX_LOG_DATA_PER_SECTOR (((BANK7_SECTOR1_END_ADDRESS + 1) - \ BANK7_SECTOR1_START_ADDRESS) / MAX_JOB_DATA_SIZE_BYTES) ///< Max log data per sector (512 for now) #define MAX_NUM_OF_EVENTS_IN_SECTOR3 (MAX_NUM_OF_SECTORS_FOR_LOG_DATA * MAX_LOG_DATA_PER_SECTOR) ///< Max number of accumulated logs in sector 3 (1536) #define MAX_NUM_OF_EVENTS_IN_SECTOR2 ((MAX_NUM_OF_SECTORS_FOR_LOG_DATA - 1) * MAX_LOG_DATA_PER_SECTOR) ///< Max number of accumulated logs in sector 2 (1024) #define MAX_NUM_OF_EVENTS_IN_SECTOR1 ((MAX_NUM_OF_SECTORS_FOR_LOG_DATA - 2) * MAX_LOG_DATA_PER_SECTOR) ///< Max number of accumulated logs in sector 1 (512) // Data addresses and length in RTC RAM #define BOOTLOADER_FLAG_ADDRESS 0x00000000 ///< Bootloader start address in RTC RAM (0) #define BOOTLOADER_FLAG_LENGTH_BYTES 4U ///< Bootloader number of bytes (4 bytes) #define LOG_RECORD_START_ADDRESS 0x00000010 // 16 ///< Log record start address in RTC RAM (16) #define HD_TREATMENT_TIME_ADDRESS 0X00000030 // 48 ///< HD treatment time start address in RTC RAM (48) #define DG_CONSUMED_WATER_ADDRESS 0X00000040 // 64 ///< DG water consumption start address in RTC RAM (64) #define SERVICE_DATE_START_ADDRESS 0X00000050 // 80 ///< Service date record start address in RTC RAM (80) #define LAST_DISINFECTION_DATE_ADDRESS 0x00000060 // 96 ///< Last disinfection date start address in RTC RAM (96) // Data addresses in EEPROM #define CALIBRATION_RECORD_START_ADDRESS ( BANK7_SECTOR0_START_ADDRESS + sizeof(MFG_RECORD_T) ) ///< Calibration record start address in EEPROM #define COMMAND_TIME_OUT 500U // time in ms ///< Timeout for an EEPROM or RTC command in ms /// NVDataMgmt self test states typedef enum NVDataMgmt_Self_Test_States { NVDATAMGMT_SELF_TEST_STATE_START = 0, ///< Self test start NVDATAMGMT_SELF_TEST_STATE_ENABLE_EEPROM, ///< Self test enable EERPOM NVDATAMGMT_SELF_TEST_STATE_READ_BOOTLOADER_FLAG, ///< Self test read bootloader NVDATAMGMT_SELF_TEST_STATE_READ_LOG_RECORD, ///< Self test read log record NVDATAMGMT_SELF_TEST_STATE_READ_TREATMENT_TIME, ///< Self test read treatment time NVDATAMGMT_SELF_TEST_STATE_READ_WATER_CONSUMPTION, ///< Self test read water consumption NVDATAMGMT_SELF_TEST_STATE_READ_MFG_RECORD, ///< Self test read manufacturing record NVDATAMGMT_SELF_TEST_STATE_READ_CAL_RECORD, ///< Self test read calibration record NVDATAMGMT_SELF_TEST_STATE_READ_SERVICE_RECORD, ///< Self test read service record NVDATAMGMT_SELF_TEST_STATE_READ_LAST_DISINFECTION_DATE, ///< Self test read last disinfection date NVDATAMGMT_SELF_TEST_STATE_CHECK_CRC, ///< Self test check CRC NVDATAMGMT_SELF_TEST_STATE_COMPLETE, ///< Self test complete NUM_OF_NVDATAMGMT_SELF_TEST_STATES ///< Total number of self test states } NVDATAMGMT_SELF_TEST_STATE_T; /// NVDataMgmt Exec states typedef enum NVDataMgmt_Exec_State { NVDATAMGMT_EXEC_STATE_WAIT_FOR_POST = 0, ///< Exec state wait for POST NVDATAMGMT_EXEC_STATE_IDLE, ///< Exec state Idle NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM, ///< Exec state write to EEPROM NVDATAMGMT_EXEC_STATE_READ_FROM_EEPROM, ///< Exec state read from EEPROM NVDATAMGMT_EXEC_STATE_ERASE_EEPROM, ///< Exec state erase EEPROM NVDATAMGMT_EXEC_STATE_WRITE_TO_RTC, ///< Exec state write to RTC NVDATAMGMT_EXEC_STATE_READ_FROM_RTC, ///< Exec state read from RTC NUM_OF_NVDATAMGMT_EXEC_STATES ///< Total number of exec states } NVDATAMGMT_EXEC_STATE_T; /// NVDataMgmt memory operation modes typedef enum NVDataMgmt_Operation { NVDATAMGMT_NONE = 0, ///< Default mode to prevent any accidental ops NVDATAMGMT_WRITE, ///< Operation mode write NVDATAMGMT_READ, ///< Operation mode read NVDATAMGMT_ERASE_SECTOR, ///< Operation mode erase a sector (EEPROM) NUM_OF_NVDATAMGMT_OPS_STATES ///< Total number of operation states } NVDATAMGMT_OPERATION_STATE_T; /// NVDataMgmt memory locations typedef enum NVDataMgmt_Location { NVDATAMGMT_EEPROM = 0, ///< Location EEPROM NVDATAMGMT_RTC, ///< Location RTC (RAM) NUM_OF_NVDATAMGMT_LOC_STATES ///< Total number of location states } NVDATAMGMT_LOCATION_STATE_T; #pragma pack(push, 1) /// Memory operations struct typedef struct { NVDATAMGMT_OPERATION_STATE_T memoryOperation; ///< Memory operation NVDATAMGMT_LOCATION_STATE_T memoryLocation; ///< Memory location U32* startAddress; ///< Operation start address U08 buffer [ MAX_JOB_DATA_SIZE_BYTES ]; ///< Buffer READ_DATA_T* externalAddress; ///< External address of a buffer U32 length; ///< Length of a buffer } MEMORY_OPS_T; /// Log header struct typedef struct { U16 recordCount; ///< Record count U16 nextWriteIndex; ///< Next write index U16 nextReadIndex; ///< Next read index BOOL isHdrCorrupted; ///< Log header corruption flag } LOG_HEADER_T; /// Log record struct typedef struct { LOG_HEADER_T logHeader; ///< Log header struct U16 crc; ///< Log header CRC } LOG_RECORD_T; /// Treatment time struct typedef struct { U32 treatmentTime; ///< Treatment time U16 crc; ///< Treatment time CRC } TREATMENT_TIME_RECORD_T; /// Water consumption struct typedef struct { U32 waterConsumption; ///< Water consumption U16 crc; ///< Water consumption CRC } WATER_CONSUMPTION_RECORD_T; /// Manufacturing data struct typedef struct { MFG_DATA_T mfgData; ///< Manufacturing data struct U16 crc; ///< Manufacturing data CRC U08 Padding[ 0x30 - sizeof(MFG_DATA_T) - sizeof(U16) ]; ///< Padding for reserved mfg data space } MFG_RECORD_T; /// Service record struct typedef struct { SERVICE_DATA_T serviceData; ///< Service date struct U16 crc; ///< Service data CRC } SERVICE_RECORD_T; /// Calibration record struct typedef struct { CALIBRATION_DATA_T calData; ///< Calibration data struct U16 crc; ///< Calibration data CRC U08 Padding[ 0x50 - sizeof(CALIBRATION_DATA_T) - sizeof(U16) ]; ///< Padding for reserved cal data space } CALIBRATION_RECORD_T; /// Last disinfection record struct typedef struct { DISINFECTION_DATE_T date; ///< Disinfection date (char array) U16 crc; ///< Disinfection date CRC } LAST_DISINFECTION_RECORD_T; #pragma pack(pop) // Private variables static MEMORY_OPS_T jobQueue [ QUEUE_MAX_SIZE ]; ///< Job queue buffer static MEMORY_OPS_T currentJob; ///< Current job static LOG_RECORD_T logRecord; ///< Log record variable static TREATMENT_TIME_RECORD_T treatmentTimeRecord; ///< Treatment time record static WATER_CONSUMPTION_RECORD_T waterConsumptionRecord; ///< Water consumption record static MFG_RECORD_T mfgRecord; ///< Manufacturing record static CALIBRATION_RECORD_T calibrationRecord; ///< Calibration record static SERVICE_RECORD_T serviceRecord; ///< Service record static LAST_DISINFECTION_RECORD_T lastDisinfectionRecord; ///< Disinfection record static U08 queueRearIndex = QUEUE_START_INDEX; ///< Queue rear index static U08 queueFrontIndex = QUEUE_START_INDEX; ///< Queue front index static U08 queueCount = 0; ///< Queue count static NVDATAMGMT_SELF_TEST_STATE_T NVDataMgmtSelfTestState = NVDATAMGMT_SELF_TEST_STATE_START; ///< NVDataMgmt self test state variable static NVDATAMGMT_EXEC_STATE_T NVDataMgmtExecState = NVDATAMGMT_EXEC_STATE_WAIT_FOR_POST; ///< NVDataMgmt exec state variable static SELF_TEST_STATUS_T NVDataMgmtSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; ///< NVDataMgmt self test result static U32 bootloaderFlag = 0; ///< Bootloader flag static BOOL hasCommandTimedout = FALSE; ///< Boolean flag for timeout of the commands static U32 currentTime = 0; ///< Current time static BOOL calRecordIsValid = FALSE; ///< Flag indicates whether stored calibration record was found to be valid static volatile BOOL powerOffIsImminent = FALSE; ///< Power off warning has been signaled. Non-volatile memory operations should be completed ASAP and then ceased. // Private functions static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestStart ( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestEnableEEPROM ( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadBootloaderFlag ( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadHDTreatmentTime ( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadDGWaterConsumption ( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadLogRecord ( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadMfgRecord ( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadCalibrationRecord ( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadServiceRecord ( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadLastDisinfectionDate ( void ); static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestCheckCRC ( void ); static NVDATAMGMT_EXEC_STATE_T handleExecWaitForPostState ( void ); static NVDATAMGMT_EXEC_STATE_T handleExecIdleState ( void ); static NVDATAMGMT_EXEC_STATE_T handleExecEraseState ( void ); static NVDATAMGMT_EXEC_STATE_T handleExecWriteToEEPROMState ( void ); static NVDATAMGMT_EXEC_STATE_T handleExecReadFromEEPROMState ( void ); static NVDATAMGMT_EXEC_STATE_T handleExecWriteToRAMState ( void ); static NVDATAMGMT_EXEC_STATE_T handleExecReadFromRAMState ( void ); // Queue functions static void setMemoryOpsStruct ( NVDATAMGMT_OPERATION_STATE_T ops, NVDATAMGMT_LOCATION_STATE_T location, U32 startAddress, U08* data, READ_DATA_T* extAddress, U32 length ); static void enqueue ( NVDATAMGMT_OPERATION_STATE_T ops, NVDATAMGMT_LOCATION_STATE_T location, U32 startAddress, U08* data, READ_DATA_T* extAddress, U32 length ); static void dequeue ( void ); static U32 prepareWriteLogJobAndGetStartAddress ( U08* data ); static U32 prepareReadLogJobAndGetStartAddress ( void ); static BOOL isQueueEmpty ( void ); static BOOL isQueueFull ( void ); static U32 getAvailableQueueCount ( void ); static BOOL enqueueBank7Sector0Records ( void ); // Helper functions static BOOL didCommandTimeout ( ALARM_ID_T alarm, U08* state ); static BOOL eraseDataLogSectors ( void ); /************************************************************************* * @brief initNVDataMgmt * The initNVDataMgmt initializes EEPROM * @details * Inputs : none * Outputs : none * @return none *************************************************************************/ void initNVDataMgmt ( void ) { NVDataMgmtSelfTestState = NVDATAMGMT_SELF_TEST_STATE_START; NVDataMgmtExecState = NVDATAMGMT_EXEC_STATE_WAIT_FOR_POST; NVDataMgmtSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; queueRearIndex = QUEUE_START_INDEX; queueFrontIndex = QUEUE_START_INDEX; queueCount = 0; Fapi_initializeFlashBanks( ROUNDED_HCLK_FREQ ); Fapi_setActiveFlashBank( Fapi_FlashBank7 ); } /************************************************************************* * @brief * The signalPowerOffWarning signals this module that system power off is \n * imminent and any non-volatile data writes in progress should be wrapped up \n * quickly and any pending non-volatile data writes should not be started. * @details * Inputs : none * Outputs : powerOffIsImminent * @return none *************************************************************************/ void signalPowerOffWarning( void ) { powerOffIsImminent = TRUE; } /************************************************************************* * @brief setMfgData * The setMfgData updates the struct that holds the manufacturing data, * calls another function to calculate the CRC for the provided data and * calls another function to erase sector 0 and write the new manufacturing * data. * @details * Inputs : MFG_DATA_T (data) * Outputs : BOOL * @return BOOL *************************************************************************/ BOOL setMfgData ( MFG_DATA_T data ) { mfgRecord.mfgData = data; mfgRecord.crc = crc16 ( (U08*)&mfgRecord.mfgData, sizeof(MFG_DATA_T) ); BOOL status = enqueueBank7Sector0Records(); return status; } /************************************************************************* * @brief getMfgData * The getMfgData returns the data in the struct that holds manufacturing * record to buffer that the caller has provided * @details * Inputs : MFG_DATA_T* (buffer) * Outputs : BOOL (status) * @return BOOL (status) *************************************************************************/ BOOL getMfgData ( MFG_DATA_T* buffer ) { BOOL status = FALSE; if ( buffer != NULL ) { memcpy ( buffer, (U08*)&mfgRecord.mfgData, sizeof(MFG_DATA_T) ); status = TRUE; } return status; } /************************************************************************* * @brief setCalibrationData * The setCalibrationData updates the struct that holds the calibration data, * calls another function to calculate the CRC for the provided data and * calls another function to erase sector 0 and write the new manufacturing * data. * @details * Inputs : MFG_DATA_T (data) * Outputs : BOOL * @return BOOL *************************************************************************/ BOOL setCalibrationData ( CALIBRATION_DATA_T data ) { BOOL status; calibrationRecord.calData = data; calibrationRecord.crc = crc16( (U08*)&calibrationRecord.calData, sizeof(CALIBRATION_DATA_T) ); // The entire sector 0 must be erased and re-written again status = enqueueBank7Sector0Records(); return status; } /************************************************************************* * @brief getCalibrationData * The getCalibrationData returns the data in the struct that hold calibration * record to buffer that the caller has provided * @details * Inputs : CALIBRATION_DATA_T* (buffer) * Outputs : BOOL (status) * @return BOOL (status) *************************************************************************/ BOOL getCalibrationData ( CALIBRATION_DATA_T* buffer ) { BOOL status = FALSE; if ( buffer != NULL ) { memcpy ( buffer, (U08*)&calibrationRecord.calData, sizeof(CALIBRATION_DATA_T) ); status = calRecordIsValid; } return status; } /************************************************************************* * @brief setServiceDate * The setServiceDate updates the struct that holds the calibration data, * calls another function to calculate the CRC for the provided data if * there is enough queues available, it schedules a write to RTC RAM * @details * Inputs : SERVICE_DATA_T (data) * Outputs : BOOL * @return BOOL *************************************************************************/ BOOL setServiceDate ( SERVICE_DATA_T data ) { BOOL status = FALSE; serviceRecord.serviceData = data; serviceRecord.crc = crc16 ( (U08*)&serviceRecord.serviceData, sizeof(SERVICE_DATA_T) ); if ( !isQueueFull() ) { enqueue( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, SERVICE_DATE_START_ADDRESS, (U08*)&serviceRecord, 0, sizeof(SERVICE_RECORD_T) ); status = TRUE; } return status; } /************************************************************************* * @brief getServiceDate * The getServiceDate returns the data in the struct that holds service * date to buffer that the caller has provided * @details * Inputs : SERVICE_DATA_T* (buffer) * Outputs : BOOL (status) * @return BOOL (status) *************************************************************************/ BOOL getServiceDate ( SERVICE_DATA_T* buffer ) { BOOL status = FALSE; if ( buffer != NULL ) { memcpy ( buffer, (U08*)&serviceRecord.serviceData, sizeof(SERVICE_DATA_T) ); status = TRUE; } return status; } /************************************************************************* * @brief writeLogData * The writeLogData checks if the queue is not full and if it is not, it calls * enqueue to schedule a job for writing the log data * @details * Inputs : LOG_DATA_T* (data) * Outputs : BOOL * @return BOOL *************************************************************************/ BOOL writeLogData ( LOG_DATA_T* data ) { BOOL status = FALSE; // If the header is corrupted, no more data is allowed to be written if ( !isQueueFull() && logRecord.logHeader.isHdrCorrupted == FALSE ) { enqueue ( NVDATAMGMT_WRITE, NVDATAMGMT_EEPROM, 0, (U08*)&data, 0, sizeof(LOG_DATA_T) ); status = TRUE; } return status; } /************************************************************************* * @brief readLogData * The readLogData checks if the queue is not full and if it is not, it calls * enqueue to schedule a job for writing the log data * @details * Inputs : READ_DATA_T* (buffer), U32 (length) * Outputs : BOOL * @return BOOL *************************************************************************/ BOOL readLogData ( READ_DATA_T* buffer, U32 length ) { BOOL status = FALSE; U32 availableQueue = getAvailableQueueCount(); if ( availableQueue >= MIN_JOBS_NEEDED_FOR_DATA_LOG ) { enqueue( NVDATAMGMT_READ, NVDATAMGMT_EEPROM, 0, 0, buffer, length ); status = TRUE; } return status; } /************************************************************************* * @brief setTreatmentTime * The setTreatmentTime sets a queue job to write the treatment time in * the specified RAM address * @details * Inputs : U32 (mins) * Outputs : BOOL * @return BOOL *************************************************************************/ BOOL setTreatmentTime ( U32 hours ) { BOOL status = FALSE; treatmentTimeRecord.treatmentTime = hours; treatmentTimeRecord.crc = crc16 ( (U08*)&treatmentTimeRecord.treatmentTime, sizeof(U32) ); if ( !isQueueFull() ) { enqueue( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, HD_TREATMENT_TIME_ADDRESS, (U08*)&treatmentTimeRecord, 0, sizeof(TREATMENT_TIME_RECORD_T) ); status = TRUE; } return status; } /************************************************************************* * @brief getTreatmentTime * The getTreatmentTime returns the total number treatment hours of the * HD device * @details * Inputs : none * Outputs : U32 * @return U32 *************************************************************************/ U32 getTreatmentTime ( void ) { return treatmentTimeRecord.treatmentTime; } /************************************************************************* * @brief setWaterConsumption * The setWaterConsumption sets a queue job to write the amount of water * that has been consumed in DG * @details * Inputs : U32 (liters) * Outputs : BOOL * @return BOOL *************************************************************************/ BOOL setWaterConsumption ( U32 liters ) { BOOL status = FALSE; waterConsumptionRecord.waterConsumption = liters; waterConsumptionRecord.crc = crc16 ( (U08*)&waterConsumptionRecord.waterConsumption, sizeof(U32) ); if ( !isQueueFull() ) { enqueue( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, DG_CONSUMED_WATER_ADDRESS, (U08*)&waterConsumptionRecord, 0, sizeof(WATER_CONSUMPTION_RECORD_T) ); status = TRUE; } return status; } /************************************************************************* * @brief getWaterConsumption * The getWaterConsumption returns the amount of consumed water * @details * Inputs : none * Outputs : U32 * @return U32 *************************************************************************/ U32 getWaterConsumption ( void ) { return waterConsumptionRecord.waterConsumption; } /************************************************************************* * @brief setDisinfectionDate * The setDisinfectionDate gets the last disinfection date, calculates the * CRC for it and if the queue is not full, it schedules a write to RTC RAM * to store the last date * @details * Inputs : DISINFECTION_DATE_T (date) * Outputs : BOOL (status) * @return BOOL (status) *************************************************************************/ BOOL setDisinfectionDate ( DISINFECTION_DATE_T date ) { BOOL status = FALSE; memcpy ( lastDisinfectionRecord.date.disinfectionDate, date.disinfectionDate, sizeof(DISINFECTION_DATE_T) ); lastDisinfectionRecord.crc = crc16 ( (U08*)&lastDisinfectionRecord.date.disinfectionDate, sizeof(DISINFECTION_DATE_T) ); if ( !isQueueFull() ) { enqueue( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, LAST_DISINFECTION_DATE_ADDRESS, (U08*)&lastDisinfectionRecord, 0, sizeof(LAST_DISINFECTION_RECORD_T) ); status = TRUE; } return status; } /************************************************************************* * @brief getDisinfectionDate * The getDisinfectionDate populates the provided buffer with the last * disinfection date * @details * Inputs : DISINFECTION_DATE_T* (buffer) * Outputs : BOOL (status) * @return BOOL (status) *************************************************************************/ BOOL getDisinfectionDate ( DISINFECTION_DATE_T* buffer ) { BOOL status = FALSE; if ( buffer != NULL ) { memcpy ( buffer, lastDisinfectionRecord.date.disinfectionDate, sizeof(DISINFECTION_DATE_T) ); status = TRUE; } return status; } /************************************************************************* * @brief execNVDataMgmtSelfTest * The execNVDataMgmtSelfTest runs the NVDataMgmt POST during the self test * @details * Inputs : none * Outputs : SELF_TEST_STATUS_T * @return SELF_TEST_STATUS_T *************************************************************************/ SELF_TEST_STATUS_T execNVDataMgmtSelfTest ( void ) { switch ( NVDataMgmtSelfTestState ) { case NVDATAMGMT_SELF_TEST_STATE_START: NVDataMgmtSelfTestState = handleSelfTestStart(); break; case NVDATAMGMT_SELF_TEST_STATE_ENABLE_EEPROM: NVDataMgmtSelfTestState = handleSelfTestEnableEEPROM(); break; case NVDATAMGMT_SELF_TEST_STATE_READ_BOOTLOADER_FLAG: NVDataMgmtSelfTestState = handleSelfTestReadBootloaderFlag(); break; case NVDATAMGMT_SELF_TEST_STATE_READ_LOG_RECORD: NVDataMgmtSelfTestState = handleSelfTestReadLogRecord(); break; case NVDATAMGMT_SELF_TEST_STATE_READ_TREATMENT_TIME: NVDataMgmtSelfTestState = handleSelfTestReadHDTreatmentTime(); break; case NVDATAMGMT_SELF_TEST_STATE_READ_WATER_CONSUMPTION: NVDataMgmtSelfTestState = handleSelfTestReadDGWaterConsumption(); break; case NVDATAMGMT_SELF_TEST_STATE_READ_MFG_RECORD: NVDataMgmtSelfTestState = handleSelfTestReadMfgRecord(); break; case NVDATAMGMT_SELF_TEST_STATE_READ_CAL_RECORD: NVDataMgmtSelfTestState = handleSelfTestReadCalibrationRecord(); break; case NVDATAMGMT_SELF_TEST_STATE_READ_SERVICE_RECORD: NVDataMgmtSelfTestState = handleSelfTestReadServiceRecord(); break; case NVDATAMGMT_SELF_TEST_STATE_READ_LAST_DISINFECTION_DATE: NVDataMgmtSelfTestState = handleSelfTestReadLastDisinfectionDate(); break; case NVDATAMGMT_SELF_TEST_STATE_CHECK_CRC: NVDataMgmtSelfTestState = handleSelfTestCheckCRC(); break; case NVDATAMGMT_SELF_TEST_STATE_COMPLETE: // Done with POST. Do nothing break; default: #ifdef _DG_ SET_ALARM_WITH_2_U32_DATA ( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_NVDATAMGMT_INVALID_SELF_TEST_STATE, NVDataMgmtSelfTestState ); #else SET_ALARM_WITH_2_U32_DATA ( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_NVDATAMGMT_INVALID_SELF_TEST_STATE, NVDataMgmtSelfTestState ); #endif NVDataMgmtSelfTestState = NVDATAMGMT_SELF_TEST_STATE_COMPLETE; NVDataMgmtSelfTestResult = SELF_TEST_STATUS_FAILED; break; } return NVDataMgmtSelfTestResult; } /************************************************************************* * @brief setBootloaderFlag * The setBootloaderFlag sets the bootloader flag to RTC RAM * @details * Inputs : U32 (flag) * Outputs : BOOL * @return BOOL *************************************************************************/ BOOL setBootloaderFlag ( U32 flag ) { BOOL status = FALSE; if ( !isQueueFull() ) { enqueue ( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, BOOTLOADER_FLAG_ADDRESS, (U08*)&flag, 0, BOOTLOADER_FLAG_LENGTH_BYTES ); status = TRUE; } return status; } /************************************************************************* * @brief getBootloaderFlag * The getBootloaderFlag gets the bootloader flag from RTC RAM * @details * Inputs : none * Outputs : U32 (flag value) * @return U32 (flag value) *************************************************************************/ U32 getBootloaderFlag( void ) { return bootloaderFlag; } /************************************************************************* * @brief execNVDataMgmt * The execNVDataMgmt runs the NVDataMgmt main tasks * @details * Inputs : none * Outputs : none * @return none *************************************************************************/ void execNVDataMgmt( void ) { // TODO - not sure where it should go Dara, but need to look at powerOffIsImminent flag and block queuing of any new jobs, maybe even block starting of any new jobs if flag is set switch ( NVDataMgmtExecState ) { case NVDATAMGMT_EXEC_STATE_WAIT_FOR_POST: NVDataMgmtExecState = handleExecWaitForPostState(); break; case NVDATAMGMT_EXEC_STATE_IDLE: NVDataMgmtExecState = handleExecIdleState(); break; case NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM: NVDataMgmtExecState = handleExecWriteToEEPROMState(); break; case NVDATAMGMT_EXEC_STATE_READ_FROM_EEPROM: NVDataMgmtExecState = handleExecReadFromEEPROMState(); break; case NVDATAMGMT_EXEC_STATE_ERASE_EEPROM: NVDataMgmtExecState = handleExecEraseState(); break; case NVDATAMGMT_EXEC_STATE_WRITE_TO_RTC: NVDataMgmtExecState = handleExecWriteToRAMState(); break; case NVDATAMGMT_EXEC_STATE_READ_FROM_RTC: NVDataMgmtExecState = handleExecReadFromRAMState(); break; default: #ifdef _DG_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_NVDATAMGMT_EXEC_INVALID_STATE, NVDataMgmtExecState ); #else SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_NVDATAMGMT_EXEC_INVALID_STATE, NVDataMgmtExecState ); #endif NVDataMgmtExecState = NVDATAMGMT_EXEC_STATE_IDLE; break; } } // Private functions /************************************************************************* * @brief handleSelfTestStart * The handleSelfTestStart enables the EEPROM bank sectors * @details * Inputs : none * Outputs : NVDATAMGMT_SELF_TEST_STATE_T * @return NVDATAMGMT_SELF_TEST_STATE_T *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestStart ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_ENABLE_EEPROM; Fapi_enableEepromBankSectors( BANK7_SECTOR_0_31_ENABLE_BIT_MASK, BANK7_SECTOR_32_63_ENABLE_BIT_MASK ); // Get the time to check for timeout currentTime = getMSTimerCount(); return state; } /************************************************************************* * @brief handleSelfTestEnableEEPROM * The handleSelfTestEnableEEPROM waits for EEPROM to return with ready from * enabling the EEPROM command * @details * Inputs : none * Outputs : NVDATAMGMT_SELF_TEST_STATE_T * @return NVDATAMGMT_SELF_TEST_STATE_T *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestEnableEEPROM ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_ENABLE_EEPROM; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_NVDATA_EEPROM_OPS_FAILURE, (U08*)&state ); if ( FAPI_CHECK_FSM_READY_BUSY == Fapi_Status_FsmReady || timeoutStatus == TRUE ) { // Enable was successful, Request a read for bootloader if ( getRTCRAMState() == RTC_RAM_STATE_READY ) { readFromRAM( BOOTLOADER_FLAG_ADDRESS, BOOTLOADER_FLAG_LENGTH_BYTES ); // Get the time to check for timeout currentTime = getMSTimerCount(); state = NVDATAMGMT_SELF_TEST_STATE_READ_BOOTLOADER_FLAG; } } return state; } /************************************************************************* * @brief handleSelfTestReadBootloaderFlag * The handleSelfTestReadBootloaderFlag reads the bootloader flag * from RTC RAM * @details * Inputs : none * Outputs : NVDATAMGMT_SELF_TEST_STATE_T * @return NVDATAMGMT_SELF_TEST_STATE_T *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadBootloaderFlag ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_BOOTLOADER_FLAG; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); // If the read for bootloader is done, read it if ( getRTCRAMStatus() == RTC_RAM_STATUS_IDLE || timeoutStatus == TRUE ) { getDataFromRAM( (U08*)&bootloaderFlag, BOOTLOADER_FLAG_LENGTH_BYTES ); // If the device is HD, read treatment time, // If the device is DG, read water consumption #ifdef _HD_ if ( getRTCRAMState() == RTC_RAM_STATE_READY ) { readFromRAM ( HD_TREATMENT_TIME_ADDRESS, sizeof(TREATMENT_TIME_RECORD_T) ); currentTime = getMSTimerCount(); state = NVDATAMGMT_SELF_TEST_STATE_READ_TREATMENT_TIME; } #else if ( getRTCRAMState() == RTC_RAM_STATE_READY ) { readFromRAM ( DG_CONSUMED_WATER_ADDRESS, sizeof(WATER_CONSUMPTION_RECORD_T) ); currentTime = getMSTimerCount(); state = NVDATAMGMT_SELF_TEST_STATE_READ_WATER_CONSUMPTION; } #endif } return state; } /************************************************************************* * @brief handleSelfTestReadHDTreatmentTime * The handleSelfTestReadHDTreatmentTime reads the HD treatment time * from RTC RAM * @details * Inputs : none * Outputs : NVDATAMGMT_SELF_TEST_STATE_T * @return NVDATAMGMT_SELF_TEST_STATE_T *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadHDTreatmentTime ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_TREATMENT_TIME; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); // If the RTC RAM is ready, read the results if ( getRTCRAMStatus() == RTC_RAM_STATUS_IDLE || timeoutStatus == TRUE ) { getDataFromRAM ( (U08*)&treatmentTimeRecord, sizeof(TREATMENT_TIME_RECORD_T) ); // If the RAM is ready, request a read for the log records (RAM) if ( getRTCRAMState() == RTC_RAM_STATE_READY ) { readFromRAM ( LOG_RECORD_START_ADDRESS, sizeof(LOG_RECORD_T) ); currentTime = getMSTimerCount(); state = NVDATAMGMT_SELF_TEST_STATE_READ_LOG_RECORD; } } return state; } /************************************************************************* * @brief handleSelfTestReadDGWaterConsumption * The handleSelfTestReadDGWaterConsumption reads the DG water consumption * from RTC RAM * @details * Inputs : none * Outputs : NVDATAMGMT_SELF_TEST_STATE_T * @return NVDATAMGMT_SELF_TEST_STATE_T *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadDGWaterConsumption ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_WATER_CONSUMPTION; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); // If the RAM is ready, request a read for water consumption if ( getRTCRAMStatus() == RTC_RAM_STATUS_IDLE || timeoutStatus == TRUE ) { getDataFromRAM ( (U08*)&waterConsumptionRecord, sizeof(WATER_CONSUMPTION_RECORD_T) ); if ( getRTCRAMState() == RTC_RAM_STATE_READY ) { readFromRAM ( LOG_RECORD_START_ADDRESS, sizeof(LOG_RECORD_T) ); currentTime = getMSTimerCount(); state = NVDATAMGMT_SELF_TEST_STATE_READ_LOG_RECORD; } } return state; } /************************************************************************* * @brief handleSelfTestReadLogRecord * The handleSelfTestReadLogRecord reads the log record from RTC RAM * @details * Inputs : none * Outputs : NVDATAMGMT_SELF_TEST_STATE_T * @return NVDATAMGMT_SELF_TEST_STATE_T *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadLogRecord ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_LOG_RECORD; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); // If the RAM is in Idle, read the log records if ( getRTCRAMStatus() == RTC_RAM_STATUS_IDLE || timeoutStatus == TRUE ) { U32 len = sizeof(MFG_RECORD_T) / sizeof(U32); U32 *addr = (U32*)&mfgRecord; getDataFromRAM ( (U08*)&logRecord, sizeof(LOG_RECORD_T) ); currentTime = getMSTimerCount(); // Get ready for reading the manufacturing record Fapi_doMarginRead( (U32*)BANK7_SECTOR0_START_ADDRESS, addr, len, Fapi_NormalRead ); state = NVDATAMGMT_SELF_TEST_STATE_READ_MFG_RECORD; } return state; } /************************************************************************* * @brief handleSelfTestReadMfgRecords * The handleSelfTestReadMfgRecords reads the manufacturing record from * EEPROM * @details * Inputs : none * Outputs : NVDATAMGMT_SELF_TEST_STATE_T * @return NVDATAMGMT_SELF_TEST_STATE_T *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadMfgRecord ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_MFG_RECORD; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_NVDATA_EEPROM_OPS_FAILURE, (U08*)&state ); // Wait for the read from EEPROM to be successful if ( FAPI_CHECK_FSM_READY_BUSY == Fapi_Status_FsmReady || timeoutStatus == TRUE ) { currentTime = getMSTimerCount(); // Send the read command for calibration record Fapi_doMarginRead ( (U32*)CALIBRATION_RECORD_START_ADDRESS, (U32*)&calibrationRecord, sizeof(CALIBRATION_RECORD_T) / sizeof(U32), Fapi_NormalRead ); state = NVDATAMGMT_SELF_TEST_STATE_READ_CAL_RECORD; } return state; } /************************************************************************* * @brief handleSelfTestReadCalibrationRecord * The handleSelfTestReadCalibrationRecord reads the calibration record * from EEPROM * @details * Inputs : none * Outputs : NVDATAMGMT_SELF_TEST_STATE_T * @return NVDATAMGMT_SELF_TEST_STATE_T *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadCalibrationRecord ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_CAL_RECORD; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_NVDATA_EEPROM_OPS_FAILURE, (U08*)&state ); // If the EEPROM read was successful, request a read from RAM to // read the service record if ( FAPI_CHECK_FSM_READY_BUSY == Fapi_Status_FsmReady || timeoutStatus == TRUE ) { if ( getRTCRAMState() == RTC_RAM_STATE_READY ) { currentTime = getMSTimerCount(); readFromRAM( SERVICE_DATE_START_ADDRESS, sizeof(SERVICE_RECORD_T) ); state = NVDATAMGMT_SELF_TEST_STATE_READ_SERVICE_RECORD; } } return state; } /************************************************************************* * @brief handleSelfTestReadServiceRecord * The handleSelfTestReadServiceRecord reads the service dates from RTC * RAM * @details * Inputs : none * Outputs : NVDATAMGMT_SELF_TEST_STATE_T * @return NVDATAMGMT_SELF_TEST_STATE_T *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadServiceRecord ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_SERVICE_RECORD; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); if ( getRTCRAMStatus() == RTC_RAM_STATUS_IDLE || timeoutStatus == TRUE ) { getDataFromRAM ( (U08*)&serviceRecord, sizeof(SERVICE_RECORD_T) ); if ( getRTCRAMState() == RTC_RAM_STATE_READY ) { currentTime = getMSTimerCount(); readFromRAM( LAST_DISINFECTION_DATE_ADDRESS, sizeof(LAST_DISINFECTION_RECORD_T) ); state = NVDATAMGMT_SELF_TEST_STATE_READ_LAST_DISINFECTION_DATE; } } return state; } /************************************************************************* * @brief handleSelfTestReadLastDisinfectionDate * The handleSelfTestReadLastDisinfectionDate reads the last disinfection date * from RTC RAM * @details * Inputs : none * Outputs : NVDATAMGMT_SELF_TEST_STATE_T * @return NVDATAMGMT_SELF_TEST_STATE_T *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestReadLastDisinfectionDate ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_READ_LAST_DISINFECTION_DATE; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); if ( getRTCRAMStatus() == RTC_RAM_STATUS_IDLE || timeoutStatus == TRUE ) { getDataFromRAM ( (U08*)&lastDisinfectionRecord, sizeof(LAST_DISINFECTION_RECORD_T) ); state = NVDATAMGMT_SELF_TEST_STATE_CHECK_CRC; } return state; } /************************************************************************* * @brief handleSelfTestCheckCRC * The handleSelfTestCheckCRC calculates the CRC of the records and compares * them to the CRC that was read. If they don't match, it will fail POST * @details * Inputs : none * Outputs : NVDATAMGMT_SELF_TEST_STATE_T * @return NVDATAMGMT_SELF_TEST_STATE_T *************************************************************************/ static NVDATAMGMT_SELF_TEST_STATE_T handleSelfTestCheckCRC ( void ) { NVDATAMGMT_SELF_TEST_STATE_T state = NVDATAMGMT_SELF_TEST_STATE_COMPLETE; BOOL hasCRCPassed = TRUE; U16 calcCRC; U16 recordCRC; #ifdef _HD_ calcCRC = crc16 ( (U08*)&treatmentTimeRecord.treatmentTime, sizeof(U32) ); recordCRC = treatmentTimeRecord.crc; #ifndef LIMITED_NVDATA_CRC_CHECKS if ( calcCRC != recordCRC ) { hasCRCPassed = FALSE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_NVDATA_HW_USAGE_DATA_CRC_ERROR, recordCRC, calcCRC ); } #endif #else calcCRC = crc16 ( (U08*)&waterConsumptionRecord.waterConsumption, sizeof(U32) ); recordCRC = waterConsumptionRecord.crc; if ( calcCRC != recordCRC ) { hasCRCPassed = FALSE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_NVDATA_HW_USAGE_DATA_CRC_ERROR, recordCRC, calcCRC ); } // Check the CRC for last disinfection date calcCRC = crc16 ( (U08*)&lastDisinfectionRecord.date.disinfectionDate, sizeof(DISINFECTION_DATE_T) ); recordCRC = lastDisinfectionRecord.crc; if ( calcCRC != recordCRC ) { hasCRCPassed = FALSE; SET_ALARM_WITH_2_U32_DATA( AlARM_ID_NVDATA_DISINFECTION_DATE_CRC_ERROR, recordCRC, calcCRC ); } #endif #ifndef LIMITED_NVDATA_CRC_CHECKS // Check log header record calcCRC = crc16 ( (U08*)&logRecord.logHeader, sizeof(LOG_HEADER_T) ); recordCRC = logRecord.crc; if ( calcCRC != recordCRC ) { hasCRCPassed = FALSE; // CRC for the log record has failed. The flag will be set to true // The log header will be set to full by having the record count be at // maximum number of counts (1536 in this case) and the read index be a 0 // of the beginning of the log sectors logRecord.logHeader.recordCount = MAX_NUM_OF_EVENTS_IN_SECTOR3; logRecord.logHeader.nextReadIndex = 0; logRecord.logHeader.nextReadIndex = 0; logRecord.logHeader.isHdrCorrupted = TRUE; } // Check CRC for manufacturing record calcCRC = crc16 ( (U08*)&mfgRecord.mfgData, sizeof(MFG_DATA_T) ); recordCRC = mfgRecord.crc; if ( calcCRC != recordCRC ) { hasCRCPassed = FALSE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_NVDATA_MFG_RECORD_CRC_ERROR, recordCRC, calcCRC ); } #endif // Check CRC for calibration record calcCRC = crc16( (U08*)&calibrationRecord.calData, sizeof(CALIBRATION_DATA_T) ); recordCRC = calibrationRecord.crc; if ( calcCRC != recordCRC ) { hasCRCPassed = FALSE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_NVDATA_CAL_RECORD_CRC_ERROR, recordCRC, calcCRC ); } else { calRecordIsValid = TRUE; } #ifndef LIMITED_NVDATA_CRC_CHECKS // Check CRC for service record calcCRC = crc16 ( (U08*)&serviceRecord.serviceData, sizeof(SERVICE_DATA_T) ); recordCRC = serviceRecord.crc; if ( calcCRC != recordCRC ) { hasCRCPassed = FALSE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_NVDATA_SRVC_RECORD_CRC_ERROR, recordCRC, calcCRC ); } #endif // There should be no failed CRCs and no command should timeout for the self test to pass if ( hasCRCPassed && !hasCommandTimedout ) { NVDataMgmtSelfTestResult = SELF_TEST_STATUS_PASSED; } else { NVDataMgmtSelfTestResult = SELF_TEST_STATUS_FAILED; } return state; } /************************************************************************* * @brief handleExecWaitForPostState * The handleExecWaitForPostState waits for POST to be completed * @details * Inputs : none * Outputs : NVDATAMGMT_EXEC_STATE_T * @return NVDATAMGMT_EXEC_STATE_T *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecWaitForPostState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_WAIT_FOR_POST; if ( NVDataMgmtSelfTestState == NVDATAMGMT_SELF_TEST_STATE_COMPLETE ) { state = NVDATAMGMT_EXEC_STATE_IDLE; } return state; } /************************************************************************* * @brief handleExecIdleState * The handleExecIdleState checks if the queue is empty and if it is not * empty, it sets the state of the job * @details * Inputs : none * Outputs : NVDATAMGMT_EXEC_STATE_T * @return NVDATAMGMT_EXEC_STATE_T *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecIdleState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_IDLE; if ( !isQueueEmpty() ) { dequeue(); NVDATAMGMT_OPERATION_STATE_T ops = currentJob.memoryOperation; NVDATAMGMT_LOCATION_STATE_T location = currentJob.memoryLocation; U32* startAddress = currentJob.startAddress; U32 length = currentJob.length; if ( ops == NVDATAMGMT_WRITE && location == NVDATAMGMT_EEPROM ) { currentTime = getMSTimerCount(); Fapi_issueProgrammingCommand ( startAddress, currentJob.buffer, length, 0x00, 0, Fapi_DataOnly ); state = NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM; } else if ( ops == NVDATAMGMT_READ && location == NVDATAMGMT_EEPROM ) { currentTime = getMSTimerCount(); Fapi_doMarginRead ( startAddress, (U32*)( currentJob.externalAddress->externalBuffer ), length, Fapi_NormalRead ); // Change the status to in progress until the read operation is done currentJob.externalAddress->status = NVDATAMGMT_READ_IN_PROGRESS; state = NVDATAMGMT_EXEC_STATE_READ_FROM_EEPROM; } else if ( ops == NVDATAMGMT_ERASE_SECTOR ) { currentTime = getMSTimerCount(); Fapi_issueAsyncCommandWithAddress ( Fapi_EraseSector, startAddress ); state = NVDATAMGMT_EXEC_STATE_ERASE_EEPROM; } else if ( ops == NVDATAMGMT_WRITE && location == NVDATAMGMT_RTC ) { if ( getRTCRAMState() == RTC_RAM_STATE_READY ) { currentTime = getMSTimerCount(); writeToRAM ( (U32)startAddress, currentJob.buffer, length ); state = NVDATAMGMT_EXEC_STATE_WRITE_TO_RTC; } } else if ( ops == NVDATAMGMT_READ && location == NVDATAMGMT_RTC ) { if ( getRTCRAMState() == RTC_RAM_STATE_READY ) { currentTime = getMSTimerCount(); readFromRAM( (U32)startAddress, length ); currentJob.externalAddress->status = NVDATAMGMT_READ_IN_PROGRESS; state = NVDATAMGMT_EXEC_STATE_READ_FROM_RTC; } } } return state; } /************************************************************************* * @brief handleExecWriteToEEPROMState * The handleExecWriteToEEPROMState issues a write command to EEPROM on entry * and if the write was successful, it sets the state to Idle * @details * Inputs : none * Outputs : NVDATAMGMT_EXEC_STATE_T * @return NVDATAMGMT_EXEC_STATE_T *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecWriteToEEPROMState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_NVDATA_EEPROM_OPS_FAILURE, (U08*)&state ); if ( FAPI_CHECK_FSM_READY_BUSY == Fapi_Status_FsmReady || timeoutStatus == TRUE ) { state = NVDATAMGMT_EXEC_STATE_IDLE; } return state; } /************************************************************************* * @brief handleExecReadFromEEPROMState * The handleExecReadFromEEPROMState issues a read command to EEPROM on entry * and if the read was successful, it sets the state to Idle * @details * Inputs : none * Outputs : NVDATAMGMT_EXEC_STATE_T * @return NVDATAMGMT_EXEC_STATE_T *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecReadFromEEPROMState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_READ_FROM_EEPROM; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_NVDATA_EEPROM_OPS_FAILURE, (U08*)&state ); if ( FAPI_CHECK_FSM_READY_BUSY == Fapi_Status_FsmReady || timeoutStatus == TRUE ) { currentJob.externalAddress->status = NVDATAMGMT_READ_COMPLETE; state = NVDATAMGMT_EXEC_STATE_IDLE; } return state; } /************************************************************************* * @brief handleExecEraseState * The handleExecEraseState issues an erase command to EEPROM on entry * and if the erase was successful, it sets the state to Idle * @details * Inputs : none * Outputs : NVDATAMGMT_EXEC_STATE_T * @return NVDATAMGMT_EXEC_STATE_T *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecEraseState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_ERASE_EEPROM; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_NVDATA_EEPROM_OPS_FAILURE, (U08*)&state ); if ( FAPI_CHECK_FSM_READY_BUSY == Fapi_Status_FsmReady || timeoutStatus == TRUE ) { state = NVDATAMGMT_EXEC_STATE_IDLE; } return state; } /************************************************************************* * @brief handleExecWriteToRAMState * The handleExecWriteToRAMState issues a write command to RTC RAM if the * RAM was ready, and if the RAM got back to Idle after the write command, * it sets the state to Idle * @details * Inputs : none * Outputs : NVDATAMGMT_EXEC_STATE_T * @return NVDATAMGMT_EXEC_STATE_T *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecWriteToRAMState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_WRITE_TO_RTC; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); if ( getRTCRAMStatus() == RTC_RAM_STATUS_IDLE || timeoutStatus == TRUE ) { state = NVDATAMGMT_EXEC_STATE_IDLE; } return state; } /************************************************************************* * @brief handleExecReadFromRAMState * The handleExecReadFromRAMState issues a read command to RTC RAM if the * RAM was ready, and if the RAM got back to Idle after the read command, * it calls another function to read the data back from RTC RAM and * it sets the state to Idle * @details * Inputs : none * Outputs : NVDATAMGMT_EXEC_STATE_T * @return NVDATAMGMT_EXEC_STATE_T *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecReadFromRAMState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_READ_FROM_RTC; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_RTC_RAM_OPS_ERROR, (U08*)&state ); if ( getRTCRAMStatus() == RTC_RAM_STATUS_IDLE || timeoutStatus == TRUE ) { getDataFromRAM( currentJob.externalAddress->externalBuffer, currentJob.length ); currentJob.externalAddress->status = NVDATAMGMT_READ_COMPLETE; state = NVDATAMGMT_EXEC_STATE_IDLE; } return state; } /************************************************************************* * @brief setMemoryOpsStruct * The setMemoryOpsStruct fills up the job struct * @details * Inputs : NVDATAMGMT_OPERATION_STATE_T (ops), NVDATAMGMT_LOCATION_STATE_T * (location), U32 (startAddress), U08* (data), READ_DATA_T* (extAddress) * U32 (length) * Outputs : none * @return none *************************************************************************/ static void setMemoryOpsStruct ( NVDATAMGMT_OPERATION_STATE_T ops, NVDATAMGMT_LOCATION_STATE_T location, U32 startAddress, U08* data, READ_DATA_T* extAddress, U32 length ) { U32 myAddIndex; _disable_IRQ(); myAddIndex = queueRearIndex; if ( FALSE == isQueueFull() ) { queueCount++; queueRearIndex = INC_WRAP( queueRearIndex, 0, QUEUE_MAX_SIZE - 1 ); jobQueue [ myAddIndex ].memoryOperation = ops; jobQueue [ myAddIndex ].memoryLocation = location; jobQueue [ myAddIndex ].startAddress = (U32*)startAddress; jobQueue [ myAddIndex ].length = length; jobQueue [ myAddIndex ].externalAddress = extAddress; if ( extAddress == 0 ) { memcpy ( jobQueue [ myAddIndex ].buffer, data, length ); } } _enable_IRQ(); } /************************************************************************* * @brief prepareWriteLogJobAndGetStartAddress * The prepareWriteLogJobAndGetStartAddress checks whether the next write * is at edge of the any of the sectors and if yes, it will update the log * header accordingly. If the write is not at the edge, it will prepare a * normal write to EEPROM job. * @details * Inputs : U08* (data) * Outputs : U32 (opsStartAddress) * @return U32 (opsStartAddress) *************************************************************************/ static U32 prepareWriteLogJobAndGetStartAddress ( U08* data ) { U32 modulus = 0; U32 opsStartAddress = 0; U16 readIndexChange = 0; U16 writeIndexChange = 0; U16 recordCountChange = 1; // The write address is calculated using the next write index and is and offset from // the start of sector 1. Sectors 1,2, and 3 have been allocated for logging data opsStartAddress = ( logRecord.logHeader.nextWriteIndex * MAX_JOB_DATA_SIZE_BYTES ) + BANK7_SECTOR1_START_ADDRESS; modulus = logRecord.logHeader.nextWriteIndex % MAX_LOG_DATA_PER_SECTOR; // Modulus is 0 so it is at any of the edges if ( modulus == 0 ) { // If full (1536) // 1. set readIndexChange = +512 // 2. set recordCountchange = -512 if ( logRecord.logHeader.recordCount >= MAX_NUM_OF_EVENTS_IN_SECTOR3 - 1 ) { logRecord.logHeader.recordCount = logRecord.logHeader.recordCount - MAX_LOG_DATA_PER_SECTOR; readIndexChange = MAX_LOG_DATA_PER_SECTOR; } setMemoryOpsStruct ( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_EEPROM, opsStartAddress, 0, 0, 0 ); } // Check for wrap in 1536 which is at the end of sector 3 writeIndexChange = INC_WRAP( logRecord.logHeader.nextWriteIndex, LOG_DATA_START_INDEX, MAX_NUM_OF_EVENTS_IN_SECTOR3 - 1 ); logRecord.logHeader.nextWriteIndex = writeIndexChange; logRecord.logHeader.recordCount = logRecord.logHeader.recordCount + recordCountChange; logRecord.logHeader.nextReadIndex = logRecord.logHeader.nextReadIndex + readIndexChange; logRecord.logHeader.isHdrCorrupted = FALSE; // Calculate the CRC for the new record logRecord.crc = crc16 ( (U08*)&logRecord.logHeader, sizeof(LOG_HEADER_T) ); // Update the log record setMemoryOpsStruct ( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, LOG_RECORD_START_ADDRESS, (U08*)&logRecord, 0, sizeof(LOG_RECORD_T) ); return opsStartAddress; } /************************************************************************* * @brief prepareReadLogJobAndGetStartAddress * The prepareReadLogJobAndGetStartAddress prepares a read from the specified * address of the EEPROM. * @details * Inputs : none * Outputs : U32 (opsStartAddress) * @return U32 (opsStartAddress) *************************************************************************/ static U32 prepareReadLogJobAndGetStartAddress ( void ) { // The read address is calculated using the next read index and is an offset from // the start of sector 1. Sectors 1,2, and 3 have been allocated for logging data U32 opsStartAddress = ( logRecord.logHeader.nextReadIndex * MAX_JOB_DATA_SIZE_BYTES ) + BANK7_SECTOR1_START_ADDRESS; U16 readIndexChange = INC_WRAP( logRecord.logHeader.nextReadIndex, LOG_DATA_START_INDEX, MAX_NUM_OF_EVENTS_IN_SECTOR3 - 1 ); logRecord.logHeader.nextReadIndex = readIndexChange; // Check if the read index has been wrapped to 0 and the flag is for data corruption // is true, the record count will be 0 and the data corruption flag will be false for // normal operations if ( logRecord.logHeader.nextReadIndex == 0 && logRecord.logHeader.isHdrCorrupted == TRUE ) { logRecord.logHeader.recordCount = 0; // If there are enough queues to schedule 3 erases // the flag will be turned off if ( eraseDataLogSectors () ) { logRecord.logHeader.isHdrCorrupted = FALSE; } } logRecord.crc = crc16 ( (U08*)&logRecord.logHeader, sizeof(LOG_HEADER_T) ); // Update the log record setMemoryOpsStruct ( NVDATAMGMT_WRITE, NVDATAMGMT_RTC, LOG_RECORD_START_ADDRESS, (U08*)&logRecord, 0, sizeof(LOG_RECORD_T) ); return opsStartAddress; } /************************************************************************* * @brief enqueue * The enqueue prepares the jobs to be processed. It checks if the requested * job is read or write, or if it is RTC or EEPROM and sets the job struct. * It checks for the corner cases, for instance, if the next write log to EEPROM * is at the beginning of the next sector, it schedules and erase first and then * it schedules the write jobs. The function breaks the write to EEPROM jobs to * 16 bytes at the time * @details * Inputs : NVDATAMGMT_OPERATION_STATE_T (ops), NVDATAMGMT_LOCATION_STATE_T * (location), U32 (startAddress), U08* (data), READ_DATA_T* (extAddress) * U32 (length) * Outputs : none * @return none *************************************************************************/ static void enqueue ( NVDATAMGMT_OPERATION_STATE_T ops, NVDATAMGMT_LOCATION_STATE_T location, U32 startAddress, U08* data, READ_DATA_T* extAddress, U32 length ) { U32 quotient = 0; U32 modulus = 0; U32 maxBufferLength = length; U32 opsStartAddress = startAddress; U08 i; // Setup EEPROM write log event if ( ops == NVDATAMGMT_WRITE && location == NVDATAMGMT_EEPROM ) { maxBufferLength = MAX_EEPROM_WRITE_BUFFER_BYTES; if ( startAddress == 0 ) { opsStartAddress = prepareWriteLogJobAndGetStartAddress ( data ); } } // Setup EEPROM read log event else if ( ops == NVDATAMGMT_READ && location == NVDATAMGMT_EEPROM ) { if ( startAddress == 0 ) { opsStartAddress = prepareReadLogJobAndGetStartAddress(); } } // Maximum length for write is 16 bytes // Maximum length for read is 32 bytes // If the length is greater than the specified max lengths, // the operation will be broken into multiple pieces if ( length > maxBufferLength ) { quotient = length / maxBufferLength; modulus = length % maxBufferLength; for ( i = 0; i < quotient; i++ ) { setMemoryOpsStruct ( ops, location, ( opsStartAddress + ( i * maxBufferLength ) ), data + ( i * maxBufferLength ), extAddress, maxBufferLength ); } if ( modulus != 0 ) { setMemoryOpsStruct ( ops, location, ( opsStartAddress + ( quotient * maxBufferLength ) ), data + ( quotient * maxBufferLength ), extAddress, modulus ); } } // The length is less than maximum specified, normal operations else { setMemoryOpsStruct ( ops, location, opsStartAddress, data, extAddress, length ); } } /************************************************************************* * @brief dequeue * The dequeue increments the front index counter and if it is equal to * rear index, it sets it to -1, meaning that the queue is empty * @details * Inputs : none * Outputs : none * @return none *************************************************************************/ static void dequeue ( void ) { U32 tempIndex; _disable_IRQ(); tempIndex = queueFrontIndex; if ( !isQueueEmpty() ) { queueFrontIndex = INC_WRAP( queueFrontIndex, 0, QUEUE_MAX_SIZE - 1 ); currentJob = jobQueue [ tempIndex ]; } if ( queueCount > 0 ) { queueCount--; } _enable_IRQ(); } /************************************************************************* * @brief isQueueEmpty * The isQueueEmpty checks whether the queue is empty and if it is empty, * it will return a false * @details * Inputs : none * Outputs : isEmpty (BOOL) * @return isEmpty (BOOL) *************************************************************************/ static BOOL isQueueEmpty ( void ) { BOOL isEmpty = TRUE; if ( queueCount > 0 ) { isEmpty = FALSE; } return isEmpty; } /************************************************************************* * @brief isQueueFull * The isQueueFull checks whether the queue is full and if it is full, * it will return a true * @details * Inputs : none * Outputs : isFull (BOOL) * @return isFull (BOOL) *************************************************************************/ static BOOL isQueueFull ( void ) { BOOL isFull = FALSE; if ( queueCount >= QUEUE_MAX_SIZE - 1 ) { isFull = TRUE; } return isFull; } /************************************************************************* * @brief getAvailableQueueCount * The getAvailableQueueCount returns the number of available queues left * @details * Inputs : none * Outputs : U32 * @return U32 *************************************************************************/ static U32 getAvailableQueueCount ( void ) { return QUEUE_MAX_SIZE - queueCount; } /************************************************************************* * @brief enqueueBank7Sector0Records * The enqueueBank7Sector0Records checks whether there are enough number of * queues for erasing and setting the records. If there are enough queues, * it schedules an erase job and then schdules jobs to write all the records * back to Bank 7 Sector 0 * @details * Inputs : none * Outputs : BOOL * @return BOOL *************************************************************************/ static BOOL enqueueBank7Sector0Records( void ) { BOOL status = FALSE; U32 currentQueueCount = getAvailableQueueCount(); if ( currentQueueCount >= MIN_JOBS_NEEDED_FOR_SECTOR_0 ) { // Sector 0 must be erased first enqueue ( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_EEPROM, BANK7_SECTOR0_START_ADDRESS, 0, 0, 0 ); enqueue ( NVDATAMGMT_WRITE, NVDATAMGMT_EEPROM, BANK7_SECTOR0_START_ADDRESS, (U08*)&mfgRecord, 0, sizeof(MFG_RECORD_T) ); enqueue ( NVDATAMGMT_WRITE, NVDATAMGMT_EEPROM, CALIBRATION_RECORD_START_ADDRESS, (U08*)&calibrationRecord, 0, sizeof(CALIBRATION_RECORD_T) ); status = TRUE; } return status; } /************************************************************************* * @brief didCommandTimedout * The didCommandTimedout checks whether the a command whether RTC RAM or * EEPROM has timedout. If it has timedout, it sets the alarm and turns * flag to TRUE * @details * Inputs : ALARM_ID_T (alarm), U08* state (pointer to the state) * Outputs : BOOL * @return BOOL *************************************************************************/ static BOOL didCommandTimeout ( ALARM_ID_T alarm, U08* state ) { /* * NOTE: The state is a pointer so it can cover both the self test * states and exec states */ BOOL status = FALSE; if ( didTimeout( currentTime, COMMAND_TIME_OUT ) ) { SET_ALARM_WITH_1_U32_DATA( alarm, *state ); hasCommandTimedout = TRUE; status = TRUE; } return status; } /************************************************************************* * @brief eraseDataLogSectors * The eraseDataLogSectors checks whether there are enough queues available * and if there are, it schedules 3 erases to erase sectors 1,2, and 3 * @details * Inputs : none * Outputs : BOOL * @return BOOL *************************************************************************/ static BOOL eraseDataLogSectors ( void ) { BOOL status = FALSE; U32 availableQueue = getAvailableQueueCount(); if ( availableQueue >= MIN_JOBS_NEEDED_FOR_DATA_LOG ) { enqueue( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_EEPROM, BANK7_SECTOR1_START_ADDRESS, 0, 0, 0 ); enqueue( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_EEPROM, BANK7_SECTOR2_START_ADDRESS, 0, 0, 0 ); enqueue( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_EEPROM, BANK7_SECTOR3_START_ADDRESS, 0, 0, 0 ); status = TRUE; } return status; }