Hiding Non-standard C Keywords for Off-Target Testing

Some silicon vendors extend the C language so the programmers can easily interact with the silicon. Using these extensions tie production code to the silicon vendors compiler and consequently the code can only run on the target system. This is not a problem during production, but is a problem for off-target unit testing.

The good news is that we may be able to get around this problem without having to change production code, one of our goals when adding tests to legacy code.

Two common offenders are specialized C adornments like cregister and interrupt. cregister might be used like this

//snip...
/* Address Mode Register */
extern cregister volatile unsigned int AMR; 
//snip...

interrupt is used like this:

//snip...
interrupt void one_ms_tic(void)
{
  ...
}
//snip...

These are pretty easy portability problems to get rid of. Use the C preprocessor to define cregister and interrupt to be nothing when compiling for off target execution with an include file like this:

#define cregister
#define interrupt

Now we have to go add this as the first line in every source file. What! never! you say. Yes that is too much trouble; let’s let the preprocessor do it for us using a forced include. If you are using CppUTest with the GCC toolchain, add this to your CppUTest based Makefile.

  CPPUTEST_CPPFLAGS += -include mocks/hideStuff.h

If you are not using CppUTest, but are using GCC, add the forced include to the CPPFLAGS variable or somehow get it to your compilation command line.

If you are off-target testing with some form of Visual Studio, it too has a forced include control in the project settings.

I’ve shown another example of using forced includes in Spying on Embedded ‘asm’ directives.

OK, so we have work arounds for header file dependencies on the target compiler. I am hoping that you also know that you should limit your production code dependencies on the target-only specific constructs.

If this helped, tell me about it. If I’m missing something or there is a better way, tell me that too.

6 thoughts on “Hiding Non-standard C Keywords for Off-Target Testing

  1. Great tip! I’m just starting to use TDD, with the help of your book, for 8051 C code, where there are several compiler extensions to deal with in this manner.

  2. Hi James,

    This is a good point. One thing to remember though that some compilers (Keil 8051 – I’m looking at you) declare some really general keywords like “data”. In these cases it is better to use an additional level of redirection (with a little bit more effort required).

    ProductionMacros.h:
    #define DATAMEM data

    TestMacros.h
    #define DATAMEM

    Code.c:
    int my_func(int DATAMEM x, int DATAMEM y){
    }

  3. Hi Mike

    If I am creating new code with TDD, I would definitely use what you suggest. I would also be very careful to limit which files need specific hardware vendor compiler features. Given legacy code, I’d first try these compiler/preprocessor tricks to minimize code changes. Later, once the code is under test, we could start to fix the code for some of the sins of the past.

    James

  4. Nice post James.

    As I also discussed on a LinkedIn forum recently, there is a huge payback to be gained from compiling and running code on a host system. One of the (many) great side effects of “writing on the PC first” (I even do this on tiny PIC projects) is that it forces you to come up with usable language abstractions that work across platforms, such as the ones you describe above. I have a cpu.h now that is force included on all my projects, and it selects the correct macro replacements for the chosen platform, where PLATFORM_XXX is defined in the compiler options to select the chosen platform.

    One of the bigger challenges (especially when writing code that runs on multiple platforms, and compilers, as I often do), is those pesky #pragma statements. e.g. #pragma interrupt MyHandler 6 to set up interrupt vector 6 to MyHandler. A multi platform piece of library code often has lots of #ifdefs around the compiler specific #pragmas to support all the different platforms (I have a multi-platform pre-rolled rs232 driver that I port between all my projects, so you can imagine the mess in the interrupt setup part!)

    It would be worth mentioning at this point that C99 introduced the _Pragma() unary operator that adds that all needed “extra level of indirection” as opposed to #pragma, allowing you to write (taken from C99 section 6.10.9):

    #define LISTING(x) PRAGMA(listing on #x)
    #define PRAGMA(x) _Pragma(#x)
    ….
    LISTING(..\listing.dir)

    David.

Leave a Reply

Your email address will not be published. Required fields are marked *


nine × = 18

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">