Task priority, interrupt, semaphore problem

eriksage wrote on Wednesday, August 08, 2012:

Dear support forum,
I have a problem regarding tha task priority, interrupt priority and the use of semaphores.

At initialization, a startupThread is created and the scheduler is started.
If the task priority of the startupThread is lower than 9, the code is working. If it is higher than 9, the code only works if I put a breakpoint in the xPortPendSVHandler and steps through the code.

The problem occurs when the startupThread is waiting for a semaphore that is released in an I2C interrupt. When the semaphore is released, the StartupThread does not continue to run. Neither because of a semaphore timeout or because of the release. I can see that the semaphore is released and that portEND_SWITCHING_ISR ( xHigherPriorityTaskWoken ) is called with xHigherPriorityTaskWoken == 1. However the startupThread is never continued. Since the semaphore also have a timeout I find this very strange.

I suspect that there might be an issue regarding the setup of the interrupt priorities or task priorities. Any ideas? Can the increased size of the taskList cause more delay and trigger a problem with a race-condition?
Is there a limit for the task priority other than configMAX_PRIORITIES?

Info:
FreeRTOS V7.0.1
MCU: Freescale Kinetis K10.
Compiler: IAR 6.21

#define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 12 )
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5

Best regards,
Erik Agerup

rtel wrote on Wednesday, August 08, 2012:

Have you followed all the instructions on this page?
http://www.freertos.org/RTOS-Cortex-M3-M4.html

Regards.

eriksage wrote on Thursday, August 09, 2012:

Yes, I have followed the instructions, and I can see that the BASEPRI register is set to 0x50 during the first xTaskCreate().
However, after calling vTaskStartScheduler() and starting the first task, the BASEPRI register is reset to 0. This is also the case in the FreeRTOS blinky demo. Is this correct?

Regards

eriksage wrote on Thursday, August 09, 2012:

Forget my previous post, I now understand the BASEPRI register, and I also found the error:)

In xPortPendSVHandler the interupts are disabled, vTaskSwitchContext is run and the interrupts are enabled again.

stmdb sp!, {r3, r14}
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0
bl vTaskSwitchContext
mov r0, #0
msr basepri, r0
ldmia sp!, {r3, r14}

But in vTaskSwitchContext, the function portGET_RUN_TIME_COUNTER_VALUE is run when configGENERATE_RUN_TIME_STATS is enabled, and that function calls xTaskGetTickCountFromISR();

xTaskGetTickCountFromISR() disables interrupts, and enables them again.
The remaining code of vTaskSwitchContext is now run with interrupts enabled.

portTickType xTaskGetTickCountFromISR( void )
{
             portTickType xReturn;
             unsigned portBASE_TYPE uxSavedInterruptStatus;
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
xReturn = xTickCount;
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
return xReturn;
}
I am sure the intension is to store the previous state of the interrupt in uxSavedInterruptStatus, but that does not work, it is always 0.

I think these defines need to be changed in portmacro.h:
#define portSET_INTERRUPT_MASK_FROM_ISR() 0;vPortSetInterruptMask()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortClearInterruptMask();(void)x

If 0 always is returned from portSET_INTERRUPT_MASK_FROM_ISR(), interrupts will be enabled when they are not supposed to.

How can I update these macros such that the BASEPRI register is read instead of 0?

Regards

rtel wrote on Thursday, August 09, 2012:

In xPortPendSVHandler the interupts are disabled, vTaskSwitchContext is run and the interrupts are enabled again.

No they are not.  The intelligent Cortex-M NVIC (nested interrupt controller) will not accept an interrupt at a priority above the currently running interrupt.  As BASE_PRI is set to 0 the code effectively sets the interrupt mask to the priority of the existing interrupt. 

I think these defines need to be changed in portmacro.h:
#define portSET_INTERRUPT_MASK_FROM_ISR() 0;vPortSetInterruptMask()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortClearInterruptMask();(void)x

It is set to zero because saving the old interrupt state and then restoring the old interrupt state would cause the compiler to generate more assembly code with no difference being made to the effective state of the compiler or the execution of your code.  As it is, turning the optimiser on in the compiler settings will just remove the code all together as it does nothing.

Regards.

eriksage wrote on Thursday, August 09, 2012:

Ok, I understand, and thank you for your reply.
That was strange, because changing the code solved my problem.

The strange thing is that if I either disable configGENERATE_RUN_TIME_STATS or uncomment as shown below, the code is working.
If I don’t do this, it depends on the value of the highest task priority if it works or not. I suspect that a higher task priority value will make it take a longer time to go through the task list in vTaskSwitchContext and therefore it is more likely that the I2C interrupt is occurring here. I can see that the I2C interrupt is run while vTaskSwitchContext is running.

//uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
xReturn = xTickCount;
//portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );

So, it is OK that the BASEPRI is changed from 0x50 to 0x00 during vTaskSwitchContext if and only if configGENERATE_RUN_TIME_STATS is enabled?

Can you think of something else causing my problem, when changing this makes my problem disappear.

Regards.

edwards3 wrote on Friday, August 10, 2012:

When configGENERATE_RUN_TIME_STATS  is set to 1, the kernel will try an call user defined functions to first initialize the run time stats gathering, and then obtain the run time stats pointers. How are your run time stats functions implemented?

rtel wrote on Thursday, August 16, 2012:

Update -

This is probably the cause of the issue.  The run time stats function is called from within a critical section, and exiting the critical section from the run time stats implementation would be a problem on a Cortex-M.  I have just looked at the run time stats implementation in the FreeRTOS/Demo projects, and see that it specifically accounts for this potential issue be re-entering the critical section in the code.  See the call to portSET_INTERRUPT_MASK_FROM_ISR() in the snipped below, which is taken from the official Kinetis K60 demo.

	/* The SysTick is a down counter.  How many clocks have passed since it was
	last reloaded? */
	ulSysTickCounts = ulSysTickReloadValue - *pulCurrentSysTickCount;
	
	/* How many times has it overflowed? */
	ulTickCount = xTaskGetTickCountFromISR();
	/* This is called from the context switch, so will be called from a
	critical section.  xTaskGetTickCountFromISR() contains its own critical
	section, and the ISR safe critical sections are not designed to nest,
	so reset the critical section. */
	portSET_INTERRUPT_MASK_FROM_ISR();
	
	/* Is there a SysTick interrupt pending? */
	if( ( *pulInterruptCTRLState & ulSysTickPendingBit ) != 0UL )
	{
		/* There is a SysTick interrupt pending, so the SysTick has overflowed
		but the tick count not yet incremented. */
		ulTickCount++;
		
		/* Read the SysTick again, as the overflow might have occurred since
		it was read last. */
		ulSysTickCounts = ulSysTickReloadValue - *pulCurrentSysTickCount;
	}	
	
	/* Convert the tick count into tenths of a millisecond.  THIS ASSUMES
	configTICK_RATE_HZ is 1000! */
	ulReturn = ( ulTickCount * 10UL ) ;
		
	/* Add on the number of tenths of a millisecond that have passed since the
	tick count last got updated. */
	ulReturn += ( ulSysTickCounts / ulClocksPer10thOfAMilliSecond );
	
	return ulReturn;	

I might put something into the core code to guard against this, as I can see it is an area that could cause users an issue.

Regards.