I have a small application using FreeRTOS with 2 tasks. Task1 sends a char to a UART and then goes to vTaskDelay for 100 msec. Task 2 starts a timer and the goes to xQueueReceive. When the timer fires it calls its callback which sends an item to the queue task2 waits for. This works fine. But if I reduce configTIMER_TASK_PRIORITY below task 2 priority then everything crashes the moment the timer fires, even task1 does not send its characters anymore. I thought while all tasks are blocked only idle task is active and this has prio 0, right?
Any ideas? I am using the port for Cortex M0 and it runs on a STM32G070.
Thanks for any help
What do you mean by everything crashes? Are you getting a fault?
Also, please share the code snippets of your tasks and timer callback so that we can see if there is a problem.
with “everything crashes” I meant that the program is not running anymore as seen from outside ie there is no more output on the UART interface. As both task and also the timer callback produce a UART output I think the scheduler stopped working.
Meanwhile I boiled down the whole project to a minimal project that should still have this problem. But somewhere during this process the problem disappeared. I can now reduce configTIMER_TASK_PRIORITY to 0 and it still works, as expected.
So my question turned into something else:
when a timer fires and sends something on a queue, which other action or configuration can then make the scheduler stop to work. I have several other peripherals running, using interrupts and DMA. But all of that works fine as long as configTIMER_TASK_PRIORITY is above task 2 priority.
I know this is a strange question but I have no idea where to look for an “error” as there is obviously no real error.
So any ideas are welcome.
Thanks a lot
The scheduler should never automatically stop. So whatever you are observing is an issue somewhere.
Can you break in the debugger and see what the code is doing when you do not see any output? Also, have you defined
configASSERT, stack over checking and malloc failed checking?
in my experience, the only promising strategy to nail down the issue is to re-do your reduction and document every step (eg in a local GIT repository) to determine which exact step makes the problem disappear. In an embedded system, whenever you turn a screw, three bolts loosen somewhere else is the gizmo, as the saying goes, so trying to deduct causes from symptoms will only work very very rarely.
yes, that’s exactly my experience too. And meanwhile I am on this way. But it is a lot of work so giving some potentially shorter way a try may be worth while.
I am excited to see what is the cause of this behaviour.
thanks for the hints, I will try.
I had doubled all stacks before without any change.
I am using a fully static setup so malloc should never be used
I think I found the cause of the problem.
Contrary to what I said before I did not try doubling the stack of the timer task. Now I did it and that solved it. But this raises another question or a need for some explanation.
I now have
configMINIMAL_STACK_SIZE set to 50
configTIMER_TASK_PRIORITY set to 0
configTIMER_TASK_STACK_DEPTH set to configMINIMAL_STACK_SIZE * 2
this runs fine, removing “* 2” results in the described behaviour.
Now the compiler output map file tells me
uxIdleTaskStack is 0xC8 = 200d bytes large ( = 50 uint32_t)
uxTimerTaskStack is 0x190 = 400d bytes ( = 50 * 2 uint32_t)
So my question is why does the timer task need so much stack? Is the timer callback function running in the timer task which would mean the timer task stack size is dependant on the callback function code?
Thanks for any help
yes. (I need to type at least 20 characters, otherwise those three would be sufficient )
Now I reduced the callback to preparing a struct variable of 3 uint32_t and one call of xQueueSendToBack to the queue waiting for the timer event.
Why does this need > 200 bytes stack? Can I find this out somewhere? Is there some data on how much stack each FreeRTOS function needs?
Thanks for any help
how easy it is to figure this out depends on the IDE you are using. Decoding the stack is easy and straightforward on some but messy on others.
How did you create your queue object, ie what is the size of a queue element? What you may be seeing here is that a copy of the object passed to the queue is made but padded up to the full length of a queue element. Please post the code of your timer callback in full here.
The context of a task (which comprises of hardware registers) is stored on the task stack when a task is switched out.
In addition to what @RAc mentioned, you can use the function
uxTaskGetStackHighWaterMark to determine the stack space used by a task.
I just want to come back here to say that I have to postpone this work due to other urgent things. But I will get back to it later and post some results.
Thanks for your help so far and I hope you are still there when I return.
I came back to this issue today and made some tests:
- using configCHECK_FOR_STACK_OVERFLOW I could see that it is really a stack overflow
- when I take xQueueSendToBack out of the timer callback routine I don’t get a stack overflow
- in the generated map file I see that after uxTimerTaskStack there first is pxCurrentTCB followed by a number of other I think critical FreeRTOS variables. So it is quite clear that even a small stack overflow will make the system crash.
- I am using STM32CubeIDE (based on Eclipse CDT) and that has a static stack analyser. This shows me that xQueueSendToBack needs 128 Bytes of stack and also I found that many other FreeRTOS functions use a lot of stack. I think I would have to dig deeper into FreeRTOS to understand why. (by the way: is there a documentation explaining FreeRTOS under the hood?)
- I also used usStackHighWaterMark and it confirmed a high stack usage when using xQueueSendToBack.
What I take from this is to watch out for stack usage when using FreeRTOS and frequently check if there is still some reserve.
Thanks for your help and hints.
One thing to point out is that 128 bytes of stack for a 32-bit processor isn’t really that much. It is only 32 words of stack, and by the time you include the need to save registers for a call, it isn’t hard to use up that much stack.
Note, this is one reason almost every 32-bit processor comes with 64k or more of RAM. (while 8 and 16-bit processors can sometimes come with a lot less).