The definitive guide to ARM Cortex-M0/M0+: Ultralow-power designs - Embedded.com

The definitive guide to ARM Cortex-M0/M0+: Ultralow-power designs

Editor's Note: In designing deeply embedded systems, engineers face ongoing tradeoffs between power and performance. The rapid emergence of opportunities for personal electronics, wearables and Internet of Things (IoT) applications only exacerbates challenges in reducing power while enhancing performance. The ARM Cortex-M0 and Cortex-M0+ processors have emerged as a leading solution, providing the core for a broad range of microcontrollers designed to meet tough requirements for low-power, high-performance operation. In The Definitive Guide to ARM® Cortex®-M0 and Cortex-M0+ Processors, 2nd Edition, Joseph Yiu offers a comprehensive view of these processors. As Jack Gannsle wrote, these books will “…give you the insight you need to be productive on real projects.”

CHAPTER 19. Ultralow-Power Designs

19.1 Examples of Using Low-Power Features

19.1.1 Overview
More and more chip designers are using the ARM® Cortex®-M0 and Cortex-M0+ processors in wide range of ultralow-power (ULP) microcontrollers and System-on-Chip products. In Section 2.6.1 (Chapter 2) we have already covered the low-power benefits of the Cortex-M0 and Cortex-M0+ processors, and then in Chapter 9, we have also covered the low-power features of the Cortex-M0 and Cortex-M0+ processors. Here we will go into more details of how to utilize various features, and what we should be aware of when creating low-power applications.

Before we start going into the details, a key point that software developers need to understand is that low-power features are very device specific. What we illustrated in the examples here is not sufficient to enable the software developers to get the longest battery life. Software developers should refer to application notes or examples from microcontroller vendors to utilize the low-power features available.

19.1.2 Entering Sleep Modes
By default, the Cortex-M0 and Cortex-M0+ processors support a sleep mode and a deep sleep mode. However, please note that microcontroller vendors can define additional sleep modes using device-specific programmable registers. Inside the processor, the selection between sleep mode and deep sleep mode is defined by the SLEEPDEEP bit in the System Control Register (Table 9.9).

For the users of CMSIS-compliant device driver library, the System Control Register can be accessed by the register symbol “SCB->SCR.” For example, to enable deep sleep mode, you can use:

SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; /* Enable deep sleep feature */

The System Control Register must be accessed using a word size transfer.

The actual differences between normal sleep mode and deep sleep mode on a microcontroller depend on the chip's system level design. For example, normal sleep might result in some of the clock signals being switched off, while deep sleep might also reduce voltage supplies to the memory blocks and might switch off additional components in the system.

After selecting the sleep mode, you can enter sleep mode using either the WFE (Wait-for-Event) or WFI (Wait-for-Interrupt) instructions. It is recommended to add a DSB (Data Synchronization Barrier) instruction before executing WFI/WFE to allow better portability (e.g., in other high-performance processors, there could be outstanding memory transfers that need to be completed before entering sleep).

In most cases, the device driver libraries from microcontroller vendors contain functions to enter low-power modes that are customized for the corresponding microcontrollers. Using these functions will often help achieving the best level of power optimization for the microcontrollers.

However, if you are developing C code that needs to be portable between multiple Cortex-M microcontrollers, you can use the following CMSIS functions to access WFE and WFI instructions directly (Table 19.1).

Table 19.1: CMSIS intrinsic functions for WFE and WFI instructions

For users that are not using CMSIS-compliant device drivers, you can use intrinsic functions provided by the C compilers, or using in-line assembly to generate the WFE and WFI instructions. In these cases, the software code will be tool chain dependent and less portable. For example, Keil® MDK-ARM and ARM DS-5™ provides the following C intrinsic functions (unlike the CMSIS version, they are in lower cases) (Table 19.2).

Table 19.2: Keil® MDK or ARM® DS-5 intrinsic functions for WFI and WFE

