Assertions in Microcontrollers

Introduction to Assertions:

Assertions are a simple way of testing your programming logic.  The basic idea behind assertions is testing a piece of code that you assume will always work as designed.  As programmers, we inheritantly do this all the time while writing code.  We find ourselves writing small print statements to check that what we just coded produced the right output.  Assertions are a formal way of doing this.  The typical assertion header file looks like this:

#ifndef _ASSERT_H_
#define _ASSERT_H_

#if ENABLE_ASSERTIONS==1
void assertion_failure(char* expr, char* file, char* baseFile, int line);
#define assert(expr) \
    if (expr) ; \
    else assertion_failure(#expr,__FILE__,__BASE_FILE__,__LINE__)
#else
#define assert(expr) /*nothing here*/
#endif

#endif // _ASSERT_H_

And a programmer would use it like this:

#define ENABLE_INSERTIONS 1
#include "assert.h"
double nasa_rocket() {
    int i;

    // thrust value for the four engines
    double thrust_vals[4];

    // average thrust percentage
    double thrust_percentage;

    // get thrust from all rocket engines
    for (i=0; i<4; i++)
        thrust_vals[i] = getMotorThrust(i);

    // call function to compute average thrust percentage
    thrust_percentage = computeAverageThrustPercentage(thrust_vals);

    // double check programming logic
    assert(thrust_percentage >= 0.0);
    assert(thrust_percentage <= 100.0);

    return thrust_percentage;
}

As shown in the first code snippet, when an assertion fails, it calls ‘assert_failure()’ and passes information about the tested expression, file names, and line number.  This function usually uses ‘fprintf()’ to write this information to the error console.  After printing the information, the ‘assertion_failure()’ function either calls a system halt function, kills the program, or puts itself into an infinite loop.

The NASA software engineer could verify here that the returned ‘thrust_percentage’ would not be out of the standard bounds of percentages (0% to 100%).  This would not be a check you’d want to do in code because it should always work and extra checks will just slow it down.  However, you do want to make sure it works before sending your billion dollar rocket into space.  After the engineer has verified that the code works, he can simply disable assertions using the ‘ENABLE_ASSERTIONS’ define statement.  Doing this causes the C preprocessor to produce blank lines for all calls to ‘assert()’.  After all, it’s better to use invalid numbers than to cause an infinite loop inside an ‘assertion_failure()’ function!

How to assert in a microcontroller:

The first program you write for a microcontroller is much different than the first program you write for a personal computer.  You don’t have any libraries or operating system to support the standard “Hello World!” program.  Usually your first program is making an LED flash.  GPIO is the easiest thing to implement in a microcontroller.  Setting up a console for a microcontroller is a big process.  What happens if you wish to use assertions to test the console driver your are creating?  How can you assert without a console to print to?  There are many answers to this.  This post attempts to give the most robust implementation for microcontrollers.

First thing is first, you need a working LED that you can set aside for assertion failures.  That’s it!  Using some simple code, you can be notified (by the LED) of a failure and check where it came from using the debugger.

Here is my implementation for assertions:

#ifndef _ASSERTS_H_
#define _ASSERTS_H_

void assert_init(void (*assert_indicator)(void));
#if ENABLE_ASSERTIONS==1
void assertion_failure(char* expr, char* file, char* basefile, int linenum)
#define assert(expr) \
    if (expr) ; \
    else assertion_failure(#expr,__FILE__,__BASE_FILE__,__LINE__))
#else
#define assert(expr) /*nothing*/
#endif // ENABLE_ASSERTIONS==1

#endif // _ASSERTS_H_
#define ENABLE_ASSERTIONS 1
#include "asserts.h"

// function pointer to assertion indicator
static void (*assert_ind)(void) = NULL;

// sets user's choice of failure indicator function
void assert_init(void (*assert_indicator)(void)) {
    assert_ind = assert_indicator;
}

// failure function called by macro.
#if ENABLE_ASSERTIONS==1
void assertion_failure(char* expr, char* file, char* basefile, int linenum) {
    // debugger can look at these
    volatile const char* v_expr;
    volatile const char* v_file;
    volatile const char* v_basefile;
    volatile int v_linenum;

    // assign variables to volatiles
    v_expr     = expr;
    v_file     = file;
    v_basefile = basefile;
    v_linenum  = linenum;

    // call function that indicates an assertion failure
    assert_ind();

    // halt
    while (1);
}
#endif

As shown, my implementation has an extra function for initialization. The user simply passes a function pointer to the ‘assert_init()’ function that corresponds to the function that would indicate to the user of an assertion failure.  The simplest solution is a function that turns on an LED designated for assertion failure indication.

The ‘assertion_failure()’ implementation simply copies the input values to local volatile versions.  It then calls the user’s function (which illuminates the LED) then puts itself into a infinite loop.

When the user sees the LED illuminate, he knows an assertion has failed.  Using the debugger, he can then pause the program (which is still in the ‘assertion_failure()’ function) and inspect the variables which indicate the failing expression, file location of the failing expression, and line number within the file of the failing expression.  Using this information, he can then fix the mistake(s).

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: