Weird issue with local variables located *outside* a FreeRTOS task stack - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: May 18, 2022, 11:00:38 am
content: ST 32F417 (arm32). ST Cube IDE.This is the relevant part of a task called APP:Code:// Simple RTOS thread for testing stuffvoid vAPPTask(void *pvParameters){uint32_t count=0;uint32_t err=0;uint32_t page=0;uint8_t buf;// some code to stop buf[] getting optimised outfor (int i=0; i<512; i++){buf=0xfe;}In Cube IDE you can get a display of the FreeRTOS tasks (once stopped e.g. with a breakpoint) e.g.Notice the APP task stack start and end addresses.Now look at where buf[] is located in the variables listIt is entirely outside the task stack.The SP is 0x100057e8 i.e. at the address of buf.It looks like the stack allocation declared by FreeRTOS isafterall local (stack based) variables within that task have been allocated.I prefill the whole FreeRTOS stack area with 0xA5 so this memory dump shows (the uppermost part) the unused part of itIt also shows buf filled with 0xFE with my loop, as expected. In between the two is some stuff like initialised variables.At the very bottom of the memory dump is the unused stack of the next task. And so it goes, all the way up the memory (which is the CCM actually, 0x10000000 and for 64k).Is this how it is supposed to work?The APP task is created withxTaskCreate(vAPPTask, "APP", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);so it gets 512 words i.e. 2k bytes of stack.But all these numbers are a bit vague and don't really add up.The only foolproof way I have found of checking that the allocated stacks aren't overflowing is to look at the memory dump and see there is plenty of 0xA5s left for each task.FreeRTOS offers a stack check feature which I think check if there is enough each time it switches tasks (only).

author: abyrvalg
author_rank: Frequent Contributor
timestamp: May 18, 2022, 12:50:35 pm
content: Stack on Thumb grows downwards, so the initial SP of APP is 100051F8 + 2k = 100059F8, so your buf is well within the declared area and “Top of stack” is SP value after grows downwards.

author: peter-h
author_rank: Super Contributor
timestamp: May 18, 2022, 01:10:45 pm
content: All stacks I have ever seen grow downwards; it is the value being reported in the FreeRTOS debugging feature in Cube (Eclipse) which shows only a part of it.I think the distance between these two points in the memory dump is actually the stack value specified in the task invocationIt would be good to be able to see the stack remaining for each taskduring operation. The ST sample code includes an http server in which you can see the stack high water mark e.g.but those values aren't directly usable either. What is needed is a task which plots the whole RTOS memory (the whole 64k CCM in this case) onto a graphical LCD, a pixel for each byte which is 0xA5A good programming exercise

author: bson
author_rank: Unknown
timestamp: May 18, 2022, 05:30:46 pm
content: Perhaps FreeRTOS allocates stacks on the heap when tasks/threads are created.

author: jc101
author_rank: Frequent Contributor
timestamp: May 18, 2022, 05:47:08 pm
content: Quote from: bson on May 18, 2022, 05:30:46 pmYep, a task's stack is allocated from the FreeRTOS heap when the task is created...https://www.freertos.org/a00125.html

author: eutectique
author_rank: Frequent Contributor
timestamp: May 18, 2022, 06:01:53 pm
content: Which version of FreeRTOS are you using?What are the addresses of the other three variables?Place a breakpoint on the linevoid vAPPTask(void *pvParameters), start, when the breakpoint is hit, check the value of SP. Step into, check SP again.My version is 10.4.1, I've got one task which has a similar layout, with a buffer of 1KiB which is allocated on task's stack, not outside.Oh, and the use ofconfigMINIMAL_STACK_SIZEis questionable, I would say. Seehttps://www.freertos.org/a00110.html#configMINIMAL_STACK_SIZE

author: peter-h
author_rank: Super Contributor
timestamp: May 18, 2022, 06:06:01 pm
content: Yes, you give it a chunk of RAM and as you define tasks it drops them into that one after the other, starting near the bottom (the lowest address). You can call that RAM block a "heap", I guess. It is a kind of "private heap", not one which uses the existing malloc(). Also nothing ever gets freed from this "heap". This product I am doing does not use malloc() because it is too dangerous. Well, it does it use it for larger buffers etc for certain product options which, once enabled, never terminate.In this case I have given FreeRTOS the whole 64k CCM. It is adequate for the product but not with a massive margin, which is why I've been checking stack usage carefully.What surprised me is that within the "stack space" specified for each task, it allocates any variables first and then reports as "stack size" what is left after that. This is confusing as hell because the RTOS stack start and end values reported in the Cube FreeRTOS parameter display don't make a lot of sense.I still think the only sure way to check each task's stack usage is with a special pattern fill and then a memory dump after the system has been running for a while.I am sure configMINIMAL_STACK_SIZE has confused a lot of people, being in words not bytesFreeRTOS Kernel V10.0.1I ought to check the other three variables but a) right now they are getting optimised out and b) I am sure they will be just before or just after the 512 byte buffer.

