/************************************************************************** * * 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 CommBuffers.c * * @author (last) Sean Nash * @date (last) 10-Sep-2025 * * @author (original) Sean Nash * @date (original) 29-Aug-2024 * ***************************************************************************/ #include // For memcpy() #include "CommBuffers.h" #include "Messaging.h" #ifdef _TD_ #include "SystemCommTD.h" #endif #ifdef _DD_ #include "SystemCommDD.h" #endif #include "Timers.h" /** * @addtogroup CommBuffers * @{ */ // ********** private definitions ********** #define COMM_BUFFER_LENGTH 768 ///< Max bytes in each comm buffer (each side of double buffer is this size) #define DOUBLE_BUFFERS 2 ///< Need 2 buffers for double buffering #define BUFFER_OVERFLOW_PERSISTENCE_MS 5000 ///< How many ms buffer overflows must persist before fault // ********** private data ********** static volatile U32 commBufferByteCount[ NUM_OF_COMM_BUFFERS ][ DOUBLE_BUFFERS ]; ///< For each buffer, how many bytes does it contain? (also index to next available) static volatile U32 activeDoubleBuffer[ NUM_OF_COMM_BUFFERS ]; ///< For each buffer, which double buffer is being fed right now? static U08 commBuffers[ NUM_OF_COMM_BUFFERS ][ DOUBLE_BUFFERS ][ COMM_BUFFER_LENGTH ]; ///< Each is double buffered to avoid thread contention static U32 firstBufferOverflowTimeStamp = 0; ///< Time stamp of a prior overflow event - allows for an overflow persistence check // ********** private function prototypes ********** static U32 switchDoubleBuffer( COMM_BUFFER_T buffer ); static void getDataFromInactiveBuffer( COMM_BUFFER_T buffer, U08 *data, U32 len ); /*********************************************************************//** * @brief * The initCommBuffers function initializes the CommBuffers unit. * @details Inputs: none * @details Outputs: CommBuffers unit initialized. * @return none *************************************************************************/ void initCommBuffers( void ) { S32 b; // Reset and zero out all buffers for ( b = 0; b < NUM_OF_COMM_BUFFERS; b++ ) { clearBuffer( (COMM_BUFFER_T)b ); } } /*********************************************************************//** * @brief * The clearBuffer function clears (empties) a given buffer. * @note This function is thread safe. IRQ interrupts are disabled while * buffers are being cleared. * @details Inputs: none * @details Outputs: given buffer is cleared. * @param buffer ID of the buffer to clear * @return none *************************************************************************/ void clearBuffer( COMM_BUFFER_T buffer ) { if ( buffer < NUM_OF_COMM_BUFFERS ) { S32 d; // Thread protection for queue operations _disable_IRQ(); activeDoubleBuffer[ buffer ] = 0; for ( d = 0; d < DOUBLE_BUFFERS; d++ ) { commBufferByteCount[ buffer ][ d ] = 0; memset( &commBuffers[ buffer ][ d ][ 0 ], 0, COMM_BUFFER_LENGTH ); } // Release thread protection _enable_IRQ(); } } /*********************************************************************//** * @brief * The addToCommBuffer function adds data of specified length to a given * communication buffer. * @note This function will add to the active side of the double buffer. * @note This function is thread safe. IRQ interrupts are disabled during * buffer operations. * @details Alarm: ALARM_ID_XX_SOFTWARE_FAULT if given buffer is invalid or * it is full. * @details Inputs: commBufferByteCount[], activeDoubleBuffer[] * @details Outputs: commBuffers[], commBufferByteCount[] * @param buffer ID of buffer to add data to * @param data Pointer to byte array containing data to add to buffer * @param len Length of data (in bytes) * @return TRUE if data added to buffer successfully, FALSE if not *************************************************************************/ BOOL addToCommBuffer( COMM_BUFFER_T buffer, U08* data, U32 len ) { BOOL result = FALSE; // Verify given buffer if ( buffer < NUM_OF_COMM_BUFFERS ) { BOOL bufferFull = FALSE; U32 activeBuffer; U32 currentActiveBufCount; // Where to start adding new data to buffer (after existing data) if ( ( FALSE == isOnlyCANNode() ) || ( FALSE == isCANBoxForXmit( (CAN_MESSAGE_BOX_T)buffer ) ) ) { // Thread protection for queue operations _disable_IRQ(); activeBuffer = activeDoubleBuffer[ buffer ]; currentActiveBufCount = commBufferByteCount[ buffer ][ activeBuffer ]; // Check to make sure buffer is not too full to service this add if ( len <= ( COMM_BUFFER_LENGTH - currentActiveBufCount ) ) { U08 *buffPtr; // Buffer destination for added data // Set destination pointer to end of active buffer data buffPtr = &commBuffers[ buffer ][ activeBuffer ][ currentActiveBufCount ]; // Copy source data to destination buffer memcpy( buffPtr, data, len ); // Adjust buffer count per this data add (also reserves space to add data before releasing thread protection) commBufferByteCount[ buffer ][ activeBuffer ] += len; // Data successfully added to buffer result = TRUE; } else // Buffer too full to add this much data { bufferFull = TRUE; } // Release thread protection _enable_IRQ(); } // If buffer was full, check persistence - trigger s/w fault if persists if ( TRUE == bufferFull ) { clearBuffer( buffer ); // Not first overflow? if ( firstBufferOverflowTimeStamp != 0 ) { // If buffer overflows persists, fault if ( calcTimeSince( firstBufferOverflowTimeStamp ) > BUFFER_OVERFLOW_PERSISTENCE_MS ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_ADD_TOO_MUCH_DATA, (U32)buffer ) } } else // First overflow - set time stamp for persistence check { firstBufferOverflowTimeStamp = getMSTimerCount(); } } else { // If good for persistence time period, reset persistence check if ( ( firstBufferOverflowTimeStamp != 0 ) && ( calcTimeSince( firstBufferOverflowTimeStamp ) > BUFFER_OVERFLOW_PERSISTENCE_MS ) ) { firstBufferOverflowTimeStamp = 0; } } } else // Invalid buffer given { #ifdef _TD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_ADD_INVALID_BUFFER, buffer ) #endif #ifdef _DD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_ADD_INVALID_BUFFER, buffer ) #endif } return result; } /*********************************************************************//** * @brief * The getFromCommBuffer function fills a given byte array with a specified * number of bytes from a given buffer and returns the number of bytes * retrieved from the buffer. * @note This function will draw from the inactive side of the double buffer * and, if needed, switch double buffers to draw the rest of the requested data. * @note This function is thread safe. IRQ interrupts are disabled during buffer * operations. * @details Alarm: ALARM_ID_XX_SOFTWARE_FAULT if given buffer is invalid. * @details Inputs: commBuffers[], commBufferByteCount[], activeDoubleBuffer[] * @details Outputs: commBuffers[], commBufferByteCount[], activeDoubleBuffer[], * and the given data array is populated with data from the buffer. * @param buffer ID of buffer to retrieve data from * @param data Pointer to byte array to populate with buffer data * @param len Number of bytes to retrieve from buffer into given byte array. * @return the number of bytes retrieved. *************************************************************************/ U32 getFromCommBuffer( COMM_BUFFER_T buffer, U08* data, U32 len ) { U32 result = 0; // Verify given buffer if ( buffer < NUM_OF_COMM_BUFFERS ) { // Thread protection for queue operations _disable_IRQ(); // Verify requested # of bytes to get are in the buffer if ( ( len <= ( COMM_BUFFER_LENGTH * DOUBLE_BUFFERS ) ) && ( len <= numberOfBytesInCommBuffer( buffer ) ) ) { U32 activeBuffer = activeDoubleBuffer[ buffer ]; U32 inactiveBuffer = GET_TOGGLE( activeBuffer, 0, 1 ); U32 bytesInInactiveBuffer = commBufferByteCount[ buffer ][ inactiveBuffer ]; U32 sizeOfFirstConsumption = MIN( len, bytesInInactiveBuffer ); // See what we can get from inactive buffer getDataFromInactiveBuffer( buffer, data, sizeOfFirstConsumption ); // Will switch double buffers if we empty inactive buffer // Will return # of bytes consumed result = sizeOfFirstConsumption; // Do we need more from active buffer? if ( len > sizeOfFirstConsumption ) { U32 remNumOfBytes = len - sizeOfFirstConsumption; U08 *remPtr = data + sizeOfFirstConsumption; getDataFromInactiveBuffer( buffer, remPtr, remNumOfBytes ); // Will return # of bytes consumed result += remNumOfBytes; } } // Release thread protection _enable_IRQ(); } else // Invalid buffer given { #ifdef _TD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_GET_INVALID_BUFFER, buffer ) #endif #ifdef _DD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_GET_INVALID_BUFFER, buffer ) #endif } return result; } /*********************************************************************//** * @brief * The peekFromCommBuffer function fills a given byte array with a given * number of bytes from a given buffer. * @note This function does NOT consume the bytes - it only peeks at them. * A call to numberOfBytesInCommBuffer() should be made prior to calling * this function to determine how many bytes are currently in the buffer. * @warning Do not call this function with a "len" greater than the number * of bytes currently in the buffer. * @details Alarm: ALARM_ID_XX_SOFTWARE_FAULT if given buffer is invalid. * @details Inputs: commBuffers[], commBufferByteCount[], activeDoubleBuffer[] * @details Outputs: given array populated with requested number of bytes from the buffer. * @param buffer ID of buffer to retrieve data from * @param data Pointer to byte array to populate with buffer data * @param len Number of bytes to retrieve from the given buffer. * @return the number of bytes retrieved. *************************************************************************/ U32 peekFromCommBuffer( COMM_BUFFER_T buffer, U08 *data, U32 len ) { U32 numOfBytesPeeked = 0; // Verify given buffer if ( buffer < NUM_OF_COMM_BUFFERS ) { // Thread protection for queue operations _disable_IRQ(); // Verify requested # of bytes to peek are in the buffer if ( ( len <= ( COMM_BUFFER_LENGTH * DOUBLE_BUFFERS ) ) && ( len <= numberOfBytesInCommBuffer( buffer ) ) ) { U32 activeBuffer = activeDoubleBuffer[ buffer ]; U32 inactiveBuffer = GET_TOGGLE( activeBuffer, 0, 1 ); U32 bytesInInactiveBuffer = commBufferByteCount[ buffer ][ inactiveBuffer ]; if ( len <= bytesInInactiveBuffer ) { memcpy( data, &commBuffers[ buffer ][ inactiveBuffer ][ 0 ], len ); numOfBytesPeeked = len; } else // Will need to get the rest from active buffer { U32 remNumOfBytes = len - bytesInInactiveBuffer; U08 *remPtr = data + bytesInInactiveBuffer; memcpy( data, &commBuffers[ buffer ][ inactiveBuffer ][ 0 ], bytesInInactiveBuffer ); memcpy( remPtr, &commBuffers[ buffer ][ activeBuffer ][ 0 ], remNumOfBytes ); numOfBytesPeeked = bytesInInactiveBuffer + remNumOfBytes; } } // Release thread protection _enable_IRQ(); } else // Invalid buffer given { #ifdef _TD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_PEEK_INVALID_BUFFER, buffer ) #endif #ifdef _DD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_PEEK_INVALID_BUFFER, buffer ) #endif } return numOfBytesPeeked; } /*********************************************************************//** * @brief * The numberOfBytesInCommBuffer function determines how many bytes * are currently contained in a given comm buffer. Both sides of the * double buffers are considered for this. * @details Alarm: ALARM_ID_XX_SOFTWARE_FAULT if given buffer is invalid. * @details Inputs: activeDoubleBuffer[], commBufferByteCount[] * @details Outputs: none * @param buffer ID of buffer to get byte count for * @return the number of bytes currently in the given comm buffer. *************************************************************************/ U32 numberOfBytesInCommBuffer( COMM_BUFFER_T buffer ) { U32 result = 0; // Verify given buffer if ( buffer < NUM_OF_COMM_BUFFERS ) { U32 activeBuffer = activeDoubleBuffer[ buffer ]; U32 inactiveBuffer = GET_TOGGLE( activeBuffer, 0, 1 ); result = commBufferByteCount[ buffer ][ inactiveBuffer ] + commBufferByteCount[ buffer ][ activeBuffer ]; } else // Invalid buffer { #ifdef _TD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_COUNT_INVALID_BUFFER, buffer ) #endif #ifdef _DD_ SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_COUNT_INVALID_BUFFER, buffer ) #endif } return result; } /*********************************************************************//** * @brief * The switchDoubleBuffer function switches the active and inactive sides * of the given buffer. * @warning This function should only be called when the current inactive * buffer has been emptied. Any unconsumed data in inactive buffer will be * lost. * @details Inputs: activeDoubleBuffer[] * @details Outputs: activeDoubleBuffer[], commBufferByteCount[] * @param buffer ID of buffer to switch double buffers * @return the new active side of the given double buffer (0 or 1) *************************************************************************/ static U32 switchDoubleBuffer( COMM_BUFFER_T buffer ) { U32 activeBuffer = activeDoubleBuffer[ buffer ]; U32 inactiveBuffer = GET_TOGGLE( activeBuffer, 0, 1 ); // Ensure inactive buffer is reset before making active commBufferByteCount[ buffer ][ inactiveBuffer ] = 0; // Switch buffers activeDoubleBuffer[ buffer ] = inactiveBuffer; // Return the new active buffer (was just inactive) return inactiveBuffer; } /*********************************************************************//** * @brief * The getDataFromInactiveBuffer function retrieves a given number of bytes * from the inactive side of a given buffer. * @warning This function should only be called by getFromCommBuffer(). * Parameters will be pre-validated there. * @details Inputs: commBuffers[], activeDoubleBuffer[], commBufferByteCount[] * @details Outputs: commBuffers[], activeDoubleBuffer[], commBufferByteCount[] * @param buffer ID of buffer to get data from inactive side of * @param data Pointer to byte array to populate with data from inactive * side of buffer * @param len Number of bytes to get from the buffer * @return none *************************************************************************/ static void getDataFromInactiveBuffer( COMM_BUFFER_T buffer, U08 *data, U32 len ) { U32 activeBuffer = activeDoubleBuffer[ buffer ]; U32 inactiveBuffer = GET_TOGGLE( activeBuffer, 0, 1 ); U32 bytesInInactiveBuffer = commBufferByteCount[ buffer ][ inactiveBuffer ]; // Get the requested data from inactive buffer memcpy( data, &commBuffers[ buffer ][ inactiveBuffer ][ 0 ], len ); if ( len < bytesInInactiveBuffer ) { U08 *endPtr = (&commBuffers[ buffer ][ inactiveBuffer ][ 0 ] + len); // Move un-consumed data in inactive buffer to start of inactive buffer memcpy( &commBuffers[ buffer ][ inactiveBuffer ][ 0 ], endPtr, ( bytesInInactiveBuffer - len ) ); // Reduce byte count for inactive buffer by # of bytes consumed commBufferByteCount[ buffer ][ inactiveBuffer ] -= len; } else { // Inactive buffer has been emptied so switch double buffers switchDoubleBuffer( buffer ); // Switch will zero count off inactive buffer } } /**@}*/