The resulting size of the stack in bytes is sizeof( StackType_t) * StackDepth
So with StackType_t being e.g. uint16_t and StackDepth = 100 the size of the stack is 200 bytes. In some cases it might be slightly different because the stack address is aligned corresponding to the given StackType_t if not already aligned accordingly.
Words (Typically 2 or 4 byte units). I personally would be susfipicious of a stack size specified that way. configMINIMAL_STACK_SIZE is basically a constant that defines the basic level of stack for a don’t do much task. It handles initial configuration and library overhead. To that you should ADD an amount of additional stack you need by approximately counting up your usage. Multiplying makes no sense.
Currently, mind tied in a knot, hence looking at all definitions everywhere.
Trying to run mbedtls, CPU appeared to be frozen, where it should not have happened. Just before return tcp_connect() in lwip.
In a ST ssl client example, they increased the stack size with * 15 which made it work in that example context.
But adding some more code into that example, that froze as well.
Which made me to assume that the stack got exhausted.
The right way would be to do a callgraph, see the max size of the stack, add some overhead to it ?
The only place the kernel actually uses configMINIMAL_STACK_SIZE is to dimension the size of the stack used by the idle task - and normally on the assumption the idle task is not doing anything much so doesn’t require much stack. The FreeRTOS standard demo and test tasks use multiples of configMINIMAL_STACK_SIZE so the same code is portable across many different architectures.
I recommend having stack overflow detection turned on while developing - then you are not left guessing as to what the issue is. I would also recommend using configASSERT(), especially on Cortex-M devices. You can read about how to do both of those things here https://www.freertos.org/FAQHelp.html
#define configCHECK_FOR_STACK_OVERFLOW 1
#define configUSE_RECURSIVE_MUTEXES 1
#define configUSE_MALLOC_FAILED_HOOK 1
void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName)
{
(void) pcTaskName;
(void) pxTask;
/**
* Run time stack overflow checking is performed
* if configCHECK_FOR_STACK_OVERFLOW is defined
* to 1 or 2. This hook function is called if
* a stack overflow is detected.
*/
taskDISABLE_INTERRUPTS();
for (;;);
}
void vApplicationMallocFailedHook(void)
{
/**
* vApplicationMallocFailedHook()
* will only be called if configUSE_MALLOC_FAILED_HOOK
* is set to 1 in FreeRTOSConfig.h.
* It is a hook function that will get called if a call
* to pvPortMalloc() fails.
*
* pvPortMalloc() is called internally by the kernel
* whenever a task, queue, timer or semaphore is created.
* It is also called by various parts of the demo
* application.
*
* If heap_1.c or heap_2.c are used, then the heap size
* available to pvPortMalloc() is defined by
* configTOTAL_HEAP_SIZE in FreeRTOSConfig.h
* and the xPortGetFreeHeapSize() API function can be
* used to query the remaining free heap size.
* (although it does not provide information on
* how the remaining heap might be fragmented).
*/
taskDISABLE_INTERRUPTS();
for (;;);
}
Now that you’ve mentioned it, noticed the hook functions
Maybe there is an overflow and it is in the indefinite loop…
Once the stack overflow hook is triggered, what would be the best possible thing to do, while developing ?
The second parameter to the overflow hook vApplicationStackOverflowHook provides the task name. The next step would be to increase the stack size for the task and once everything is working you can use uxTaskGetStackHighWaterMark to fine tune the stack size.
It depends on which heap you are using. If you are using heap_4, you can use xPortGetMinimumEverFreeHeapSize or vPortGetHeapStats for more detailed stats: https://www.freertos.org/a00111.html
What is your definition of the hook? If you disable interrupts and loop forever (as shown in many of our examples), you may not get printf output depending on how printf is implemented. You can utilized debugger to examine the value of pcTaskName.
So, the best way to handle the hook would be to implement a printf in that indefinite loop, that way, I dont need to fire up the debugger, I get simply have a look at the serial console. That would be really nice.
The thing I was trying to point out is that if you disable interrupts in vApplicationStackOverflowHook and any of the USART functions in fputc require interrupts, fputc may not be able to transmit data on USART.
Yeah, I agree. The printf() is based on a dead loop, no DMA, no interrupts, with the minimal config, so that it can work when something is really wrong. It uses the STLink USB cable itself, so no additional cable is also required. The same USB cable itself can be used.
#define configMINIMAL_STACK_SIZE ((uint16_t) 32)
#define configTOTAL_HEAP_SIZE ((size_t) (31 * 1024)) // was 20
#define configCHECK_FOR_STACK_OVERFLOW 1
#define configUSE_RECURSIVE_MUTEXES 1
#define configUSE_MALLOC_FAILED_HOOK 1
void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName)
{
(void) pcTaskName;
(void) pxTask;
/**
* Run time stack overflow checking is performed
* if configCHECK_FOR_STACK_OVERFLOW is defined
* to 1 or 2. This hook function is called if
* a stack overflow is detected.
*/
taskDISABLE_INTERRUPTS();
while (*pcTaskName != '\0') {
while (!LL_USART_IsActiveFlag_TXE(USART3)) {};
LL_USART_TransmitData8(USART3, *pcTaskName);
pcTaskName += 1;
}
while (1);
}
void vApplicationMallocFailedHook(void)
{
/**
* vApplicationMallocFailedHook()
* will only be called if configUSE_MALLOC_FAILED_HOOK
* is set to 1 in FreeRTOSConfig.h.
* It is a hook function that will get called if a call
* to pvPortMalloc() fails.
*
* pvPortMalloc() is called internally by the kernel
* whenever a task, queue, timer or semaphore is created.
* It is also called by various parts of the demo
* application.
*
* If heap_1.c or heap_2.c are used, then the heap size
* available to pvPortMalloc() is defined by
* configTOTAL_HEAP_SIZE in FreeRTOSConfig.h
* and the xPortGetFreeHeapSize() API function can be
* used to query the remaining free heap size.
* (although it does not provide information on
* how the remaining heap might be fragmented).
*/
taskDISABLE_INTERRUPTS();
for (;;);
}
int ferror(FILE *f)
{
return 0;
}
void my_CrashFunction(void)
{
uint32_t i = 0;
const TickType_t xDelay = 50 / portTICK_PERIOD_MS;
char *StackCrashTest = "WIll this crash the Stack by any means ? If so \
the name should be visible in the hook ? \
Did that happen ? If not, Why did that not happen ? \
Now, that is indeed one little excercise!\0";
while (i < 100) {
LL_GPIO_TogglePin(GPIOB, LL_GPIO_PIN_14);
vTaskDelay(xDelay);
i += 1;
}
printf(" %s:\n", StackCrashTest);
return;
}
void TEST_Thread(void *arg)
{
my_CrashFunction();
printf(" (%d) %s: Hello\r\n", __LINE__, __FUNCTION__);
while (1);
}
static TaskHandle_t h_test_task;
int main(void)
{
HAL_Init(); /* TIM6 generates 1mS IRQ */
SystemClock_Config(); /* system clock = 400 MHz */
gpio_init();
BSP_Config();
config_usart();
rng_init();
printf(" -----------------------------\n");
printf(" -* STM32H7x mbedTLS Test *- \n");
printf(" -----------------------------\n");
xTaskCreate(TEST_Thread, /* Task function */
"TEST", /* Task name */
configMINIMAL_STACK_SIZE, /* Stack size */
NULL, /* optional arg */
tskIDLE_PRIORITY, /* priority */
&h_test_task); /* Task Handle */
vTaskStartScheduler(); /* Start Scheduler */
while (1);
}
Which results in:
-----------------------------
-* STM32H7x mbedTLS Test *-
-----------------------------
WIll this crTEST
I was in a bit too much expectation that it was going to provide me the function name, basically due to my current debug situation. But it provided the task name. Which is also good enough, though I have a single task at the moment.