ulTaskNotifyTake doesn't block and priority grouping assert fails

Hello together,
I am using a STM32F429 together with CubeMX and the STM32HAL (both in CubeIDE).

Project has the following setup:
In CubeMX NVIC settings, priority group is set to 4bits pre-emption and 0 bits subpriority (this is CMSIS priority group 4). I am using an external interrupt with priority 11 and a timer interrupt with priority 10 (however, the timer interrupt is inactive for now).
In freeRTOSconfig.h configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY is set to 5.

Upon the EXTI, the following HAL handler is triggered:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	printf("Interrupt Executing\n");
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;

	vTaskNotifyGiveFromISR(xControlTimerTask, &xHigherPriorityTaskWoken);

	portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

This sends a notification to another task:

void vTaskControlTimer(void *pvParameters)
{
	int numEvents;
	while(1)
	{
		numEvents=ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
		printf("TaskControlTimer: Notification received!\n");
	}
}

(Task handles match)

When debugging, following behavior is observed:

When starting the program, vTaskControlTimer starts executing, goes into suspended state after executing ulTaskNotifyTake. So far so good.

When triggering the interrupt, vTaskControlTimer continues to execute, sends out the message, and enters suspended state again. Again, thats expected behaviour.

When triggering the interrupt for the second time, vTaskControlTimer continues to execute, but doesnt enter the suspended state again. It seems like it’s ignoring the NotifyTake completely, and it continiously sends out the message. Sometimes this also happens at the first time of triggering the interrupt, sometimes at the 4th, 5th, … time.

When triggering the interrupt again (while messages are printed out), the program stops at

 volatile uint32_t ulCurrentInterruptGrouping;
        ulCurrentInterruptGrouping = ( portAIRCR_REG & portPRIORITY_GROUP_MASK );
        configASSERT( ulCurrentInterruptGrouping <= ulMaxPRIGROUPValue );

I tracked the values of ulCurrentInterruptGrouping and ulMaxPRIGROUPValue throughout the tests.
ulCurrentInterruptGrouping is 768 consistently, when the assert is true and when it fails.

At the first interrupt (where everything is normal), ulCurrentInterruptGrouping is 0d2779096485 (0xa5a5a5a5).

When the assert fails, the value of ulCurrentInterruptGrouping changes, I’ve seen values of 0d28, 0d38 and 0d33.

I have no idea what the root of these problems are (i’m fairly sure they are connected)…
Any help with that? Thanks in advance!

Are you really sure that printf can be used in ISRs ?
Usually it’s not and can cause all kinds of problems.
I’d propose to retry without

printf("Interrupt Executing\n");

in your ISR.

As per hs2 - putting a printf() inside an interrupt is going to cause an issue.

Additionally, 0xa5a5a5a5 is the value used to fill a newly allocated stack, so you could have stack issues here - again printf() can be a culprit as it can be hugely stack hungry - especially in GCC. Do you have stack overflow detection on?

1 Like

Thank you for your replies! I see why printf should not be executed. I removed the printf call in the ISR, but that doesn’t fix the problem.

I implemented the stack overflow detection (method 1) and i immediately get a stack overflow after creating the task and starting the scheduler.

  xTaskCreate(vTaskControlTimer,"ControlTimer",32, NULL, 1, &xControlTimerTask);
  printf("Starting FreeRTOS scheduler...\n");
  vTaskStartScheduler();

I changed the stack size of the task to 128 words, and now everything works.

But why is 32 words as stack size too low? Does printf allocate so much memory on the tasks stack, that 32 words * 4 bytes isn’t enough? In freeRTOS.h minimal stack size is configured to 128, but according to the documentation this shouldn’t affect stack sizes of created tasks, or does it?

It depends on what the task does. There is a minimal stack size which is still needed to store the context when the task is switched out. You can use uxTaskGetStackHighWaterMark API to find the stack usage of a task: FreeRTOS - A FREE RTOS for small real time embedded systems

Only if you use the define to specify the stack size of a task:

xTaskCreate( vTaskFunction,
             "Check",
             configMINIMAL_STACK_SIZE,
             NULL,
             tskIDLE_PRIORITY,
             NULL );

Thanks.

Thank you for your reply. I will try to monitor the stack usage.

Adding onto that, when i look at the FreeRTOS Task List in the CubeIDE i see the following:

When calculating the size of the stack of the ControlTimer Task, i end up with 0x2000035c-0x200001e8=0x174 or 0d372. That differs quite much from the 128 words (=512byte) i definded in the task creation. Do i have an error in my calculation?

This is correct as it means current stack stack usage which is 372 bytes. You still have (512 - 372) bytes remaining.

1 Like

That makes sense. One last question :wink: :
I excluded the printf statement from the task. It looks like this now:

void vTaskControlTimer(void *pvParameters)
{
	UBaseType_t HighWaterMark;
	while(1)
	{

		ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
		HighWaterMark = uxTaskGetStackHighWaterMark( NULL );
	}
}

After execution the HighWaterMark is set to 94, meaning 94 words (or 94*4=376 bytes) of 512bytes stack are unused.

Looking at the task list:


0x20000354-0x200001e8=0x16c or 0d364 (91 words) of stack are used.

This doesn’t work out, can i even trust the Task list, or is it inaccurate?

You are using a Cortex-M4 platform on which the stack grows down. So, 0x20000354-0x200001e8=0x16c or 0d364 is amount of free stack space.

While as per uxTaskGetStackHighWaterMark(), the minimum amount free stack space ever was 376 which is 12 bytes more than 364. I cannot say how the IDE implements this - can you check the value of HighWaterMark after running it for couple more cycles?

Thanks.

I assume that the IDE checks the 0xa5a5a5a5 pattern and counts until the value changes? That explains why it counts the unused stack space.

I ran it about 15 times, all values are consistent. HighWaterMark stays at 94 and Start and Top of stack stay at 0x20000354 and 0x200001e8.

You will need to check that with ST.

I tried this and I was able to repro this difference of 12 bytes on STM32H743. Here is the explanation - uxTaskGetStackHighWaterMark checks for 0xA5 in the stack to find out the unused stack. We do not initialize some registers in the initial context and, as a result, the value on the stack remains 0xA5 even when the stack area is in use.

I’d say that when you use uxTaskGetStackHighWaterMark to tune your stack sizes, always keep some buffer for less executed paths or future changes.

Fundamentally, empirically measuring the stack at run-time is imprecise and you need to use a LOT of care to make sure you have determined worse case.

Method one only detects the usage at the point of test, so misses previous or future usages in excess of the current, so you need to find the real peak usage and add a test in the leaf function.

Method two can miss uninitialized buffers, as appears to be sort of your case here. It also doesn’t measure code that hasn’t been used yet, so you need to make sure that you have gone into the worse case paths.