How to use usStackDepth and uxTaskGetStackHighWaterMark

jeanalban wrote on Monday, March 02, 2015:

Hello everybody,

I have a beginner question about the memory size of tasks.

First, when I create a task with xTaskCreate, I don’t understand how to size the task stack. usStackDepth must be defined as the number of variables in the task. It’s not easy to determine the number of variables in my tasks because I have created a large number of variables. Moreover, the stack storage is calculated with usStackDepth (number of variables) * stack wide (bits). How can I set the stack wide? Is it always 4 because I use a 32-bit microcontroler?

My second question is about uxTaskGetStackHighWaterMark(NULL) which must return the remaining free space memory in the stack. When I use this instruction in the task, it returns me always a number very close to the usStackDepth defined for the task. For example, if my task is created with usStackDepth = 2048, the return value of uxTaskGetStackHighWaterMark is around 1928, thus 120*4 = 480 bytes of used memory space. I’m right?

Thanks for your help!

davedoors wrote on Monday, March 02, 2015:

Tasks run C code, the C code is compiled by a compiler that will use the stack each time a function is called, or a variable is allocated on the stack. Calling nested functions in a task will use exactly the same amount of stack as calling the same nested functions from main(). The compiler does not know the difference between the two. Its all just compiled C code. The stack required is related to the number of variables, but is not equal to the number of variables. It is a lot more complex than that and is very dependent on the compiler optimization setting.

On a 32 bit chip the stack is normally 32 bits wide. That is a characteristic of the hardware, not something that can be changed in FreeRTOS, which is just software running on the hardware.

uxTaskGetStackHighWaterMark() does not return the remaining free space, but the smallest amount of remaining free space there has ever been since the task ran for the first time. If the number is large then you are not coming close to overflowing the stack and can probably reduce the stack given to the task. If the number is small then you are in danger of overflowing the stack at some point in your code.

jeanalban wrote on Tuesday, March 03, 2015:

Thank you Dave, It’s clearer now. In fact, I don’t understand the memory space usStackDepth allocated for a task.

For example, I created two tasks: vCore_Loop and vScreen_Loop. Each of these tasks call their own function, and each of these functions have their own variables. Does the variables memory space of the functions called by vCore_Loop must be included in usStackDepth of vCore_Loop ? Apparently not, uxTaskGetStackHighWaterMark of vCore_Loop returns me a high value close to the usStackDepth value, meaning that vCore_Loop use little memory (while I know that the functions called by vCore_Loop use a lot of memory space).

Another point of misunderstanding: what happens if a function is called by both of the two tasks? Is it a problem?

Thanks a lot!

rtel wrote on Tuesday, March 03, 2015:

If there is a function that looks like this:

void vFunction( int x )
int y;

 /* My code here. */


Then x and y will be allocated by the compiler either on the stack, or
in a register. What actually happens depends on your compiler, and on
the options passed to the compiler when you build your application.

However, if there is a function

void vAnotherFunction( void )
static int z; /* This time the variable is static. */

 /* My code here. */


Then there will be only one copy of z, because it is static, and z will
not be on the stack.

So far that has nothing to do with FreeRTOS. It is a C question, and a
compiler question.

The next part is a FreeRTOS question - is it ok for two tasks to call
the same function?

In the vFunction() case, yes, each FreeRTOS task has its own stack and
maintains its own copy of registers. So in the first example above x
and y will be on the stack unique to the task, or in the copy of the
registers unique to the the task - and neither task can corrupt the
other task’s copy.

In the vAnotherFunction() case, no, each task will access the same copy
of z because z is not on the stack.


jeanalban wrote on Tuesday, March 03, 2015:

Thanks! I think I understand better know, I had not realized the difference between static and non-static variables.

In my case, vCore_Loop contains a lot of static variables (that I did not see with uxTaskGetStackHighWaterMark because static variables are not allocated in the stack task) and a few non-static variables (that I can see with uxTaskGetStackHighWaterMark).

Am I right?
Thanks a lot!