vAssertCalled called when using Harmony's SYS_DEBUG_MESSAGE()

Hi,
the code below is an ISR callback.
If in that callback or any other ones I uncomment the SYS_DEBUG_MESSAGE() lines it fails the freeRTOS assert.
I assume it is because that function (microchip built-in Harmony library) uses freeRTOS functionality…? Can anybody confirm that is the case?

That function is a non-blocking function which uses freeRTOS functionality and I thought it would just pass the string to a buffer (to be processes later) then return immediately. But perhaps there are other reasons for it to call vAssertCalled()?

Most importantly, is there a way around it? i.e. are there ways which are freeRTOS safe to send a debug message to a 232 port inside an ISR?

I am using Microchip’s PIC32MZ with XC32 compiler and Harmony framework.

void CBK_CHANGE_NOTIF_ProductSense(GPIO_PIN pin, uintptr_t context)
{
    BaseType_t xHigherPriorityTaskWoken;
    UBaseType_t uxSavedInterruptStatus;
    xHigherPriorityTaskWoken = pdFALSE; 
    uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); 
    
    switch(task_sensorsData.xBtn_ProdSenseSwitchState)
    {
        case BTN_STATE_AT_REST:
        {
            //check CN was because button was pressed
            if (PROD_SENSE_Get() == PIN_STATE_PROD_SENSE_TRIGGERED)
            {
                //configure timer for debouncing 
                xTimerChangePeriodFromISR(task_sensorsData.xTimer_Handle_ProdSenseSwitch, 
                                        pdMS_TO_TICKS(TMR_RELOAD_PRODUCT_SENSE_PRESS_DEBOUNCE_MS),
                                        0);
                xTimerResetFromISR(task_sensorsData.xTimer_Handle_ProdSenseSwitch, 0);
                xTimerStartFromISR(task_sensorsData.xTimer_Handle_ProdSenseSwitch, 0);
                //disable CHANGE NOTIF interrupt for Product Sense Switch
                PROD_SENSE_InterruptDisable();
                //change state to BTN_STATE_DEBOUNCING
                task_sensorsData.xBtn_ProdSenseSwitchState = BTN_STATE_PRESS_DEBOUNCING;
//                SYS_DEBUG_MESSAGE(SYS_ERROR_DEBUG, "Changing to STATE_PRESS\r\n");
            }
            break;
        }
        case BTN_STATE_PRESSED:
        {
            //check CN was because button was pressed
            if (PROD_SENSE_Get() == PIN_STATE_PROD_SENSE_RELEASED)
            {
                //configure timer for debouncing 
                xTimerChangePeriodFromISR(task_sensorsData.xTimer_Handle_ProdSenseSwitch, 
                                        pdMS_TO_TICKS(TMR_RELOAD_PRODUCT_SENSE_RELEASE_DEBOUNCE_MS),
                                        0);
                xTimerResetFromISR(task_sensorsData.xTimer_Handle_ProdSenseSwitch, 0);
                xTimerStartFromISR(task_sensorsData.xTimer_Handle_ProdSenseSwitch, 0);
                //disable CHANGE NOTIF interrupt for Product Sense Switch
                PROD_SENSE_InterruptDisable();
                //change state to BTN_STATE_DEBOUNCING
                task_sensorsData.xBtn_ProdSenseSwitchState = BTN_STATE_RELEASE_DEBOUNCING;
//                SYS_DEBUG_MESSAGE(SYS_ERROR_DEBUG, "Changing to RELEASE\r\n");
            }
            break;
        }
        case BTN_STATE_PRESS_DEBOUNCING:
        case BTN_STATE_RELEASE_DEBOUNCING:
        default:
        {
//            SYS_DEBUG_MESSAGE(SYS_ERROR_ERROR, "DEBUG ERROR - Unused state\r\n");
        }
    }
    taskEXIT_CRITICAL_FROM_ISR(uxSavedInterruptStatus);
    portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}

Thank you as always! :slight_smile:

Which assert is it calling?

Since this is ISR code, why do you claim the critical section? As you should know by now, the only scenario where you would want to do that is when there are potential access conflicts with higher priority ISRs. I don’t know if this addresses your problem (as Richard pointed out, more info is needed), but as always in concurrent programming, too much synchronization is as dangerous as too little of it.

@rtel It calls the vAssertCalled() which is defined in the freertos_hooks.c

@RAc I claim the critical section because there is a higher priority interrupt used for synching with an external signal (printhead nozzle control/timing) and might affect a couple of variables used in this ISR the variable including task_sensorsData.xBtn_ProdSenseSwitchState. (There is more code in that ISR but I cut it out to keep it short for the post.)
With regard to that am I still using it incorrectly?

Thank you both as always!! :slight_smile:

@rtel wants to know the call stack resp. the source location where vAssertCalled is invoked which should be investigated 1st by yourself to identify the problem the assert checks.

No, from what I can tell, that sounds legitimate and valid, just to make sure!

1 Like

Thank you RAc.

@rtel and @hs2
please see attached screenshot of the call stack.

It seems to block on the following line:

const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting;

in the xQueueSemaphoreTake() function (see further below freeRTOS code).

I noticed that the line above it has a taskENTER_CRITICAL()

Is that the reason? Perhaps taskENTER_CRITICAL() cannot be nested (I guess in a way might make sense as the first ExitCritical would void the other outer ones)?

If so I guess I cannot use that USART non-blocking function inside an ISR? Or are there ways around it?


BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait )
{
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = xQueue;

#if( configUSE_MUTEXES == 1 )
	BaseType_t xInheritanceOccurred = pdFALSE;
#endif

	/* Check the queue pointer is not NULL. */
	configASSERT( ( pxQueue ) );

	/* Check this really is a semaphore, in which case the item size will be
	0. */
	configASSERT( pxQueue->uxItemSize == 0 );

	/* Cannot block if the scheduler is suspended. */
	#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
	{
		configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
	}
	#endif


	/*lint -save -e904 This function relaxes the coding standard somewhat to allow return
	statements within the function itself.  This is done in the interest
	of execution time efficiency. */
	for( ;; )
	{
		taskENTER_CRITICAL();
		{
			/* Semaphores are queues with an item size of 0, and where the
			number of messages in the queue is the semaphore's count value. */
			const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting;

			/* Is there data in the queue now?  To be running the calling task
			must be the highest priority task wanting to access the queue. */
			if( uxSemaphoreCount > ( UBaseType_t ) 0 )
			{
				traceQUEUE_RECEIVE( pxQueue );

				/* Semaphores are queues with a data size of zero and where the
				messages waiting is the semaphore's count.  Reduce the count. */
				pxQueue->uxMessagesWaiting = uxSemaphoreCount - ( UBaseType_t ) 1;

				#if ( configUSE_MUTEXES == 1 )
				{
					if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
					{
						/* Record the information required to implement
						priority inheritance should it become necessary. */
						pxQueue->u.xSemaphore.xMutexHolder = pvTaskIncrementMutexHeldCount();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				#endif /* configUSE_MUTEXES */

				/* Check to see if other tasks are blocked waiting to give the
				semaphore, and if so, unblock the highest priority such task. */
				if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
				{
					if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
					{
						queueYIELD_IF_USING_PREEMPTION();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}

				taskEXIT_CRITICAL();
				return pdPASS;
			}
			else
			{
				if( xTicksToWait == ( TickType_t ) 0 )
				{
					/* For inheritance to have occurred there must have been an
					initial timeout, and an adjusted timeout cannot become 0, as
					if it were 0 the function would have exited. */
					#if( configUSE_MUTEXES == 1 )
					{
						configASSERT( xInheritanceOccurred == pdFALSE );
					}
					#endif /* configUSE_MUTEXES */

					/* The semaphore count was 0 and no block time is specified
					(or the block time has expired) so exit now. */
					taskEXIT_CRITICAL();
					traceQUEUE_RECEIVE_FAILED( pxQueue );
					return errQUEUE_EMPTY;
				}
				else if( xEntryTimeSet == pdFALSE )
				{
					/* The semaphore count was 0 and a block time was specified
					so configure the timeout structure ready to block. */
					vTaskInternalSetTimeOutState( &xTimeOut );
					xEntryTimeSet = pdTRUE;
				}
				else
				{
					/* Entry time was already set. */
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		taskEXIT_CRITICAL();

Thank you

It looks like the routine is not coded to be used inside an ISR, which is what you are doing.

Thank you Richard,
is that for the reason I mentioned (that routine itself uses taskENTER_CRITICAL() and taskENTER_CRITICAL() cannot be nested) or some other reason?

Thank you

Well, it starts with using xSemaphoreTake instead of xSemaphoreTakeFromISR.
The code violates the API restrictions for ISRs as Richard correctly mentioned and as documented and checked by an assert.

You’re welcome!

I was referring to the CBK_CHANGE_NOTIF_ProductSense() callback fn code you posted earlier. As Hartmut and Richard pointed out, the code you posted afterwards is not ISR compatible.

Thank you Hartmut, I misread Richard’s answer. I thought he was referring to the Harmony function, hence my subsequent question.
All clear now! :slight_smile:

@RAc
Yes all clear now.
Thank you

The Harmony function calls routines that ultimate use functions that aren’t designed for use inside an ISR, in particular, the xQueueSemaphoreTake function.

ISR need to be careful as to what resources they use, which can sometimes be easy to forget.

Thank you Richard! :slight_smile: