/************************************************************************** * * 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) 24-Sep-2020 * * @author (original) Dara Navaei * @date (original) 12-Feb-2020 * ***************************************************************************/ // Includes #include // For memcpy #include // For ceil function #include "system.h" #include "F021.h" #include "MsgQueues.h" #include "NVDataMgmt.h" #include "RTC.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" #include "Utilities.h" /** * @addtogroup NVDataMgmt * @{ */ // 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_RTC_RAM_OPS_BUFFER_BYTES 64U ///< Max target RTC RAM operations (read/write) 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 LAST_SERVICE_RECORD_START_ADDRESS 0X00000050 // 80 ///< Last service date record start address in RTC RAM (HD/DG) (80). #define LAST_DISINFECTION_DATE_ADDRESS 0x00000060 // 96 //TODO remove ///< Last disinfection date start address in RTC RAM (96). #define DG_SCHEDULER_RECORD_START_ADDRESS 0x00000070 // 112 ///< DG scheduler record start address in RTC RAM (112). // 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. #define ERASE_CALIBRATION_KEY 0xD2C3B4A5 ///< 32-bit key required for clearing calibration data. // ********** Calibration data defines ********** #define NUM_OF_CONCENTRATE_PUMPS 2U ///< Number of concentrate pumps. #define NUM_OF_BYTES_PER_CAL_PAYLOAD 150U ///< Number of bytes per calibration payload. #define CAL_DATA_SEND_INTERVAL_COUNT ( MS_PER_SECOND / ( 5 * TASK_GENERAL_INTERVAL ) ) ///< Calibration data send time interval in counts. #define CAL_DATA_RECEIVE_TIMEOUT_MS ( 4 * MS_PER_SECOND ) ///< Calibration data receive all the data packets timeout in ms. #define CAL_DATA_MAX_MESSAGE_DFFIRENCE 1 ///< Calibration data receive message different from the previous message. #define CAL_DATA_FIRST_RECEIVING_MSG_NUM 1 ///< Calibration data first receiving message number. #define SYSTEM_DATA_NV_MEM_START_ADDRESS BANK7_SECTOR0_START_ADDRESS + 2048 ///< System record storage start address in NV memory. #define DG_CAL_STRUCTS_SIZE ( sizeof(DG_PRES_SENSORS_CAL_RECORD_T) + sizeof(DG_FLOW_SENSORS_CAL_RECORD_T) + \ sizeof(DG_LOAD_CELLS_CAL_RECORD_T) + sizeof(DG_TEMP_SENSORS_CAL_RECORD_T) + \ sizeof(DG_COND_SENSORS_CAL_RECORD_T) + sizeof(U16) ) ///< DG calibration data structure size (without padding). // Padding length calculation: (DG struct size % bytes to write(16) == 0 ? 0 : (DG struct size / bytes to write(16)) + 1) * bytes to write(16) #define DG_PADDING_LENGTH ( ( DG_CAL_STRUCTS_SIZE % MAX_EEPROM_WRITE_BUFFER_BYTES == 0 ? \ 0 : ((U32)(DG_CAL_STRUCTS_SIZE / MAX_EEPROM_WRITE_BUFFER_BYTES)) + 1 ) * \ MAX_EEPROM_WRITE_BUFFER_BYTES ) ///< DG padding length that is calculated with the size of the cal structures. /// NVDataMgmt self-test states enumeration. 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 enumeration. 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_VERIFY_WRITE, ///< Exec state verify write (EEPROM and RTC RAM). 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 enumeration. 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 enumeration 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; /// NVDataMgmt process records states typedef enum NVDataMgmt_Process_Records_States { NVDATAMGMT_PROCESS_RECORD_STATE_IDLE = 0, ///< NVDataMgmt process record idle state. NVDATAMGMT_PROCESS_RECORD_STATE_SEND_RECORD, ///< NVDataMgmt process record send record state. NUM_OF_NVDATAMGMT_PROCESS_RECORD_STATES ///< Number of NVDataMgmt process records state. } PROCESS_RECORD_STATE_T; /// NVDataMgmt receive records states typedef enum NVDataMgmt_Receive_Records_States { NVDATAMGMT_RECEIVE_RECORD_IDLE = 0, ///< NVDataMgmt receive record idle. NVDATAMGMT_RECEIVE_RECORD_RECEIVE, ///< NVDataMgmt receive record receive. NUM_OF_NVDATAMGMT_RECEIVE_RECORD_STATES ///< Number of NVDataMgmt receive record. } RECEIVE_RECORD_STATE_T; /// NVDataMgmt records' jobs states typedef enum NVDataMgmt_Records_Jobs { NVDATAMGMT_WRITE_DG_CALIBRATION_RECORD = 0, ///< NVDataMgmt process DG write calibration record. NVDATAMGMT_WRITE_DG_SYSTEM_RECORD, ///< NVDataMgmt process DG write system record. NVDATAMGMT_READ_DG_CALIBRATION_RECORD, ///< NVDataMgmt process DG read calibration record. NVDATAMGMT_READ_DG_SYSTEM_RECORD, ///< NVDataMgmt process DG read system record. NVDATAMGMT_PROCESS_DG_LAST_SERVICE_RECORD, ///< NVDATAMgmt process DG last service record. NVDATAMGMT_PROCESS_DG_SCHEDULER_RECORD, ///< NVDataMgmt process DG scheduler record. NVDATAMGMT_WRITE_HD_CALIBRATION_RECORD, ///< NVDataMgmt process HD write calibration record. NVDATAMGMT_WRITE_HD_SYSTEM_RECORD, ///< NVDataMgmt process HD write system record. NVDATAMGMT_READ_HD_CALIBRATION_RECORD, ///< NVDataMgmt process HD read calibration record. NVDATAMGMT_READ_HD_SYSTEM_RECORD, ///< NVDataMgmt process HD read system record. NVDATAMGMT_PROCESS_HD_SCHEDULER_RECORD, ///< NVDataMgmt process HD scheduler record. NUM_OF_NVDATMGMT_RECORDS_JOBS ///< Number of NVDataMgmt records jobs. } RECORD_JOBS_STATE_T; /// NVDataMgmt write record validity check states typedef enum NVDataMgmt_Write_Record_Validity_Check { NVDATAMGMT_RECORD_NOT_CHECKED = 0, ///< NVDataMgmt (written) record not checked. NVDATAMGMT_RECORD_VALID, ///< NVDataMgmt record is valid. NVDATAMGMT_RECORD_NOT_VALID, ///< NVDataMgmt record is not valid. NUM_OF_NVDATAMGMT_RECORD_VALIDITY_CHECK ///< Number of NVDataMgmt validity check states. } RECORD_VALIDITY_CHECK_T; #pragma pack(push, 1) /// Process records specifications structure typedef struct { U32 startAddress; ///< Jobs spec start address. U32 sizeofJob; ///< Jobs spec size of job. U32 maxBufferSize; ///< Jobs max allowed processing buffer size. } PROCESS_RECORD_SPECS_T; /// Process records job structure typedef struct { NVDATAMGMT_OPERATION_STATE_T memoryOperation; ///< Memory operation. NVDATAMGMT_LOCATION_STATE_T memoryLocation; ///< Memory location. RECORD_JOBS_STATE_T recordJob; ///< Record job (i.e sector 0). } PROCESS_RECORD_JOB_T; /// Memory operations structure. 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_LOG_OPS_T; /// Log header structure. 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 structure. typedef struct { LOG_HEADER_T logHeader; ///< Log header struct U16 crc; ///< Log header CRC } LOG_RECORD_T; /// Treatment time structure. typedef struct { U32 treatmentTime; ///< Treatment time U16 crc; ///< Treatment time CRC } TREATMENT_TIME_RECORD_T; /// Water consumption structure. typedef struct { U32 waterConsumption; ///< Water consumption U16 crc; ///< Water consumption CRC } WATER_CONSUMPTION_RECORD_T; /// Manufacturing data structure. 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 structure. typedef struct { SERVICE_DATA_T serviceData; ///< Service date struct U16 crc; ///< Service data CRC } SERVICE_RECORD_T; /// Calibration record structure. 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; //TODO remove /// Last disinfection record structure. typedef struct { DISINFECTION_DATE_T date; ///< Disinfection date (char array) U16 crc; ///< Disinfection date CRC } LAST_DISINFECTION_RECORD_T; // ********** Calibration data struts ********** /// Pressure sensors calibration structure typedef struct { LINEAR_CAL_PAYLOAD_T pressureSensors[ NUM_OF_CAL_DATA_PRES_SENSORS ]; ///< Pressure sensors to calibrate // NOTE: The reserved space is for 6 sensors. This portion of the struct should be eventually be // reduced, so #define was not used for the size of the array LINEAR_CAL_PAYLOAD_T reservedSpace[ 6 ]; ///< Reserved space for future pressure sensors } DG_PRES_SENSORS_CAL_RECORD_T; /// Flow sensors calibration structure typedef struct { LINEAR_CAL_PAYLOAD_T flowSensors[ NUM_OF_CAL_DATA_FLOW_SENSORS ]; ///< Flow sensors to calibrate // NOTE: The reserved space is for 6 sensors. This portion of the struct should be eventually be // reduced, so #define was not used for the size of the array LINEAR_CAL_PAYLOAD_T reservedSpace[ 3 ]; ///< Reserved space for future flow sensors } DG_FLOW_SENSORS_CAL_RECORD_T; /// Load cells calibration structure typedef struct { LINEAR_CAL_PAYLOAD_T loadCells[ NUM_OF_CAL_DATA_LOAD_CELLS ]; ///< Load cells to calibrate } DG_LOAD_CELLS_CAL_RECORD_T; /// Temperature sensors calibration structure typedef struct { LINEAR_CAL_PAYLOAD_T tempSensors[ NUM_OF_CAL_DATA_TEMP_SENSORS ]; ///< Temperature sensors to calibrate // NOTE: The reserved space is for 6 sensors. This portion of the struct should be eventually be // reduced, so #define was not used for the size of the array LINEAR_CAL_PAYLOAD_T reservedSpace[ 5 ]; ///< Reserved space for future temp sensors } DG_TEMP_SENSORS_CAL_RECORD_T; /// Conductivity sensors calibration structure typedef struct { LINEAR_CAL_PAYLOAD_T condSensors[ NUM_OF_CAL_DATA_COND_SENSORS ]; ///< Conductivity sensors to calibrate // NOTE: The reserved space is for 6 sensors. This portion of the struct should be eventually be // reduced, so #define was not used for the size of the array LINEAR_CAL_PAYLOAD_T reservedSpace[ 2 ]; ///< Reserved space for future conductivity sensors } DG_COND_SENSORS_CAL_RECORD_T; /// DG pumps calibration structure typedef struct { // Enum was not used for this array because the DG pumps enum contain DG and RO pumps too DG_CONCENTRATE_PUMPS_PAYLOAD_T concentratePump[ NUM_OF_CONCENTRATE_PUMPS ]; ///< Concentrate pumps ratios DG_DRAIN_PUMP_PAYLOAD_T drainPump; ///< Drain pump ratios DG_RO_PUMP_PAYLOAD_T roPump; ///< RO pump ratios (unknown for now) } DG_PUMPS_CAL_RECORD_T; // TODO add more calibration data /// DG calibration records structure typedef struct { DG_PRES_SENSORS_CAL_RECORD_T presSensorsCalRecord; ///< DG pressure sensors. DG_FLOW_SENSORS_CAL_RECORD_T flowSensorsCalRecord; ///< DG flow sensors. DG_LOAD_CELLS_CAL_RECORD_T loadCellsCalRecord; ///< DG load cells. DG_TEMP_SENSORS_CAL_RECORD_T tempSensorsCalRecord; ///< DG temperature sensors. DG_COND_SENSORS_CAL_RECORD_T condSensorsCalRecord; ///< DG conductivity sensors. //DG_PUMPS_CAL_RECORD_T pumpsCalRecord; ///< DG pumps. U08 padding[ DG_PADDING_LENGTH - DG_CAL_STRUCTS_SIZE ]; ///< DG calibration record padding byte array. U16 crc; ///< CRC for the DG calibration record structure. } DG_CALIBRATION_RECORD_T; /// DG systems record structure typedef struct { char topLevelPN[ MAX_TOP_LEVEL_PN_CHARS ]; ///< DG top level part number. char topLevelSN[ MAX_TOP_LEVEL_SN_CHARS ]; ///< DG top level serial number. MFG_LOCATION_T mfgLocation; ///< DG manufacturing location. U32 mfgDate; ///< DG manufacturing date. U16 crc; ///< CRC for the DG system record structure. } DG_SYSTEM_RECORD_T; /// DG last service record structure typedef struct { LAST_SERVICE_LOCATION_T lastServiceLoc; ///< DG last service location. U32 lastServiceDate; ///< DG last service date. U16 crc; ///< CRC for the DG last service record structure. } DG_LAST_SERVICE_RECORD_T; /// DG scheduler record structure typedef struct { //TODO fill up } DG_SCHEDULER_RECORD_T; #pragma pack(pop) // Process records specifications const PROCESS_RECORD_SPECS_T RECORDS_SPECS [ NUM_OF_NVDATMGMT_RECORDS_JOBS ] = { {BANK7_SECTOR0_START_ADDRESS, sizeof(DG_CALIBRATION_RECORD_T), MAX_EEPROM_WRITE_BUFFER_BYTES}, // NVDATAMGMT_WRITE_DG_CALIBRATION_RECORD {SYSTEM_DATA_NV_MEM_START_ADDRESS, sizeof(DG_SYSTEM_RECORD_T), MAX_EEPROM_WRITE_BUFFER_BYTES}, // NVDATAMGMT_WRITE_DG_SYSTEM_RECORD {BANK7_SECTOR0_START_ADDRESS, sizeof(DG_CALIBRATION_RECORD_T), sizeof(DG_CALIBRATION_RECORD_T)}, // NVDATAMGMT_READ_DG_CALIBRATION_RECORD {SYSTEM_DATA_NV_MEM_START_ADDRESS, sizeof(DG_SYSTEM_RECORD_T), sizeof(DG_SYSTEM_RECORD_T)}, // NVDATAMGMT_READ_DG_SYSTEM_RECORD {LAST_SERVICE_RECORD_START_ADDRESS, sizeof(DG_LAST_SERVICE_RECORD_T), MAX_RTC_RAM_OPS_BUFFER_BYTES}, // NVDATAMGMT_PROCESS_DG_LAST_SERVICE_RECORD {DG_SCHEDULER_RECORD_START_ADDRESS, sizeof(DG_SCHEDULER_RECORD_T), MAX_RTC_RAM_OPS_BUFFER_BYTES} // NVDATAMGMT_PROCESS_DG_SCHEDULER_RECORD }; // Calibration variables static PROCESS_RECORD_STATE_T NVDataMgmtExecProcessRecordState = NVDATAMGMT_PROCESS_RECORD_STATE_IDLE; ///< NVDataMgmt exec process record state. static DG_CALIBRATION_RECORD_T dgCalibrationRecord; ///< DG calibration record. static RECEIVE_RECORD_STATE_T NVDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; ///< NVDataMgmt exec receive record state. static BOOL isPublishCalRequested = FALSE; ///< Calibration state machine flag. static U32 calPublishMessageCount = 1; ///< Calibration data publish message counter. static U32 calPublishTotalMessages = 1; ///< Calibration data total number of messages to be sent. static U32 calSendDataIntervalCounter = 0; ///< Calibration data send to CAN bust interval counter. static U32 previousCalMessageNum = 0; ///< Calibration previous message number. static U32 dgCalRecordUpdateAddress = 0; ///< DG calibration record update address. static U32 calDataReceiveStartTime = 0; ///< Time stamp the calibration/service was received. static PROCESS_RECORD_JOB_T recordJobQueue[ QUEUE_MAX_SIZE ]; ///< Record queue jobs. static U08 recordQueueRearIndex; ///< Record queue rear index. static U08 recordQueueFrontIndex; ///< Record queue front index. static U08 recordQueueCount; ///< Record queue count. static PROCESS_RECORD_JOB_T recordCurrentJob; ///< Record queue current job. static U32 recordAddressOffset; ///< Record address offset. static RECORD_VALIDITY_CHECK_T writtenRecordStatus; ///< Record data write validity check. static U08 writtenRecordCheckBuffer[ MAX_EEPROM_WRITE_BUFFER_BYTES ]; ///< Written record validity check buffer. // Private variables static MEMORY_LOG_OPS_T jobQueue [ QUEUE_MAX_SIZE ]; ///< Job queue buffer. static MEMORY_LOG_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. // *** This declaration will cause a compiler error if CALIBRATION_DATA_T record size exceeds max message payload size. U08 calRecordSizeAssertion[ ( sizeof( CALIBRATION_DATA_T ) <= MAX_MSG_PAYLOAD_SIZE ? 1 : -1 ) ]; // 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 handleExecVerifyWriteState ( void ); static NVDATAMGMT_EXEC_STATE_T handleExecReadFromEEPROMState ( void ); static NVDATAMGMT_EXEC_STATE_T handleExecWriteToRAMState ( void ); static NVDATAMGMT_EXEC_STATE_T handleExecReadFromRAMState ( void ); static PROCESS_RECORD_STATE_T handleExecProcessRecordIdleState( void ); static PROCESS_RECORD_STATE_T handleExecProcessRecordSendRecordState( void ); // Log 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 ); // TODO remove // Helper functions static BOOL didCommandTimeout ( ALARM_ID_T alarm, U08* state ); static BOOL eraseDataLogSectors ( void ); // Record operations queue functions static void enqueueRecordJob( NVDATAMGMT_OPERATION_STATE_T ops, NVDATAMGMT_LOCATION_STATE_T location, RECORD_JOBS_STATE_T job ); static void dequeueRecordJob( void ); static BOOL isRecordQueueEmpty( void ); static U32 getAvailableRecordQueueCount( void ); static BOOL enqueueSector0Records( void ); /*********************************************************************//** * @brief * The initNVDataMgmt function initializes the module. * @details Inputs: NVDataMgmtSelfTestState, NVDataMgmtExecState, NVDataMgmtSelfTestResult, * queueRearIndex, queueFrontIndex, queueCount * @details Outputs: NVDataMgmtSelfTestState, NVDataMgmtExecState, NVDataMgmtSelfTestResult, * queueRearIndex, queueFrontIndex, queueCount * @return none *************************************************************************/ void initNVDataMgmt( void ) { NVDataMgmtSelfTestState = NVDATAMGMT_SELF_TEST_STATE_START; NVDataMgmtExecState = NVDATAMGMT_EXEC_STATE_WAIT_FOR_POST; NVDataMgmtSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; NVDataMgmtExecProcessRecordState = NVDATAMGMT_PROCESS_RECORD_STATE_IDLE; NVDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; queueRearIndex = QUEUE_START_INDEX; queueFrontIndex = QUEUE_START_INDEX; queueCount = 0; dgCalRecordUpdateAddress = 0; recordQueueRearIndex = QUEUE_START_INDEX; recordQueueFrontIndex = QUEUE_START_INDEX; recordQueueCount = 0; recordAddressOffset = 0; writtenRecordStatus = NVDATAMGMT_RECORD_NOT_CHECKED; Fapi_initializeFlashBanks( ROUNDED_HCLK_FREQ ); Fapi_setActiveFlashBank( Fapi_FlashBank7 ); Fapi_enableEepromBankSectors( BANK7_SECTOR_0_31_ENABLE_BIT_MASK, BANK7_SECTOR_32_63_ENABLE_BIT_MASK ); // TODO FOR TESTING ONLY, REMOVE dgCalibrationRecord.presSensorsCalRecord.pressureSensors[0].offset = 5.69; dgCalibrationRecord.presSensorsCalRecord.pressureSensors[0].gain = 15.2; dgCalibrationRecord.presSensorsCalRecord.pressureSensors[0].calibrationTime = 12345; dgCalibrationRecord.presSensorsCalRecord.pressureSensors[0].crc = 159; dgCalibrationRecord.condSensorsCalRecord.condSensors[CAL_DATA_CD2_COND_SENSOR].gain = 56.89; dgCalibrationRecord.condSensorsCalRecord.condSensors[CAL_DATA_CD2_COND_SENSOR].offset = 122.3; dgCalibrationRecord.condSensorsCalRecord.condSensors[CAL_DATA_CD2_COND_SENSOR].calibrationTime = 567890; dgCalibrationRecord.condSensorsCalRecord.condSensors[CAL_DATA_CD2_COND_SENSOR].crc = 3579; //dgCalibrationRecord.pumpsCalRecord.roPump.gain3Ratio = 56.78; //dgCalibrationRecord.pumpsCalRecord.roPump.calibrationTime = 85236; //dgCalibrationRecord.pumpsCalRecord.roPump.crc = 6789; dgCalibrationRecord.crc = 23678; // TODO FOR TESTING ONLY, REMOVE } /*********************************************************************//** * @brief * The signalPowerOffWarning signals this module that system power off is * imminent and any non-volatile data writes in progress should be wrapped up * quickly and any pending non-volatile data writes should not be started. * @details Inputs: none * @details Outputs: powerOffIsImminent * @return none *************************************************************************/ void signalPowerOffWarning( void ) { powerOffIsImminent = TRUE; } /*********************************************************************//** * @brief * The execNVDataMgmt runs the NVDataMgmt main tasks. * @details Inputs: NVDataMgmtExecState * @details Outputs: NVDataMgmtExecState, alarm if exec state was invalid * @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_VERIFY_WRITE: NVDataMgmtExecState = handleExecVerifyWriteState(); 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; } } /*********************************************************************//** * @brief * The execNVDataMgmtSelfTest runs the NVDataMgmt POST during the self-test. * @details Inputs: NVDataMgmtSelfTestState * @details Outputs: NVDataMgmtSelfTestState, alarm if there was a software fault * @return NVDataMgmtSelfTestResult the result of self-test *************************************************************************/ 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 * The execNVDataMgmtSendRecord runs the NVDataMgmt send records related * tasks. * @details Inputs: NVDataMgmtExecCalState * @details Outputs: NVDataMgmtExecCalState, alarm if exec state was invalid * @return none *************************************************************************/ void execNVDataMgmtSendRecord( void ) { switch ( NVDataMgmtExecProcessRecordState ) { case NVDATAMGMT_PROCESS_RECORD_STATE_IDLE: NVDataMgmtExecProcessRecordState = handleExecProcessRecordIdleState(); break; case NVDATAMGMT_PROCESS_RECORD_STATE_SEND_RECORD: NVDataMgmtExecProcessRecordState = handleExecProcessRecordSendRecordState(); break; default: #ifdef _DG_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_NVDATAMGMT_EXEC_CAL_STATE, NVDataMgmtExecProcessRecordState ); #else SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_NVDATAMGMT_EXEC_CAL_STATE, NVDataMgmtExecProcessRecordState ); #endif NVDataMgmtExecProcessRecordState = NVDATAMGMT_PROCESS_RECORD_STATE_IDLE; break; } // Check if the exec receive records is not idle if ( NVDataMgmtExecReceiveRecordState != NVDATAMGMT_RECEIVE_RECORD_IDLE ) { // Check if the data receiving process has timed out. The exec receive record // state machine is asynchronous so it is checked in this state machine if ( didTimeout( calDataReceiveStartTime, CAL_DATA_RECEIVE_TIMEOUT_MS ) ) { // TODO schedule a read NVDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; } } } /*********************************************************************//** * @brief * The getDGCalibrationRecord function sets the calibration state machine * to read and publish calibration record. * @details Inputs: isPublishCalRequested, NVDataMgmtExecCalState * @details Outputs: isPublishCalRequested * @return TRUE if the request was successfully registered *************************************************************************/ BOOL getDGCalibrationRecord( void ) { BOOL status = FALSE; // Check if the state machine is in idle state and then set the request if ( NVDataMgmtExecProcessRecordState == NVDATAMGMT_PROCESS_RECORD_STATE_IDLE ) { isPublishCalRequested = TRUE; status = TRUE; } return status; } /*********************************************************************//** * @brief * The setDGCalibrationRecord function writes the calibration record that * is received from Dialin into the calibration structure. * @details Inputs: previousCalMessageNum, dgCalRecordUpdateAddress, * calDataReceiveTime * @details Outputs: previousCalMessageNum, dgCalRecordUpdateAddress, * calDataReceiveTime * @param currentMessage: current message number that is received from * Dialin * @param totalMessages: total number of messages from Dialin * @param length: message length in bytes * @param *addressPtr: address to the beginning of the calibration data from * Dialin * @return TRUE if the request was successfully registered *************************************************************************/ BOOL setDGCalibrationRecord( U32 currentMessage, U32 totalMessages, U32 length, U08 *addressPtr ) { BOOL status = TRUE; // If the calibration message number is the first message number and receive exec state is idle, switch to idle if ( CAL_DATA_FIRST_RECEIVING_MSG_NUM == currentMessage && NVDataMgmtExecReceiveRecordState == NVDATAMGMT_RECEIVE_RECORD_IDLE ) { NVDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_RECEIVE; calDataReceiveStartTime = getMSTimerCount(); previousCalMessageNum = 0; dgCalRecordUpdateAddress = 0; } // Check if there is still a message left to be received if ( NVDataMgmtExecReceiveRecordState == NVDATAMGMT_RECEIVE_RECORD_RECEIVE && currentMessage <= totalMessages ) { // Check if the current message is different from the previous message by 1 if ( CAL_DATA_MAX_MESSAGE_DFFIRENCE == ( currentMessage - previousCalMessageNum ) ) { // Define a pointer that points to the DG calibration record U08* ptr = (U08*)&dgCalibrationRecord; // Offset the pointer to length that we should start writing from ptr += dgCalRecordUpdateAddress; memcpy(ptr, addressPtr, length); // Check if the current message is total messages // and 0 everything out since we are done writing if ( currentMessage == totalMessages ) { // Get the CRC of the structure without the last 16 bits which is the CRC as well as the padding values U16 calcCRC = crc16 ( (U08*)&dgCalibrationRecord, sizeof(DG_CALIBRATION_RECORD_T) - sizeof(dgCalibrationRecord.crc) ); U16 recordCRC = dgCalibrationRecord.crc; if ( calcCRC != recordCRC ) { // CRC failed, request a read to read the data back from NV memory and update the structure enqueueRecordJob( NVDATAMGMT_READ, NVDATAMGMT_EEPROM, NVDATAMGMT_READ_DG_CALIBRATION_RECORD ); NVDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; status = FALSE; } else { // CRC passed, enqueue an erase, a write of calibration data and a write of service record BOOL scheduleStatus = enqueueSector0Records(); // Done with receiving data, go back to idle NVDataMgmtExecReceiveRecordState = NVDATAMGMT_RECEIVE_RECORD_IDLE; } } else { // Update the length as it has successfully been written dgCalRecordUpdateAddress += length; // Now the current message is the previous message previousCalMessageNum = currentMessage; } } } return status; } /*********************************************************************//** * @brief * The getDGPressureSensorsCalibrationRecord function copies the DG pressure * sensors calibration record into a buffer. * @details Inputs: none * @details Outputs: none * @param buffer: address to the buffer to copy the data into it * @return none *************************************************************************/ void getDGPressureSensorsCalibrationRecord( U08* buffer ) { memcpy( buffer, &dgCalibrationRecord.presSensorsCalRecord, sizeof(dgCalibrationRecord.presSensorsCalRecord)); } void getDGFlowSensorsCalibrationRecord( U08* buffer ) { memcpy( buffer, &dgCalibrationRecord.flowSensorsCalRecord, sizeof(dgCalibrationRecord.condSensorsCalRecord)); } /*********************************************************************//** * @brief * The setMfgData updates the structure 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: mfgRecord * @details Outputs: mfgRecord * @return TRUE is the enqueue was successfully scheduled *************************************************************************/ 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 * The getMfgData returns the data in the structure that holds manufacturing * record to buffer that the caller has provided. * @details Inputs: mfgRecord * @details Outputs: none * @param buffer address of manufacturing data buffer * @return TRUE if the buffer pointer was not null and the copy was successful *************************************************************************/ 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 * The setCalibrationData updates the structure 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: calibrationRecord * @details Outputs: calibrationRecord * @param data calibration data structure * @return TRUE if updating was successfully scheduled *************************************************************************/ 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 * The getCalibrationData function returns the data in the structure that hold * calibration record to buffer that the caller has provided. * @details Inputs: calibrationRecord * @details Outputs: none * @param buffer address of calibration data buffer * @return TRUE if calibration record valid *************************************************************************/ 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 * The testResetCalibrationData function erases the calibration data. * @details * Inputs: calibrationRecord * Outputs: none * @param key a key is required to erase calibration data * @return TRUE if calibration record valid *************************************************************************/ BOOL testResetCalibrationData( U32 key ) { BOOL status = FALSE; // tester must be logged in if ( TRUE == isTestingActivated() ) { // verify key if ( ERASE_CALIBRATION_KEY == key ) { // erase calibration record memset( (U08*)&calibrationRecord.calData, 0, sizeof(CALIBRATION_DATA_T) ); // zero CRC so integrity check will fail unless calibration data set again calibrationRecord.crc = 0; // save erased calibration record // queue job to re-store all non-volatile data records to memory bank 7, sector 0 (sector allocated for these records) from RAM status = enqueueBank7Sector0Records(); } } return status; } /*********************************************************************//** * @brief * The setServiceDate updates the structure 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: serviceRecord * @details Outputs: serviceRecord * @param data service data buffer * @return TRUE if the queue had enough space to schedule the job *************************************************************************/ 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 * The getServiceDate returns the data in the structure that holds service * date to buffer that the caller has provided. * @details Inputs: serviceRecord * @details Outputs: none * @param buffer address of service data buffer * @return TRUE if the buffer address was not null *************************************************************************/ 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 * 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: logRecord * @details Outputs: none * @param data address of log data buffer * @return TRUE if the queue is not full and if the log header is not corrupted *************************************************************************/ 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 * 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: none * @details Outputs: none * @param buffer address of read data buffer * @param length length of the data log * @return TRUE if there are enough queues available for the job *************************************************************************/ 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 * The setTreatmentTime sets a queue job to write the treatment time in * the specified RAM address. * @details Inputs: treatmentTimeRecord * @details Outputs: treatmentTimeRecord * @param hours treatmet time in hours * @return TRUE if the queue was not full so the job was scheduled successfully *************************************************************************/ 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 * The getTreatmentTime returns the total number treatment hours of the * HD device. * @details Inputs: none * Output: none * @return treatment time as U32 *************************************************************************/ U32 getTreatmentTime ( void ) { return treatmentTimeRecord.treatmentTime; } /*********************************************************************//** * @brief * The setWaterConsumption sets a queue job to write the amount of water * that has been consumed in DG. * @details Inputs: waterConsumptionRecord * @details Outputs: waterConsumptionRecord * @param liters consumed water is liters * @return TRUE if queue is not full *************************************************************************/ 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 * The getWaterConsumption returns the amount of consumed water. * @details Inputs: none * @details Outputs: none * @return water consumption as a U32 *************************************************************************/ U32 getWaterConsumption ( void ) { return waterConsumptionRecord.waterConsumption; } /*********************************************************************//** * @brief * 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: lastDisinfectionRecord * @details Outputs: lastDisinfectionRecord * @param date disinfection date buffer * @return TRUE if the queue was not full *************************************************************************/ 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 * The getDisinfectionDate populates the provided buffer with the last * disinfection date. * @details Inputs: lastDisinfectionRecord * @details Outputs: none * @param buffer address of disinfection date buffer * @return TRUE if the buffer address was not null *************************************************************************/ 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 * The setBootloaderFlag sets the bootloader flag to RTC RAM. * @details Inputs: none * @details Outputs: none * @param flag the bootloader flag * @return TRUE if the queue was not full *************************************************************************/ 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 * The getBootloaderFlag gets the bootloader flag from RTC RAM. * @details Inputs: none * @details Outputs: none * @return bootloader flag as a U32 *************************************************************************/ U32 getBootloaderFlag( void ) { return bootloaderFlag; } /*********************************************************************//** * @brief * The handleSelfTestStart enables the EEPROM bank sectors. * @details Inputs: currentTime * @details Outputs: currentTime * @return next state *************************************************************************/ 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 * The handleSelfTestEnableEEPROM waits for EEPROM to return with ready from * enabling the EEPROM command. * @details Inputs: currentTime * @details Outputs: currentTime * @return next state *************************************************************************/ 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 * The handleSelfTestReadBootloaderFlag reads the bootloader flag * from RTC RAM. * @details Inputs: currentTime, bootloaderFlag * @details Outputs: currentTime * @return next state *************************************************************************/ 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 * The handleSelfTestReadHDTreatmentTime reads the HD treatment time * from RTC RAM. * @details Inputs: currentTime, treatmentTimeRecord * @details Outputs: currentTime * @return next state *************************************************************************/ 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 * The handleSelfTestReadDGWaterConsumption reads the DG water consumption * from RTC RAM. * @details Inputs: currentTime, waterConsumptionRecord * @details Outputs: currentTime * @return next state *************************************************************************/ 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 * The handleSelfTestReadLogRecord reads the log record from RTC RAM. * @details Inputs: currentTime, logRecord, mfgRecord * @details Outputs: currentTime * @return next state *************************************************************************/ 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 * The handleSelfTestReadMfgRecords reads the manufacturing record from * EEPROM. * @details Inputs: currentTime, calibrationRecord * @details Outputs: currentTime * @return next state *************************************************************************/ 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 * The handleSelfTestReadCalibrationRecord reads the calibration record * from EEPROM. * @details Inputs: currentTime * @details Outputs: currentTime * @return next state *************************************************************************/ 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 * The handleSelfTestReadServiceRecord reads the service dates from RTC * RAM. * @details Inputs: currentTime, serviceRecord * @details Outputs: currentTime * @return next state *************************************************************************/ 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 * The handleSelfTestReadLastDisinfectionDate reads the last disinfection date * from RTC RAM. * @details Inputs: lastDisinfectionRecord * @details Outputs: none * @return next state *************************************************************************/ 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 * 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: treatmentTimeRecord, waterConsumptionRecord, lastDisinfectionRecord, * logRecord, mfgRecord, calibrationRecord, calibrationRecord, serviceRecord, * NVDataMgmtSelfTestResult * @details Outputs: NVDataMgmtSelfTestResult, alarm if any of the CRC checks failed * @return next state *************************************************************************/ 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 * The handleExecWaitForPostState waits for POST to be completed. * @details Inputs: NVDataMgmtSelfTestState * @details Outputs: none * @return next state *************************************************************************/ 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 * The handleExecIdleState checks if the queue is empty and if it is not * empty, it sets the state of the job. * @details Inputs: currentJob, currentTime * @details Outputs: currentJob * @return next state *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecIdleState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_IDLE; NVDATAMGMT_OPERATION_STATE_T ops; NVDATAMGMT_LOCATION_STATE_T location; U32 startAddress; U08* bufferAddress; U32 maxBufferLength; BOOL areQueuesEmpty = FALSE; // If the record processing queue is not empty, process the queues if ( !isRecordQueueEmpty() ) { dequeueRecordJob(); RECORD_JOBS_STATE_T job = recordCurrentJob.recordJob; PROCESS_RECORD_SPECS_T jobSpecs = RECORDS_SPECS [ job ]; // Set the record address offset to 0 since a job will just be started recordAddressOffset = 0; areQueuesEmpty = TRUE; ops = recordCurrentJob.memoryOperation; location = recordCurrentJob.memoryLocation; currentTime = getMSTimerCount(); startAddress = jobSpecs.startAddress + recordAddressOffset; bufferAddress = (U08*)&dgCalibrationRecord + recordAddressOffset; maxBufferLength = jobSpecs.maxBufferSize; } // Check if a queue job is available if ( areQueuesEmpty ) { if ( ops == NVDATAMGMT_ERASE_SECTOR ) { Fapi_issueAsyncCommandWithAddress ( Fapi_EraseSector, (U32*)startAddress ); state = NVDATAMGMT_EXEC_STATE_ERASE_EEPROM; } else if ( ops == NVDATAMGMT_WRITE && location == NVDATAMGMT_EEPROM ) { Fapi_issueProgrammingCommand ( (U32*)startAddress, (U08*)bufferAddress, maxBufferLength, 0x00, 0, Fapi_DataOnly ); state = NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM; } else if ( ops == NVDATAMGMT_READ && location == NVDATAMGMT_EEPROM ) { Fapi_doMarginRead ( (U32*)startAddress, (U32*)bufferAddress, maxBufferLength, Fapi_NormalRead ); state = NVDATAMGMT_EXEC_STATE_READ_FROM_EEPROM; } } // TODO clean up // Check if the queue is not empty and the calibration/service status is none // Processing calibration and service status have priority /*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 ( (U32*)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 ( (U32*)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, (U32*)startAddress ); state = NVDATAMGMT_EXEC_STATE_ERASE_EEPROM; } else if ( ops == NVDATAMGMT_WRITE && location == NVDATAMGMT_RTC ) { if ( getRTCRAMState() == RTC_RAM_STATE_READY ) { currentTime = getMSTimerCount(); writeToRAM ( 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( startAddress, length ); currentJob.externalAddress->status = NVDATAMGMT_READ_IN_PROGRESS; state = NVDATAMGMT_EXEC_STATE_READ_FROM_RTC; } } }*/ return state; } /*********************************************************************//** * @brief * The handleExecWriteToEEPROMState issues a write command to EEPROM and * if the write was successful, it sets the state to Idle. * @details Inputs: recordAddressOffset, writtenRecordCheckBuffer, * writtenRecordStatus * @details Outputs: recordAddressOffset, writtenRecordCheckBuffer, * writtenRecordStatus * @return next state of the state machine *************************************************************************/ 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 ); RECORD_JOBS_STATE_T job = recordCurrentJob.recordJob; PROCESS_RECORD_SPECS_T jobSpecs = RECORDS_SPECS [ job ]; // Check if the fapi has finished if ( FAPI_CHECK_FSM_READY_BUSY == Fapi_Status_FsmReady ) { // Check the integrity of data (the 16 bytes that were written to EEPROM should be read and be checked for each byte) if ( writtenRecordStatus == NVDATAMGMT_RECORD_NOT_CHECKED ) { currentTime = getMSTimerCount(); U32 startAddress = jobSpecs.startAddress + recordAddressOffset; // Clear the buffer from the previous content memset( writtenRecordCheckBuffer, 0, sizeof(writtenRecordCheckBuffer)); // Issue a FAPI read command Fapi_doMarginRead ( (U32*)startAddress, (U32*)writtenRecordCheckBuffer, jobSpecs.maxBufferSize, Fapi_NormalRead ); state = NVDATAMGMT_EXEC_STATE_VERIFY_WRITE; } else if ( writtenRecordStatus == NVDATAMGMT_RECORD_VALID ) { // If the data is valid, and if it is at the end of the write, change to idle if ( jobSpecs.sizeofJob == recordAddressOffset ) { state = NVDATAMGMT_EXEC_STATE_IDLE; } else { // Update the variables and issue the next write command currentTime = getMSTimerCount(); recordAddressOffset += jobSpecs.maxBufferSize; U32 memoryPtr = jobSpecs.startAddress + recordAddressOffset; U08* structPtr = (U08*)&dgCalibrationRecord + recordAddressOffset; writtenRecordStatus = NVDATAMGMT_RECORD_NOT_CHECKED; Fapi_issueProgrammingCommand ( (U32*)memoryPtr, structPtr, jobSpecs.maxBufferSize, 0x00, 0, Fapi_DataOnly ); } } } // If timed out, get back to Idle else if ( timeoutStatus == TRUE ) { // TODO schedule another write? recordAddressOffset = 0; state = NVDATAMGMT_EXEC_STATE_IDLE; } return state; } /*********************************************************************//** * @brief * The handleExecVerifyWriteState checks all the bytes that were written * to either EEPROM or RTC RAM to ensure they match the data in the RAM. * @details Inputs: recordAddressOffset * @details Outputs: recordAddressOffset * @return next state of the state machine *************************************************************************/ static NVDATAMGMT_EXEC_STATE_T handleExecVerifyWriteState ( void ) { NVDATAMGMT_EXEC_STATE_T state = NVDATAMGMT_EXEC_STATE_VERIFY_WRITE; BOOL timeoutStatus = didCommandTimeout ( ALARM_ID_NVDATA_EEPROM_OPS_FAILURE, (U08*)&state ); RECORD_JOBS_STATE_T job = recordCurrentJob.recordJob; PROCESS_RECORD_SPECS_T jobSpecs = RECORDS_SPECS [ job ]; // Check if the write job is EEPROM or RTC RAM if ( recordCurrentJob.memoryLocation == NVDATAMGMT_EEPROM && FAPI_CHECK_FSM_READY_BUSY == Fapi_Status_FsmReady ) { // Check if the job is write calibration or system record if ( job == NVDATAMGMT_WRITE_DG_CALIBRATION_RECORD ) { U32 i; // Set the buffer pointer to beginning of the bytes that were written and needs to be checked U08* bufferPtr = (U08*)&dgCalibrationRecord + recordAddressOffset; // Loop through the bytes in the buffer for ( i = 0; i < jobSpecs.maxBufferSize; i++ ) { // Check if data in the buffer is not the same as the data in the structure if ( writtenRecordCheckBuffer[ i ] != *bufferPtr ) { // Data is not valid. Schedule an erase and write of the calibration record since only a part of // data cannot be written to EEPROM. writtenRecordStatus = NVDATAMGMT_RECORD_NOT_VALID; enqueueRecordJob( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_EEPROM, NVDATAMGMT_WRITE_DG_CALIBRATION_RECORD ); enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_EEPROM, NVDATAMGMT_WRITE_DG_CALIBRATION_RECORD ); state = NVDATAMGMT_EXEC_STATE_IDLE; // Exit the loop since there is no point to check the rest of data break; } else { // Everything is good increment the pointer to check the next byte. bufferPtr++; // Record data is valid so far writtenRecordStatus = NVDATAMGMT_RECORD_VALID; // Go back write to EEPROM state to continue writing the record data state = NVDATAMGMT_EXEC_STATE_WRITE_TO_EEPROM; } } } } 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: currentJob * @details Outputs: currentJob * @return next state *************************************************************************/ 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 * 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 * @details Outputs: none * @return next state *************************************************************************/ 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 * 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 * @details Outputs: none * @return next state *************************************************************************/ 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 * 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: currentJob * @details Outputs: currentJob * @return next state *************************************************************************/ 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 * The handleExecProcessRecordIdleState handles the idle state of the * exec cal state machine. * @details Inputs: isPublishCalRequested, isPublishCalRequested, * calPublishTotalMessages, calPublishMessageCount, calSendDataIntervalCounter * @details Outputs: isPublishCalRequested, calPublishTotalMessages, * calPublishMessageCount, calSendDataIntervalCounter * @return next state of the state machine *************************************************************************/ static PROCESS_RECORD_STATE_T handleExecProcessRecordIdleState( void ) { PROCESS_RECORD_STATE_T state = NVDATAMGMT_PROCESS_RECORD_STATE_IDLE; if ( isPublishCalRequested ) { // Set the calibration flag to FALSE isPublishCalRequested = FALSE; // Calculate the total number of messages required to be sent using ceilf function. This function rounds up the // value and its result is converted to U32. calPublishTotalMessages = (U32)ceilf( (F32)sizeof( DG_CALIBRATION_RECORD_T ) / (F32)NUM_OF_BYTES_PER_CAL_PAYLOAD ); calPublishMessageCount = 0; // Time interval in between data to be sent. It is set to the interval count so on the first call // of the send calibration record function, the first packet of data is sent calSendDataIntervalCounter = CAL_DATA_SEND_INTERVAL_COUNT; state = NVDATAMGMT_PROCESS_RECORD_STATE_SEND_RECORD; } return state; } /*********************************************************************//** * @brief * The handleExecProcessRecordSendRecordState handles the send calibration * record state of the state machine. * @details Inputs: dgCalibrationRecord, calPublishTotalMessages, * calPublishMessageCount, calSendDataIntervalCounter * @details Outputs: calPublishTotalMessages, calPublishMessageCount, * calSendDataIntervalCounter * @return next state of the state machine *************************************************************************/ static PROCESS_RECORD_STATE_T handleExecProcessRecordSendRecordState( void ) { PROCESS_RECORD_STATE_T state = NVDATAMGMT_PROCESS_RECORD_STATE_SEND_RECORD; // If the current message number is less than the total, keep sending if ( calPublishMessageCount < calPublishTotalMessages ) { // If it is time to send data if ( ++calSendDataIntervalCounter >= CAL_DATA_SEND_INTERVAL_COUNT ) { // Set to default cal data payload length U32 length = NUM_OF_BYTES_PER_CAL_PAYLOAD; U08* startPtr = (U08*)&dgCalibrationRecord; // If this is the last calibration data payload, calculate the remainder of the bytes to send if ( ( calPublishMessageCount + 1 ) == calPublishTotalMessages ) { length = sizeof( DG_CALIBRATION_RECORD_T ) - ( calPublishMessageCount * NUM_OF_BYTES_PER_CAL_PAYLOAD ); } // Find the new location of the pointer which is the start of the calibration payload to be sent startPtr += calPublishMessageCount * NUM_OF_BYTES_PER_CAL_PAYLOAD; // Pass the information to be written to CAN bus sendDGCalibrationRecord( calPublishMessageCount + 1, calPublishTotalMessages, length, startPtr ); calPublishMessageCount++; calSendDataIntervalCounter = 0; } } else { state = NVDATAMGMT_PROCESS_RECORD_STATE_IDLE; } return state; } /*********************************************************************//** * @brief * The setMemoryOpsStruct function checks whether it is an EEPROM write, * EEPROM read, or RTC RAM operation. For read or write data logs into EEPROM, * it calls other functions to check for wraps and to find the next address for * read or write. The function then checks for the length of the buffer and if * it is greater than the specified max buffer, it breaks the operation into * multiple jobs. * @details Inputs: jobQueue, queueCount, queueRearIndex * @details Outputs: jobQueue, queueCount, queueRearIndex * @param ops type of operation (i.e. write to EEPROM) * @param location location of operation (i.e. RTC RAM) * @param startAddress start address of operation * @param data address to the data buffer * @param extAddress address of the external buffer. Used for reading the log * data by a client * @param length length of data for operation * @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 = startAddress; jobQueue [ myAddIndex ].length = length; jobQueue [ myAddIndex ].externalAddress = extAddress; if ( extAddress == 0 ) { memcpy ( jobQueue [ myAddIndex ].buffer, data, length ); } } _enable_IRQ(); } /*********************************************************************//** * @brief * 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: logRecord * @details Outputs: logRecord * @param data address of data buffer * @return address of the next write as a U32 *************************************************************************/ 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 * The prepareReadLogJobAndGetStartAddress prepares a read from the specified * address of the EEPROM. * @details Inputs: logRecord * @details Outputs: logRecord * @return address of the next read as a U32 *************************************************************************/ 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 * 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: none * @details Outputs: none * @param ops type of operation (i.e. write to EEPROM) * @param location location of operation (i.e. RTC RAM) * @param startAddress start address of operation * @param data address to the data buffer * @param extAddress address of the external buffer. Used for reading the log * data by a client * @param length length of data for operation * @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 the start address is 0, it means it is a data log and the start // address must be found by referring to the log record header if ( startAddress == 0 ) { opsStartAddress = prepareWriteLogJobAndGetStartAddress ( data ); } } // Setup EEPROM read log event else if ( ops == NVDATAMGMT_READ && location == NVDATAMGMT_EEPROM ) { // If the start address is 0, it means it is a data log and the start // address must be found by referring to the log record header 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 * 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: queueFrontIndex, queueCount * @details Outputs: queueFrontIndex, queueCount * @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 * The isQueueEmpty checks whether the queue is empty and if it is empty, * it will return a false. * @details Inputs: queueCount * @details Outputs: none * @return TRUE if queue is not empty *************************************************************************/ static BOOL isQueueEmpty ( void ) { BOOL isEmpty = TRUE; if ( queueCount > 0 ) { isEmpty = FALSE; } return isEmpty; } /*********************************************************************//** * @brief * The isQueueFull checks whether the queue is full and if it is full, * it will return a true. * @details Inputs: queueCount * @details Outputs: none * @return TRUE is queue is full *************************************************************************/ static BOOL isQueueFull ( void ) { BOOL isFull = FALSE; if ( queueCount >= QUEUE_MAX_SIZE - 1 ) { isFull = TRUE; } return isFull; } /*********************************************************************//** * @brief * The getAvailableQueueCount returns the number of available queues left. * @details Inputs: queueCount * @details Outputs: none * @return available queue counts as a U32 *************************************************************************/ static U32 getAvailableQueueCount ( void ) { return QUEUE_MAX_SIZE - queueCount; } /*********************************************************************//** * @brief * 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: mfgRecord, calibrationRecord * @details Outputs: none * @return TRUE if there are enough queue jobs available *************************************************************************/ 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 * 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: none * @details Outputs: alarm if command timed out * @param alarm alarm ID * @param state the state that the command timed out * @return TRUE if a command timed out *************************************************************************/ 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 * 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 * @details Outputs: non * @return TRUE if there are enough queues available *************************************************************************/ 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; } /*********************************************************************//** * @brief * 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: queueFrontIndex, queueCount * @details Outputs: queueFrontIndex, queueCount * @param ops: memory operation (i.e write, read) * @param location: memory location which are either EEPROM or RTC RAM * @param job: type of job (i.e write calibration data) * @return none *************************************************************************/ static void enqueueRecordJob( NVDATAMGMT_OPERATION_STATE_T ops, NVDATAMGMT_LOCATION_STATE_T location, RECORD_JOBS_STATE_T job ) { PROCESS_RECORD_JOB_T currentJob; currentJob.memoryLocation = location; currentJob.memoryOperation = ops; currentJob.recordJob = job; recordJobQueue[ recordQueueRearIndex ] = currentJob; recordQueueCount++; recordQueueRearIndex = INC_WRAP( recordQueueRearIndex, 0, QUEUE_MAX_SIZE - 1 ); } /*********************************************************************//** * @brief * The dequeueRecordJob 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: recordQueueFrontIndex, recordQueueCount, recordCurrentJob, * recordJobQueue * @details Outputs: recordQueueFrontIndex, recordQueueCount, recordCurrentJob * @return none *************************************************************************/ static void dequeueRecordJob( void ) { U32 tempIndex; _disable_IRQ(); tempIndex = recordQueueFrontIndex; if ( !isRecordQueueEmpty() ) { recordQueueFrontIndex = INC_WRAP( recordQueueFrontIndex, 0, QUEUE_MAX_SIZE - 1 ); recordCurrentJob = recordJobQueue[ tempIndex ]; } if ( recordQueueCount > 0 ) { recordQueueCount--; } _enable_IRQ(); } /*********************************************************************//** * @brief * The isRecordQueueEmpty checks whether the queue is empty and if it is * empty, it will return a false. * @details Inputs: recordQueueCount * @details Outputs: none * @return TRUE if queue is not empty *************************************************************************/ static BOOL isRecordQueueEmpty( void ) { BOOL isEmpty = TRUE; if ( recordQueueCount > 0 ) { isEmpty = FALSE; } return isEmpty; } /*********************************************************************//** * @brief * The getAvailableRecordQueueCount returns the number of available record * queues. * @details Inputs: recordQueueCount * @details Outputs: none * @return available record queues *************************************************************************/ static U32 getAvailableRecordQueueCount( void ) { return QUEUE_MAX_SIZE - recordQueueCount; } /*********************************************************************//** * @brief * The enqueueSector0Records checks whether there are enough jobs available * to be able to enqueue to the records. * @details Inputs: none * @details Outputs: none * @return TRUE if the job were successfully enqueued otherwise, FLASE *************************************************************************/ static BOOL enqueueSector0Records( void ) { BOOL status = FALSE; if ( getAvailableRecordQueueCount() >= MIN_JOBS_NEEDED_FOR_SECTOR_0 ) { enqueueRecordJob( NVDATAMGMT_ERASE_SECTOR, NVDATAMGMT_EEPROM, NVDATAMGMT_WRITE_DG_CALIBRATION_RECORD ); enqueueRecordJob( NVDATAMGMT_WRITE, NVDATAMGMT_EEPROM, NVDATAMGMT_WRITE_DG_CALIBRATION_RECORD ); // TODO schedule systems record status = TRUE; } return status; } /**@}*/