From architecture point of view, a DSB instruction should be executed before executing WFE or WFI. This ensures that outstanding data memory operations (e.g., buffered write) are completed before entering sleep. However, on existing Cortex-M0 and Cortex-M0+ processor, omitting the DSB instruction does not cause any issue.

Since the WFE can be woken up by various sources of events, including event occurred in the past, it is usually used in an idle loop. For example:

while (processing_required()==0) {

    __DSB();// Use of memory barrier is recommended for portability

    __WFE();

    }

Users of assembly programming environments can use WFE and WFI directly in their assembly codes.

Continue to the next page >>

19.1.3 WFE versus WFI
One of the commonly asked questions about sleep modes on the Cortex-M processors is when to use WFI and when to use WFE. Typically, for interrupt-driven applications, the WFI instruction is used.

A simple interrupt-driven application

int main(void)

{

    peripheral_setup();

    while (1) {

    __DSB();// Use of memory barrier is recommended for portability

    __WFI();

    }

}

void Timer0_Handler(void)

{

    // do work

    …

}

However, if there are interactions between the interrupt handlers and the main program, the WFE instruction should be used.

A simple application with interaction between interrupt handler and the main program

volatile int timer_irq_occurred = 0;

int main(void)

{

    peripheral_setup();

    while (1) {

       while (timer_irq_occurred==0) {

          __DSB();// Use of memory barrier is recommended for portability

          __WFE();

          }

       printf (“[Timer IRQ]”);

       …

       timer_irq_occurred = 0;

    }

}

void Timer0_Handler(void)

{

    // do work

    …

    timer_irq_occurred = 1;

}

The reason for using WFE is to prevent a corner case that if the interrupt took place between the comparison of “timer_irq_occurred” and the sleep operation, the processor would go to sleep despite the timer interrupt has took place and the main program should continue. By using WFE, the processor's event register is set by the IRQ and therefore the WFE will not enter sleep, thus enable the “printf” statement to execute.

19.1.4 Using Sleep-On-Exit Feature
The Sleep-On-Exit feature is ideal for interrupt-driven applications. When it is enabled, the processor can enter sleep as soon as it completes an exception handler and returns to thread mode. It does not cause the processor to enter sleep if the exception handler is returning to another exception handler (nested interrupt). By using Sleep-On-Exit, the microcontroller can stay in sleep mode as much as possible (Figure 19.1).


Figure 19.1 Sleep-On-Exit operations.  

When the Cortex-M processor enters sleep using Sleep-On-Exit, it is just like executing WFI immediately after the exception exit. However, the unstacking process is not carried out because the registers will have to be pushed on to the stack at the next exception entry. The Sleep-On-Exit feature reduces the power consumption of the system by

  1. avoiding unnecessary program execution in thread in interrupt-driven applications and

  2. reducing unnecessary stack push and pop operations.

In the case when the processor is woken up by a halt debug request, then the unstacking process will be carried out automatically.

When the Sleep-On-Exit feature is used, the WFE or WFI instruction is normally placed in an idle loop.

SCB->SCR |= SCB_SCR_SLEEPONEXIT_Msk; // Enable Sleep-On-Exit feature

while (1) {

    __DSB(); // Use of memory barrier is recommended for portability

    __WFI(); // Execute WFI and enter sleep

    };

The loop is required because if the processor is woken up by a halt debug request, the instruction after the WFI (branch back to WFI loop) would be executed when the processor is unhalted after debugging.

If you are not using CMSIS-compliant device driver, you can use the following C code to enable the Sleep-On-Exit feature.

#define SCB_SCR (∗((volatile unsigned long ∗)(0xE000ED10)))

/∗ Set SLEEPONEXIT bit in System Control Register ∗/

SCB_SCR = SCB_SCR | 0x2;

Users of assembly language can enable this feature using the following assembly code.

LDR r0, =0xE000ED10 ; System Control Register address

LDR r1, [r0]

MOVS r2, #0x2

ORR r1, r2 ; Set SLEEPONEXIT bit

