xQueueSendFromISR() & xQueueReceiveFromISR() causing configASSERT() failure in V8.2.3

ajellisuk wrote on Friday, February 19, 2016:

Hi,

I have a block of code for handling USART interrupts on an STM32F407 discovery board:

void USART2_IRQHandler( void )
{
	portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
	portCHAR cChar;

 	if( USART_GetITStatus( USART2, USART_IT_TXE ) == SET )
	{
		/* The interrupt was caused by the TX register becoming empty.  Are
		there any more characters to transmit? */
		if( xQueueReceiveFromISR( xCharsForTx, &cChar, &xHigherPriorityTaskWoken ) == pdTRUE )
		{
			// A character was retrieved from the queue so can be sent to the
			// USART now.
			USART_SendData( USART2, cChar );
		}
		else
		{
			USART_ITConfig( USART2, USART_IT_TXE, DISABLE );
		}
	}

	if( USART_GetITStatus( USART2, USART_IT_RXNE ) == SET )
	{
		/* A character has been received on the USART, send it to the Rx
		handler task. */
		cChar = USART_ReceiveData( USART2 );
		xQueueSendFromISR( xRxedChars, &cChar, &xHigherPriorityTaskWoken );
	}

	/* If sending or receiving from a queue has caused a task to unblock, and
	the unblocked task has a priority equal to or higher than the currently
	running task (the task this ISR interrupted), then xHigherPriorityTaskWoken
	will have automatically been set to pdTRUE within the queue send or receive
	function.  portEND_SWITCHING_ISR() will then ensure that this ISR returns
	directly to the higher priority unblocked task. */
	portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}

Previously I was using V7.1.0, which is available through CooCox. The code worked fine with this version of FreeRTOS, but I needed to upgrade to V8.2.3 so that I could access some of the new features, such as xTaskNotify. When I try and run my firmware, whenever either of the aforementioned functions are used, my firmware ends up hitting configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); in the following block of code:

#if( configASSERT_DEFINED == 1 )

	void vPortValidateInterruptPriority( void )
	{
	uint32_t ulCurrentInterrupt;
	uint8_t ucCurrentPriority;

		/* Obtain the number of the currently executing interrupt. */
		__asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) );

		/* Is the interrupt number a user defined interrupt? */
		if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER )
		{
			/* Look up the interrupt's priority. */
			ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ];

			/* The following assertion will fail if a service routine (ISR) for
			an interrupt that has been assigned a priority above
			configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API
			function.  ISR safe FreeRTOS API functions must *only* be called
			from interrupts that have been assigned a priority at or below
			configMAX_SYSCALL_INTERRUPT_PRIORITY.

			Numerically low interrupt priority numbers represent logically high
			interrupt priorities, therefore the priority of the interrupt must
			be set to a value equal to or numerically *higher* than
			configMAX_SYSCALL_INTERRUPT_PRIORITY.

			Interrupts that	use the FreeRTOS API must not be left at their
			default priority of	zero as that is the highest possible priority,
			which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY,
			and	therefore also guaranteed to be invalid.

			FreeRTOS maintains separate thread and ISR API functions to ensure
			interrupt entry is as fast and simple as possible.

			The following links provide detailed information:
			http://www.freertos.org/RTOS-Cortex-M3-M4.html
			http://www.freertos.org/FAQHelp.html */
			configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );
		}

		/* Priority grouping:  The interrupt controller (NVIC) allows the bits
		that define each interrupt's priority to be split between bits that
		define the interrupt's pre-emption priority bits and bits that define
		the interrupt's sub-priority.  For simplicity all bits must be defined
		to be pre-emption priority bits.  The following assertion will fail if
		this is not the case (if some bits represent a sub-priority).

		If the application only uses CMSIS libraries for interrupt
		configuration then the correct setting can be achieved on all Cortex-M
		devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the
		scheduler.  Note however that some vendor specific peripheral libraries
		assume a non-zero priority group setting, in which cases using a value
		of zero will result in unpredicable behaviour. */
		configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue );
	}

#endif /* configASSERT_DEFINED */

Can anyone suggest what the issue is here please? As I say the code worked fine with an older version of FreeRTOS, so is there something extra I need to use xQueueSendFromISR() & xQueueReceiveFromISR() in later versions of FreeRTOS.

Thanks

Andrew

rtel wrote on Saturday, February 20, 2016:

I’m not sure what more I can say over and above the information provided in the comments of the code you have already posted…you have an incorrect interrupt priority.

You didn’t hit this assert before because the assert was added to the code after the older FreeRTOS version you were using, and it seems your code will always have had this bug but you were just lucky enough not to hit it - yet.

More information is provided on the following link: http://www.freertos.org/RTOS-Cortex-M3-M4.html

Regards.

ajellisuk wrote on Monday, February 22, 2016:

