/************************************************************************** * * Copyright (c) 2024-2025 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 PersistentAlarm.c * * @author (last) Sean Nash * @date (last) 10-Sep-2025 * * @author (original) Sean Nash * @date (original) 01-Aug-2024 * ***************************************************************************/ #include "Common.h" #include "PersistentAlarm.h" #include "Timers.h" /** * @addtogroup PersistentAlarm * @{ */ // ********** private definitions ********** #define NUM_OF_FPGA_ALARMS_PER_GROUP 2 ///< Number of FPGA alarms per group. #define FPGA_READ_ALARM_INDEX 0 ///< FPGA read alarm index number. #define FPGA_ERROR_ALARM_INDEX 1 ///< FPGA error alarm index number. /// FPGA persistent alarm types typedef enum { FPGA_READ_ERROR = 0, ///< FPGA read error type. FPGA_ERROR_ERROR, ///< FPGA error error type. NUM_OF_FPGA_ERROR_TYPES ///< Number of FPGA error types. } FPGA_ERROR_TYPE_T; /// Persistent alarm structure typedef struct { ALARM_ID_T alarm; ///< Alarm ID. U32 persistentClearPeriod_ms; ///< Persistent time limit before clear alarm (in ms). U32 persistentTriggerPeriod_ms; ///< Persistent time limit before trigger alarm (in ms). U32 errorClearedStartTime; ///< Error cleared start time stamp. U32 errorOccurredStartTime; ///< Error occurred start time stamp. BOOL alarmActive; ///< State of alarm last time alarm checked. } PERSISTENT_ALARM_DATA_T; /// FPGA persistent alarm data structure typedef struct { U32 fpagErrorClearedStartTime; ///< FPGA error cleared start time. U32 fpgaErrorOccurredStartTime; ///< FPGA error occurred start time. U32 fpgaPreviousCount; ///< FPGA previous read count. BOOL fpgaIsConditionClear; ///< FPGA is persistent condition clear. } FPGA_ALARM_DATA_T; /// FPGA persistent alarm structure typedef struct { ALARM_ID_T fpgaAlarm; ///< FPGA read alarm. U32 fpgaPersistentClearPeriod_ms; ///< FPGA persistent time limit before clear alarm (in ms). U32 fpgaPersistentTriggerPeriod_ms; ///< FPGA persistent time limit before trigger alarm (in ms). FPGA_ALARM_DATA_T fpgaAlarmData[ NUM_OF_FPGA_ERROR_TYPES ]; ///< FPGA persistent alarm data. } FPGA_PERSISTENT_ALARM_GROUP_T; // ********** private data ********** static PERSISTENT_ALARM_DATA_T persistentAlarms[ NUM_OF_ALARM_IDS ]; ///< Array of persistent alarm structure. static FPGA_PERSISTENT_ALARM_GROUP_T fpgaPersistentAlarmGroup[ NUM_OF_FPGA_SENSOR_GROUPS ]; ///< FPGA persistent alarm group. // ********** private function prototypes ********** static BOOL isFPGAPersistentAlarmTriggered( FPGA_PERSISTENT_ALARM_GROUP_T* alarmGroup, U32 fpgaCount, FPGA_ERROR_TYPE_T errorType ); /*********************************************************************//** * @brief * The initPersistentAlarm function initializes a given persistent alarm. * @details \b Alarm: ALARM_ID_XX_SOFTWARE_FAULT if given persistent alarm is invalid. * @details \b Inputs: none * @details \b Outputs: persistentAlarms[] * @param alarmId ID of alarm to initialize persistence for * @param persistentClearPeriod Persistent period for clearing alarm condition (in ms) * @param persistentTriggerPeriod Persistent period for triggering alarm (in ms) * @return none *************************************************************************/ void initPersistentAlarm( ALARM_ID_T alarmId, U32 persistentClearPeriod, U32 persistentTriggerPeriod ) { if ( alarmId < NUM_OF_ALARM_IDS ) { persistentAlarms[ alarmId ].persistentClearPeriod_ms = persistentClearPeriod; persistentAlarms[ alarmId ].persistentTriggerPeriod_ms = persistentTriggerPeriod; persistentAlarms[ alarmId ].errorClearedStartTime = 0U; persistentAlarms[ alarmId ].errorOccurredStartTime = 0U; persistentAlarms[ alarmId ].alarmActive = FALSE; } else { #ifdef _TD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_PERSISTENT_ALARM_INVALID_INDEX1, alarmId ); #endif #ifdef _DD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_PERSISTENT_ALARM_INVALID_INDEX1, alarmId ); #endif } } /*********************************************************************//** * @brief * The initFPGAPersistentAlarm function initializes a given FPGA persistent alarm. * @details \b Inputs: none * @details \b Outputs: fpgaPersistentAlarmGroup * @param group ID of FPGA persistent alarm group to initialize * @param alarmId ID of alarm to associate with the given FPGA persistent alarm group * @param persistentClearPeriod Persistent period for clearing alarm (in ms) * @param persistentTriggerPeriod Persistent period for triggering alarm (in ms) * @return none *************************************************************************/ void initFPGAPersistentAlarm( FPGA_PERSISTENT_ALARMS_GROUP_T group, ALARM_ID_T alarmIndex, U32 persistentClearPeriod, U32 persistentTriggerPeriod ) { if ( ( group < NUM_OF_FPGA_SENSOR_GROUPS ) && ( alarmIndex < NUM_OF_ALARM_IDS ) ) { fpgaPersistentAlarmGroup[ group ].fpgaAlarm = alarmIndex; fpgaPersistentAlarmGroup[ group ].fpgaPersistentClearPeriod_ms = persistentClearPeriod; fpgaPersistentAlarmGroup[ group ].fpgaPersistentTriggerPeriod_ms = persistentTriggerPeriod; fpgaPersistentAlarmGroup[ group ].fpgaAlarmData[ FPGA_READ_ALARM_INDEX ].fpgaIsConditionClear = TRUE; fpgaPersistentAlarmGroup[ group ].fpgaAlarmData[ FPGA_READ_ALARM_INDEX ].fpagErrorClearedStartTime = 0; fpgaPersistentAlarmGroup[ group ].fpgaAlarmData[ FPGA_READ_ALARM_INDEX ].fpgaErrorOccurredStartTime = 0; fpgaPersistentAlarmGroup[ group ].fpgaAlarmData[ FPGA_READ_ALARM_INDEX ].fpgaPreviousCount = 0; fpgaPersistentAlarmGroup[ group ].fpgaAlarmData[ FPGA_ERROR_ALARM_INDEX ].fpgaIsConditionClear = TRUE; fpgaPersistentAlarmGroup[ group ].fpgaAlarmData[ FPGA_ERROR_ALARM_INDEX ].fpagErrorClearedStartTime = 0; fpgaPersistentAlarmGroup[ group ].fpgaAlarmData[ FPGA_ERROR_ALARM_INDEX ].fpgaErrorOccurredStartTime = 0; fpgaPersistentAlarmGroup[ group ].fpgaAlarmData[ FPGA_ERROR_ALARM_INDEX ].fpgaPreviousCount = 0; } } /*********************************************************************//** * @brief * The isPersistentAlarmTriggered function checks if the error condition has * persisted long enough to meet its trigger persistence time. * @details \b Alarm: ALARM_ID_XX_SOFTWARE_FAULT if given alarm ID is invalid. * @details \b Inputs: persistentAlarms[] * @details \b Outputs: none * @param alarmId ID of alarm to check persistence for * @param isErrorOccurred has alarm criteria otherwise been met (T/F) * @return TRUE if error condition has persisted long enough to be triggered, FALSE if not *************************************************************************/ BOOL isPersistentAlarmTriggered( ALARM_ID_T alarmId, BOOL const isErrorOccurred ) { BOOL isAlarmTriggered = FALSE; if ( alarmId < NUM_OF_ALARM_IDS ) { BOOL alarmIsActive = isAlarmActive( alarmId ); // Reset persistence if alarm was just cleared if ( ( FALSE == alarmIsActive ) && ( TRUE == persistentAlarms[ alarmId ].alarmActive ) ) { persistentAlarms[ alarmId ].errorOccurredStartTime = 0; } persistentAlarms[ alarmId ].alarmActive = alarmIsActive; // remember latest alarm state for next time // Update start time when error occurs for the first time if ( ( TRUE == isErrorOccurred ) && ( 0 == persistentAlarms[ alarmId ].errorOccurredStartTime ) ) { persistentAlarms[ alarmId ].errorOccurredStartTime = getMSTimerCount(); } if ( TRUE == isErrorOccurred ) { isAlarmTriggered = didTimeout( persistentAlarms[ alarmId ].errorOccurredStartTime, persistentAlarms[ alarmId ].persistentTriggerPeriod_ms ); } else { persistentAlarms[ alarmId ].errorOccurredStartTime = 0; } } else { #ifdef _TD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_PERSISTENT_ALARM_INVALID_INDEX2, alarmId ); #endif #ifdef _DD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_PERSISTENT_ALARM_INVALID_INDEX2, alarmId ); #endif } return isAlarmTriggered; } /*********************************************************************//** * @brief * The isPersistentAlarmConditionCleared function checks if the error condition has * been cleared long enough to clear the alarm condition. * @details \b Alarm: ALARM_ID_XX_SOFTWARE_FAULT if given alarm ID is invalid. * @details \b Inputs: persistentAlarms[] * @details \b Outputs: persistentAlarms[] * @param alarmId AD of alarm to check clear persistence for * @param isErrorOccurred Flag indicates whether error condition is occurring or not * @return TRUE if error condition has been cleared long enough to clear the condition, FALSE if not *************************************************************************/ BOOL isPersistentAlarmConditionCleared( ALARM_ID_T alarmId, BOOL const isErrorOccurred ) { BOOL isErrorConditionCleared = FALSE; if ( alarmId < NUM_OF_ALARM_IDS ) { // Update start time when error condition clears for the first time if ( ( FALSE == isErrorOccurred ) && ( 0 == persistentAlarms[ alarmId ].errorClearedStartTime ) ) { persistentAlarms[ alarmId ].errorClearedStartTime = getMSTimerCount(); } if ( FALSE == isErrorOccurred ) { isErrorConditionCleared = didTimeout( persistentAlarms[ alarmId ].errorClearedStartTime, persistentAlarms[ alarmId ].persistentClearPeriod_ms ); } else { persistentAlarms[ alarmId ].errorClearedStartTime = 0; } } else { #ifdef _TD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_PERSISTENT_ALARM_INVALID_INDEX3, alarmId ) #endif #ifdef _DD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_PERSISTENT_ALARM_INVALID_INDEX3, alarmId ) #endif } return isErrorConditionCleared; } /*********************************************************************//** * @brief * The checkPersistentAlarm function triggers or clears an alarm if an alarm condition * has persisted or been cleared long enough to meet the persistence time. * @details \b Alarm: given alarm is triggered if trigger persistence period elapsed * @details \b Alarm: given alarm condition is cleared if clear persistence period elapsed * @details \b Inputs: none * @details \b Outputs: checks whether an alarm is triggered or an alarm condition is cleared * @param alarmID ID of alarm to check persistence for * @param isErrorOccured Flag indicates whether alarm condition is active or not * @param data alarm data * @param limit alarm condition limit * @return none *************************************************************************/ void checkPersistentAlarm( ALARM_ID_T alarm, BOOL isErrorOccured, F32 data, F32 limit ) { if ( TRUE == isPersistentAlarmTriggered( alarm, isErrorOccured ) ) { SET_ALARM_WITH_2_F32_DATA( alarm, data, limit ); } if ( TRUE == isPersistentAlarmConditionCleared( alarm, isErrorOccured ) ) { clearAlarmCondition( alarm ); } } /*********************************************************************//** * @brief * The resetPersistentAlarmTimer function resets the timing for a given alarm. * @details \b Inputs: none * @details \b Outputs: persistentAlarms[] * @param alarmId ID of alarm to reset persistence for * @return none *************************************************************************/ void resetPersistentAlarmTimer( ALARM_ID_T alarmId ) { persistentAlarms[ alarmId ].errorOccurredStartTime = 0; persistentAlarms[ alarmId ].errorClearedStartTime = 0; } /*********************************************************************//** * @brief * The checkFPGAPersistentAlarms function checks the FPGA persistent * read count of the provided FPGA persistent sensor group. * @details \b Alarm: Alarm associated with given FPGA alarm group triggered * if given sensor has not been read since last check. * @details \b Alarm: ALARM_ID_XX_SOFTWARE_FAULT if given FPGA sensor group is invalid. * @details \b Inputs: fpgaPersistentAlarmGroup * @details \b Outputs: fpgaPersistentAlarmGroup * @param alarmGroup ID of FPGA sensor group to check * @param readCount Current read count reported by FPGA * @return none *************************************************************************/ void checkFPGAPersistentAlarms( FPGA_PERSISTENT_ALARMS_GROUP_T group, U32 readCount ) { if ( group < NUM_OF_FPGA_SENSOR_GROUPS ) { FPGA_ERROR_TYPE_T type = FPGA_READ_ERROR; BOOL isReadPersTrgrd = isFPGAPersistentAlarmTriggered( &fpgaPersistentAlarmGroup[ group ], readCount, type ); if ( TRUE == isReadPersTrgrd ) { SET_ALARM_WITH_2_U32_DATA( fpgaPersistentAlarmGroup[ group ].fpgaAlarm, type, group ) } } else { #ifdef _TD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_FPGA_SENSOR_GROUP_SELECTED1, (U32)group ) #endif #ifdef _DD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_FPGA_SENSOR_GROUP_SELECTED1, (U32)group ) #endif } } /*********************************************************************//** * @brief * The checkFPGAPersistentErrorCountAlarm function checks the FPGA persistent * error count of the provided FPGA persistent sensor group. * @details \b Alarm: Alarm associated with given FPGA alarm group triggered * if error count changes or read count does not change since last read. * @details \b Alarm: ALARM_ID_XX_SOFTWARE_FAULT if given FPGA alarm group is invalid. * @details \b Inputs: fpgaPersistentAlarmGroup * @details \b Outputs: none * @param alarmGroup ID of FPGA sensor group to check counts for * @param errorCount Current error count reported by FPGA * @return none *************************************************************************/ void checkFPGAPersistentErrorCountAlarm( FPGA_PERSISTENT_ALARMS_GROUP_T group, U32 errorCount ) { if ( group < NUM_OF_FPGA_SENSOR_GROUPS ) { BOOL isErrorPersTrgrd = isFPGAPersistentAlarmTriggered( &fpgaPersistentAlarmGroup[ group ], errorCount, FPGA_ERROR_ERROR ); if ( TRUE == isErrorPersTrgrd ) { SET_ALARM_WITH_2_U32_DATA( fpgaPersistentAlarmGroup[ group ].fpgaAlarm, FPGA_ERROR_ERROR, group ) } } else { #ifdef _TD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_FPGA_SENSOR_GROUP_SELECTED2, (U32)group ) #endif #ifdef _DD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_FPGA_SENSOR_GROUP_SELECTED2, (U32)group ) #endif } } /*********************************************************************//** * @brief * The isFPGAPersistentAlarmTriggered function checks whether a given FPGA sensor * group has stopped reading or reported a new error (depending on given error type). * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if given FPGA error type is invalid. * @details \b Inputs: none * @details \b Outputs: none * @param alarmGroup ID of FPGA sensor group to report error status for * @param fpgaCount Current error or read count for FPGA sensor group * @param errorType The type of error status being requested (error or failure to read) * @return TRUE if the given error type is detected, otherwise FALSE *************************************************************************/ static BOOL isFPGAPersistentAlarmTriggered( FPGA_PERSISTENT_ALARM_GROUP_T* alarmGroup, U32 fpgaCount, FPGA_ERROR_TYPE_T errorType ) { BOOL isPersistentTriggered = FALSE; BOOL hasErrorOccured = FALSE; switch ( errorType ) { case FPGA_READ_ERROR: hasErrorOccured = ( fpgaCount == alarmGroup->fpgaAlarmData[ errorType ].fpgaPreviousCount ? TRUE : FALSE ); break; case FPGA_ERROR_ERROR: hasErrorOccured = ( alarmGroup->fpgaAlarmData[ errorType ].fpgaPreviousCount != fpgaCount ? TRUE : FALSE ); break; default: #ifdef _TD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_FPGA_SENSOR_GROUP_SELECTED3, errorType ) #endif #ifdef _DD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_FPGA_SENSOR_GROUP_SELECTED3, errorType ) #endif break; } if ( TRUE == hasErrorOccured ) { if ( TRUE == alarmGroup->fpgaAlarmData[ errorType ].fpgaIsConditionClear ) { alarmGroup->fpgaAlarmData[ errorType ].fpgaIsConditionClear = FALSE; alarmGroup->fpgaAlarmData[ errorType ].fpgaErrorOccurredStartTime = getMSTimerCount(); } else if ( TRUE == didTimeout( alarmGroup->fpgaAlarmData[ errorType ].fpgaErrorOccurredStartTime, alarmGroup->fpgaPersistentTriggerPeriod_ms ) ) { isPersistentTriggered = TRUE; } } else { alarmGroup->fpgaAlarmData[ errorType ].fpgaIsConditionClear = TRUE; } alarmGroup->fpgaAlarmData[ errorType ].fpgaPreviousCount = fpgaCount; return isPersistentTriggered; } /**@}*/