-1

I have created several tasks, the body of which is the same function. Inside the function, there is a delay that is the same for every task. So, when this delay is large enough, the stack of each task is filled with 6 words less than when the delay is less. As far as I understand, the stack of tasks increases when there is a situation with several tasks with the status Ready.

1.In this situation, some additional 6-word context is written to the task stack?

2.In my example, it turns out 6 words (24 bytes), can this value change somehow?

3.What else can affect the increase in the stack, such as jumping into an interrupt handler?

int main(void){
  xTaskCreate(Task_PrintCountString, "Task_1", mySTACK_SIZE, &param1, 1, NUUL);
  xTaskCreate(Task_PrintCountString, "Task_2", mySTACK_SIZE, &param2, 1, NUUL);
  xTaskCreate(Task_PrintCountString, "Task_3", mySTACK_SIZE, &param3, 1, NUUL);
  vTaskStartScheduler();
}
#define myDELAY     100
void Task_PrintCountString(void *pParams){
  uint16_t c=0; 
  for(;;){
    if(xSemaphoreTake(WriteCountMutex, portMAX_DELAY) == pdTRUE){
        PrintCountString(*(uint8_t *)pParams, c++);
        xSemaphoreGive(WriteCountMutex);
    }       
  vTaskDelay(myDELAY/portTICK_PERIOD_MS);//When myDELAY=1, the task stack is 6 words more than when myDELAY= 100!       
  } 
}

enter image description here

Address 0x20000158 is the top of the stack for one of the tasks. Similarly, others!

The only function PrintCountString always the same depth. But at the same time, the stack can grow to its maximum knowledge in several stages, going through dozens of iterations of the task cycle! It turns out that not only the context is saved to the stack, but something else?

P.S. I use ARM CM0 port and heap_1.

I made an observation: In the PrintCountString function there was a block for waiting for the SPI flag - while(). I replaced the DMA transfer method and removed the while() loop. The stack of both tasks began to fill up immediately to the maximum value, regardless of delays.

MaxNTF
  • 33
  • 1
  • 7
  • How do you measure task stack usage? What is the value of portTICK_PERIOD_MS? – Tagli Jan 23 '21 at 13:49
  • I look at the top of the stack and count the remaining number of bytes to be `0xA5`. portTICK_PERIOD_MS = 1 – MaxNTF Jan 23 '21 at 14:07
  • What if you use same delay in all tasks? Might be different nestedness of semaphore functions as mentioned in answers. – Osman-pasha Feb 15 '22 at 06:33

2 Answers2

1

FreeRTOS is just C code so, with one exception, the stack usage is determined by the compiler, including the selected optimisation level. The one exception be that when a task is not running its context is saved to its stack. The context size is fixed in all cases that matter. The size of the context depends on which FreeRTOS port you are using (there are more than 40).

There are a few things in your post I'm not sure about.

when this delay is large enough, the stack of each task is filled with 6 words less than when the delay is less

As above, the C code doesn't change depending on how long a delay you have - the compiler knows nothing about FreeRTOS or the concept of a delay. You may see different stack depths depending on where the code was in the call tree when an interrupt occurs (including interrupts that cause context switches).

I look at the top of the stack and count the remaining number of bytes to be 0xA5

You don't say which port you are using, so I don't know if the stack grows up or down. In any case the stack is filled with 0xa5 when the task is created but never touched by the kernel again so the number of 0xa5s left shows the maximum amount of stack the task has used since it was created. That value is returned by calling uxTaskGetStackHighWaterMark().

Richard
  • 3,081
  • 11
  • 9
  • I am using port `heap_1`. Compiler optimization disabled `level 0`. `uxTaskGetStackHighWaterMark` shows me the value I see myself. I am looking at the top of the task stack at the address given in `pxCurrentTCB->pxStack`. My goal is to develop for myself a mechanism for calculating the required size of the task stack. So I'm wondering why in my situation I can get a different maximum stack size? – MaxNTF Jan 25 '21 at 07:50
  • It turns out that the size of the context that is pushed onto the stack can be different? – MaxNTF Jan 25 '21 at 07:54
1

In general the stack memory is used by the microcontroller itself. There are several instructions which writes/reads some data to/from the stack.

  • The PUSH instruction copies the content of one register to the stack and modifies the stack pointer register
  • The POP instruction reads a word from the stack into a register and modifies the stack pointer register

Both instructions are managed by the compiler. If a functions is executed the compiler creates some PUSH instructions to "free" some registers to work with. At the end of the function the original register state will be restored using the POP instructions to guarantee the upcoming control flow.

  • and each branch instruction stores several register values to the stack

The amount of used stack memory depends on the call hierarchy.

According to your example, if the SemaphoreTake function is called and finds a released semaphore the call hierarchy is different from a situation where the semaphore is blocked. This creates a different stack usage.

Sven K.
  • 11
  • 1
  • Does not look like it. 1.Function `PrintCountString` is not nested in function `xSemaphoreTake`. 2.In the case of a delay of 100, there is always a lot of space in the stack. And if the delay is 10, then the stack is reduced to the minimum value, but not immediately, but after dozens of iterations for each task. In this case, the reduction of the stack occurs in parts (according to my observation, 4 bytes). I only understand that when the delay is greater, then only one task has time to acquire the `Ready` state during the semaphore check. – MaxNTF Jan 27 '21 at 09:22