Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sleep: add sleep manager API (feature-hal-spec branch) #4882

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
102 changes: 102 additions & 0 deletions hal/mbed_sleep_manager.c
@@ -0,0 +1,102 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "mbed_sleep.h"
#include "mbed_critical.h"
#include "mbed_error.h"
#include "sleep_api.h"
#include <limits.h>

#if DEVICE_SLEEP

// deep sleep locking counter. A target is allowed to deep sleep if counter == 0
static uint16_t deep_sleep_lock = 0U;

void sleep_manager_lock_deep_sleep(void)
{
core_util_critical_section_enter();
if (deep_sleep_lock == USHRT_MAX) {
core_util_critical_section_exit();
error("Deep sleep lock would overflow (> USHRT_MAX)");
}
core_util_atomic_incr_u16(&deep_sleep_lock, 1);
core_util_critical_section_exit();
}

void sleep_manager_unlock_deep_sleep(void)
{
core_util_critical_section_enter();
if (deep_sleep_lock == 0) {
core_util_critical_section_exit();
error("Deep sleep lock would underflow (< 0)");
}
core_util_atomic_decr_u16(&deep_sleep_lock, 1);
core_util_critical_section_exit();
}

bool sleep_manager_can_deep_sleep(void)
{
core_util_critical_section_enter();

bool can_deep_sleep;
if (deep_sleep_lock == 0) {
can_deep_sleep = true;
} else {
can_deep_sleep = false;
}

core_util_critical_section_exit();
return can_deep_sleep;
}

void sleep_manager_sleep_auto(void)
{
core_util_critical_section_enter();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure it's going to work as intended? If we go to a shallow sleep and the interrupts are blocked we won't be wakening up.

Copy link
Contributor Author

@0xc0170 0xc0170 Aug 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will that is a feature of cortex.

// debug profile should keep debuggers attached, no deep sleep allowed
#ifdef MBED_DEBUG
hal_sleep();
#else
if (sleep_manager_can_deep_sleep()) {
hal_deepsleep();
} else {
hal_sleep();
}
#endif
core_util_critical_section_exit();
}

#else

// locking is valid only if DEVICE_SLEEP is defined
// we provide empty implementation

void sleep_manager_lock_deep_sleep(void)
{

}

void sleep_manager_unlock_deep_sleep(void)
{

}

bool sleep_manager_can_deep_sleep(void)
{
// no sleep implemented
return false;
}

