Developers like to believe that the microprocessor their software runs on will faithfully follow the program flow that they created and run as expected indefinitely. The fact of the matter is that sometimes things just go wrong and the program runs off into the weeds. There are many possible causes for this such as overwriting the boundary of an array, a pointer being used before it is initialized or any number of other possible reasons. One of the most useful techniques to recover a system when this occurs is to fill unused memory with something that will catch the fault.
There are a number of possibilities of what the unused memory can be filled with. The first is the reset vector location. In the event that things go amiss, the CPU will load the reset vector and the program will start back at square one. This will be more of a soft and uncontrolled termination of the application with little to no debug information being available as to the cause. The hardware will be in a nondeterministic state from which there is no guarantee that the system will recover from reset properly.
A second, and possibly more appropriate behavior would be to instead place a known interrupt vector in the empty memory. In this case when things go terribly wrong the application has an opportunity to capture the event and provide some clues such as CPU register states as to what happened. This then allows developers to reproduce and debug the cause rather than blindly guessing.
A third option that could be used if debug information is not of interest would be to fill memory with a HALT or branch-to-self instruction. This would have the effect of stopping the microcontroller in its tracks. The advantage of this technique is that it prevents the microcontroller from running off and continuing to errantly execute code. Instead everything stops and the trusty watchdog timer, that every developer enables (I hope), comes to the rescue by timing out and performing a hard reset of the system.
A final option that really isn’t recommended would be to fill memory with gibberish. Select a bit pattern such as 0x55, 0xAA, 0xAA55 and just hope for the best. In this instance the filling of memory is not really to improve the robustness of the application but most likely to provide known values other than 0xFF so that a ROM checksum can be performed. A brief search of the Internet reveals some clever bit patterns such as 0xDEADC0DE, 0xDEADBEEF and so on.
Now there are many different ways in which to fill memory with a specific bit pattern. They mostly fall into two different categories; IDE or Linker based. In the IDE implementation, hidden somewhere deep within the property menu’s will be an option to “Fill Memory”. Depending on the IDE and tool chain there may be additional options to set a specific pattern or the tool may decide on it’s own. Most of the IDE tools when the option to “Fill Memory” is selected require the developer to select the memory range that will be filled. This in turn forces the developer to continually monitor where the end of the application is in the .text section of the linker.
The linker-based method provides the developer with the ability to customize the fill solution with a bit more flexibility. For example, using the GNU tool chain a new section output can be created that contains the FILL() linker command. This command is passed the bit pattern that should be used to fill memory. From this memory section the ORIGIN and LENGTH functions can be used to get the size of the memory section that is going to be filled. Allowing the memory space to be automatically filled with the bit pattern without developer management being required. An example of what the fill section might look like for a GNU linker with a .text section named m_text can be found in Figure 1.
. = ORIGIN(m_text) + LENGTH(m_text) – 1;
} > m_text
Figure 1 – Example Linker File Section using GNU
The result on empty memory is that upon reviewing the memory file generated during compile time, the requested bit pattern fills memory:
Figure 2 – Bit Pattern in S-Record
One of the advantages of using the linker file is that the project can then be configured to switch between a linker that fills memory and one that doesn’t. One of the disadvantages of using memory fill is that every memory location is written to. This means that if you are trying to debug an application, every time you make a code change you have to wait for ALL of flash to be written even if the application doesn’t affect it. This can add considerable time to the implementation phase. Therefore memory filling should be setup at the start and as part of any code commitments but still provide the ability to enable and disable it for debugging. Keep in mind that a developer wants to use this memory fill as much as possible to catch any unexpected behavior.
Properly implementing the FILL command with an interrupt vector or halt command can provide an increase in the integrity of the software system. In the event that an errant pointer, single event upset or other issue arises that causes the application to start executing code from an unexpected memory location, the filled bit pattern can direct the program execution back to a known location where proper error handling procedures can recover the system. Each IDE and MCU has a slightly different way of implementing this feature but it is usually not much more complicated than reviewing the linker datasheet on how to use the FILL command.