Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Real time embedded FreeRTOS mailing list 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem TCP & FAT Training




Loading

Main stack pointer reset when starting the scheduler

Posted by bschleusner on January 1, 2015

Hello,

I've been digging through the source of the ARM_CM3 port of FreeRTOS (V8.1.2 and older), and noticed that in the prvPortStartFirstTask function resets the MSR register to the top of the stack. Is there a reason why the MSR needs to be reset?

I bring this up because I have been running into issues with variables located on the stack in main() getting corrupted. Looking into the corruption issue I noticed that the prvPortStartFirstTask was reseting MSR, which leads to privlidged code corrupting the main stack.

Excerpt from FreeRTOS/Source/portable/GCC/ARM_CM3/port.c

~~~~ static void prvPortStartFirstTask( void ) { __asm volatile( " ldr r0, =0xE000ED08 n" /* Use the NVIC offset register to locate the stack. / " ldr r0, [r0] n" " ldr r0, [r0] n" " msr msp, r0 n" / Set the msp back to the start of the stack. / " cpsie i n" / Globally enable interrupts. / " cpsie f n" " dsb n" " isb n" " svc 0 n" / System call to start first task. */ " nop n" ); } ~~~~

I've disabled the part that resets the MSR and it appears to function just fine. It seams like a bug to be overwriting the stack.

Thanks


Main stack pointer reset when starting the scheduler

Posted by richard_damon on January 1, 2015

Some of the ports will reuse the main tasks stack to be used as the ISR stack. Since after starting up the scheduler, main will never be re-entered it was considered safe to reuse this space.

There really is no reason to be trying to store stuff in the stack of main that needs to be used later, you can just place those in global (or file static) space.

What are you keeping an the stack of main that you need?


Main stack pointer reset when starting the scheduler

Posted by bschleusner on January 1, 2015

Some of the ports will reuse the main tasks stack to be used as the ISR stack.