Thank you for getting back to me.

I’ll take a look at the link you sent me, and I’ll let you know how I get on.

Regards

Andrew

ajellisuk wrote on Tuesday, February 23, 2016:

Hi,

I have read through the link you have provided, and I’m still unclear what I’m doing wrong. I have attached my project. I have placed a couple of snippets of code which I beleive to be where my problem lies.

In FreeRTOSConfig.h the relevent interrupt priority macros are defined as:

/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
	/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
	#define configPRIO_BITS       		__NVIC_PRIO_BITS
#else
	#define configPRIO_BITS       		4        /* 15 priority levels */
#endif

/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			0xf

/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY	5

/* Interrupt priorities used by the kernel port layer itself.  These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY 		( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

In my serial.c file, the usart interrupts are set up with:

NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY + 10;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // Not used as 4 bits are used for the pre-emption priority.
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_Init( &NVIC_InitStructure );
		USART_ITConfig( USART2, USART_IT_RXNE, ENABLE );

If I have understood the documentation correctly, then the usart interrupt priority should be well within the range of the interrupt priority limts. If I comment out xQueueSendFromISR() & xQueueReceiveFromISR() in my usart isr, the firmware does not hit configASSERT.

rtel wrote on Tuesday, February 23, 2016:

Those priorities look ok. As you are using an STM32, have you also
called NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ); ?

koor1200 wrote on Tuesday, February 23, 2016:

Hi,
Thank you for your response.
My target is not STM32. It is the KINETIS K64 series as stated in the original post.
There are 4 tasks:

OSA_TaskCreate(task_two,“task_two”,TASK_ONE_STACK_SIZE,0,6u,NULL,false,&task_two_task_handler);
OSA_TaskCreate(vATask,“vATask”,TASK_ONE_STACK_SIZE,0,5u,NULL,false,&vATask_task_handler);
OSA_TaskCreate(vADifferentTask,“vADifferentTask”,TASK_ONE_STACK_SIZE,0,5u,NULL,false,&vADifferentTask_task_handler);

//create task
result = OSA_TaskCreate(task_one,
		(uint8_t *)"task_one",
		TASK_ONE_STACK_SIZE,
		0,
		6u,
		(task_param_t)0,
		false,
		&task_one_task_handler);
        
        and here:
        
        void vATask( void *pvParameters )

{
struct AMessage *pxMessage;
uint32_t * test_ptr;

// Create a queue capable of containing 10 pointers to AMessage structures.
// These should be passed by pointer as they contain a lot of data.
xQueue = xQueueCreate( 16, sizeof( unsigned int*  ) );
if( xQueue == 0 )
{
    // Failed to create the queue.
}



// ...



// Send a pointer to a struct AMessage object.  Don't block if the
// queue is already full.
pxMessage = & xMessage;
test_ptr= &test_var;
xQueueSend( xQueue, ( void * )/* &pxMessage*/&test_ptr, ( TickType_t ) 0 );



// ... Rest of task code.

}

koor1200 wrote on Tuesday, February 23, 2016:

Sorry my mistake.
My reply doe not belong to the this original post.

ajellisuk wrote on Wednesday, February 24, 2016:

I have called NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ); just before calling vTaskStartScheduler();

My main() function reads:

int main( void ) {

    HwInit();

    vDebugInitQueue();
	vDebugPrintf( "\r\nEPS Test1 0.0.1 - 1/4/2012\r\n" );
	vDebugPrintResetType();

    // Tasks get started here...
    xTaskCreate( vTimeTask, (signed char *) "TIME", configMINIMAL_STACK_SIZE,
            NULL, mainTIME_TASK_PRIORITY, &hTimeTask );
    xTaskCreate( vMemsTask, (signed char *) "MEMS", configMINIMAL_STACK_SIZE,
            NULL, mainMEMS_TASK_PRIORITY, &hMemsTask );
   xTaskCreate( vDebugTask, (signed char *) "DEBUG", configMINIMAL_STACK_SIZE,
            NULL, mainDEBUG_TASK_PRIORITY, &hDebugTask );

   NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );

    vTaskStartScheduler(); // This should never return.

    // Will only get here if there was insufficient memory to create
    // the idle task.
    for( ;; );  
}

When I step into xQueueReceiveFromISR(), the code reaches vPortValidateInterruptPriority() as I would expect, then I get some results which I’m perplexed by.

When asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) ); is called ulCurrentInterrupt is 32 which according to the stm32 reference manual is I2C1_ER. The ISR which calls xQueueReceiveFromISR() is USART2_IRQHandler(), and has a value of 38.

edwards3 wrote on Wednesday, February 24, 2016:

Looks like it is not the interrupt you think, but be careful with the numbering in case the numbers are an index into the vector table or from the first non system interrupt.