2017-08-30 10:25:49 +00:00
|
|
|
/* 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 "utest/utest.h"
|
|
|
|
#include "unity/unity.h"
|
|
|
|
#include "greentea-client/test_env.h"
|
2018-07-18 14:19:06 +00:00
|
|
|
#include <limits.h>
|
|
|
|
#include "mbed.h"
|
|
|
|
#include "mbed_lp_ticker_wrapper.h"
|
2018-10-09 15:15:51 +00:00
|
|
|
#include "../sleep/sleep_test_utils.h"
|
2018-07-18 14:19:06 +00:00
|
|
|
#include "sleep_manager_api_tests.h"
|
2017-08-30 10:25:49 +00:00
|
|
|
|
|
|
|
#if !DEVICE_SLEEP
|
|
|
|
#error [NOT_SUPPORTED] test not supported
|
|
|
|
#endif
|
|
|
|
|
2018-10-09 15:15:51 +00:00
|
|
|
#define SLEEP_DURATION_US 20000ULL
|
2018-08-28 13:36:40 +00:00
|
|
|
#define DEEP_SLEEP_TEST_CHECK_WAIT_US 2000
|
2018-09-24 12:07:15 +00:00
|
|
|
#define DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US 500
|
2017-08-30 10:25:49 +00:00
|
|
|
|
2018-07-18 14:19:06 +00:00
|
|
|
using utest::v1::Case;
|
|
|
|
using utest::v1::Specification;
|
|
|
|
using utest::v1::Harness;
|
|
|
|
|
|
|
|
void test_lock_unlock()
|
|
|
|
{
|
|
|
|
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
|
|
|
|
|
|
|
sleep_manager_lock_deep_sleep();
|
|
|
|
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
|
|
|
|
|
|
|
|
sleep_manager_unlock_deep_sleep();
|
|
|
|
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
|
|
|
}
|
|
|
|
|
|
|
|
void test_lock_eq_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());
|
|
|
|
}
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2018-08-28 13:36:40 +00:00
|
|
|
#if DEVICE_LPTICKER
|
|
|
|
#if DEVICE_USTICKER
|
2018-10-10 20:24:40 +00:00
|
|
|
utest::v1::status_t testcase_setup(const Case *const source, const size_t index_of_case)
|
2018-07-18 14:19:06 +00:00
|
|
|
{
|
2018-10-09 15:15:51 +00:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2018-10-10 20:24:40 +00:00
|
|
|
utest::v1::status_t testcase_teardown(const Case *const source, const size_t passed, const size_t failed,
|
|
|
|
const utest::v1::failure_t failure)
|
2018-10-09 15:15:51 +00:00
|
|
|
{
|
|
|
|
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);
|
2018-07-18 14:19:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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()
|
2017-08-30 10:25:49 +00:00
|
|
|
{
|
2018-10-09 15:15:51 +00:00
|
|
|
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;
|
2018-07-27 09:17:07 +00:00
|
|
|
|
2017-08-30 10:25:49 +00:00
|
|
|
sleep_manager_lock_deep_sleep();
|
2018-10-09 15:15:51 +00:00
|
|
|
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);
|
2018-07-18 14:19:06 +00:00
|
|
|
|
|
|
|
// 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");
|
2017-08-30 10:25:49 +00:00
|
|
|
|
|
|
|
sleep_manager_unlock_deep_sleep();
|
2018-07-18 14:19:06 +00:00
|
|
|
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
|
|
|
|
|
|
|
// Wait for hardware serial buffers to flush.
|
2018-10-09 15:15:51 +00:00
|
|
|
busy_wait_ms(SERIAL_FLUSH_TIME_MS);
|
2018-07-18 14:19:06 +00:00
|
|
|
|
2018-10-09 15:15:51 +00:00
|
|
|
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);
|
2018-07-18 14:19:06 +00:00
|
|
|
|
2018-10-09 15:15:51 +00:00
|
|
|
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);
|
2018-07-18 14:19:06 +00:00
|
|
|
|
|
|
|
// 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");
|
2018-10-09 15:15:51 +00:00
|
|
|
|
|
|
|
set_us_ticker_irq_handler(us_ticker_irq_handler_org);
|
|
|
|
set_lp_ticker_irq_handler(lp_ticker_irq_handler_org);
|
2017-08-30 10:25:49 +00:00
|
|
|
}
|
2018-07-18 14:19:06 +00:00
|
|
|
#endif
|
2017-08-30 10:25:49 +00:00
|
|
|
|
2018-08-28 13:36:40 +00:00
|
|
|
void test_lock_unlock_test_check()
|
|
|
|
{
|
2018-10-09 15:15:51 +00:00
|
|
|
// Make sure HAL tickers are initialized.
|
|
|
|
ticker_read(get_us_ticker_data());
|
|
|
|
ticker_read(get_lp_ticker_data());
|
|
|
|
|
2018-08-28 13:36:40 +00:00
|
|
|
// 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
|
|
|
|
|
2018-07-18 14:19:06 +00:00
|
|
|
utest::v1::status_t testsuite_setup(const size_t number_of_cases)
|
2017-08-30 10:25:49 +00:00
|
|
|
{
|
2018-07-18 14:19:06 +00:00
|
|
|
GREENTEA_SETUP(10, "default_auto");
|
|
|
|
return utest::v1::greentea_test_setup_handler(number_of_cases);
|
2017-08-30 10:25:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Case cases[] = {
|
2018-07-18 14:19:06 +00:00
|
|
|
Case("deep sleep lock/unlock", test_lock_unlock),
|
|
|
|
Case("deep sleep locked USHRT_MAX times", test_lock_eq_ushrt_max),
|
2018-08-28 13:36:40 +00:00
|
|
|
#if DEVICE_LPTICKER
|
|
|
|
#if DEVICE_USTICKER
|
2018-10-09 15:15:51 +00:00
|
|
|
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),
|
2018-08-28 13:36:40 +00:00
|
|
|
#endif
|
|
|
|
Case("deep sleep lock/unlock test_check", test_lock_unlock_test_check),
|
2018-07-18 14:19:06 +00:00
|
|
|
#endif
|
2017-08-30 10:25:49 +00:00
|
|
|
};
|
|
|
|
|
2018-10-09 15:15:51 +00:00
|
|
|
Specification specification(testsuite_setup, cases);
|
2017-08-30 10:25:49 +00:00
|
|
|
|
2018-07-27 09:17:07 +00:00
|
|
|
int main()
|
|
|
|
{
|
2018-07-18 14:19:06 +00:00
|
|
|
return !Harness::run(specification);
|
2017-08-30 10:25:49 +00:00
|
|
|
}
|