Stack Size

In one of the examples that I have at my end:

configMINIMAL_STACK_SIZE * 15;
while creating a Thread/task.

where in FreeRTOSConfig.h

#define configMINIMAL_STACK_SIZE ((uint16_t) 128)

So stack size=15 * 128 = 1920

That’s bytes, or kbytes ?

Thanks,
Manu

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.

Thanks, Hartmut.

I missed the datatype, though. The eyes probably saw it, the mind did not. Mind was lost in knots with probably a stack related issue.

1 Like

Hi Richard,

What would be the right way to do it ?

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 ?

Thanks,
Manu

If you have a calligraphy function to see, use it.

I normally define my stack requirements as configMINIMUM_STACK_SIZE+xxxx

Where xxxx is my estimate of what the task needs to do its own stuff. Check carefully for buffers declared inside tasks on their stack.

Also, turn on Stack Checking so you have a chance to get warned of stack overruns. It doesn’t catch them all, but can help.

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

In addition, you can use uxTaskGetStackHighWaterMark to fine tune your stack size: https://www.freertos.org/uxTaskGetStackHighWaterMark.html

Thanks.

Hi Richard Barry,
(not to get mixed up. :slight_smile: )

I am already using these:

#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 ?

Thanks,

Manu

Hi Gaurav,

Have been looking for some functionality, what you stated just now.
I will try to utilize it.
Is there something similar for the heap as well ?

Thanks,

Manu

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.

Thanks.

So, I can just do a printf() in there with: pcTaskName, just before the endless for(), that what you meant ?

Thanks,
Manu

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

Thanks.

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.

Thanks.

Yes, I am indeed using heap_4.c

#if defined(__GNUC__)
 #define configTOTAL_HEAP_SIZE			((size_t) (25 * 1024))
#else
 #define configTOTAL_HEAP_SIZE			((size_t) (31 * 1024)) // was 20
#endif

printf, I have done a retarget to the USART,

struct __FILE {
	int handle;
};

FILE __stdout;
FILE __stdin;

int ferror(FILE *f)
{
	return 0;
}

int fputc(int ch, FILE *f)
{
	while (!LL_USART_IsActiveFlag_TXE(USART3)) {};		/* Wait for Tx buffer empty flag */
		if (ch == '\n')
			LL_USART_ClearFlag_TC(USART3);
	LL_USART_TransmitData8(USART3, ch);
	return ch;
}

By hook, I meant.
vApplicationStackOverflowHook()

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.

Thanks,
Manu

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.

Thanks.

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.

void config_usart(void)
{
	LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOD);			/* GPIO Clk */

	LL_GPIO_SetPinMode(GPIOD, STLINK_TX_Pin, LL_GPIO_MODE_ALTERNATE);	/* PD.8 -> TX */
	LL_GPIO_SetAFPin_8_15(GPIOD, STLINK_TX_Pin, LL_GPIO_AF_7);
	LL_GPIO_SetPinSpeed(GPIOD, STLINK_TX_Pin, LL_GPIO_SPEED_FREQ_HIGH);
	LL_GPIO_SetPinOutputType(GPIOD, STLINK_TX_Pin, LL_GPIO_OUTPUT_PUSHPULL);
	LL_GPIO_SetPinPull(GPIOD, STLINK_TX_Pin, LL_GPIO_PULL_UP);

	LL_GPIO_SetPinMode(GPIOD, STLINK_RX_Pin, LL_GPIO_MODE_ALTERNATE);	/* PD.9 -> RX */
	LL_GPIO_SetAFPin_8_15(GPIOD, STLINK_RX_Pin, LL_GPIO_AF_7);
	LL_GPIO_SetPinSpeed(GPIOD, STLINK_RX_Pin, LL_GPIO_SPEED_FREQ_HIGH);
	LL_GPIO_SetPinOutputType(GPIOD, STLINK_RX_Pin, LL_GPIO_OUTPUT_PUSHPULL);
	LL_GPIO_SetPinPull(GPIOD, STLINK_RX_Pin, LL_GPIO_PULL_UP);

	LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART3);			/* USART3 Clk */
	LL_RCC_SetUSARTClockSource(LL_RCC_USART234578_CLKSOURCE_PCLK1);		/* Clk source */

	LL_USART_SetTransferDirection(USART3, LL_USART_DIRECTION_TX_RX);
	LL_USART_ConfigCharacter(USART3,
				 LL_USART_DATAWIDTH_8B,
				 LL_USART_PARITY_NONE,
				 LL_USART_STOPBITS_1);

	LL_USART_SetBaudRate(USART3,
			     SystemCoreClock/APB_Div,
			     LL_USART_PRESCALER_DIV1,
			     LL_USART_OVERSAMPLING_16,
			     115200);

	LL_USART_Enable(USART3);

	while ((!(LL_USART_IsActiveFlag_TEACK(USART3))) ||
	       (!(LL_USART_IsActiveFlag_REACK(USART3)))) { }
}

I tried the following:

    #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.

Thanks,
Manu