mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #7582 from fkjagodzinski/test_update-sleep_manager
Update sleep manager testspull/8364/merge
commit
9deefe5de1
|
@ -25,26 +25,9 @@
|
|||
#include "greentea-client/test_env.h"
|
||||
#include "mbed_lp_ticker_wrapper.h"
|
||||
|
||||
#include "sleep_test_utils.h"
|
||||
#include "sleep_api_tests.h"
|
||||
|
||||
#define US_PER_S 1000000
|
||||
|
||||
/* Flush serial buffer before deep sleep
|
||||
*
|
||||
* Since deepsleep() may shut down the UART peripheral, we wait for some time
|
||||
* to allow for hardware serial buffers to completely flush.
|
||||
*
|
||||
* Take NUMAKER_PFM_NUC472 as an example:
|
||||
* Its UART peripheral has 16-byte Tx FIFO. With baud rate set to 9600, flush
|
||||
* Tx FIFO would take: 16 * 8 * 1000 / 9600 = 13.3 (ms). So set wait time to
|
||||
* 20ms here for safe.
|
||||
*
|
||||
* This should be replaced with a better function that checks if the
|
||||
* hardware buffers are empty. However, such an API does not exist now,
|
||||
* so we'll use the busy_wait_ms() function for now.
|
||||
*/
|
||||
#define SERIAL_FLUSH_TIME_MS 20
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
static char info[512] = {0};
|
||||
|
@ -67,71 +50,6 @@ static const uint32_t sleep_mode_delta_us = (10 + 4 + 5);
|
|||
* delta = default 10 ms + worst ticker resolution + extra time for code execution */
|
||||
static const uint32_t deepsleep_mode_delta_us = (10000 + 125 + 5);
|
||||
|
||||
unsigned int ticks_to_us(unsigned int ticks, unsigned int freq)
|
||||
{
|
||||
return (unsigned int)((unsigned long long) ticks * US_PER_S / freq);
|
||||
}
|
||||
|
||||
unsigned int us_to_ticks(unsigned int us, unsigned int freq)
|
||||
{
|
||||
return (unsigned int)((unsigned long long) us * freq / US_PER_S);
|
||||
}
|
||||
|
||||
unsigned int overflow_protect(unsigned int timestamp, unsigned int ticker_width)
|
||||
{
|
||||
unsigned int counter_mask = ((1 << ticker_width) - 1);
|
||||
|
||||
return (timestamp & counter_mask);
|
||||
}
|
||||
|
||||
bool compare_timestamps(unsigned int delta_ticks, unsigned int ticker_width, unsigned int expected, unsigned int actual)
|
||||
{
|
||||
const unsigned int counter_mask = ((1 << ticker_width) - 1);
|
||||
|
||||
const unsigned int lower_bound = ((expected - delta_ticks) & counter_mask);
|
||||
const unsigned int upper_bound = ((expected + delta_ticks) & counter_mask);
|
||||
|
||||
if (lower_bound < upper_bound) {
|
||||
if (actual >= lower_bound && actual <= upper_bound) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ((actual >= lower_bound && actual <= counter_mask) || (actual >= 0 && actual <= upper_bound)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void busy_wait_ms(int ms)
|
||||
{
|
||||
const ticker_info_t *info = us_ticker_get_info();
|
||||
uint32_t mask = (1 << info->bits) - 1;
|
||||
int delay = (int)((uint64_t)ms * info->frequency / 1000);
|
||||
|
||||
uint32_t prev = us_ticker_read();
|
||||
while (delay > 0) {
|
||||
uint32_t next = us_ticker_read();
|
||||
delay -= (next - prev) & mask;
|
||||
prev = next;
|
||||
}
|
||||
}
|
||||
|
||||
void us_ticker_isr(const ticker_data_t *const ticker_data)
|
||||
{
|
||||
us_ticker_clear_interrupt();
|
||||
}
|
||||
|
||||
#ifdef DEVICE_LPTICKER
|
||||
void lp_ticker_isr(const ticker_data_t *const ticker_data)
|
||||
{
|
||||
lp_ticker_clear_interrupt();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Test that wake-up time from sleep should be less than 10 us and
|
||||
* high frequency ticker interrupt can wake-up target from sleep. */
|
||||
void sleep_usticker_test()
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hal_sleep_test_utils
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef MBED_SLEEP_TEST_UTILS_H
|
||||
#define MBED_SLEEP_TEST_UTILS_H
|
||||
|
||||
#include "hal/ticker_api.h"
|
||||
#include "hal/us_ticker_api.h"
|
||||
#include "hal/lp_ticker_api.h"
|
||||
|
||||
/* Flush serial buffer before deep sleep
|
||||
*
|
||||
* Since deepsleep() may shut down the UART peripheral, we wait for some time
|
||||
* to allow for hardware serial buffers to completely flush.
|
||||
*
|
||||
* Take NUMAKER_PFM_NUC472 as an example:
|
||||
* Its UART peripheral has 16-byte Tx FIFO. With baud rate set to 9600, flush
|
||||
* Tx FIFO would take: 16 * 8 * 1000 / 9600 = 13.3 (ms). So set wait time to
|
||||
* 20ms here for safe.
|
||||
*
|
||||
* This should be replaced with a better function that checks if the
|
||||
* hardware buffers are empty. However, such an API does not exist now,
|
||||
* so we'll use the busy_wait_ms() function for now.
|
||||
*/
|
||||
#define SERIAL_FLUSH_TIME_MS 20
|
||||
|
||||
#define US_PER_S 1000000
|
||||
|
||||
unsigned int ticks_to_us(unsigned int ticks, unsigned int freq)
|
||||
{
|
||||
return (unsigned int) ((unsigned long long) ticks * US_PER_S / freq);
|
||||
}
|
||||
|
||||
unsigned int us_to_ticks(unsigned int us, unsigned int freq)
|
||||
{
|
||||
return (unsigned int) ((unsigned long long) us * freq / US_PER_S);
|
||||
}
|
||||
|
||||
unsigned int overflow_protect(unsigned int timestamp, unsigned int ticker_width)
|
||||
{
|
||||
unsigned int counter_mask = ((1 << ticker_width) - 1);
|
||||
|
||||
return (timestamp & counter_mask);
|
||||
}
|
||||
|
||||
bool compare_timestamps(unsigned int delta_ticks, unsigned int ticker_width, unsigned int expected, unsigned int actual)
|
||||
{
|
||||
const unsigned int counter_mask = ((1 << ticker_width) - 1);
|
||||
|
||||
const unsigned int lower_bound = ((expected - delta_ticks) & counter_mask);
|
||||
const unsigned int upper_bound = ((expected + delta_ticks) & counter_mask);
|
||||
|
||||
if (lower_bound < upper_bound) {
|
||||
if (actual >= lower_bound && actual <= upper_bound) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ((actual >= lower_bound && actual <= counter_mask) || (actual >= 0 && actual <= upper_bound)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void busy_wait_ms(int ms)
|
||||
{
|
||||
const ticker_info_t *info = us_ticker_get_info();
|
||||
uint32_t mask = (1 << info->bits) - 1;
|
||||
int delay = (int) ((uint64_t) ms * info->frequency / 1000);
|
||||
|
||||
uint32_t prev = us_ticker_read();
|
||||
while (delay > 0) {
|
||||
uint32_t next = us_ticker_read();
|
||||
delay -= (next - prev) & mask;
|
||||
prev = next;
|
||||
}
|
||||
}
|
||||
|
||||
void us_ticker_isr(const ticker_data_t * const ticker_data)
|
||||
{
|
||||
us_ticker_clear_interrupt();
|
||||
}
|
||||
|
||||
#ifdef DEVICE_LPTICKER
|
||||
void lp_ticker_isr(const ticker_data_t * const ticker_data)
|
||||
{
|
||||
lp_ticker_clear_interrupt();
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/** @}*/
|
|
@ -16,46 +16,286 @@
|
|||
#include "utest/utest.h"
|
||||
#include "unity/unity.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include <limits.h>
|
||||
#include "mbed.h"
|
||||
#include "mbed_lp_ticker_wrapper.h"
|
||||
#include "../sleep/sleep_test_utils.h"
|
||||
#include "sleep_manager_api_tests.h"
|
||||
|
||||
#if !DEVICE_SLEEP
|
||||
#error [NOT_SUPPORTED] test not supported
|
||||
#endif
|
||||
|
||||
using namespace utest::v1;
|
||||
#define SLEEP_DURATION_US 20000ULL
|
||||
#define DEEP_SLEEP_TEST_CHECK_WAIT_US 2000
|
||||
#define DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US 500
|
||||
|
||||
void sleep_manager_deepsleep_counter_test()
|
||||
using utest::v1::Case;
|
||||
using utest::v1::Specification;
|
||||
using utest::v1::Harness;
|
||||
|
||||
static uint32_t num_test_errors = 0UL;
|
||||
|
||||
mbed_error_status_t mbed_error(mbed_error_status_t error_status, const char *error_msg, unsigned int error_value,
|
||||
const char *filename, int line_number)
|
||||
{
|
||||
bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
||||
TEST_ASSERT_TRUE(deep_sleep_allowed);
|
||||
(void) error_status;
|
||||
(void) error_msg;
|
||||
(void) error_value;
|
||||
(void) filename;
|
||||
(void) line_number;
|
||||
|
||||
num_test_errors++;
|
||||
return MBED_SUCCESS;
|
||||
}
|
||||
|
||||
void test_lock_unlock()
|
||||
{
|
||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
||||
|
||||
sleep_manager_lock_deep_sleep();
|
||||
deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
||||
TEST_ASSERT_FALSE(deep_sleep_allowed);
|
||||
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
|
||||
|
||||
sleep_manager_unlock_deep_sleep();
|
||||
deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
||||
TEST_ASSERT_TRUE(deep_sleep_allowed);
|
||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
||||
}
|
||||
|
||||
utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason)
|
||||
void test_lone_unlock()
|
||||
{
|
||||
greentea_case_failure_abort_handler(source, reason);
|
||||
return STATUS_CONTINUE;
|
||||
uint32_t expected_err_count = num_test_errors + 1;
|
||||
sleep_manager_unlock_deep_sleep();
|
||||
TEST_ASSERT_EQUAL_UINT32(expected_err_count, num_test_errors);
|
||||
|
||||
// Make sure upcoming tests won't be broken.
|
||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
||||
}
|
||||
|
||||
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||
void test_lock_eq_ushrt_max()
|
||||
{
|
||||
GREENTEA_SETUP(20, "default_auto");
|
||||
return greentea_test_setup_handler(number_of_cases);
|
||||
uint32_t lock_count = 0;
|
||||
while (lock_count < USHRT_MAX) {
|
||||
sleep_manager_lock_deep_sleep();
|
||||
lock_count++;
|
||||
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
|
||||
}
|
||||
while (lock_count > 1) {
|
||||
sleep_manager_unlock_deep_sleep();
|
||||
lock_count--;
|
||||
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
|
||||
}
|
||||
sleep_manager_unlock_deep_sleep();
|
||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
||||
}
|
||||
|
||||
void test_lock_gt_ushrt_max()
|
||||
{
|
||||
uint32_t lock_count = 0;
|
||||
while (lock_count < USHRT_MAX) {
|
||||
sleep_manager_lock_deep_sleep();
|
||||
lock_count++;
|
||||
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
|
||||
}
|
||||
|
||||
uint32_t expected_err_count = num_test_errors + 1;
|
||||
sleep_manager_lock_deep_sleep();
|
||||
TEST_ASSERT_EQUAL_UINT32(expected_err_count, num_test_errors);
|
||||
|
||||
// Make sure upcoming tests won't be broken.
|
||||
while (lock_count > 0) {
|
||||
sleep_manager_unlock_deep_sleep();
|
||||
lock_count--;
|
||||
}
|
||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
||||
}
|
||||
|
||||
#if DEVICE_LPTICKER
|
||||
#if DEVICE_USTICKER
|
||||
utest::v1::status_t testcase_setup(const Case * const source, const size_t index_of_case)
|
||||
{
|
||||
// Suspend the RTOS kernel scheduler to prevent interference with duration of sleep.
|
||||
osKernelSuspend();
|
||||
#if DEVICE_LPTICKER
|
||||
ticker_suspend(get_lp_ticker_data());
|
||||
#if (LPTICKER_DELAY_TICKS > 0)
|
||||
// Suspend the low power ticker wrapper to prevent interference with deep sleep lock.
|
||||
lp_ticker_wrapper_suspend();
|
||||
#endif
|
||||
#endif
|
||||
ticker_suspend(get_us_ticker_data());
|
||||
// Make sure HAL tickers are initialized.
|
||||
us_ticker_init();
|
||||
#if DEVICE_LPTICKER
|
||||
lp_ticker_init();
|
||||
#endif
|
||||
return utest::v1::greentea_case_setup_handler(source, index_of_case);
|
||||
}
|
||||
|
||||
utest::v1::status_t testcase_teardown(const Case * const source, const size_t passed, const size_t failed,
|
||||
const utest::v1::failure_t failure)
|
||||
{
|
||||
ticker_resume(get_us_ticker_data());
|
||||
#if DEVICE_LPTICKER
|
||||
#if (LPTICKER_DELAY_TICKS > 0)
|
||||
lp_ticker_wrapper_resume();
|
||||
#endif
|
||||
ticker_resume(get_lp_ticker_data());
|
||||
#endif
|
||||
osKernelResume(0);
|
||||
return utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
|
||||
}
|
||||
|
||||
/* This test is based on the fact that the high-speed clocks are turned off
|
||||
* in deep sleep mode but remain on in the ordinary sleep mode. Low-speed
|
||||
* clocks stay on for both sleep and deep sleep modes.
|
||||
*
|
||||
* The type of sleep that was actually used by sleep_manager_sleep_auto()
|
||||
* can be detected by comparing times measured by us and lp tickers.
|
||||
*/
|
||||
void test_sleep_auto()
|
||||
{
|
||||
const ticker_info_t *us_ticker_info = get_us_ticker_data()->interface->get_info();
|
||||
const unsigned us_ticker_mask = ((1 << us_ticker_info->bits) - 1);
|
||||
const ticker_irq_handler_type us_ticker_irq_handler_org = set_us_ticker_irq_handler(us_ticker_isr);
|
||||
const ticker_info_t *lp_ticker_info = get_lp_ticker_data()->interface->get_info();
|
||||
const unsigned lp_ticker_mask = ((1 << lp_ticker_info->bits) - 1);
|
||||
const ticker_irq_handler_type lp_ticker_irq_handler_org = set_lp_ticker_irq_handler(lp_ticker_isr);
|
||||
us_timestamp_t us_ts1, us_ts2, lp_ts1, lp_ts2, us_diff1, us_diff2, lp_diff1, lp_diff2;
|
||||
|
||||
sleep_manager_lock_deep_sleep();
|
||||
uint32_t lp_wakeup_ts_raw = lp_ticker_read() + us_to_ticks(SLEEP_DURATION_US, lp_ticker_info->frequency);
|
||||
timestamp_t lp_wakeup_ts = overflow_protect(lp_wakeup_ts_raw, lp_ticker_info->bits);
|
||||
lp_ticker_set_interrupt(lp_wakeup_ts);
|
||||
us_ts1 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
|
||||
lp_ts1 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
|
||||
|
||||
sleep_manager_sleep_auto();
|
||||
us_ts2 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
|
||||
us_diff1 = (us_ts1 <= us_ts2) ? (us_ts2 - us_ts1) : (us_ticker_mask - us_ts1 + us_ts2 + 1);
|
||||
lp_ts2 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
|
||||
lp_diff1 = (lp_ts1 <= lp_ts2) ? (lp_ts2 - lp_ts1) : (lp_ticker_mask - lp_ts1 + lp_ts2 + 1);
|
||||
|
||||
// Deep sleep locked -- ordinary sleep mode used:
|
||||
// * us_ticker powered ON,
|
||||
// * lp_ticker powered ON,
|
||||
// so both should increment equally.
|
||||
|
||||
// Verify us and lp tickers incremented equally, with 10% tolerance.
|
||||
TEST_ASSERT_UINT64_WITHIN_MESSAGE(
|
||||
SLEEP_DURATION_US / 10ULL, lp_diff1, us_diff1,
|
||||
"Deep sleep mode locked, but still used");
|
||||
|
||||
sleep_manager_unlock_deep_sleep();
|
||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
||||
|
||||
// Wait for hardware serial buffers to flush.
|
||||
busy_wait_ms(SERIAL_FLUSH_TIME_MS);
|
||||
|
||||
lp_wakeup_ts_raw = lp_ticker_read() + us_to_ticks(SLEEP_DURATION_US, lp_ticker_info->frequency);
|
||||
lp_wakeup_ts = overflow_protect(lp_wakeup_ts_raw, lp_ticker_info->bits);
|
||||
lp_ticker_set_interrupt(lp_wakeup_ts);
|
||||
us_ts1 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
|
||||
lp_ts1 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
|
||||
|
||||
sleep_manager_sleep_auto();
|
||||
us_ts2 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
|
||||
us_diff2 = (us_ts1 <= us_ts2) ? (us_ts2 - us_ts1) : (us_ticker_mask - us_ts1 + us_ts2 + 1);
|
||||
lp_ts2 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
|
||||
lp_diff2 = (lp_ts1 <= lp_ts2) ? (lp_ts2 - lp_ts1) : (lp_ticker_mask - lp_ts1 + lp_ts2 + 1);
|
||||
|
||||
// Deep sleep unlocked -- deep sleep mode used:
|
||||
// * us_ticker powered OFF,
|
||||
// * lp_ticker powered ON.
|
||||
// The us_ticker increment represents only the code execution time
|
||||
// and should be much shorter than both:
|
||||
// 1. current lp_ticker increment,
|
||||
// 2. previous us_ticker increment (locked sleep test above)
|
||||
|
||||
// Verify that the current us_ticker increment:
|
||||
// 1. is at most 10% of lp_ticker increment
|
||||
// 2. is at most 10% of previous us_ticker increment.
|
||||
TEST_ASSERT_MESSAGE(us_diff2 < lp_diff2 / 10ULL, "Deep sleep mode unlocked, but not used");
|
||||
TEST_ASSERT_MESSAGE(us_diff2 < us_diff1 / 10ULL, "Deep sleep mode unlocked, but not used");
|
||||
|
||||
set_us_ticker_irq_handler(us_ticker_irq_handler_org);
|
||||
set_lp_ticker_irq_handler(lp_ticker_irq_handler_org);
|
||||
}
|
||||
#endif
|
||||
|
||||
void test_lock_unlock_test_check()
|
||||
{
|
||||
// Make sure HAL tickers are initialized.
|
||||
ticker_read(get_us_ticker_data());
|
||||
ticker_read(get_lp_ticker_data());
|
||||
|
||||
// Use LowPowerTimer instead of Timer to prevent deep sleep lock.
|
||||
LowPowerTimer lp_timer;
|
||||
us_timestamp_t exec_time_unlocked, exec_time_locked;
|
||||
LowPowerTimeout lp_timeout;
|
||||
|
||||
// Deep sleep unlocked:
|
||||
// * sleep_manager_can_deep_sleep() returns true,
|
||||
// * sleep_manager_can_deep_sleep_test_check() returns true instantly.
|
||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
||||
lp_timer.start();
|
||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
|
||||
lp_timer.stop();
|
||||
exec_time_unlocked = lp_timer.read_high_resolution_us();
|
||||
|
||||
// Deep sleep locked:
|
||||
// * sleep_manager_can_deep_sleep() returns false,
|
||||
// * sleep_manager_can_deep_sleep_test_check() returns false with 2 ms delay.
|
||||
sleep_manager_lock_deep_sleep();
|
||||
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
|
||||
lp_timer.reset();
|
||||
lp_timer.start();
|
||||
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep_test_check());
|
||||
lp_timer.stop();
|
||||
exec_time_locked = lp_timer.read_high_resolution_us();
|
||||
TEST_ASSERT_UINT64_WITHIN(DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US, DEEP_SLEEP_TEST_CHECK_WAIT_US,
|
||||
exec_time_locked - exec_time_unlocked);
|
||||
|
||||
// Deep sleep unlocked with a 1 ms delay:
|
||||
// * sleep_manager_can_deep_sleep() returns false,
|
||||
// * sleep_manager_can_deep_sleep_test_check() returns true with a 1 ms delay,
|
||||
// * sleep_manager_can_deep_sleep() returns true when checked again.
|
||||
lp_timer.reset();
|
||||
lp_timeout.attach_us(mbed::callback(sleep_manager_unlock_deep_sleep_internal),
|
||||
DEEP_SLEEP_TEST_CHECK_WAIT_US / 2);
|
||||
lp_timer.start();
|
||||
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
|
||||
lp_timer.stop();
|
||||
TEST_ASSERT_UINT64_WITHIN(DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US, DEEP_SLEEP_TEST_CHECK_WAIT_US / 2,
|
||||
lp_timer.read_high_resolution_us());
|
||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
||||
}
|
||||
#endif
|
||||
|
||||
utest::v1::status_t testsuite_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(10, "default_auto");
|
||||
return utest::v1::greentea_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("sleep manager - deep sleep counter", sleep_manager_deepsleep_counter_test, greentea_failure_handler),
|
||||
Case("deep sleep lock/unlock", test_lock_unlock),
|
||||
Case("deep sleep unbalanced unlock", test_lone_unlock),
|
||||
Case("deep sleep locked USHRT_MAX times", test_lock_eq_ushrt_max),
|
||||
Case("deep sleep locked more than USHRT_MAX times", test_lock_gt_ushrt_max),
|
||||
#if DEVICE_LPTICKER
|
||||
#if DEVICE_USTICKER
|
||||
Case("sleep_auto calls sleep/deep sleep based on lock",
|
||||
(utest::v1::case_setup_handler_t) testcase_setup,
|
||||
test_sleep_auto,
|
||||
(utest::v1::case_teardown_handler_t) testcase_teardown),
|
||||
#endif
|
||||
Case("deep sleep lock/unlock test_check", test_lock_unlock_test_check),
|
||||
#endif
|
||||
};
|
||||
|
||||
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||
Specification specification(testsuite_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
Harness::run(specification);
|
||||
return !Harness::run(specification);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hal_sleep_manager_tests
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef MBED_HAL_SLEEP_MANAGER_API_TESTS_H
|
||||
#define MBED_HAL_SLEEP_MANAGER_API_TESTS_H
|
||||
|
||||
#if DEVICE_SLEEP
|
||||
|
||||
/** Test lock/unlock
|
||||
*
|
||||
* Given no prior calls to lock/unlock
|
||||
* When the deep sleep status is checked
|
||||
* Then the deep sleep is allowed
|
||||
*
|
||||
* When the lock function is called
|
||||
* Then the deep sleep is not allowed
|
||||
*
|
||||
* When the unlock function is called
|
||||
* Then the deep sleep is allowed again
|
||||
*/
|
||||
void test_lock_unlock();
|
||||
|
||||
/** Test an unbalanced unlock call
|
||||
*
|
||||
* Given the deep sleep has not been locked
|
||||
* When the deep sleep mode is unlocked
|
||||
* Then an mbed_error is raised
|
||||
*/
|
||||
void test_lone_unlock();
|
||||
|
||||
/** Test lock USHRT_MAX times
|
||||
*
|
||||
* Given a device with sleep mode support
|
||||
* When deep sleep mode is locked USHRT_MAX times
|
||||
* Then the deep sleep mode is locked
|
||||
*
|
||||
* When unlock is called repeatedly
|
||||
* Then deep sleep mode stays locked until the number
|
||||
* of unlock calls is equal to number of lock calls
|
||||
*/
|
||||
void test_lock_eq_ushrt_max();
|
||||
|
||||
/** Test lock more than USHRT_MAX times
|
||||
*
|
||||
* Given the deep sleep has already been locked USHRT_MAX times
|
||||
* When the deep sleep mode is locked again
|
||||
* Then an mbed_error is raised
|
||||
*/
|
||||
void test_lock_gt_ushrt_max();
|
||||
|
||||
/** Test sleep_auto calls sleep and deep sleep based on lock
|
||||
*
|
||||
* Given a device with sleep mode support
|
||||
* When the deep sleep mode is locked
|
||||
* Then sleep_auto uses sleep mode
|
||||
*
|
||||
* When the deep sleep mode is unlocked
|
||||
* Then sleep_auto uses deep sleep mode
|
||||
*/
|
||||
void test_sleep_auto();
|
||||
|
||||
/** Test lock/unlock test_check fun
|
||||
*
|
||||
* Given the deep sleep has not been locked
|
||||
* When the deep sleep status is checked
|
||||
* Then sleep_manager_can_deep_sleep() returns true
|
||||
* and sleep_manager_can_deep_sleep_test_check() returns true instantly.
|
||||
*
|
||||
* When the deep sleep mode is locked
|
||||
* Then sleep_manager_can_deep_sleep() returns false
|
||||
* and sleep_manager_can_deep_sleep_test_check() returns false with 2 ms delay.
|
||||
*
|
||||
* When the deep sleep mode is unlocked with a 1 ms delay
|
||||
* Then sleep_manager_can_deep_sleep() returns false
|
||||
* and sleep_manager_can_deep_sleep_test_check() returns true with 1 ms delay
|
||||
* and sleep_manager_can_deep_sleep() returns true when checked again.
|
||||
*/
|
||||
void test_lock_unlock_test_check();
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/** @}*/
|
|
@ -13,10 +13,11 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "utest/utest.h"
|
||||
#include "unity/unity.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "mbed.h"
|
||||
#include "sleep_manager_api_racecondition_tests.h"
|
||||
|
||||
#if !DEVICE_SLEEP
|
||||
#error [NOT_SUPPORTED] test not supported
|
||||
|
@ -94,8 +95,8 @@ utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
|||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("sleep manager HAL - locking multithreaded", sleep_manager_multithread_test),
|
||||
Case("sleep manager HAL - locking IRQ", sleep_manager_irq_test),
|
||||
Case("deep sleep lock/unlock is thread safe", sleep_manager_multithread_test),
|
||||
Case("deep sleep lock/unlock is IRQ safe", sleep_manager_irq_test),
|
||||
};
|
||||
|
||||
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hal_sleep_manager_tests
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef MBED_HAL_SLEEP_MANAGER_API_RACECONDITION_TESTS_H
|
||||
#define MBED_HAL_SLEEP_MANAGER_API_RACECONDITION_TESTS_H
|
||||
|
||||
/** Test lock/unlock is thread safe
|
||||
*
|
||||
* Given a device with sleep mode support
|
||||
* When multiple threads are using the sleep manager API
|
||||
* Then lock/unlock calls are thread safe
|
||||
*/
|
||||
void sleep_manager_multithread_test();
|
||||
|
||||
/** Test lock/unlock is IRQ safe
|
||||
*
|
||||
* Given a device with sleep mode support
|
||||
* When the sleep manager API is used from IRQ and the main thread concurrently
|
||||
* Then lock/unlock calls are IRQ safe
|
||||
*/
|
||||
void sleep_manager_irq_test();
|
||||
|
||||
#endif
|
||||
|
||||
/** @}*/
|
|
@ -163,6 +163,10 @@ void sleep_manager_lock_deep_sleep_internal(void)
|
|||
if (deep_sleep_lock == USHRT_MAX) {
|
||||
core_util_critical_section_exit();
|
||||
MBED_ERROR1(MBED_MAKE_ERROR(MBED_MODULE_HAL, MBED_ERROR_CODE_OVERFLOW), "DeepSleepLock overflow (> USHRT_MAX)", deep_sleep_lock);
|
||||
// When running sleep_manager tests, the mbed_error() is overridden
|
||||
// and no longer calls mbed_halt_system(). Return to prevent
|
||||
// execution of the following code.
|
||||
return;
|
||||
}
|
||||
core_util_atomic_incr_u16(&deep_sleep_lock, 1);
|
||||
core_util_critical_section_exit();
|
||||
|
@ -174,6 +178,10 @@ void sleep_manager_unlock_deep_sleep_internal(void)
|
|||
if (deep_sleep_lock == 0) {
|
||||
core_util_critical_section_exit();
|
||||
MBED_ERROR1(MBED_MAKE_ERROR(MBED_MODULE_HAL, MBED_ERROR_CODE_UNDERFLOW), "DeepSleepLock underflow (< 0)", deep_sleep_lock);
|
||||
// When running sleep_manager tests, the mbed_error() is overridden
|
||||
// and no longer calls mbed_halt_system(). Return to prevent
|
||||
// execution of the following code.
|
||||
return;
|
||||
}
|
||||
core_util_atomic_decr_u16(&deep_sleep_lock, 1);
|
||||
core_util_critical_section_exit();
|
||||
|
|
|
@ -32,7 +32,8 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Sleep manager API
|
||||
/**
|
||||
* @defgroup hal_sleep_manager Sleep manager API
|
||||
* The sleep manager provides API to automatically select sleep mode.
|
||||
*
|
||||
* There are two sleep modes:
|
||||
|
@ -43,6 +44,17 @@ extern "C" {
|
|||
* are not allowed (=disabled) during the deepsleep. For instance, high frequency
|
||||
* clocks.
|
||||
*
|
||||
* # Defined behavior
|
||||
* * The lock is a counter
|
||||
* * The lock can be locked up to USHRT_MAX - Verified by ::test_lock_eq_ushrt_max and ::test_lock_gt_ushrt_max
|
||||
* * The lock has to be equally unlocked as locked - Verified by ::test_lone_unlock and ::test_lock_eq_ushrt_max
|
||||
* * The function sleep_manager_lock_deep_sleep_internal() locks the automatic deep mode selection - Verified by ::test_lock_unlock
|
||||
* * The function sleep_manager_unlock_deep_sleep_internal() unlocks the automatic deep mode selection - Verified by ::test_lock_unlock
|
||||
* * The function sleep_manager_sleep_auto() chooses the sleep or deep sleep modes based on the lock - Verified by ::test_sleep_auto
|
||||
* * The function sleep_manager_lock_deep_sleep_internal() is IRQ and thread safe - Verified by ::sleep_manager_multithread_test and ::sleep_manager_irq_test
|
||||
* * The function sleep_manager_unlock_deep_sleep_internal() is IRQ and thread safe - Verified by ::sleep_manager_multithread_test and ::sleep_manager_irq_test
|
||||
* * The function sleep_manager_sleep_auto() is IRQ and thread safe
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
*
|
||||
|
@ -63,7 +75,19 @@ extern "C" {
|
|||
* return _sensor.start(event, callback);
|
||||
* }
|
||||
* @endcode
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup hal_sleep_manager_tests Sleep manager API tests
|
||||
* Tests to validate the proper implementation of the sleep manager
|
||||
*
|
||||
* To run the sleep manager hal tests use the command:
|
||||
*
|
||||
* mbed test -t <toolchain> -m <target> -n tests-mbed_hal-sleep_manager*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef MBED_SLEEP_TRACING_ENABLED
|
||||
|
||||
void sleep_tracker_lock(const char *const filename, int line);
|
||||
|
@ -203,15 +227,6 @@ static inline void deepsleep(void)
|
|||
#endif /* DEVICE_SLEEP */
|
||||
}
|
||||
|
||||
/** Resets the processor and most of the sub-system
|
||||
*
|
||||
* @note Does not affect the debug sub-system
|
||||
*/
|
||||
static inline void system_reset(void)
|
||||
{
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
/** Provides the time spent in sleep mode since boot.
|
||||
*
|
||||
* @return Time spent in sleep
|
||||
|
@ -240,6 +255,17 @@ us_timestamp_t mbed_time_idle(void);
|
|||
*/
|
||||
us_timestamp_t mbed_uptime(void);
|
||||
|
||||
/** @}*/
|
||||
|
||||
/** Resets the processor and most of the sub-system
|
||||
*
|
||||
* @note Does not affect the debug sub-system
|
||||
*/
|
||||
static inline void system_reset(void)
|
||||
{
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue