Index: RTC.c =================================================================== diff -u -r4ecbb2c37b64a4b09f3173aa9e505749d156d466 -re5968292e753478c4d28d6db94d3772c36a1c33b --- RTC.c (.../RTC.c) (revision 4ecbb2c37b64a4b09f3173aa9e505749d156d466) +++ RTC.c (.../RTC.c) (revision e5968292e753478c4d28d6db94d3772c36a1c33b) @@ -7,8 +7,8 @@ * * @file RTC.c * -* @author (last) Bill Bracken -* @date (last) 30-Jun-2023 +* @author (last) Michael Garthwaite +* @date (last) 01-Mar-2023 * * @author (original) Dara Navaei * @date (original) 11-Jan-2020 @@ -129,11 +129,15 @@ #define LEAP_YEAR_MONTH ( 2 - 1 ) ///< Leap year month is February. Subtract 1 month for zero-based months. #define SECONDS_IN_NORMAL_YEAR ( SECONDS_IN_A_DAY * 365 ) ///< Number of seconds in a normal year. #define SECONDS_IN_LEAP_YEAR ( SECONDS_IN_A_DAY * 366 ) ///< Number of seconds in a leap year. +#define DAYS_IN_NORMAL_YEAR (365) ///< Number of days in a normal year. +#define DAYS_IN_LEAP_YEAR (366) ///< Number of days in a leap year. /// Number of seconds in 4 years. Includes an extra day for 1 leap within any 4 year period. static const U32 SECONDS_IN_4_YEARS = SECONDS_IN_NORMAL_YEAR * 3 + SECONDS_IN_LEAP_YEAR; /// Number of seconds in 2 years. Assumes no leap year in 2 year period. static const U32 SECONDS_IN_2_YEARS = SECONDS_IN_NORMAL_YEAR * 2; +#define MIN_EPOCH_DATE ( 1689958503 ) ///< Epoch dates must be greater than 8/11/2023 + /// RTC self-test state enumeration. typedef enum RTC_Self_Test_States { @@ -165,6 +169,7 @@ RTC_EXEC_STATE_READ_FROM_RAM, ///< Exec state read from RAM RTC_EXEC_STATE_READ, ///< Exec state read RTC_EXEC_STATE_WRITE, ///< Exec state write + RTC_EXEC_STATE_VERIFY_WRITE, ///< Exec state verify RTC write NUM_OF_RTC_EXEC_STATES ///< Total number of exec states } RTC_EXEC_STATE_T; @@ -211,6 +216,12 @@ static U16 previousFPGATimerCount; ///< Previous FPGA timer count; static OVERRIDE_U32_T rtcControlRegister1 = { 0, 0, 0, 0 }; ///< RTC control register 1. static OVERRIDE_U32_T rtcControlRegister3 = { 0, 0, 0, 0 }; ///< RTC control register 3. + + +// DEN-15979 testing +static U08 debugEventBuffer[ DEBUG_EVENT_FIXED_BUFFER_SIZE ]; ///< debug event buffer +static U16 writeSaveBuffer[ MIBSPI_MAX_BUFFER_LENGTH + 1 ]; ///< Buffer to store data written to RTC. + #ifdef _DG_ static BOOL syncDG2HDDateTimeFlag; ///< Flag indicating whether DG RTC should be sync'd to HD RTC. #endif @@ -221,6 +232,7 @@ // ********** Private function prototypes ********* static BOOL serviceRTC( U16* bufferTransmit, U16* bufferReceive, U16 bufferLength ); +static void logSPIFailure( void ); static BOOL isRTCFunctional( void ); static U08 convertBCD2Decimal( U08 bcd ); static U08 convertDecimal2BCD( U08 decimal ); @@ -244,7 +256,9 @@ static RTC_EXEC_STATE_T handleExecWriteToRAMState( void ); static RTC_EXEC_STATE_T handleExecReadFromRAMState( void ); static RTC_EXEC_STATE_T handleExecWriteState( void ); +static RTC_EXEC_STATE_T handleExecVerifyWriteState( void ); + /*********************************************************************//** * @brief * The initRTC initializes the RTC module. @@ -347,21 +361,16 @@ * @brief * The setRTCEpoch sets the clock from a given epoch. * @details Inputs: none -* @details Outputs: RTCNewTimestampStruct +* @details Outputs: none * @param epoch date/time stamp epoch -* @return TRUE if clock set, FALSE if not. +* @return TRUE if valid epoch time received, FALSE if not. +* @note If the epoch time is valid, the RTCNewTimeStampStruct updated *************************************************************************/ BOOL setRTCEpoch( U32 epoch ) { BOOL result; result = convertEpoch2DateTime( epoch ); - result = setRTCTimestamp( RTCNewTimestampStruct.seconds, - RTCNewTimestampStruct.minutes, - RTCNewTimestampStruct.hours, - RTCNewTimestampStruct.days, - RTCNewTimestampStruct.months, - RTCNewTimestampStruct.years); return result; } @@ -447,6 +456,10 @@ RTCExecState = handleExecWriteState(); break; + case RTC_EXEC_STATE_VERIFY_WRITE: + RTCExecState = handleExecVerifyWriteState(); + break; + case RTC_EXEC_STATE_READ: RTCExecState = handleExecReadState(); break; @@ -705,6 +718,9 @@ * @details Outputs: isRTCServiceOnEntry, RTCServiceState, numberOfFailedRTCTransfers, * previousTransferLength * @return TRUE if RTC operation was successful + * + * @NOTE If function returns false and RTCServiceOnEntry is RTC_SERVICE_COMPLETE + * indicates that the function failed. *************************************************************************/ static BOOL serviceRTC( U16* bufferTransmit, U16* bufferReceive, U16 bufferLength ) { @@ -732,41 +748,50 @@ { mibspiSetData( mibspiREG3, MIBSPI_GROUP_ZERO, bufferTransmit ); mibspiTransfer( mibspiREG3, MIBSPI_GROUP_ZERO ); - numberOfFailedRTCTransfers = 0; previousTransferLength = bufferLength; RTCServiceState = RTC_WAIT_FOR_TRANSFER_AND_READ; } - else + else if ( numberOfFailedRTCTransfers >= MAX_ALLOWED_FAILED_RTC_TRANSFERS ) { + logSPIFailure(); RTCServiceState = RTC_SERVICE_COMPLETE; } - isRTCServiceOnEntry = FALSE; + else + { + numberOfFailedRTCTransfers++; + logSPIFailure(); + } + //wjbisRTCServiceOnEntry = FALSE; break; case RTC_WAIT_FOR_TRANSFER_AND_READ: if ( mibspiIsTransferComplete( mibspiREG3, MIBSPI_GROUP_ZERO ) ) { mibspiGetData( mibspiREG3, MIBSPI_GROUP_ZERO, bufferReceive ); RTCServiceState = RTC_SERVICE_COMPLETE; + numberOfFailedRTCTransfers = 0; result = TRUE; } else if ( numberOfFailedRTCTransfers >= MAX_ALLOWED_FAILED_RTC_TRANSFERS ) { + logSPIFailure(); RTCServiceState = RTC_SERVICE_COMPLETE; + numberOfFailedRTCTransfers = 0; } else { - // Transfer to RTC failed. This transfer - // should be done in 50ms + // Transfer to RTC failed. This transfer should be done in 50ms numberOfFailedRTCTransfers++; + logSPIFailure( ); } - // Done with read (successful of failed) + // Done with read (successful or failed) // get ready for another call isRTCServiceOnEntry = TRUE; break; case RTC_SERVICE_COMPLETE: // Done with reading and transfer + numberOfFailedRTCTransfers = 0; break; default: @@ -779,6 +804,21 @@ /*********************************************************************//** * @brief + * The logSPIFailur logs an occurrence of an SPI failure. + * @details Inputs: none + * @details Outputs: debugEventBuffer, RTCServiceState, + * numberOfFaileRTCTransfers + * @return None + *************************************************************************/ +static void logSPIFailure( void ) +{ + memset( debugEventBuffer, 0x00, sizeof(debugEventBuffer) ); + snprintf( debugEventBuffer, sizeof( debugEventBuffer ), "$RTC SPI: %d, %d", RTCServiceState, numberOfFailedRTCTransfers ); + broadcastData( DEBUG_EVENT_MSG_ID, COMM_BUFFER_OUT_CAN_BROADCAST, debugEventBuffer, sizeof( debugEventBuffer ) ); +} + +/*********************************************************************//** + * @brief * The isRTCFunctional checks whether the RTC is still functional by checking * the bits in the first 3 control registers. The function ignore the clear * flags. @@ -962,27 +1002,28 @@ *************************************************************************/ static BOOL convertEpoch2DateTime( U32 epoch ) { + BOOL result; RTC_TIMESTAMP_T dtTime; U32 i, dayCnt; - U32 fourYears = epoch / SECONDS_IN_4_YEARS; - U32 twoYrEpoch = epoch - ( fourYears * SECONDS_IN_4_YEARS ); - U32 twoYears = twoYrEpoch / SECONDS_IN_2_YEARS; - U32 oneYrEpoch = twoYrEpoch - ( twoYears * SECONDS_IN_2_YEARS ); - U32 oneYears = ( twoYears > 0 ? oneYrEpoch / SECONDS_IN_LEAP_YEAR : oneYrEpoch / SECONDS_IN_NORMAL_YEAR ); - U32 days34 = ( oneYears > 0 ? ( oneYrEpoch - SECONDS_IN_LEAP_YEAR ) / SECONDS_IN_A_DAY : oneYrEpoch / SECONDS_IN_A_DAY ); - U32 days12 = ( oneYears > 0 ? ( oneYrEpoch - SECONDS_IN_NORMAL_YEAR ) / SECONDS_IN_A_DAY : oneYrEpoch / SECONDS_IN_A_DAY ); - U32 days = ( twoYears > 0 ? days34 : days12 ); - U32 wholeYears = ( fourYears * 4 ) + ( twoYears * 2 ) + oneYears; - BOOL leapYear = ( twoYears > 0 && 0 == oneYears ? TRUE : FALSE ); - U32 oneDayEpoch= oneYrEpoch % SECONDS_IN_A_DAY; - U32 hours = oneDayEpoch / SECS_IN_HOUR; - U32 oneHrEpoch = oneDayEpoch - ( hours * SECS_IN_HOUR ); - U32 mins = oneHrEpoch / SEC_PER_MIN; - U32 secs = oneHrEpoch - ( mins * SEC_PER_MIN ); + U32 fourYears = epoch / SECONDS_IN_4_YEARS; + U32 twoYrEpoch = epoch - ( fourYears * SECONDS_IN_4_YEARS ); + U32 twoYears = twoYrEpoch / SECONDS_IN_2_YEARS; + U32 oneYrEpoch = twoYrEpoch - ( twoYears * SECONDS_IN_2_YEARS ); + U32 oneYears = ( twoYears > 0 ? oneYrEpoch / SECONDS_IN_LEAP_YEAR : oneYrEpoch / SECONDS_IN_NORMAL_YEAR ); + U32 days34 = ( oneYears > 0 ? ( oneYrEpoch - SECONDS_IN_LEAP_YEAR ) / SECONDS_IN_A_DAY : oneYrEpoch / SECONDS_IN_A_DAY ); + U32 days12 = ( oneYears > 0 ? ( oneYrEpoch - SECONDS_IN_NORMAL_YEAR ) / SECONDS_IN_A_DAY : oneYrEpoch / SECONDS_IN_A_DAY ); + U32 days = ( twoYears > 0 ? days34 : days12 ); + U32 wholeYears = ( fourYears * 4 ) + ( twoYears * 2 ) + oneYears; + BOOL leapYear = ( twoYears > 0 && 0 == oneYears ? TRUE : FALSE ); + U32 oneDayEpoch = oneYrEpoch % SECONDS_IN_A_DAY; + U32 hours = oneDayEpoch / SECS_IN_HOUR; + U32 oneHrEpoch = oneDayEpoch - ( hours * SECS_IN_HOUR ); + U32 mins = oneHrEpoch / SEC_PER_MIN; + U32 secs = oneHrEpoch - ( mins * SEC_PER_MIN ); + dtTime.years = EPOCH_BASE_YEAR + wholeYears; + dtTime.months = 0; - dtTime.years = EPOCH_BASE_YEAR + wholeYears; - dtTime.months = 0; dayCnt = 0; daysInMonth[ LEAP_YEAR_MONTH ] = ( TRUE == leapYear ? 29 : 28 ); for ( i = 0; i < MONTHS_IN_YEAR; i++ ) @@ -994,13 +1035,14 @@ } dayCnt += daysInMonth[ i ]; } - dtTime.days += 1; // days will be number of full days in current month so need to add 1 for the partial current day to get day of month + dtTime.days += 1; // days will be number of full days in current month so need to add 1 for the partial current day to get day of month dtTime.hours = hours; dtTime.minutes = mins; dtTime.seconds = secs; result = setRTCTimestamp( dtTime.seconds, dtTime.minutes, dtTime.hours, dtTime.days, dtTime.months, dtTime.years ); return result; + } /*********************************************************************//** @@ -1187,12 +1229,15 @@ if ( RTCServiceState == RTC_SERVICE_COMPLETE && isStatusOk ) { + memcpy(writeSaveBuffer, txBuffer, ( RTC_GENERAL_BUFFER_LENGTH * 2 ) ); + releaseSemaphore( SEMAPHORE_RTC ); // Reset the counter to start with the new read timeCounter = 1; hasWriteToRTCRequested = FALSE; - result = RTC_EXEC_STATE_IDLE; + prepBufferForReadCommand(RTC_GENERAL_BUFFER_LENGTH); + result = RTC_EXEC_STATE_VERIFY_WRITE; } else if ( RTCServiceState == RTC_SERVICE_COMPLETE ) { @@ -1201,7 +1246,7 @@ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_NVDATA_RTC_RAM_OPS_FAILURE, result ) #endif #ifdef _HD_ - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_NVDATA_RTC_RAM_OPS_FAILURE, result ) + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_NVDATA_RTC_RAM_OPS_FAILURE, result ) #endif result = RTC_EXEC_STATE_IDLE; hasWriteToRTCRequested = FALSE; @@ -1241,7 +1286,7 @@ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_NVDATA_RTC_RAM_OPS_FAILURE, result ) #endif #ifdef _HD_ - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_NVDATA_RTC_RAM_OPS_FAILURE, result ) + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_NVDATA_RTC_RAM_OPS_FAILURE, result ) #endif result = RTC_EXEC_STATE_IDLE; } @@ -1320,7 +1365,7 @@ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_NVDATA_RTC_RAM_OPS_FAILURE, result ) #endif #ifdef _HD_ - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_NVDATA_RTC_RAM_OPS_FAILURE, result ) + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_NVDATA_RTC_RAM_OPS_FAILURE, result ) #endif result = RTC_EXEC_STATE_IDLE; hasReadFromRAMRequested = FALSE; @@ -1355,12 +1400,62 @@ timeCounter = 1; data.epochTime = lastEpochTime; + broadcastData( EPOCH_DATA_MSG_ID, COMM_BUFFER_OUT_CAN_BROADCAST, (U08*)&data, sizeof( RTC_DATA_T ) ); + } + + releaseSemaphore( SEMAPHORE_RTC ); + + result = RTC_EXEC_STATE_IDLE; + } + else if ( RTC_SERVICE_COMPLETE == RTCServiceState ) + { + releaseSemaphore( SEMAPHORE_RTC ); #ifdef _DG_ - broadcastData( MSG_ID_DG_RTC_EPOCH_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( RTC_DATA_T ) ); + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_NVDATA_RTC_RAM_OPS_FAILURE, result ) #endif #ifdef _HD_ - broadcastData( MSG_ID_RTC_EPOCH_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&data, sizeof( RTC_DATA_T ) ); + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_NVDATA_RTC_RAM_OPS_FAILURE, result ) #endif + result = RTC_EXEC_STATE_IDLE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The handleExecVerifyWriteState reads timestamp from RT to verify the + * RTC write was successful. + * @details Inputs: RTCServiceState, lastEpochTime, timeCounter, txBuffer, rxBuffer + * @details Outputs: lastEpochTime, timeCounter, alarm if any RTC operations error + * occurred + * @return next state + *************************************************************************/ +static RTC_EXEC_STATE_T handleExecVerifyWriteState( void ) +{ + RTC_EXEC_STATE_T result = RTC_EXEC_STATE_VERIFY_WRITE; + BOOL isStatusOk = serviceRTC( txBuffer, rxBuffer, RTC_GENERAL_BUFFER_LENGTH ); + + if ( ( RTC_SERVICE_COMPLETE == RTCServiceState ) && ( TRUE == isStatusOk ) ) + { + // Compare received buffer with what was transmitted + if ( 0 != memcmp( &writeSaveBuffer[1], &rxBuffer[1], ( RTC_GENERAL_BUFFER_LENGTH - 1 )* 2 ) ) + { + memset( debugEventBuffer, 0x00, sizeof( debugEventBuffer ) ); + snprintf( debugEventBuffer, sizeof( debugEventBuffer ), "$RTC Verify: %02X:%02X, %02X:%02X, %02X:%02X, %02X:%02X", + rxBuffer[0], writeSaveBuffer[0], rxBuffer[1], writeSaveBuffer[1], rxBuffer[2], writeSaveBuffer[2], rxBuffer[3], writeSaveBuffer[3] ); + broadcastData( DEBUG_EVENT_MSG_ID, COMM_BUFFER_OUT_CAN_BROADCAST, debugEventBuffer, sizeof( debugEventBuffer ) ); + + memset( debugEventBuffer, 0x00, sizeof( debugEventBuffer ) ); + snprintf( debugEventBuffer, sizeof( debugEventBuffer ), "$RTC Verify: %02X:%02X, %02X:%02X, %02X:%02X, %02X:%02X", + rxBuffer[4], writeSaveBuffer[4], rxBuffer[5], writeSaveBuffer[5], rxBuffer[6], writeSaveBuffer[6], rxBuffer[7], writeSaveBuffer[7] ); + broadcastData( DEBUG_EVENT_MSG_ID, COMM_BUFFER_OUT_CAN_BROADCAST, debugEventBuffer, sizeof( debugEventBuffer ) ); + + memset( debugEventBuffer, 0x00, sizeof( debugEventBuffer ) ); + snprintf( debugEventBuffer, sizeof( debugEventBuffer ), "$RTC Verify: %02X:%02X, %02X:%02X, %02X:%02X", + rxBuffer[8], writeSaveBuffer[8], rxBuffer[9], writeSaveBuffer[9], rxBuffer[10], writeSaveBuffer[10] ); + broadcastData( DEBUG_EVENT_MSG_ID, COMM_BUFFER_OUT_CAN_BROADCAST, debugEventBuffer, sizeof( debugEventBuffer ) ); + } releaseSemaphore( SEMAPHORE_RTC ); @@ -1373,9 +1468,6 @@ #ifdef _DG_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_NVDATA_RTC_RAM_OPS_FAILURE, result ) #endif -#ifdef _HD_ - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_NVDATA_RTC_RAM_OPS_FAILURE, result ) -#endif result = RTC_EXEC_STATE_IDLE; } @@ -1600,7 +1692,33 @@ return getU16OverrideValue(&rtcControlRegister3); } +/*********************************************************************//** + * @brief + * The isEpochValid check if epoch is valid + * @details Inputs: epoch time + * @details Outputs: None + * @return TRUE if epoch is valid, else false + *************************************************************************/ +BOOL isEpochValid( U32 epoch ) +{ + BOOL result = TRUE; + memset( debugEventBuffer, 0x00, sizeof( debugEventBuffer ) ); + snprintf( debugEventBuffer, sizeof( debugEventBuffer ), "$RTC HD epoch check" ); + broadcastData( DEBUG_EVENT_MSG_ID, COMM_BUFFER_OUT_CAN_BROADCAST, debugEventBuffer, sizeof( debugEventBuffer ) ); + + if ( epoch < MIN_EPOCH_DATE ) + { + result = FALSE; + + memset( debugEventBuffer, 0x00, sizeof( debugEventBuffer ) ); + snprintf( debugEventBuffer, sizeof( debugEventBuffer ), "$RTC HD epoch: %d", epoch ); + broadcastData( DEBUG_EVENT_MSG_ID, COMM_BUFFER_OUT_CAN_BROADCAST, debugEventBuffer, sizeof( debugEventBuffer ) ); + } + + return result; +} + /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/