At the beginning of main I have two variables, namely:
unsigned char audioDataBuffer1[512]; unsigned char audioDataBuffer2[512];
Later in the code I use them as buffers for audio data from SD Card.
The problem is that the stack pointer is set too low and these big variables overwrite my stack pointer which is set automatically by Keil startup code. How is this possible?
I have noticed that the problem disappears when I set above variables to global, or when I use malloc instead.
I would appreciate if you could explain what goes wrong here and is there any reason I should not declare large variables like this?
Thank you.
About the end of main, my friend Chiu has a real story, he personally experienced.
When Chiu was a new guy in a CMMI level 3 qualified company, he got an assignment to study and maintain (temporarily) an already released project. That project was created and modified by many engineers. Chiu was been told that, that device/project has some strange behaviors, such like when the device is waken up from sleep mode, some variables would be destroyed.
Chiu did quite a lot of study to better understand that device/project. He discovered, that project used a third-party software package, which was acquired from the customer legally; and it was a mandatory requirement for project development. The third party had been a jet fighter maker, so their software package has some low level shutdown procedure. (concept only)
Based on their design, when the device has nothing to do, the device exits from the main(), below the main(), it is the low level shutdown procedure, users have to implement their own appropriate functionality, such as to maintain or not to maintain the output signal. After the low level shutdown procedure, the device enters sleep mode. when the device is waken up from sleep mode, it triggers a software reset. (Chiu discovered this design concept from the third-party's example code.)
The customer and third-party also have another mandatory requirement, use FRAM to store important data.
So, my friend, Chiu's understanding is: users have two options. 1. Before the device enters sleep mode, store important data into FRAM, when the device is waken up, it restores important data from FRAM. After that, everything is clean and fresh. 2. The device ignores the low level shutdown procedure; it enters sleep mode within main(), and is waken up from sleep mode within main().
Chiu studied the source code of that project, and discovered that, in this CMMI qualified company, no one knows what is startup code and what is C runtime environment. So the creator and the early engineers of that project, modified whatever they like in the third-party startup code, trying to keep important data from the software reset. However, after the software reset, C runtime environment initials global/static variables. Without leveraging the FRAM, of course they failed. Somehow, they contacted the Chip supplier and got an perfect fix, they then use noinit pragma to protect the important data, so that C runtime environment would not initial the important data.
-- I resigned from my ex-company in April, and joined a very small company in May. Not sure if it is a good place to stay, but there is not much opportunity.
You, as the creator of the system, should do something appropriate
Absolutely. As should all aspects of a system being created.
Fear of globals is primarily motivated by possible namespace pollution and lack of encapsulation
Nah. It's normally an irrational fear triggered by the the expert headlines, along with "goto is evil" and "software timers must never be used" and "cheese reduces life expectancy".
Except in cases where randomly resetting equipment isn't seen as a solution, but rather could create some even more dangerous situations to arise. In those cases I'd generally argue against it.
Sometimes it's appropriate to shutdown into a safe state and sound an alarm.
Blanket statements of what something should do, without really thinking it through, are a bit asinine.
There is, in some camps, a fear of global variables and nobody thinks abhout the fact that variables local to main are just as much "forever variables" as globals.
And curiously enough some people arrive at such a point of view without even learning enough about their programming language to recognize that they're mixing up two different concepts: a variable's lifetime and and its visibility (which C splits up further into "linkage" and "scope").
Fear of globals is primarily motivated by possible namespace pollution and lack of encapsulation. But these worries concern visiblity only. There's absolutely no need to abolish static lifetime just to get rid of (perceived) excess visibility.
In short: the keyword static exists, so use it.
I said "You, as the creator of the system, should do something appropriate. Like fall through to code that triggers a watchdog reset."
Is there any question about that? I hope nobody would be foolish enough to argue against it.
Just that a jump to reset may fail spectacularly - lots of startup code is written based on the assumption that the processor comes from a real reset. With just a jump to the reset vector, then timers, interrupts sources etc might already be up and spinning. After the fake-reset, the code can then get hammered by "impossible" interrupts.
Anyway - people should try to program by contract. So staying away from constructs where there doesn't exist an explicit contract promising a specific behaviour. And people should try and give some of the promises from the compiler vendor a bit less weight than they do, and try more to just follow the language standard instead of making use of a compiler-specific feature. Unless, of course, that compiler-specific feature really is important for the specific target platform.
Most always, it cleans up and calls main again.
I will avoid saying that what you say is just plain wrong. It is very unlikely.
Many embedded linkers insert a "jump reset" at the end of main, so it is, in fact likely. hiowever I see no case where relying on that would make sense.
as to some previous comments
there should always be a bit of extra thought before placing large variables on the stack. If nothing else, large stack variables are likely to trip any other developer who comes later and have to take over the code.
I have seen projects developed with a fear of global variables where you ahve a huge structure in main() and carry a pointer to it all over the place, in fact making the variables global.
NO, do not make all variables global, but THINK (sorry) before making varibles local to main().
Erik
A local function can have variables of limited size. For best utilization of stack we should avoid structures and arrays of larger size defining in the local function. Instead declare them as global variables. I have faced a similar issue , I have declared an array of 2000 Bytes for a local variable in a function. When i call that function these bytes gets corrupted at some point. Later i have made the array as global and everything works fine. I have found in some text that arrays and structures of more length should not be declared as local variables.
You, as the creator of the system, should do something appropriate. Like fall through to code that triggers a watchdog reset.
Unfortunately, from what I've seen, this is a very rare piece of forethought. Many professional and seasoned firmware designers neglect such details.
One more question, can you recommend a book which covers details and best practices in embedded programming? If its written for Cortex MCUs even better.
Well, you obviously don't know much about writing code for embedded systems as the rest of us!
IF you exit main() at the bottom, the code returns to the C startup runtime (provided by the compiler). Most always, it cleans up and calls main again. So it is infinite.
While technically allowed, that really makes no sense. There's no point having automatic variables in main(), much less ones as big as those.
Now you'll want to know how I arrived at that conclusion. Well, making variables automatic has benefits mainly in terms of limiting their lifetime, thus allowing space to be reused by other variables with different lifetimes. But the lifetime of main() itself is, effectively, infinite. I.e. an automatic variable in main() actually has infinite lifetime. It is static in all but its declaration, so it's better to match the declaration to the facts:
static unsigned char audioDataBuffer1[512]; static unsigned char audioDataBuffer2[512];
Placing variables on the stack is a good thing for variables with a limited life span, when the life spam can be matched by call tress.
Dynamic allocation is suitable when the variables have limited lifte span but the life span is hard to match to call trees. Or when the system at runtime have to decide how to slice the memory, i.e. how much memory to spend on different information.
If the goal is just to hide the variables from the global namespace, then it is often better to make them static.
Many architectures have more limitations for use of stack space than for global variable address range, so there should always be a bit of extra thought before placing large variables on the stack. If nothing else, large stack variables are likely to trip any other developer who comes later and have to take over the code.
The stack allocation is often made in startup.s (or equivalent), a stack with 0x200 bytes clearly wouldn't be adequate if your variables a auto/local in nature. If you don't want them on the stack, define them globally, or use static declaration.
View all questions in Keil forum