#endif
20 changes: 7 additions & 13 deletions hal/sleep_api.h
Expand Up @@ -29,32 +29,26 @@ extern "C" {

/** Send the microcontroller to sleep
*
* The processor is setup ready for sleep, and sent to sleep using __WFI(). In this mode, the
* The processor is setup ready for sleep, and sent to sleep. In this mode, the
* system clock to the core is stopped until a reset or an interrupt occurs. This eliminates
* dynamic power used by the processor, memory systems and buses. The processor, peripheral and
* memory state are maintained, and the peripherals continue to work and can generate interrupts.
*
* The processor can be woken up by any internal peripheral interrupt or external pin interrupt.
*
* @note
* The mbed interface semihosting is disconnected as part of going to sleep, and can not be restored.
* Flash re-programming and the USB serial port will remain active, but the mbed program will no longer be
* able to access the LocalFileSystem
* The wake-up time shall be less than 10 us.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does 10us limit comes from ? Is is a realistic value for exiting WFI whatever the clock speed ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LMESTM Do you have any use case that would not fit ? From the analysis , the latency introduced by sleep is around 2us (on top of that would be: going to sleep + interrupt latency)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@0xc0170 - I don't have a requirement. I'm thinking that people interested in saving power may lower the core and bus frequencies, which will have a direct impact on this timing (2us will easilly become 10us or more). Anyway I feel that 10us is more an order of range rather than a strict requirement - the actual limit actually depends on low level HW interfaces and devices we communicate with, so I don't have an absolute value for this. If you'd want to be more generic, then the drivers would pass the wake-up time requirement to the sleep manager as a parameter, then the more strict requirement of all drivers would be passed to MCU to decide how deep it can sleep ... But I'm fine with the generic approach and the "10us order of range" ....

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For shallow sleep I'd like to keep a strict limit, I'm more open about the deep sleep, but usually if something is not defined (or explicitly mention to be not defined) it tend to go all over the place. @c1728p9 @0xc0170 do we actually care about the deep sleep wake up time?

Copy link
Contributor Author

@0xc0170 0xc0170 Aug 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking that people interested in saving power may lower the core and bus frequencies

@LMESTM I would expect this feature for the deepsleep, the timeperiods are longer, more power savings could be introduced. Considering that sleep is default one, and a target goes to this mode very often - when there is nothing to do - idle loop (latency introduced by lowering clocks might not be worth).

the drivers would pass the wake-up time requirement to the sleep manager as a parameter,

I had a prototype with sleep(timestamp_t sleep_time) . The argument sleep_time was a hint for a target to know for how long it would sleep and based on that would choose the sleep mode. We simplified it to as it is now, having it without any hint (no changes in API). A target just needs to wake-up within certain time that it is defined.
Having the upper limit for deepsleep defined is for an application and tests - this is the upper limit of deepsleep latency that an app developer should be aware of and account with.

This is the first proposal, open for feedback.

I am also more open about the defined times for deep sleep. From various manuals we looked at, the most common wake-up time from deep sleep to sleep with external crystal is around 3ms (1-2ms for clocks to reconfigure, it varies quite a lot - some has it just hunderds of microseconds, some up 1/2 ms).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok for me !

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One note - idle loop can be reconfigured via attaching own. We are providing default implementation for tickless. Thus a user still can do further power savings (reconfiguring clocks for instance - this is currently not in the scope), same for having shut off mode (another mode that some users might want to further reduce power).

*/
void hal_sleep(void);

/** Send the microcontroller to deep sleep
*
* This processor is setup ready for deep sleep, and sent to sleep using __WFI(). This mode
* has the same sleep features as sleep plus it powers down peripherals and clocks. All state
* is still maintained.
* This processor is setup ready for deep sleep, and sent to sleep. This mode
* has the same sleep features as sleep plus it powers down peripherals and high speed clocks.
* All state is still maintained.
*
* The processor can only be woken up by an external interrupt on a pin or a watchdog timer.
* The processor can only be woken up by lp ticker, RTC, an external interrupt on a pin or a watchdog timer.
*
* @note
* The mbed interface semihosting is disconnected as part of going to sleep, and can not be restored.
* Flash re-programming and the USB serial port will remain active, but the mbed program will no longer be
* able to access the LocalFileSystem
* The wake-up time shall be less than 10 ms.
*/
void hal_deepsleep(void);

Expand Down
104 changes: 87 additions & 17 deletions platform/mbed_sleep.h
Expand Up @@ -20,37 +20,110 @@
#define MBED_SLEEP_H

#include "sleep_api.h"
#include "mbed_toolchain.h"
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get some general description of the model and intended use here.

/** Sleep manager API
* The sleep manager provides API to automatically select sleep mode.
*
* There are two sleep modes:
* - sleep
* - deepsleep
*
* Use locking/unlocking deepsleep for drivers that depend on features that
* are not allowed (=disabled) during the deepsleep. For instance, high frequency
* clocks.
*
* Example:
* @code
*
* void driver::handler()
* {
* if (_sensor.get_event()) {
* // any event - we are finished, unlock the deepsleep
* sleep_manager_unlock_deep_sleep();
* _callback();
* }
* }
*
* int driver::measure(event_t event, callback_t& callback)
* {
* _callback = callback;
* sleep_manager_lock_deep_sleep();
* // start async transaction, we are waiting for an event
* return _sensor.start(event, callback);
* }
* @endcode
*/

/** Lock the deep sleep mode
*
* This locks the automatic deep mode selection.
* sleep_manager_sleep_auto() will ignore deepsleep mode if
* this function is invoked at least once (the internal counter is non-zero)
*
* Use this locking mechanism for interrupt driven API that are
* running in the background and deepsleep could affect their functionality
*
* The lock is a counter, can be locked up to USHRT_MAX
* This function is IRQ and thread safe
*/
void sleep_manager_lock_deep_sleep(void);

/** Unlock the deep sleep mode
*
* Use unlocking in pair with sleep_manager_lock_deep_sleep().
*
* The lock is a counter, should be equally unlocked as locked
* This function is IRQ and thread safe
*/
void sleep_manager_unlock_deep_sleep(void);

/** Get the status of deep sleep allowance for a target
*
* This function is IRQ and thread safe
*
* @return true if a target can go to deepsleep, false otherwise
*/
bool sleep_manager_can_deep_sleep(void);

/** Enter auto selected sleep mode. It chooses the sleep or deeepsleep modes based
* on the deepsleep locking counter
*
* This function is IRQ and thread safe
*
* @note
* If MBED_DEBUG is defined, only hal_sleep is allowed. This ensures the debugger
* to be active for debug modes.
*
*/
void sleep_manager_sleep_auto(void);

/** Send the microcontroller to sleep
*
* @note This function can be a noop if not implemented by the platform.
* @note This function will be a noop in debug mode (debug build profile when MBED_DEBUG is defined).
* @note This function will be a noop while uVisor is in use.
*
* The processor is setup ready for sleep, and sent to sleep using __WFI(). In this mode, the
* The processor is setup ready for sleep, and sent to sleep. In this mode, the
* system clock to the core is stopped until a reset or an interrupt occurs. This eliminates
* dynamic power used by the processor, memory systems and buses. The processor, peripheral and
* memory state are maintained, and the peripherals continue to work and can generate interrupts.
*
* The processor can be woken up by any internal peripheral interrupt or external pin interrupt.
*
* @note
* The mbed interface semihosting is disconnected as part of going to sleep, and can not be restored.
* Flash re-programming and the USB serial port will remain active, but the mbed program will no longer be
* able to access the LocalFileSystem
* The wake-up time shall be less than 10 us.
*/
__INLINE static void sleep(void)
{
#if !(defined(FEATURE_UVISOR) && defined(TARGET_UVISOR_SUPPORTED))
#ifndef MBED_DEBUG
#if DEVICE_SLEEP
hal_sleep();
sleep_manager_sleep_auto();
#endif /* DEVICE_SLEEP */
#endif /* MBED_DEBUG */
#endif /* !(defined(FEATURE_UVISOR) && defined(TARGET_UVISOR_SUPPORTED)) */
}

Expand All @@ -60,25 +133,22 @@ __INLINE static void sleep(void)
* @note This function will be a noop in debug mode (debug build profile when MBED_DEBUG is defined)
* @note This function will be a noop while uVisor is in use.
*
* This processor is setup ready for deep sleep, and sent to sleep using __WFI(). This mode
* This processor is setup ready for deep sleep, and sent to sleep. This mode
* has the same sleep features as sleep plus it powers down peripherals and clocks. All state
* is still maintained.
*
* The processor can only be woken up by an external interrupt on a pin or a watchdog timer.
* The processor can only be woken up by RTC, an external interrupt on a pin or a watchdog timer.
*
* @note
* The mbed interface semihosting is disconnected as part of going to sleep, and can not be restored.
* Flash re-programming and the USB serial port will remain active, but the mbed program will no longer be
* able to access the LocalFileSystem
* The wake-up time shall be less than 10 ms.
*/

MBED_DEPRECATED_SINCE("mbed-os-5.6", "One entry point for an application, use sleep()")
__INLINE static void deepsleep(void)
{
#if !(defined(FEATURE_UVISOR) && defined(TARGET_UVISOR_SUPPORTED))
#ifndef MBED_DEBUG
#if DEVICE_SLEEP
hal_deepsleep();
sleep_manager_sleep_auto();
#endif /* DEVICE_SLEEP */
#endif /* MBED_DEBUG */
#endif /* !(defined(FEATURE_UVISOR) && defined(TARGET_UVISOR_SUPPORTED)) */
}

Expand Down