13

It may just be a coincidence but I've noticed the microcontrollers I've used rebooted when they ran out of RAM (Atmega 328 if hardware specific). Is that what microcontrollers do when they run out of memory? If not, what happens then?

Why/How? The stack pointer is certainly blindly increased to a non-allocated memory range (or rolled over), but what happens then: is there some kind of protection that makes it reboot or is it (among other effects) the result of the overwriting of critical data (which I assume different from the code which I think is run directly from flash)?

I am not sure this should be here or on Stack Overflow, please let me know if this should be moved although I'm pretty sure hardware has a role in that.

Update

I should point out I am particularly interested in the actual mechanism behind memory corruption (is it the result of the SP rolling over -> does that depend on the uC's memory mapping etc.)?

Mister Mystère
  • 9,477
  • 6
  • 51
  • 82
  • 8
    Some micros will reset if you try to access invalid addresses. It's a valuable feature implemented in hardware. Other times it may end up jumping to somewhere arbitrary (say you've clobbered the return address for an ISR), perhaps executing data rather than code if the architecture allows that, and perhaps getting caught up in a loop that the watchdog brings it out of. – Spehro Pefhany Jan 02 '15 at 23:48
  • 3
    A processor can't run out of RAM, there is no instruction that will make it run out of RAM. Running out of RAM is entirely a software concept. – user253751 Nov 21 '16 at 21:28

4 Answers4

15

In general the stack and the heap crash in to each other. At that point it all gets messy.

Depending on the MCU one of several things may (or will) happen.

  1. Variables get corrupted
  2. The stack gets corrupted
  3. The program gets corrupted

When 1 happens you start getting strange behaviour - things not doing what they should. When 2 happens all manner of hell breaks loose. If the return address on the stack (if there is one) is corrupted, then where the current call will return to is anyone's guess. At that time basically the MCU will start doing random things. When 3 happens again, who knows quite what would happen. This only happens when you're executing code out of RAM.

In general when the stack gets corrupted it's all over. Just what happens is down to the MCU.

It might be that trying to allocate the memory in the first place fails so the corruption doesn't happen. In this case the MCU might raise an exception. If there is no exception handler installed, then most often the MCU will just halt (an equivalent of while (1);. If there is a handler installed, then it might reboot cleanly.

If the memory allocation does go ahead, or if it tries, fails, and just continues with no memory allocated, then you're into the realms of "who knows?". The MCU might end up rebooting itself through the right combination of events (interrupts caused that end up resetting the chip, etc), but there's no guarantee of that happening.

What there can usually be a high probability of happening, though, if it's enabled, is the internal watchdog timer (if one exists) timing out and rebooting the chip. When the program goes completely AWOL through this kind of crash the instructions to reset the timer generally won't be run, so it will time out and reset.

Majenko
  • 55,955
  • 9
  • 105
  • 187
  • Thanks for your answer, it's an excellent summary of the effects. Perhaps I should have specified though that I would like more details on the actual mechanism of those corruptions: is the entire RAM allocated to stack and heap, such that the stack pointer rolls over and overwrites earlier variables/addresses? Or is that less dependent on each micro's memory mapping? Optionally (it's probably a topic in itself), I'd be interested in learning how those hardware handlers are implemented. – Mister Mystère Jan 04 '15 at 21:39
  • 1
    It's mostly dependent on the compiler and the standard C library in use. It's also sometimes dependant on how the compiler is configured (linker scripts, etc). – Majenko Jan 04 '15 at 22:05
  • Could you expand on that, perhaps with a couple of examples? – Mister Mystère Jan 08 '15 at 20:59
  • Not really, no. Some systems allocate finite space for different segments, some don't. Some use linker scripts to define segments, some don't. Pick a microcontroller that is of interest to you and do some research into how its memory allocations work., – Majenko Jan 08 '15 at 21:08
12

An alternative view : Microcontrollers don't run out of memory.

At least, not when programmed properly. Programming a microcontroller is not exactly like general purpose programming, to do it properly you have to be aware of its constraints and program accordingly. There are tools to help ensure this. Search them out and learn them - at least how to read linker scripts and warnings.

However as Majenko and others say, a badly programmed microcontroller may run out of memory, and then do anything including infinite loop (which at least gives the watchdog timer a chance to reset it. You did enable the watchdog timer, didn't you?)

Common programming rules for microcontrollers avoid this : for example, all memory is either allocated on the stack or statically (globally) allocated; "new" or "malloc" are forbidden. So is recursion, so that the maximum depth of subroutine nesting can be analysed and shown to fit in the available stack.

Thus the maximum storage required can be computed when the program is compiled or linked, and compared with the memory size (often encoded in the linker script) for the specific processor you are targetting.

Then the microcontroller may not run out of memory, but your program might. And in that case, you get to

  • rewrite it, smaller, or
  • pick a bigger processor (they are often available with different memory sizes).

One common set of rules for microcontroller programming is MISRA-C, adopted by the motor industry.

Best practice in my view is to use the SPARK-2014 subset of Ada. Ada actually targets small controllers like AVR, MSP430 and ARM Cortex reasonably well, and inherently provides a better model for microcontroller programming than C. But SPARK adds annotations to the program, in the form of comments, which describe what the program is doing.

Now the SPARK tools will analyze the program, including those annotations, and prove properties about it (or report potential errors). You don't have to waste time or code space dealing with erroneous memory accesses or integer overflows because they have been proven to never happen.

Although there is more up-front work involved with SPARK, experience is showing it can get to a product faster and cheaper because you don't spend time chasing mysterious reboots and other strange behaviour.

A comparison of MISRA-C and SPARK

  • 3
    +1 this. Porting `malloc()` (and it's C++ companion `new`) to the AVR is one of the worst things the arduino people could have done, and has led to many, many very confused programmers with broken code both on their forum, and the arduino stack exchange. There are very, very few situations where having `malloc` on a ATmega is beneficial. – Connor Wolf Jan 03 '15 at 11:50
  • 3
    +1 for philosophy, -1 for realism. If stuff would be programmed properly, there wouldn't be a need for this question. The question was what happens *when* microcontrollers run out of memory. How to prevent them from running out of memory is another question. On another note, recursion is a powerful tool, both for solving problems *and* running out of stack. – PkP Jan 03 '15 at 13:04
  • @PkP : I'll tentatively disagree with -1 for realism. If the embedded coder can think "how can I prevent ..." rather than "what happens when..." then we're making progress. Recursion, for example, can often be linearised, there aren't many tasks on a controller where true recursion is necessary. Now we *could* simply accept defeat and dangerous programming (and I agree that you sometimes have to : we must pick the battles carefully) but let's not make that the default. –  Jan 03 '15 at 13:55
  • 2
    @Brian, since I'm *not* an idiot, I obviously agree with you. I just like to think about it in reverse point of view - I like to hope that when you realize the horrible consequences of running out of memory (stack), you would seek ways to prevent it from happening. That way you have a real impetus towards finding good programming practices instead of just following good programming advice... and when you hit the memory barrier you're more likely to enforce the good practices even at the expense of convenience. It's just a viewpoint... – PkP Jan 03 '15 at 14:29
  • 2
    @PkP : hear ya loud and clear. I labelled this an alternative view - because it doesn't actually answer the question! –  Jan 03 '15 at 19:38
  • So microcontrollers DO run out of memory. I know they're not supposed to and I do everything to avoid that (starting with mallocs(), I use them with a maximum length value to save some RAM but the worst case scenario is the same as a statically allocated one so I wonder when it's actually preferrable), I'm just curious and this is also useful knowledge for debugging in the future. C is my preferred language (not a fan of Ada), do you know any free, simpler static code analysis tools (MISRA seems to be propriatary)? Actually I'm wondering: isn't RAM estimation platform/linker dependent? – Mister Mystère Jan 04 '15 at 21:57
  • Thanks for your answer though, this should have been my first sentence! – Mister Mystère Jan 04 '15 at 21:58
  • "not a fan of Ada " I hear that a lot - but rarely from anyone with recent experience (2005 or 2012, not Ada-83!). Why, in your case? Sorry, not an expert on C tools though I hear positive things about FRAMA-C. –  Jan 04 '15 at 23:12
  • I'm not claiming to have any specific argument against it, I just don't like the syntax and coding style just like Pascal's. I'm still open to new alternatives but for the time being the combo C, C++, VHDL and Python allows me to do pretty much anything I want comfortably. – Mister Mystère Jan 08 '15 at 20:58
  • Interesting. One benefit Ada has for me is the similarity to VHDL, whihc makes switching between SW and HW easier. (Of course I wish VHDL wasn't so restricted after using Ada for a while) I still use C sometimes, but only if customer pays enough. –  Jan 08 '15 at 21:06
  • 2
    @MisterMystère: Microcontrollers generally don't run out of memory. A microcontroller which has 4096 bytes of RAM when it is first powered on will have 4096 bytes forevermore. It's possible that code might erroneously try to access addresses that don't exist or expect that two different methods of computing addresses will access different memory when they don't, but the controller itself will simply execute the instructions it's given. – supercat Jan 19 '16 at 22:17
6

I really like Majenko's answer and +1'd it myself. But I want to clarify this to a sharp point:

Anything can happen when a microcontroller runs out of memory.

You really cannot rely on anything when it happens. When the machine runs out of stack memory, the stack most probably becomes corrupted. And as that happens, anything can happen. Variable values, spills, temp registers, all become corrupted, disrupting program flows. If/then/elses can evaluate incorrectly. Return addresses are garbled, making the program jump to random addresses. Any code you've written in the program may execute. (Consider code like : "if [condition] then {fire_all_missiles();}"). Also a whole bunch of instructions you haven't written can execute when the core jumps to a nonconnected memory location. All bets are off.

PkP
  • 7,567
  • 2
  • 23
  • 42
1

AVR has reset vector at address zero. When you overwrite the stack with random garbage you'll eventually loop around and overwrite some return address and it'll point to "nowhere"; then when you return from a subroutine to that nowhere, the execution will loop around to address 0 where a jump to reset handler usually is.

Ilia
  • 21
  • 2