Essential Best Practices and Debugging Tips for EFM32 Project Success - Part 5

by <a href=""><font color="#000000"><font size="2">Ninja</font></font> </a> lynchtron on ‎03-01-2017 08:19 PM


Build Source Code Like a Pro

You are lucky to be building your code in Simplicity Studio, because the installer sets everything up for you and you can get started with debugging your projects in minutes after the installer is finished.  But don’t overlook the build tools.  It wasn’t always this easy to get it all working and you may have lost appreciation for what all of these tools do.  There are a lot of things that are going on under the hood to take your human-readable C code and turn into a binary image that is then flashed to your MCU.


1. Don’t forget to copy or link the emlib files
When you create a new project from scratch and start to reference the emib libraries such as em_cmu.h or em_emu.h, you will need to copy or link the implementation files for those header files in your emlib directory in your new project. Simplicity Studio automatically links to the header files (the .h files) for all of the standard emlib libraries, but doesn’t link to the implementation files. Therefore, it is up to you to navigate to the Simplicity Studio installation folder that contains those library files and copy them into your emlib directory, or to use Simplicity Studio’s project Properties to link to those files.

To copy or link implementation files have different benefits and tradeoffs. If you copy the implementation files to your project, your project will retain a private copy of the implementation of a library file that won’t change as the Simplicity Studio version is updated. However, the header file will change as the Simplicity Studio version is updated. This can create some mismatches between the declarations and implementations of the library files. If you link to an implementation files, your library files will change as the Simplicity Studio version is updated, and this could have adverse implications to your software. The solution that would give you the most control is to copy both header and implementation files into your project, and remove the link to the header files.

Beware that if you forget to add a link or copy of an implementation file for a library, some functions that are implemented solely in the header files will still work, while functions that are implemented in the .c files will sometimes give non-obvious errors in the build output.

2. Solve your issues from top to bottom
Whenever you build your code, the compiler will send lots of information to the build output console window in Simplicity Studio. The window scrolls and if it finds errors, your build console will be sitting at the last error that it found. You should not debug that last error. Scroll up in the window and debug the very first problem that the compiler found. The warnings and/or errors that occur later in the build process could be due to that first error, and you will be chasing the wrong problems if you try to solve the later issues that were caused by the root issue. You can also make use of the Problems tab in the bottom of the Simplicity Studio build console.

3. Don’t ignore warnings
Compiler warnings are set to be ignored by default in the build process. If your code generates errors, the build will fail, but if it only generates warnings, the build will succeed and you can then flash your MCU with the program. You might not even notice the warnings scroll by on the build console window. You have to scroll back and look for any line that has a yellowish background. Make sure that you understand what the warning is telling you before you ignore it. Some warnings, like an unused variable warning, might be just a nuisance that you can safely ignore. However, if you intended to make use of a variable that was important for a calculation, the warning will help save you from a logically incorrect solution. If you resolve all of your warnings, you will not see any colored background scroll by as the build process commences.

In order to monitor the output of functions during debugging, you will sometimes be left with unused variable warnings that are not an issue. If this is the case, you can use a C compiler attribute instruction to tell the compiler that the unused variable is intentional with the following code:


uint32_t __attribute__((unused)) foo = some_function_with_return_value_to_inspect() ;

Or, you can create a preprocessor directive to use throughout your code:

#define UNUSED __attribute__((unused))
void some_function()
     int UNUSED foo = some_function_with_return_value_to_inspect();

There is another thing that you can do to remove unused parameters in functions. Sometimes, you don’t need to make use of all of the functions given to you in a pre-defined function, as in an interrupt handler or other system-defined callback function. By declaring the functions parameters as void in your customized implementation, you will tell the compiler that you don’t intend to use those parameters in your implementation.


void some_function(int foo)
(void) foo;

Beware that if you happen to miss a warning that scrolls by somewhere in your build window, it will mark that file as built, and it won’t rebuild it again until there are changes to that module, so you won’t see the warning again until changes are made. This could result in a problem that seemingly pops up out of nowhere in a later build when unrelated code is modified. You can always force a rebuild of all files by cleaning your build environment from the ProjectClean... menu option and then rerunning the build tools.

You can configure the compiler to treat warnings as errors if you are brave enough and committed enough to clean up all warnings. Set this option in:

Project > Properties > C/C++ Build Settings >Warnings > Warnings as Errors (-Werror)

4. Interpret the build output size report
When you build your project, a bunch of text scrolls by on the build output console. When it is done and if the build succeeded, the last thing that it does before giving you the “Build Succeeded” message is to run the size tool:


Running size tool
arm-none-eabi-size "energy_profile_only.axf"
text data bss dec hex filename
1188 104 32 1324 52c energy_profile_only.axf