STR r1, [r0]

In interrupt-driven applications, do not enable Sleep-On-Exit feature too early during the initialization. Otherwise if the processor receives an interrupt request during the initialization process, it will enter sleep automatically after the interrupt handler executed, before the rest of the initialization process completes.

19.1.5 Using Send-Event-on-Pend Feature
The Send-Event-on-Pend feature allows any interrupt (including disabled ones) to wake up the processor if the processor entered sleep by executing the WFE instruction. When the SEVONPEND bit in the System Control Register is set, an interrupt switching from inactive state to pending state generates an event, which wakes up the processor from WFE sleep.

If the pending status of an interrupt was already set before entering sleep, a new request from this interrupt during WFE sleep will not wake up the processor.

For users of CMSIS-compliant device driver libraries, the Send-Event-on-Pend feature can be enabled by setting bit 4 in the System Control Register. For example, you can use:

SCB->SCR |= SCB_SCR_SEVONPEND_Msk; /∗ Enable Send-Event-on-Pend ∗/

If you are not using a CMSIS-compliant device driver library, you can use the following C code to carry out the same operation:

#define SCB_SCR (∗((volatile unsigned long ∗)(0xE000ED10)))

/∗ Set SEVONPEND bit in System Control Register ∗/

SCB_SCR |= 1<<4;

Users of assembly language can enable this feature using the following assembly code.

LDR r0, =0xE000ED10 ; System Control Register address

LDR r1, [r0]

MOVS r2, #0x10 ; Set SEVONPEND bit

ORR r1, r2

STR r1, [r0]

To utilize the Send-Event-on-Pend feature, the program must execute WFE instruction rather than using WFI or Sleep-On-Exit to enter sleep mode.

19.1.6 Using Wake-up Interrupt Controller
The Wake-up Interrupt Controller (WIC) feature allows the Cortex-M0/Cortex-M0+ processor to enter a sleep state with all clock signals stopped, or even powered down with state retention in the processor logic, while still be able to wake up and resume operations quickly. Details of this feature are covered in Section 9.5.6.

Since the interrupt masking information is transferred between NVIC and WIC automatically using a hardware interface, there is no additional programming step for interrupt management. However, the enabling of some of the ULP states might involve additional device-specific programming steps. For example:

  • A device-specific system level power management unit might need to be programmed to enable the WIC functionality and other sleep mode options.

  • Depending on the device you are using, you might need to switch on deep sleep mode to use the WIC feature. (Note: in Cortex-M3 r2p0 and r2p1, and Cortex-M4 r0p1, it is necessary to enable deep sleep mode to use the WIC feature. Whereas in Cortex-M0 and Cortex-M0+ processors, both sleep and deep sleep modes can use the WIC feature.)

Apart from these, the presence of the WIC feature is usually transparent to the software.

Since all the clock signals connected to the processor could be stopped in WIC-enabled sleep, the SysTick timer (which is inside the processor) could also be stopped. As a result, it could be necessary to set up a separate peripheral timer to wake up the processor periodically if your application requires an embedded OS and need the OS to operate continuously. In addition, when developing simple applications that need a periodic timer interrupt, and if WIC-mode deep sleep is required, it might be necessary to use a peripheral timer for periodic interrupt generation instead of the SysTick timer even embedded OS is not used.

Not all Cortex-M processor-based microcontrollers support the WIC feature. The reduction of power using the WIC depends on the application and the semiconductor process being used. Currently, the State Retention Power Gating (see Section 9.5.6) technology is only supported in a limited number of silicon technology processes (cell libraries), therefore some chip designs might use the WIC but without using the state retention power down state.

19.1.7 Using Event Communication Interface
One of the wake-up sources for the WFE sleep operation is an external event signal. (Here the word “external” refers external to the processor boundary. The source generating the event can be on chip or off chip.) The event signal could be generated by on-chip peripherals, or another processor on the same chip. The event communication and WFE can be used together to reduce power in polling loops.

