Stack Monitoring Worries

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

I've been working with the XMEGA for the last four years, but I have never really dived "under the hood" with debugging or memory examination (until today).... which means I've been worried about stack overflow for that entire time.

 

I am using an XMEGA128A4U with 8KB of SRAM. After compiling, the output window says I am using 49% of my data memory.  So I installed Dean's Stack Monitor extension today and went and exercised every feature/menu/capability of my embedded application.

 

Afterwards, the Stack Monitor says I was using ~400 of 4160 bytes for about 10% usage.

 

!!! WHAT a relief, thank you Dean. !!!

 

So my question is, can I calmly storm ahead and keep on pushing until that Stack Monitor figure is 80%? 90%? Or.... are there other ways that excessive data memory usage can run me into a fatal barrier? 

 

Thanks for your insights

Mike in Alaska

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you add 10% and 49%, then you get 59% total. Stack is in the same ram as your statically allocated data, so if your stack is using 80% and your statically allocated data is 49% then you'd be having problems!
The stack can use as much free memory as there is available, but no more.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, thanks for your response.

 

I am only referring to the % use of the available stack space as shown in Deans Stack checker.  It seems to take the available amount of left over SRAM (after the compiler says "X%" is in taken) and then determine what % of that remainder is being used. (so.... I've got 8k total, and the compiler says 50% is taken, leaving 4k free, and the stack is using 10% of the 4k remainder, or about 400 bytes.) 

 

I took some time to read about the heap. I am doing no dynamic memory allocation whatsoever in my apps, so effectively 0k for a heap, correct?

Last Edited: Sun. Jan 17, 2016 - 09:40 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Five things are kept in your avr's limited ram:

 

1. Variables: Those that appear outside functions, and any "static" variables that appear inside functions.

2. Heap: Any space allocated using malloc.

3. Stack: This holds function return addresses, interrupt return addresses, saved registers on interrupt, some function parameters and variables that appear inside functions (though some of these might exist only in registers.)

4. String literals: Use to learn to use __flash!

5. Unused memory.

 

For variables, you know not to try to store an array of 70000 longs. In a computer you can do this, but in an avr, don't even think about it.

 

For the heap, you can't allocate very many big arrays. You'd be surprised what you can do without any dynamic allocation. For avr type programs, you likely don't need any heap.

 

The stack usually doesn't get very big. Two things consume memory for the stack: Recursion and functions with big local variables. In avr projects, you probably aren't using recursion. If you are, you're probably not going more than a few levels deep, but you need to pay attention if you're using recursion. For large local variables, you can often get away with them as they vanish as soon as the function exits. Function A can have a large variable, as well as Function B. As long as neither calls the other, you'll be ok. Other variables you might use inside functions tend not to use much memory, and the compiler often just puts them in registers anyhow.

 

Unused memory: You want the amount of unused memory to stay positive. If you've bought a chip with 32K ram, and your unused memory is 30K, you could likely get away with a cheaper chip, but unless you're manufacturing something, it probably doesn't matter.

 

There are some good programming habits that will keep you out of trouble:

 

1. If you need to store something big, make it a global (outside of a function) variable and just pass pointers to it or them.

2. Don't put big variables inside functions, especially those that might call other functions that might contain big variables.

3. Avoid recursion.

 

Unless you're making a program for a tiny 10, or trying to keep track of thousands of things, you likely don't need to sweat the stack usage. Remember, we used to build whole systems in computers that only had 64K (or less) ram and that had to hold the program too. In an AVR, your program doesn't go in ram. AVRs are intimidating because you're comparing their kilobytes of ram to your computer's gigabytes of ram, but you're not asking an avr to store images, run multiple, giant programs or play videos. (Unless you're the Atomic Zombie, who does it successfully.) I still find myself startled to hold a whole document in a variable rather than in a file and paging through it. "But it might be several megabytes!"

 

The largest known prime number: 282589933-1

It's easy to stop breaking the 10th commandment! Break the 8th instead. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Great stuff, thanks. Reassuring.

 

Looks like I am sitting really pretty with my resource consumption.

 

I honestly can't even picture why I would use something like malloc in a typical embedded control app. Can you cough up any empirical example of a time it would be useful?

 

Thanks

Mike

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I honestly can't even picture why I would use something like malloc in a typical embedded control app. Can you cough up any empirical example of a time it would be useful?

I once wrote an example using malloc/free here for someone who wanted to keep a dynamically growing "database" in their AVR. Now general databases are probably the preserve of large CPUs but I guess one could envisage a situation where you wanted to log some kind of technical data and you didn't know at design time exactly how many records you might need to accommodate.

 

Of course one might argue in such a case that the RAM resources of an MCU are so limited that what you should actually do is pre-allocate an array of "records" to hold 50 or 100 or whatever and then just give the user a limit saying "this only handles 50/100/..." because then you know that you will definitely be using "50 * sizeof(record)" and there's no question of your heap growing into your stack or whatever nasty thing is going to happen. Of course malloc() is design so that when you say "foo = malloc(sizeof(record))" and there isn't any more left it will return 0 in "foo" so if you did use it you would always be checking "foo" to see if the allocation worked. (people on PCs don't do this because it's almost bound to work however much you ask for!). Of course this protects the heap growing up into the stack. What is not protected is the stack growing down into the heap. So you should design MCU code so that there's always a big old chunk of emptiness beneath the stack.

 

Because the main use on the stack is actually local/automatic data in functions (the CALL/RET and even PUSH/POPs of preserved registers are probably fairly insignificant) then some might argue that in MCU code it is best to use globals or "statics" because the space for them is all pre-allocated at build time. Computer scientists on the other hand would have a conniption !

Last Edited: Mon. Jan 18, 2016 - 10:57 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I used malloc once in a program, then re-thought the whole idea and wrote it a different way.

The largest known prime number: 282589933-1

It's easy to stop breaking the 10th commandment! Break the 8th instead. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you must use malloc I suggest always trying to allocate however much memory you need + 100 bytes. If that works, free it and then re-allocate what you need. If it fails then you know you are getting dangerously close to the stack and treat it as an out of memory situation.

 

Or, as others have said, just avoid it altogether.