The increasing emphasis on green technologies has focused more
attention on low power design. microcontroller vendors are responding
by increasing their offerings of ultra low power devices that consume
as little as 350 µA/MHz and have sub-µA sleep modes.
Using a low power
MCU is a first step in lowering
power
consumption. But the next step should be to minimize the number of
instructions the CPU must execute to get the job done, so it can spend
as much time as possible in the deepest sleep mode available.
This can be accomplished with careful design of the application
software structure, tight coding and a
compiler
that reduces the number of required instructions.
Compilers can have a significant effect on the number of
instructions required to execute the application and the resulting
power consumption. All too often the method of compilation wastes CPU
cycles on interrupt routines and on locating addresses in memory
devices with banked memory.
Keep interrupts small
Interrupt should always be small and fast. This is especially true when
speed and/or power consumption are critical. Keeping interrupt routines
small reduces interrupt overhead.
The compiler's contribution to interrupt is in the way it generates
the context - the number of registers it saves in response to an
interrupt. Ordinary compilers save every register that might be used by
an interrupt because they have no way of knowing which registers will
or will not be used by a given interrupt.
The problem is that the number of cycles used is a direct function
of the number of registers that are saved and restored. Cycles spent
saving and restoring the context consume power.
For example, one compiler for Microchip's PIC16 always saves 8Bytes of
data for every interrupt using a total of 42 instruction cycles (23 for
the context save and 19 for the restore). This may not seem like much,
but in a interrupt intensive application, the CPU could spend thousands
of extra cycles "awake," unnecessarily consuming power.
Newer compilers are now available with omniscient code generation (OCG)
technology that has the intelligence to save only those registers that
are required for each particular interrupt. OCG works by collecting
comprehensive data on register, stack, pointer, object and variable
declarations from all program modules before compiling the code.
An OCG compiler combines all the program modules into one large
program which it loads into a call graph structure such as this:

Based on the call graph, the OCG code generator creates a pointer
reference graph (Figure 1 below)
that shows each instance of a variable having its address taken, plus
each instance of an assignment of a pointer value to a pointer (either
directly, via function return, function parameter passing, or
indirectly via another pointer).
It then identifies all objects that can possibly be referenced by
each pointer. This information is used to determine exactly how much
memory space each pointer will be required to access.
 |
| Figure
1: The pointer reference graph shows each instance of a variable
having its address taken, plus each instance of an assignment of a
pointer value to a pointer. |
Since an OCG compiler knows exactly which functions call, and are
called by, other functions, which variables and registers are required,
and which pointers are pointing to which memory banks, it also knows
exactly which registers will be used for every interrupt in the
program. It can generate code accordingly, minimizing both the code
size and the cycles required to save and restore the context.