On the Cortex-M processors, there are two signals for event communication:

  • TXEV: Transmit Event. A pulse is generated when the SEV instruction is executed.

  • RXEV: Receive Event. When a pulse is received on this signal, the event latch inside the processor would be set and can cause the processor to wake up from WFE sleep operation.

First, we look at a simple use of the event connection in a single-processor system: the event can be generated by a number of peripherals. For this example, a DMA controller is illustrated here (Figure 19.2).


Figure 19.2 Usage of event interface: example 1 – DMA controller.  

In a microcontroller system, a memory block copying process can be accelerated using a DMA controller. If a polling loop is used to determine the DMA status, this will waste energy and consume memory bandwidth and might end up slowing down the DMA operation. To save energy, WFE is used to put the processor into sleep state. When the DMA operation completes, we can then use a “Done” status signal (DMA completed) to wake up the processor and continue program execution.

In the application code, instead of using a simple polling loop that continuously monitor the status of the DMA controller, the polling loop can include WFE instruction as follows:

Enable_DMA_event_mask(); // Write to programmable enable mask register

                         // to enable DMA event

Start_DMA(); // Start DMA operation

do {

    __DSB(); // Use of memory barrier is recommended for portability

    __WFE(); // WFE Sleep operation, wake up when an event is received

} while (check_DMA_completed()==0);

Disable_DMA_event_mask(); // Write to programmable enable mask register

                          // to disable DMA event

Since the processor could be woken up by other events, the polling loop must still check the DMA controller status.

For applications using an embedded OS, an OS-specific delay function should be used instead of using WFE to allow the processor to switch to another task that is waiting to be executed. Using of embedded OS is covered in Chapter 20.

In multiprocessor systems, interprocessor communication such as spin lock often involves polling software flags in shared memory. Similar to the DMA controller example, the WFE sleep operation can be used to reduce power consumption during these activities. In a dual processor system, the event communication interface can be connected in a cross over configuration as shown in Figure 19.3.


Figure 19.3 Usage of event interface: example 2 – dual processor event cross over connection.

In this arrangement, the polling loop for a shared software flag could be written as:

do {

    __DSB(); // Use of memory barrier is recommended for portability

    __WFE(); // WFE Sleep operation, wake up when an event is received

} while (sw_flag_x==0); // poll software flag

task_X(); // execute task X when software flag for task X is received

On the other process that changes “sw_flag_x,” it needs to generate an event after the shared variable is updated. This can be done by executing the SEV (Send event) instruction.

sw_flag_x = 1; // Set software flag in shared memory

__DSB(); // Data synchronization barrier to ensure the write is completed

         // not essential for Cortex-M0/M0+ but is added for software porting

__SEV(); // execute SEV instruction

Using this arrangement, the processor running the polling loop can stay in sleep mode until it receives an event. Since the SEV execution sets the internal event latch, this method works even if the polling process and the process that sets the software variable are running at different times on the same processor, as in a single processor multitasking system.

For applications using an embedded OS, OS-specific event-passing mechanism should be used instead of directly using WFE and SEV.

Stay tuned for the next installment: Requirements for Low-Power Design

About the author
Joseph Yiu is a Senior Embedded Technology Specialist at ARM Ltd. in Cambridge, UK. He joined ARM in 2001 and has been involved in a wide range of projects including development of ARM Cortex-M processors and various on-chip system level and debug components. In addition to in-depth knowledge of the processors and microcontroller system design, Yiu also has extensive knowledge in related areas including software development for the ARM Cortex-M microcontrollers, FPGA development and System-on-Chip design technologies. He received his BEng. in Electronics Engineering from City University of Hong Kong and an MSc. In Microelectronics Systems Design from University of Southampton.

Copyright © 2016 Elsevier, Inc. All rights reserved.
Printed with permission from Newnes, a division of Elsevier. Copyright 2016. For more information on this title and other similar books, please visit www.newnespress.com.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.