author: eutectique
author_rank: Frequent Contributor
timestamp: May 18, 2022, 07:29:40 pm
content: My point about configMINIMAL_STACK_SIZE, is it enough to accommodate the buffer?And one more thing -- do you have by any chance a global variable of the same name? It could shadow the local variable.

author: peter-h
author_rank: Super Contributor
timestamp: May 18, 2022, 08:23:49 pm
content: Yes, it was. I have been going through all the task stacks and checking there is enough left on each one.Globals and locals should not conflict, surely, unless the linker is broken? Interesting point though. I tend to prefix deliberate globals with g_

author: TomS_
author_rank: Frequent Contributor
timestamp: May 19, 2022, 06:06:39 am
content: Your stack is 0x100051f8 (start) + 2048 (size, 512 * 4) = 0x100059f7 (end)Your buffer occupies 0x100057e8 through 0x100059e7.Looks like it is entirely within the task stack to me.Also worth reiterating, but FreeRTOS does not decide where variables go - that will be up to the compiler/linker. As jc101 said, FreeRTOS will allocate stack from its own managed heap (you define the size of this in FreeRTOSConfig.h), but this is only true if you have enabled dynamic allocation. If dynamic allocation is disabled then you must statically allocate an area of memory and provide that to the task during creation. This allows you to build fully static applications which must fit into available memory at compile time, meaning you cant hit a situation where you dont have enough available memory at run time.

author: peter-h
author_rank: Super Contributor
timestamp: May 19, 2022, 07:42:20 am
content: Interesting - thanks.We allocate the whole CCM to the RTOS - 64k. It was a decision between using a bit of the main RAM (128k) but the CCM is good for this, you can't run code from it anyway, and the only issue is that you cannot DMA in it which made fast SPI feeding a little more complicated (you have to use a static buffer).AIUI, stack based variables (basically everything declared within the RTOS task, which for the purpose of local variables is a function like any other) end up within the task's area - as my dumps above show, with that 512 byte buffer. And if you don't want it there, just put 'static' in front of it and it ends up in the main RAM. I've been doing this for the last year and a half that I've been working on this.The key point is that these stack variables end up on the RTOS stack, not on the general stack which is now used only for ISRs (and all stack usage before the RTOS starts). One has to watch that because there isn't a huge amount of it.That's AIUI, and it does mean the locations of stack variables are decided when the RTOS task starts, not by the linker, but that's true for all stack based variables. The linker fixes only statics.

author: newbrain
author_rank: Super Contributor
timestamp: May 19, 2022, 08:58:19 am
content: Quote from: peter-h on May 19, 2022, 07:42:20 amJust remember that if you have more than one instance of the same task, they'll all share the samestaticdeclared variables.FreeRTOS provides thread local storage pointers (five by default, IIRC) that can be used to give each instance its (static or malloced) memory area, or directly to hold correctly sized integers.

author: abyrvalg
author_rank: Frequent Contributor
timestamp: May 19, 2022, 09:12:58 am
content: Looks like you’re confusing “Top of stack” in that IDE screen for “end of stack”, but it looks more like “current SP” and the real end (calculated as start+2k) is not shown there at all (and that’s quite understandable, normally you care much more about stack overflows, watching the diff between start and current SP, underflows are rare). Is there some help page for that screen with explanation of each param?

author: TomS_
author_rank: Frequent Contributor
timestamp: May 19, 2022, 09:23:06 am
content: You can use a stack based variable as a DMA source/destination as long as you never return far enough to take it out of scope.In a task, which is not supposed to end, a stack based variable will never go out of scope because the task never ends, so you can use it for DMA becuase that memory will never be repurposed.If your task calls a function that has a stack based variable in it, then you must be careful because that variable goes out of scope when that function returns.Otherwise, yes, if you want to remove something from the stack you can make it static. I do the same in my own projects. Just be mindful of what newbrain mentioned if you are starting multiple copies of a task that uses static variables.Also be mindful of storing pointers to dynamically allocated memory in thread local storage within a task - you will have to free that memory yourself before killing off a task.

author: peter-h
author_rank: Super Contributor
timestamp: May 19, 2022, 09:31:50 am
content: QuoteSure - except in the CCM.