This tool is giving you a report about how flash and RAM is required for the code that you just built. Here is the decoder of what these things mean:


  • text
    • The number of bytes in EFM32 flash memory reserved for programming instructions and constants. The interrupt vector table is also included in this memory.
  • data
    • The number of bytes in EFM32 flash memory reserved to be used by initialized data values. These are the values that get assigned to ordinary variables either at program startup or at function invocation.
  • bss
    • The number of bytes of RAM reserved when the program starts, which is the sum of all of the global variables required by the program, which end up on the heap.
  • dec
    • The sum of text + data + bss. This number has no real significance.


If you try to build a file that has more RAM allocated than what is available in your particular EFM32 part, you won’t even get to the build report.  You will get a fairly straightforward compiler error.  For example:

int really_big_array[10000];
void some_function()
        really_big_array[0] = 0;
int main(void)
        /* Chip errata */


Results in:

c:/...bin/ld.exe: some_program.axf section `.bss' will not fit in region `RAM'
c:/...bin/ld.exe: region RAM overflowed with stack
c:/...bin/ld.exe: region `RAM' overflowed by 7368 bytes

Keep in mind that this is reporting the memory that is allocated when your program starts running.  The amount of memory in use once your program starts will change based on the functions that are called and the memory that gets allocated dynamically. 


5. Make changes to the build process in project properties
The project properties menu option has many settings that control your build. This tool is what is saving you the headache of command-line manipulation and stores all of the settings in the .project and .cproject files in your project’s directory on disk. The most important aspects for the build process are configured in the Project > Properties > C/C++ Build Settings menu. Here, the overall build can be selected, whether you are building to be able to debug a project or release it for production. Then, the Memory Layout, C Compiler, Assembler, and Linker tools can all be individually controlled, which gives you the same power and flexibility as if you had run each of these tools from the command line, one at a time.


A debug build allows you to single-step through execution and inspect the contents of variables and memory, but is not optimal for performance or code size. The top node on the tree of options is Debug Settings, which allows you to control the debug level.


A release build is optimized for better performance and code size, and the level of optimization can be controlled from within this same Settings area in the GNU ARM C CompilerOptimizations menu. You can see that selecting Release versus Debug changes the level of optimization from Most (-O3) to None (-O0).

The Memory Layout menu item in the Settings menu gives you the ability to place your program in flash and RAM at an address that you specify. You can use a custom linker script if your needs are complex, or simply click on the options to “Override default flash options” or “Override default RAM options.” By overriding the default flash option’s origin, you can write multiple programs into the same EFM32 chip without erasing the other. This is how bootloaders are configured.

A bootloader typically starts at address zero and is limited in size (with the LENGTH parameter) so that the build tools will warn if your bootloader is getting too big to fit in the allocated space. Then, your application program (an entirely different project in Simplicity Studio) is configured to start at an address that is higher than the bootloader. This way, loading your application code from Simplicity Studio over JTAG doesn’t erase the bootloader. Note that in order for your application code to run at an address other than zero, the bootloader must properly load the program counter with the address of the application program so that your application can run from reset. See chapter 16 for more detailed information about boot loaders.

6. Investigate build tool input and output further in build files
When your build process finishes and you have a successful build, Simplicity Studio places the output files of the build process in one of two directories in your project. The directories are called GNU ARM v4.8.3 – Debug or GNA ARM v4.8.3 – Release, depending on if your build was a debug or release build.




These directories are the working folders for the build process and should not be modified directly—they will be overwritten on every build, and can therefore be excluded from revision control systems. These directories contain files that are used as inputs to some of the build tools and the outputs of some tools. You can, however, examine the results of the build process to see what the tools are doing. It is beyond the scope of this chapter to describe these files in detail, but the following table provides a summary.


  • makefile and *.mk files
    • Scripts used to configure the compiler, or “make” tool, which is auto-generated by Simplicity Studio on your behalf, based on the settings in the Project Properties settings.
  • <project>.ld
    • The linker script that Simplicity Studio generated on your behalf, based on the settings in the Project Properties settings. The linker runs after the compiler.
  • <project>.map
    • The output of the linker tool. This file contains information about where in memory the build tools have placed your functions and data.
  • <project>.axf
    • Object code plus debugger information. This file is used by the Simplicity Studio debugger to be able to debug your software live on the device
  • <project>.hex
    • An ASCII-text representation of machine code for your build
  • <project>.bin
    • Machine code binary image. This is the file that will ultimately be transferred into flash during programming and the source file that is used for bootloaders.


In the next section, we will cover the final step, debugging your software on the EFM32.