Index: firmware/App/Services/CommBuffers.c =================================================================== diff -u -ra303cd4258157a8fbcbd8af4dd2bbaadec1a736c -r0dec8744af40d0c87a6d7cd1923920c1c2bd1d2f --- firmware/App/Services/CommBuffers.c (.../CommBuffers.c) (revision a303cd4258157a8fbcbd8af4dd2bbaadec1a736c) +++ firmware/App/Services/CommBuffers.c (.../CommBuffers.c) (revision 0dec8744af40d0c87a6d7cd1923920c1c2bd1d2f) @@ -1,48 +1,48 @@ -/************************************************************************** - * - * 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 CommBuffers.c - * - * @date 08-Oct-2019 - * @author S. Nash - * - * @brief CommBuffers service module. Provides data buffering functionality. \n - * Data coming in via CAN/UART Rx interrupts can be placed in a buffer to \n - * be processed at a later time. Data is double buffered to prevent \n - * contention between Rx/Tx interrupts and the processing thread(s). \n - * Data to be transmitted via CAN/UART can be placed in a buffer to be sent \n - * when the transmitter becomes available. \n - * If a buffer becomes too full to service more data, a s/w fault is triggered. - * - **************************************************************************/ +/************************************************************************** +* +* 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 CommBuffers.c +* +* @author (last) Quang Nguyen +* @date (last) 21-Jul-2020 +* +* @author (original) Dara Navaei +* @date (original) 05-Nov-2019 +* +***************************************************************************/ #include // for memcpy() -#include "Common.h" #include "CommBuffers.h" +#include "SystemCommMessages.h" +#include "Timers.h" // ********** private definitions ********** #define COMM_BUFFER_LENGTH 512 // max bytes in each comm buffer (double if you count double buffers) #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 U32 commBufferByteCount[NUM_OF_COMM_BUFFERS][DOUBLE_BUFFERS]; // for each buffer, how many bytes does it contain? (also index to next available) -static 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 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 volatile BOOL bufferGetLock[ NUM_OF_COMM_BUFFERS ]; // prevent getter from accessing active buffer while add in progress +static U32 firstBufferOverflowTimeStamp = 0; // time stamp of a prior overflow event - allows for an overflow persistence check // ********** private function prototypes ********** +static void clearBuffer( COMM_BUFFER_T buffer ); static U32 switchDoubleBuffer( COMM_BUFFER_T buffer ); static void getDataFromInactiveBuffer( COMM_BUFFER_T buffer, U08 *data, U32 len ); /************************************************************************* - * @brief initCommBuffers + * @brief * The initCommBuffers function initializes the CommBuffers module. * @details * Inputs : none @@ -52,25 +52,46 @@ *************************************************************************/ void initCommBuffers( void ) { - S32 b,d,i; + S32 b; // reset and zero out all buffers for ( b = 0; b < NUM_OF_COMM_BUFFERS; b++ ) { - activeDoubleBuffer[b] = 0; + clearBuffer( (COMM_BUFFER_T)b ); + } +} + +/************************************************************************* + * @brief + * The clearBuffer function clears (empties) a given buffer. \n + * Caller should ensure buffer won't be used while this function is clearing \n + * the buffer. + * @details + * Inputs : none + * Outputs : given buffer is cleared. + * @param buffer : the buffer to clear + * @return none + *************************************************************************/ +static void clearBuffer( COMM_BUFFER_T buffer ) +{ + if ( buffer < NUM_OF_COMM_BUFFERS ) + { + S32 d,i; + + activeDoubleBuffer[ buffer ] = 0; for ( d = 0; d < DOUBLE_BUFFERS; d++ ) { - commBufferByteCount[b][d] = 0; + commBufferByteCount[ buffer ][ d ] = 0; for ( i = 0; i < COMM_BUFFER_LENGTH; i++ ) { - commBuffers[b][d][i] = 0; + commBuffers[ buffer ][ d ][ i ] = 0; } } } } /************************************************************************* - * @brief addToCommBuffer + * @brief * The addToCommBuffer function adds data of specified length to a specified \n * communication buffer. S/W fault if buffer too full to add data. \n * This function will always add to the active double buffer. \n @@ -87,51 +108,86 @@ BOOL addToCommBuffer( COMM_BUFFER_T buffer, U08* data, U32 len ) { BOOL result = FALSE; - U32 activeBuffer = activeDoubleBuffer[buffer]; // 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) - // add requires brief thread protection because there may be multiple sources for transmits trying to add data to a buffer. + // thread protection for queue operations _disable_IRQ(); + bufferGetLock[ buffer ] = TRUE; - currentActiveBufCount = commBufferByteCount[buffer][activeBuffer]; + 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 - // adjust buffer count per this data add (also reserves space to add data before releasing thread protection) - commBufferByteCount[buffer][activeBuffer] += len; - // release thread protection - _enable_IRQ(); // set destination pointer to end of active buffer data - buffPtr = &commBuffers[buffer][activeBuffer][currentActiveBufCount]; + 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 { - // release thread protection - _enable_IRQ(); - // TODO - s/w fault? + bufferFull = TRUE; + clearBuffer( buffer ); } + // release thread protection + bufferGetLock[ buffer ] = FALSE; + _enable_IRQ(); + // if buffer was full, check persistence - trigger s/w fault if persists + if ( TRUE == bufferFull ) + { + // 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_DG_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_ADD_TOO_MUCH_DATA, len ) + } + } + else // first overflow - set time stamp for persistence check + { + firstBufferOverflowTimeStamp = getMSTimerCount(); + } +#ifdef DEBUG_ENABLED + { + // TODO - temporary debug code - remove later + char debugStr[ 256 ]; + sprintf( debugStr, "Comm Buffer Overflow:%5d \n", (U32)buffer ); + sendDebugData( (U08*)debugStr, strlen(debugStr) ); + } +#endif + } + 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 { - // TODO - s/w fault + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_ADD_INVALID_BUFFER, buffer ) } return result; } /************************************************************************* - * @brief getFromCommBuffer + * @brief * The getFromCommBuffer function fills a given byte array with a given \n * number of bytes from a given buffer and returns the number of bytes \n * retrieved from the buffer. This function will draw from the inactive \n @@ -155,12 +211,14 @@ // verify given buffer if ( buffer < NUM_OF_COMM_BUFFERS ) { - // verify size of get + // 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 activeBuffer = activeDoubleBuffer[ buffer ]; U32 inactiveBuffer = ( activeBuffer == 0 ? 1 : 0 ); - U32 bytesInInactiveBuffer = commBufferByteCount[buffer][inactiveBuffer]; + U32 bytesInInactiveBuffer = commBufferByteCount[ buffer ][ inactiveBuffer ]; U32 sizeOfFirstConsumption = MIN( len, bytesInInactiveBuffer ); // see what we can get from inactive buffer @@ -178,21 +236,19 @@ result += remNumOfBytes; } } - else // invalid peek size given - { - // TODO - s/w fault - } + // release thread protection + _enable_IRQ(); } else // invalid buffer given { - // TODO - s/w fault + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_GET_INVALID_BUFFER, buffer ) } return result; } /************************************************************************* - * @brief peekFromCommBuffer + * @brief * The peekFromCommBuffer function fills a given byte array with a given \n * number of bytes from a given buffer. This function does NOT consume \n * the bytes - it only peeks at them. A call to numberOfBytesInCommBuffer() \n @@ -214,43 +270,43 @@ // verify given buffer if ( buffer < NUM_OF_COMM_BUFFERS ) { - // verify size of peek + // 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 activeBuffer = activeDoubleBuffer[ buffer ]; U32 inactiveBuffer = ( activeBuffer == 0 ? 1 : 0 ); - U32 bytesInInactiveBuffer = commBufferByteCount[buffer][inactiveBuffer]; + U32 bytesInInactiveBuffer = commBufferByteCount[ buffer ][ inactiveBuffer ]; if ( len <= bytesInInactiveBuffer ) { - memcpy( data, &commBuffers[buffer][inactiveBuffer][0], len ); + 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 ); + memcpy( data, &commBuffers[ buffer ][ inactiveBuffer ][ 0 ], bytesInInactiveBuffer ); + memcpy( remPtr, &commBuffers[ buffer ][ activeBuffer ][ 0 ], remNumOfBytes ); numOfBytesPeeked = bytesInInactiveBuffer + remNumOfBytes; } } - else // invalid peek size given - { - // TODO - s/w fault - } + // release thread protection + _enable_IRQ(); } else // invalid buffer given { - // TODO - s/w fault + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_PEEK_INVALID_BUFFER, buffer ) } return numOfBytesPeeked; } /************************************************************************* - * @brief numberOfBytesInCommBuffer + * @brief * The numberOfBytesInCommBuffer function determines how many bytes \n * are currently contained in a given comm buffer. Both double buffers \n * are considered for this. @@ -267,18 +323,28 @@ // verify given buffer if ( buffer < NUM_OF_COMM_BUFFERS ) { - result = commBufferByteCount[buffer][0] + commBufferByteCount[buffer][1]; + U32 activeBuffer = activeDoubleBuffer[ buffer ]; + U32 inactiveBuffer = GET_TOGGLE( activeBuffer, 0, 1 ); + + if ( FALSE == bufferGetLock[ buffer ] ) + { + result = commBufferByteCount[ buffer ][ inactiveBuffer ] + commBufferByteCount[ buffer ][ activeBuffer ]; + } + else + { + result = commBufferByteCount[ buffer ][ inactiveBuffer ]; + } } else // invalid buffer { - // TODO - s/w fault. + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_COMM_BUFFERS_COUNT_INVALID_BUFFER, buffer ) } return result; } /************************************************************************* - * @brief switchDoubleBuffer + * @brief * The switchDoubleBuffer function switches the active and inactive buffers \n * for the given buffer. \n * This function should only be called when the current inactive buffer has \n @@ -291,20 +357,20 @@ *************************************************************************/ static U32 switchDoubleBuffer( COMM_BUFFER_T buffer ) { - U32 activeBuffer = activeDoubleBuffer[buffer]; + U32 activeBuffer = activeDoubleBuffer[ buffer ]; U32 inactiveBuffer = ( activeBuffer == 0 ? 1 : 0 ); // ensure inactive buffer is reset before making active - commBufferByteCount[buffer][inactiveBuffer] = 0; + commBufferByteCount[ buffer ][ inactiveBuffer ] = 0; // switch buffers - activeDoubleBuffer[buffer] = inactiveBuffer; + activeDoubleBuffer[ buffer ] = inactiveBuffer; // return the new active buffer (was just inactive) return inactiveBuffer; } /************************************************************************* - * @brief getDataFromInactiveBuffer + * @brief * The getDataFromInactiveBuffer function retrieves a given number of bytes \n * from the inactive buffer of a given buffer. This function should only be \n * called by getFromCommBuffer(). Params will be pre-validated there. @@ -318,21 +384,21 @@ *************************************************************************/ static void getDataFromInactiveBuffer( COMM_BUFFER_T buffer, U08 *data, U32 len ) { - U32 activeBuffer = activeDoubleBuffer[buffer]; + U32 activeBuffer = activeDoubleBuffer[ buffer ]; U32 inactiveBuffer = ( activeBuffer == 0 ? 1 : 0 ); - U32 bytesInInactiveBuffer = commBufferByteCount[buffer][inactiveBuffer]; + U32 bytesInInactiveBuffer = commBufferByteCount[ buffer ][ inactiveBuffer ]; // get the requested data from inactive buffer - memcpy( data, &commBuffers[buffer][inactiveBuffer][0], len ); + memcpy( data, &commBuffers[ buffer ][ inactiveBuffer ][ 0 ], len ); if ( len < bytesInInactiveBuffer ) { - U08 *endPtr = (&commBuffers[buffer][inactiveBuffer][0] + len); + 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) ); + memcpy( &commBuffers[ buffer ][ inactiveBuffer ][ 0 ], endPtr, ( bytesInInactiveBuffer - len ) ); // reduce byte count for inactive buffer by # of bytes consumed - commBufferByteCount[buffer][inactiveBuffer] -= len; + commBufferByteCount[ buffer ][ inactiveBuffer ] -= len; } else { @@ -341,4 +407,3 @@ } } -