/************************************************************************** * * Copyright (c) 2026-2027 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 NVMgmtDD.c * * @author (original) Arpita Srivastava * @date (original) 31-Mar-2026 * ***************************************************************************/ #include // For memcpy #include "Common.h" #include "NVDriver.h" #include "NVMgmtDD.h" #include "NVMsgQ.h" #include "NVRecordsDD.h" #include "Timers.h" /** * @addtogroup NVMgmtDD * @{ */ // ********** private definitions ********** /// EEPROM functions use the buffer length as the size of U32. So before send the length to any of FAPI functions, it should be divided by 4. #define EEPROM_OPS_SIZE_OF_CONVERTER 4 #define COMMAND_TIME_OUT (1 * MS_PER_SECOND) ///< Timeout for an EEPROM in ms. /// NVM Exec states enumeration. typedef enum NVM_Exec_State { NVM_EXEC_STATE_IDLE = 0, ///< Exec state Idle. NVM_EXEC_STATE_WRITE, ///< Exec state write to EEPROM. NVM_EXEC_STATE_VERIFY_WRITE, ///< Exec state verify EEPROM write. NVM_EXEC_STATE_READ, ///< Exec state read from EEPROM. NVM_EXEC_STATE_ERASE, ///< Exec state erase EEPROM. NUM_OF_NVM_EXEC_STATES ///< Total number of exec states. } NVM_EXEC_STATE_T; /// NVM write record validity check states typedef enum NVM_Write_Record_Validity_Check { NVM_RECORD_NOT_CHECKED = 0, ///< NVM (written) record not checked. NVM_RECORD_VALID, ///< NVM record is valid. NVM_RECORD_NOT_VALID, ///< NVM record is not valid. NUM_OF_NVM_RECORD_VALIDITY_CHECK ///< Number of NVM validity check states. } NVM_RECORD_VALIDITY_CHECK_T; // ********** private data ********** static NVM_EXEC_STATE_T nvmExecState; ///< NVM exec state variable. static volatile BOOL powerOffIsImminent; ///< Power off warning has been signaled. Non-volatile memory operations should be completed ASAP and then ceased. static NVM_RECORD_VALIDITY_CHECK_T writtenRecordStatus; ///< Record data write validity check. static U32 currentTime; ///< Current time. static U32 recordAddressOffset; ///< Record address offset. static U08 writtenRecordCheckBuffer[ MAX_EEPROM_WRITE_BUFFER_BYTES ]; ///< Written record validity check buffer. // ********** private function prototypes ********** // Exec functions static NVM_EXEC_STATE_T handleExecIdleState( void ); static NVM_EXEC_STATE_T handleExecEraseState( void ); static NVM_EXEC_STATE_T handleExecWriteState( void ); static NVM_EXEC_STATE_T handleExecReadState( void ); static NVM_EXEC_STATE_T handleExecVerifyWriteState( void ); // Helper functions static BOOL didCommandTimeout( NVM_EXEC_STATE_T state ); /*********************************************************************//** * @brief * The initNVMgmtDD function initializes the NV management module. * It resets internal state variables, initializes driver, * message queue, records, and enqueues read operations. * @details \b Inputs: none * @details \b Outputs: nvmExecState, powerOffIsImminent, * currentTime, writtenRecordStatus, * recordAddressOffset * @return none *************************************************************************/ void initNVMgmtDD( void ) { nvmExecState = NVM_EXEC_STATE_IDLE; powerOffIsImminent = FALSE; currentTime = 0; writtenRecordStatus = NVM_RECORD_NOT_CHECKED; recordAddressOffset = 0; initNVDriver(); initNVMsgQ(); initNVRecordsDD(); enqueueReadAllRecords(); } /*********************************************************************//** * @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 execNVM function executes the main NV management state machine. * It handles different execution states for erase, write, read, and * verification of NV memory operations. * @details \b Inputs: nvmExecState * @details \b Outputs: nvmExecState * @return none *************************************************************************/ void execNVM( void ) { switch ( nvmExecState ) { case NVM_EXEC_STATE_IDLE: nvmExecState = handleExecIdleState(); break; case NVM_EXEC_STATE_ERASE: nvmExecState = handleExecEraseState(); break; case NVM_EXEC_STATE_WRITE: nvmExecState = handleExecWriteState(); break; case NVM_EXEC_STATE_READ: nvmExecState = handleExecReadState(); break; case NVM_EXEC_STATE_VERIFY_WRITE: nvmExecState = handleExecVerifyWriteState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_NVM_INVALID_EXEC_STATE, ( U32 )nvmExecState ); nvmExecState = NVM_EXEC_STATE_IDLE; break; } } /*********************************************************************//** * @brief * The handleExecIdleState function processes the idle state of the * NVM execution state machine. It checks for pending record jobs and * initiates erase, write, or read operations. * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT if invalid operation * is encountered * @details \b Inputs: powerOffIsImminent * @details \b Outputs: recordAddressOffset, currentTime * @return state next state of the state machine *************************************************************************/ static NVM_EXEC_STATE_T handleExecIdleState( void ) { NVM_EXEC_STATE_T state = NVM_EXEC_STATE_IDLE; BOOL areQueuesNotEmpty = FALSE; NVM_OPERATION_T ops; U32 recordFlashAddress; U08* bufferAddress; PROCESS_RECORD_JOB_T recordCurrentJob; NVM_RECORD_TYPE_T job; PROCESS_RECORD_SPECS_T jobSpecs; // If power off command has been issued, do not process any new jobs if ( powerOffIsImminent != TRUE ) { // If the record processing queue is not empty, process the queues if ( ( FALSE == isRecordQueueEmpty() ) && ( TRUE == isFlashReady() ) ) { dequeueRecordJob(); recordCurrentJob = getCurrentProcessRecordJob(); job = recordCurrentJob.recordJob; jobSpecs = getProcessRecord( job ); // Set the record address offset to 0 since a job will just be started recordAddressOffset = 0; areQueuesNotEmpty = TRUE; ops = recordCurrentJob.memoryOperation; currentTime = getMSTimerCount(); recordFlashAddress = jobSpecs.recordFlashAddress + recordAddressOffset; bufferAddress = jobSpecs.structAddressPtr + recordAddressOffset; } } // Check if a queue job is available if ( TRUE == areQueuesNotEmpty ) { switch ( ops ) { case NVM_OPERATION_ERASE: eraseSector( (U32*)recordFlashAddress ); state = NVM_EXEC_STATE_ERASE; break; case NVM_OPERATION_WRITE: writeSector( (U32*)recordFlashAddress, (U08*)bufferAddress, MAX_EEPROM_WRITE_BUFFER_BYTES ); currentTime = getMSTimerCount(); state = NVM_EXEC_STATE_WRITE; break; case NVM_OPERATION_READ: readSector( (U32*)recordFlashAddress, (U32*)bufferAddress, ( jobSpecs.sizeofRecord / EEPROM_OPS_SIZE_OF_CONVERTER ) ); currentTime = getMSTimerCount(); state = NVM_EXEC_STATE_READ; break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_NVM_INVALID_OPS, ( U32 )nvmExecState ); break; } } // Update Self Test Read Records Flag if ( ( TRUE == isRecordQueueEmpty() ) && ( state == NVM_EXEC_STATE_IDLE ) ) { updateSelfTestReadRecordsFlag( TRUE ); } else { updateSelfTestReadRecordsFlag( FALSE ); } return state; } /*********************************************************************//** * @brief * The handleExecEraseState function handles the erase state of the * NVM execution state machine. It issues an erase command to EEPROM on entry, * waits for erase completion and if the erase was successful, * it transitions back to idle when done or on timeout. * @details \b Inputs: none * @details \b Outputs: none * @return state next state of the state machine *************************************************************************/ static NVM_EXEC_STATE_T handleExecEraseState( void ) { NVM_EXEC_STATE_T state = NVM_EXEC_STATE_ERASE; BOOL timeoutStatus = didCommandTimeout( state ); if ( TRUE == isFlashReady() || timeoutStatus == TRUE ) { state = NVM_EXEC_STATE_IDLE; } return state; } /*********************************************************************//** * @brief * The handleExecWriteState function handles the write state of * the NVM execution state machine. It verifies written data and * continues writing remaining data. if the write was successful, * it sets the state to Idle. * @details \b Inputs: recordAddressOffset, * writtenRecordStatus * @details \b Outputs: recordAddressOffset, currentTime, * writtenRecordCheckBuffer, writtenRecordStatus * @return state next state of the state machine *************************************************************************/ static NVM_EXEC_STATE_T handleExecWriteState( void ) { NVM_EXEC_STATE_T state = NVM_EXEC_STATE_WRITE; BOOL timeoutStatus = didCommandTimeout( state ); PROCESS_RECORD_JOB_T recordCurrentJob = getCurrentProcessRecordJob(); NVM_RECORD_TYPE_T job = recordCurrentJob.recordJob; PROCESS_RECORD_SPECS_T jobSpecs = getProcessRecord( job ); // Check if the fapi has finished if ( TRUE == isFlashReady() ) { // Check the integrity of data (the 16 bytes that were written to // EEPROM should be read and be checked for each byte) if ( writtenRecordStatus == NVM_RECORD_NOT_CHECKED ) { currentTime = getMSTimerCount(); U32 recordFlashAddress = jobSpecs.recordFlashAddress + recordAddressOffset; U32 maxBufferLength = MAX_EEPROM_WRITE_BUFFER_BYTES / EEPROM_OPS_SIZE_OF_CONVERTER; // Clear the buffer from the previous content memset( writtenRecordCheckBuffer, 0, sizeof( writtenRecordCheckBuffer ) ); // Issue a FAPI read command but only the bytes that were written, so use maxBufferLength readSector( (U32*)recordFlashAddress, (U32*)writtenRecordCheckBuffer, maxBufferLength ); state = NVM_EXEC_STATE_VERIFY_WRITE; } else if ( writtenRecordStatus == NVM_RECORD_VALID ) { // If the data is valid, and if it is at the end of the write, change to idle if ( jobSpecs.sizeofRecord == recordAddressOffset ) { state = NVM_EXEC_STATE_IDLE; } else { // Update the variables and issue the next write command currentTime = getMSTimerCount(); recordAddressOffset += MAX_EEPROM_WRITE_BUFFER_BYTES; U32 memoryPtr = jobSpecs.recordFlashAddress + recordAddressOffset; U08* structPtr = jobSpecs.structAddressPtr + recordAddressOffset; writtenRecordStatus = NVM_RECORD_NOT_CHECKED; // Issue the write command writeSector( (U32*)memoryPtr, structPtr, MAX_EEPROM_WRITE_BUFFER_BYTES ); } } } // If timed out, get back to Idle else if ( timeoutStatus == TRUE ) { recordAddressOffset = 0; state = NVM_EXEC_STATE_IDLE; } return state; } /*********************************************************************//** * @brief * The handleExecReadState function handles the read state of the NVM * execution state machine. It waits for read completion and transitions * to idle when done or on timeout. * @details \b Inputs: none * @details \b Outputs: none * @return state next state of the state machine *************************************************************************/ static NVM_EXEC_STATE_T handleExecReadState( void ) { NVM_EXEC_STATE_T state = NVM_EXEC_STATE_READ; BOOL timeoutStatus = didCommandTimeout( state ); if ( ( TRUE == isFlashReady() ) || ( TRUE == timeoutStatus ) ) { state = NVM_EXEC_STATE_IDLE; } return state; } /*********************************************************************//** * @brief * The handleExecVerifyWriteState function verifies the data written to * EEPROM by comparing it with the source data. It schedules a rewrite * if mismatch is detected or continues the write process if valid. * @details \b Inputs: recordAddressOffset,writtenRecordCheckBuffer * @details \b Outputs: writtenRecordStatus * @return state next state of the state machine *************************************************************************/ static NVM_EXEC_STATE_T handleExecVerifyWriteState( void ) { NVM_EXEC_STATE_T state = NVM_EXEC_STATE_VERIFY_WRITE; BOOL timeoutStatus = didCommandTimeout( state ); PROCESS_RECORD_JOB_T recordCurrentJob = getCurrentProcessRecordJob(); NVM_RECORD_TYPE_T job = recordCurrentJob.recordJob; PROCESS_RECORD_SPECS_T jobSpecs = getProcessRecord( job ); // Check if the flash is ready if ( TRUE == isFlashReady() ) { U32 i; U08* bufferPtr = jobSpecs.structAddressPtr + recordAddressOffset; // Loop through the bytes in the buffer for ( i = 0; i < MAX_EEPROM_WRITE_BUFFER_BYTES; 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 record writtenRecordStatus = NVM_RECORD_NOT_VALID; enqueueEraseAndWriteSector( job ); state = NVM_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 = NVM_RECORD_VALID; // Go back write to EEPROM state to continue writing the record data state = NVM_EXEC_STATE_WRITE; } } } return state; } /*********************************************************************//** * @brief * The resetNVMPOSTState function resets the NV data management POST * state and sets the self-test to start reading all the records. * @details \b Inputs: none * @details \b Outputs: none * @return none *************************************************************************/ void resetNVMPOSTState( void ) { updateNVSelfTestResult( SELF_TEST_STATUS_IN_PROGRESS ); updateNVSelfTestState( NVM_SELF_TEST_STATE_READ_RECORDS ); } /*********************************************************************//** * @brief * The execNVMProcessRecord function executes the NVM process record * handler. * @details \b Inputs: none * @details \b Outputs: none * @return none *************************************************************************/ void execNVMProcessRecord( void ) { execNVMPSendReceiveRecord(); } /*********************************************************************//** * @brief * The execNVMSelfTest function executes the NV management self-test * handler. * @details \b Inputs: none * @details \b Outputs: none * @return SELF_TEST_STATUS_T self-test result *************************************************************************/ SELF_TEST_STATUS_T execNVMSelfTest( void ) { execNVMRecordsSelfTest(); } /*********************************************************************//** * @brief * The getNVRecord2Driver function retrieves NV data by calling the * record handler after validating input parameters. * @details \b Inputs: none * @details \b Outputs: none * @param nvData The non-volatile data identifier * @param bufferAddress Pointer to the buffer where data will be copied * @param bufferLength Length of the provided buffer * @param numOfSnsrs2Check Number of sensors to check * @param nvAlarm Alarm to raise if data is invalid * @return TRUE if the operation is successful otherwise FALSE *************************************************************************/ BOOL getNVRecord2Driver( NV_DATA_T nvData, U08* bufferAddress, U32 bufferLength, U08 numOfSnsrs2Check, ALARM_ID_T nvAlarm ) { BOOL status = FALSE; if ( ( nvData < NUM_OF_NV_DD_DATA ) && ( NULL != bufferAddress ) && ( bufferLength > 0U ) ) { status = getNVMRecord(nvData, bufferAddress, bufferLength, numOfSnsrs2Check, nvAlarm); } return status; } /*********************************************************************//** * @brief * The didCommandTimeout function checks whether the command has timed * out. If a timeout occurs, it sets an alarm and returns TRUE. * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT if command timeout * occurs * @details \b Inputs: currentTime * @details \b Outputs: none * @param state The state in which the command timed out * @return TRUE if a command timed out otherwise FALSE *************************************************************************/ static BOOL didCommandTimeout( NVM_EXEC_STATE_T state ) { BOOL status = FALSE; if ( TRUE == didTimeout( currentTime, COMMAND_TIME_OUT ) ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_NVM_OPS_TIMEOUT, ( U32 )state ) status = TRUE; } return status; } /**@}*/