Saving power in embedded systems – Reducing idle CPU speed

Dynamic power, meaning power consumption that is proportional to a clock speed, is a significant part of the power usage of a computer system. Reducing CPU load is one way to reduce this. More interestingly, reducing CPU clock speed in idle mode is another way. And there is hardly any downside!

Dynamic CPU consumption

In most computing systems, the CPU is a major consumer of energy. The dynamic power that the CPU consumes basically depends on the clock frequency and the amount of time in idle mode. This is because modern CPUs have one or more instructions that they can use when idle.
In the ARM world, these instructions are WFI (Wait for interrupt) or WFE (Wait for Event). They basically gate the clock to a section in the CPU, eliminating the dynamic power consumption of that part of the CPU while in idle.

Typical code looks like:

OS_Idle(void) {
  for (;;) {    //  Endless loop. Only way out is via interrupt and RTOS scheduler.
    __wfi();    //  Wait for interrupt. Execution continues after the interrupt.
  }
}

Writing efficient programs

It always makes sense to reduce the amount of time the CPU is executing code in order to maximize the amount of time spent in idle.
This can be done using efficient programming (smart algorithms), a good compiler and a good runtime library. For a look at what makes a great runtime library different from a run-of-the-mill runtime library, see the following blog article:
Floating-point face-off

Another important point in embedded systems is to not wait for hardware (as in the example below):

//
// Wait until data is ready
//
while (NAND_FLASH_BUSY);    // Keep reading the busy signal until data is ready

Either let Ready trigger an interrupt or use some sort of periodic checking or a Delay (such as OS_TASK_Delay_us(30)) in the loop. SEGGER’s embOS-Ultra lets you set delays in microseconds, perfect for the short delay times typically required by hardware.

In most cases, the interrupt is the best way, but it requires hardware capable of generating such an interrupt, is less generic, and it requires more work from the programmer.

Dynamic CPU consumption in idle

Surprisingly, there is still CPU dynamic power consumption in idle mode!

This is because certain components (such as the interrupt controller) continue to be clocked. So even when the CPU is in idle mode, its dynamic power consumption is still proportional to the clock speed. This means that reducing clock speed in idle mode is a way to save power.

Power consumption in idle mode is lower than the consumption when executing a program, but is still fairly high. We found that in the Xilinx Zynq chip, used in our high end J-Links and Flashers, it is still about 50 % of what it is when executing a program.
At 600 MHz, the CPU seems to use about 45 mW of dynamic energy. This can be easily calculated by reducing the speed to 200 MHz and measuring the savings – about 30 mW.

This also means that if we modify OS_Idle() accordingly, we can save about 30mW!

OS_Idle(void) {
  for (;;) {                  //  Endless loop. Only way out is via interrupt and RTOS scheduler.
    HW_CPU_SetIdleSpeed();    //  Implemented in a hardware layer, usually only a single write to an sfr to change the clock divider
    __wfi();                  //  Wait for interrupt. Execution continues after the interrupt.
  }
}

One might now say: But my system is not normally idle!

I disagree. In a well-designed system, the CPU spends most of its time in idle. Just think about the computer you are sitting in front of. Most of the time it is waiting for the world, i.e. waiting for you. It is the same with most embedded systems. CPUs are so powerful, that they are only busy for short bursts of time – at least if the program is written well. (See above).

Downsides and pitfalls

One thing I have not yet mentioned is that obviously every interrupt needs to switch the CPU back to higher speed (or at least those that have to react quickly).

No big deal.

Your CPU will react somewhat more slowly to interrupts, but if you run your CPU at 1/16 of the original speed, that should not affect things too much.

And if it does, just don’t reduce it as much, maybe just to 1/2. You will still save 50 % of dynamic idle consumption, or about 15 mW in the above case, and you still won’t notice.

There is, however, one problem with this.

Unfortunately, ARM has coupled the system timer in its Cortex CPUs to the CPU frequency. So by reducing the CPU frequency, we are also reducing the frequency of the system timer. If the system timer is used for overall timing, this is a problem.

But like for most problems, there are solutions.

You can either dynamically reprogram the system timer when changing CPU speed, or simply use a timer that uses a different clock base, which is what we decided to do.

Not a new idea

This is not a new idea. SEGGER did the same thing 30 years ago on an 8-bit 78K0 CPU at the heart of an electronic torque wrench.
Reducing the clock speed in idle mode extended battery life by 50 %!
One would think that something like that would be implemented in hardware (different CPU clock divider when idle) but so far this does not seem to be the case.
Maybe an idea for CPU designers?

Other sources of power loss and what to do

There are other sources of power consumption (leakage) that could easily be the subject of a separate article. The short version is that for voltage converters, oscillators, LEDs, PLLs (Phase-locked loop), and FPGAs, use the most efficient ones you can and as few of them as possible.

Worth singling out is the use of external RAM, which it to be avoided whenever possible.
We use SEGGER’s emPower OS to significantly reduce code size and to keep systems down to an energy-saving single chip.

Fans also use a lot of energy. No SEGGER product uses a fan, yet many of our products use less power in total than the fan alone uses in some competing products.

Conclusion

There is a lot that can be done to reduce the power consumption of embedded systems.

Usually, designers of battery-powered systems are fairly good at this, while designers of grid or USB powered devices tend to not pay much attention to energy savings.
For us at SEGGER, even though most of our products are not battery powered, reducing power consumption is an ongoing effort. Even though trace and debug probes as well as flash programmers do not use very much power, usage does add up over the life of a product. If you can get the same done with less power, why not?

We have great products that use very little power but there is always more that can be done.

The idea explained here, reducing CPU speed in idle mode, has a surprisingly large effect!

I hope it will inspire others, whether using batteries or not, to minimize energy consumption in their embedded systems.