Correct. Priviledged code (ISR's) will use the main stack.

There really is no reason to be trying to store stuff in the stack of main that needs to be used later, you can just place those in global (or file static) space.

Storing things on the stack is useful for composition and ordered construction of classes / structures. Saying there is no reason to store things on the stack may be true for some designs, but that is a dangerous assumption to make.

I think that the MSP reset is premature optimization. If someone doesn't want to store anything on the stack in main, then the main stack will only be containing a few stack frames at most, and reseting it will have minimal gain. If someone has a design pattern that makes good use of the stack for construction of threads, then this optimization will lead to many bugs.


Main stack pointer reset when starting the scheduler

Posted by rtel on January 2, 2015

It has always been done this way, and is especially important on devices which only have a few K of RAM. I think the original Cortex-M ports were created on devices with 2K or RAM, in which case it was extremely important to re-use all the RAM that was not intended to be used after the scheduler has started.

Once the scheduler has started main() no longer exists. There is no way back to it and no way of [directly] referencing memory on its stack [which is a compiler controlled construct], so I would turn your argument around and say it is dangerous to try and use resources that were allocated to it, and wasteful to leave resources allocated to something which is in effect orphaned. I don't see this as being any different to attempting to access something that was on the stack of a task after the task has been deleted.

After the scheduler has started only the tasks and interrupts exist, and they are allocated stacks by the scheduler. If you want memory to be globally or file scope accessible please allocate it globally or file scope.

Regards.


Main stack pointer reset when starting the scheduler

Posted by znatok on January 2, 2015

In consideration of memory usage, variables in main() stack "accessed" from a tasks (whis is actually wrong) have no difference compared to the same variables being defined and allocated as global. FreeRTOS does it correctly being freeing main stack before starting scheduler.

I wanted to ask about another optimization mechanism. What about stack usage in IRQ handlers? As far as I understand in current implementation IRQ handlers use stak of the task being interrupted. It means that each task has to reserve piece of it's stack for IRQ handler. What if IRQ handler will switch to main or more correctly kernel stack at the beginning !? In this case IRQ stack can be reserved only once and thus each task stack could be reduced. Something like:

void IRQHandler( void ) { IRQENTER() ... ... IRQ_EXIT() }

I'm aiming Cortex architecture (not sure how it work in other cases).


Main stack pointer reset when starting the scheduler

Posted by rtel on January 2, 2015

As far as I understand in current implementation IRQ handlers use stak of the task being interrupted.

That is not correct. IRQ handlers use the stack that was being used by main() before the scheduler was started. There is no need to make any allowance for the stack used by an interrupt when allocating a stack to a task.

Regards.


Main stack pointer reset when starting the scheduler

Posted by znatok on January 2, 2015

Is it because of the MSP/PSP usage?

Attachments

alternate (1484 bytes)

Main stack pointer reset when starting the scheduler

Posted by rtel on January 2, 2015

If I understand your question, then "yes".


Main stack pointer reset when starting the scheduler

Posted by richard_damon on January 2, 2015

Ella, The key thing is that it depends on the processor and the port. The way ARM processors work, they naturally switch stacks on an interrupt, so there is a dedicated interrupt stack (which FreeRTOS shares space with the main task to save memory). Other processors more naturally use the tasks stack for the interrupt, but in some cases the port layer may automatically switch the stack to a dedicated one to save ram (or it might not). The Pic24 port, for example, doesn't switch the stack, so every task needs to have the needed extra room to handle interrupt stacks.


Main stack pointer reset when starting the scheduler

Posted by rtel on January 2, 2015

A lot of the decisions on these things depends on whether the port to the MCU supports interrupt nesting. So the PIC32, which has a full interrupt nesting model, does switch stacks in interrupts - and in that case it is done is software rather than by the hardware.

There is always a trade off between the run time overhead of checking for nesting and switching stacks, and just getting in and out of the interrupt as quickly as possible. Generally if there is no nesting then the stack usage is bounded and determinable, so the port is less likely to switch stacks.

Regards.


Main stack pointer reset when starting the scheduler

Posted by znatok on January 2, 2015

Discussion started with CortexM3, I've noticed in my message that I'm talking about Cortex ARM. And yes I understand that specific answer depends on architecture.

On Fri, Jan 2, 2015 at 3:18 PM, Real Time Engineers ltd. rtel@users.sf.net wrote:

A lot of the decisions on these things depends on whether the port to the MCU supports interrupt nesting. So the PIC32, which has a full interrupt nesting model, does switch stacks in interrupts - and in that case it is done is software rather than by the hardware.

There is always a trade off between the run time overhead of checking for nesting and switching stacks, and just getting in and out of the interrupt as quickly as possible. Generally if there is no nesting then the stack usage is bounded and determinable, so the port is less likely to switch stacks.

Regards.

Main stack pointer reset when starting the scheduler

Sent from sourceforge.net because you indicated interest in https://sourceforge.net/p/freertos/discussion/382005/

To unsubscribe from further messages, please visit https://sourceforge.net/auth/subscriptions/


Main stack pointer reset when starting the scheduler

Posted by bschleusner on January 2, 2015

It has always been done this way, and is especially important on devices which only have a few K of RAM. I think the original Cortex-M ports were created on devices with 2K or RAM, in which case it was extremely important to re-use all the RAM that was not intended to be used after the scheduler has started.

Memory allocated by the linker for the main stack is fixed. If no variables were stored on the main stack (as you intend them to be), then reseting the main stack will still have minimal gains.

There is no way back to it and no way of [directly] referencing memory on its stack [which is a compiler controlled construct], so I would turn your argument around and say it is dangerous to try and use resources that were allocated to it, and wasteful to leave resources allocated to something which is in effect orphaned.

Constructing memory on the main stack is not that uncommon of a thing, nor is it wasteful. If it has to exist in memory, then it will take up the same amount regardless of if it is on the stack or in BSS. Allocating on the stack allows for ordered and safe construction or objects.

(Simplified example)

~~~~ int main() { // Construct a task, adds itself to the scheduler, knows of the GPIO driver GPIO led(PORTA, PIN10, OUTPUTOPENDRAIN); LedTask ledTask(led); // Construct a driver on the stack, knows of the GPIO driver GPIO a2dChipSelect(PORTC, PIN3, OUTPUT); LMP901xxxDriver a2dDriver(a2dChipSelect, SPIPORT1); // Construct a task, adds itself to the scheduler, knows of a2dDriver TemperatureConversionTask(a2dDriver); vTaskStartScheduler(); } ~~~~

If a variable is on the stack, there is nothing in C or C++ stopping it from being passed to a task and used by that task. (pass by pointer or by reference)

If the programmer choses to store things on the stack, then it is up to the programmer to make sure that there is sufficient room reserved by the linker for it. I think making that decision for the programmer takes away many useful and safe design patterns.

My vote is that a setting should at least be exposed to allow main stack to be preserved.


Main stack pointer reset when starting the scheduler

Posted by davedoors on January 2, 2015

Sorry, I'm with (both) Richard(s) on this one. Stack variables are accessed via compiler generated code. You are right C++ cannot prevent you from attempting to access anything by reference, but it doesnt mean it is good practice to do so. C++ doesnt prevent me from attempting to dereference a null pointer, but I know the consequences of trying to do so. If I call a function, allocate something on the stack inside that function, return back to the calling function, then attempt to access whatever was allocated on the stack when inside the function I know I'm in for trouble because I am accessing something which no longer has a context, but C++ will of course let me try.


Main stack pointer reset when starting the scheduler

Posted by bschleusner on January 2, 2015

You are right C++ cannot prevent you from attempting to access anything by reference, but it doesnt mean it is good practice to do so.

It is infact very good practice to access by reference in modern languages. Particularily when it comes to things like RAII.

C++ doesnt prevent me from attempting to dereference a null pointer, but I know the consequences of trying to do so.

Why is that even relevent? Unless of course you mean dereference a void pointer, in which case we can debate type safety in another forum.

If I call a function, allocate something on the stack inside that function, return back to the calling function, then attempt to access whatever was allocated on the stack when inside the function I know I'm in for trouble because I am accessing something which no longer has a context, but C++ will of course let me try.

Correct. Doing that is always gauenteed to be unsafe. However, the code that I am complaining about breaks the gaurentee that stack variables will exist for the duration of a function. For example:

~~~~ void bar(SomeClass& s) { s.something(2); } int foo(void) { SomeClass a; bar(a); // do something else with a } ~~~~

When foo() calls bar(), it is gaurenteed by the language that SomeClass a will exist until foo() returns. vTaskStartScheduler() breaks the gauentee that variables created in main will exist for the duration of main, regardless of the possibility to return to main.


Main stack pointer reset when starting the scheduler

Posted by rtel on January 2, 2015

I think the discussion is getting a little academic. The port works the way it does so please work within its constraints. I'm not actually aware of anybody raising this topic before, and the port has been around for a very long time, so I'm afraid it is not going to change.

However, the code that I am complaining about breaks the gaurentee that stack variables will exist for the duration of a function. For example:

....and perhaps that is the route cause of the disagreement. As far as we are concerned main() no longer exists when the scheduler has started.

vTaskStartScheduler() breaks the gauentee that variables created in main will exist for the duration of main

Exactly. In a single threaded application main() is always at the bottom of the stack frame. In a multi-threaded application it is not, it is in a completely different context before the scheduler is started, and does not have a context after the scheduler has started. Point of view only - no need to reply.

Regards.


Main stack pointer reset when starting the scheduler

Posted by bschleusner on January 2, 2015

the port has been around for a very long time, so I'm afraid it is not going to change.

I appreciate API's that don't change behavior, but modern microcontrollers and coding practices may merit at least the introduction of a setting to configure the destruction of MSP or not.

....and perhaps that is the route cause of the disagreement. As far as we are concerned main() no longer exists when the scheduler has started.

Correct. If this is the case, then the documentation needs to reflect this. There is no mention of stack destruction in any of the header files. (task.h, portable.h)


Main stack pointer reset when starting the scheduler

Posted by richard_damon on January 3, 2015

Brad, I've used similar constructs as global (or file scoped) variables and never had a problem with initialization order (the order of initialization of globals in a given file is just as strictly controlled as auto variables in a function (like main), and have the added advantage for debugging that they have a known fixed address so the debugger can inspect their values.

I would need to go back through the documentation, but I do believe that the ARM ports I used did mention that the main stack was reused for the interrupt stack. The documentation would be in a "port specific" place, as this IS port specific (as I mentioned, the Pic24 port doesn't do this, for example).

I see it very unlikely that this behavior will change, as it will only cause existing working applications to waste space, while maintaining the current method just requires you to follow the rules you have been informed of. It is hard to imagine a variable that can't be moved to file scope, and the few oddball cases can be created when needed an put on the heap.


Main stack pointer reset when starting the scheduler

Posted by theszczur on June 30, 2015

"C/C++ guarantee that stack variables will exist for the duration of a function" - and you are braking that standard.

it would be the best to add configuration option like "configREUSEMAINSTACK"

And it should be very well documented, right now I can't find any information on the official API documentation: http://www.freertos.org/a00132.html And there is no information in the code...


[ Back to the top ]    [ About FreeRTOS ]    [ Sitemap ]    [ ]




Copyright (C) 2004-2010 Richard Barry. Copyright (C) 2010-2016 Real Time Engineers Ltd.
Any and all data, files, source code, html content and documentation included in the FreeRTOSTM distribution or available on this site are the exclusive property of Real Time Engineers Ltd.. See the files license.txt (included in the distribution) and this copyright notice for more information. FreeRTOSTM and FreeRTOS.orgTM are trade marks of Real Time Engineers Ltd.

Latest News:

FreeRTOS V9.0.0 is now available for download.


Free TCP/IP and file system demos for the RTOS


Sponsored Links

⇓ Now With No Code Size Limit! ⇓
⇑ Free Download Without Registering ⇑


FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

Renesas Electronics Gold Alliance RTOS Partner.jpg

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

Atmel RTOS partner supporting ARM Cortex-M3 and AVR32 microcontrollers

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Xilinx Microblaze and Zynq partner

Silicon Labs low power RTOS partner

Altera RTOS partner for Nios II and Cortex-A9 SoC

Freescale Alliance RTOS Member supporting ARM and ColdFire microcontrollers

Infineon ARM Cortex-M microcontrollers

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

Cypress RTOS partner supporting ARM Cortex-M3

Fujitsu RTOS partner supporting ARM Cortex-M3 and FM3

Microsemi (previously Actel) RTOS partner supporting ARM Cortex-M3

Atollic Partner

IAR Partner

Keil ARM Partner

Embedded Artists