Merge pull request #10700 from LMESTM/hal_sleep_manager_test_update

Update HAL Sleep manager test to cope with STM32 LPTIM HW
pull/10919/head
Martin Kojtal 2019-06-28 13:19:10 +01:00 committed by GitHub
commit a2c9152441
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 137 additions and 53 deletions

View File

@ -28,12 +28,34 @@
#define SLEEP_DURATION_US 20000ULL
#define DEEP_SLEEP_TEST_CHECK_WAIT_US 2000
#define DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US 500
// As sleep_manager_can_deep_sleep_test_check() is based on wait_ns
// and wait_ns can be up to 40% slower, use a 50% delta here.
#define DEEP_SLEEP_TEST_CHECK_WAIT_DELTA_US 1000
using utest::v1::Case;
using utest::v1::Specification;
using utest::v1::Harness;
#if DEVICE_LPTICKER
/* Make sure there are enough ticks to cope with more than SLEEP_DURATION_US sleep
* without hitting the wrap-around.
*/
void wraparound_lp_protect(void)
{
const uint32_t ticks_now = get_lp_ticker_data()->interface->read();
const ticker_info_t *p_ticker_info = get_lp_ticker_data()->interface->get_info();
const uint32_t max_count = ((1 << p_ticker_info->bits) - 1);
const uint32_t delta_ticks = us_to_ticks(SLEEP_DURATION_US * 1.5, p_ticker_info->frequency);
if (ticks_now < (max_count - delta_ticks)) {
return;
}
while (get_lp_ticker_data()->interface->read() > (max_count - delta_ticks));
}
#endif
void test_lock_unlock()
{
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
@ -62,8 +84,6 @@ void test_lock_eq_ushrt_max()
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.
@ -98,6 +118,8 @@ utest::v1::status_t testcase_teardown(const Case *const source, const size_t pas
return utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
}
#if DEVICE_LPTICKER
#if DEVICE_USTICKER
/* 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.
@ -115,10 +137,18 @@ void test_sleep_auto()
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();
/* Let's avoid the Lp ticker wrap-around case */
wraparound_lp_protect();
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);
/* Some targets may need an interrupt short time after LPTIM interrupt is
* set and forbid deep_sleep during that period. Let this period pass */
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
sleep_manager_lock_deep_sleep();
us_ts1 = ticks_to_us(us_ticker_read(), us_ticker_info->frequency);
lp_ts1 = ticks_to_us(lp_ticker_read(), lp_ticker_info->frequency);
@ -144,13 +174,21 @@ void test_sleep_auto()
// Wait for hardware serial buffers to flush.
busy_wait_ms(SERIAL_FLUSH_TIME_MS);
/* Let's avoid the Lp ticker wrap-around case */
wraparound_lp_protect();
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);
/* Some targets may need an interrupt short time after LPTIM interrupt is
* set and forbid deep_sleep during that period. Let this period pass */
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
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);
@ -175,65 +213,106 @@ void test_sleep_auto()
}
#endif
#define US_PER_S 1000000
uint32_t diff_us(uint32_t start_ticks, uint32_t stop_ticks, const ticker_info_t *info)
{
uint32_t counter_mask = ((1 << info->bits) - 1);
uint32_t diff_ticks = ((stop_ticks - start_ticks) & counter_mask);
return (uint32_t)((uint64_t) diff_ticks * US_PER_S / info->frequency);
}
volatile bool unlock_deep_sleep = false;
void ticker_event_handler_stub(const ticker_data_t *const ticker)
{
lp_ticker_clear_interrupt();
if (unlock_deep_sleep) {
sleep_manager_unlock_deep_sleep_internal();
unlock_deep_sleep = false;
}
}
ticker_irq_handler_type prev_irq_handler;
void test_lock_unlock_test_check()
{
// Make sure HAL tickers are initialized.
ticker_read(get_us_ticker_data());
ticker_read(get_lp_ticker_data());
prev_irq_handler = set_lp_ticker_irq_handler(ticker_event_handler_stub);
// 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;
for (int i = 0; i < 1000; i++) {
// 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();
wraparound_lp_protect();
// 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);
const ticker_info_t *p_ticker_info = get_lp_ticker_data()->interface->get_info();
// 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());
// Use LowPowerTimer instead of Timer to prevent deep sleep lock.
us_timestamp_t exec_time_unlocked, exec_time_locked;
uint32_t start, stop;
// 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());
start = lp_ticker_read();
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
stop = lp_ticker_read();
exec_time_unlocked = diff_us(start, stop, p_ticker_info);
// 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());
start = lp_ticker_read();
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep_test_check());
stop = lp_ticker_read();
exec_time_locked = diff_us(start, stop, p_ticker_info);
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.
unlock_deep_sleep = true;
/* Let's avoid the Lp ticker wrap-around case */
wraparound_lp_protect();
start = lp_ticker_read();
uint32_t lp_wakeup_ts_raw = start + us_to_ticks(DEEP_SLEEP_TEST_CHECK_WAIT_US / 2, p_ticker_info->frequency);
timestamp_t lp_wakeup_ts = overflow_protect(lp_wakeup_ts_raw, p_ticker_info->bits);
lp_ticker_set_interrupt(lp_wakeup_ts);
// Extra wait after setting interrupt to handle CMPOK
wait_ns(100000);
TEST_ASSERT_FALSE(sleep_manager_can_deep_sleep());
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
stop = lp_ticker_read();
TEST_ASSERT(diff_us(start, stop, p_ticker_info) > 0UL);
TEST_ASSERT(diff_us(start, stop, p_ticker_info) < DEEP_SLEEP_TEST_CHECK_WAIT_US);
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
}
set_lp_ticker_irq_handler(prev_irq_handler);
}
#endif
utest::v1::status_t testsuite_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(10, "default_auto");
GREENTEA_SETUP(15, "default_auto");
return utest::v1::greentea_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("deep sleep lock/unlock", test_lock_unlock),
Case("deep sleep locked USHRT_MAX times", test_lock_eq_ushrt_max),
Case("deep sleep lock/unlock",
(utest::v1::case_setup_handler_t) testcase_setup,
test_lock_unlock,
(utest::v1::case_teardown_handler_t) testcase_teardown),
Case("deep sleep locked USHRT_MAX times",
(utest::v1::case_setup_handler_t) testcase_setup,
test_lock_eq_ushrt_max,
(utest::v1::case_teardown_handler_t) testcase_teardown),
#if DEVICE_LPTICKER
#if DEVICE_USTICKER
Case("sleep_auto calls sleep/deep sleep based on lock",
@ -241,7 +320,10 @@ Case cases[] = {
test_sleep_auto,
(utest::v1::case_teardown_handler_t) testcase_teardown),
#endif
Case("deep sleep lock/unlock test_check", test_lock_unlock_test_check),
Case("deep sleep lock/unlock test_check",
(utest::v1::case_setup_handler_t) testcase_setup,
test_lock_unlock_test_check,
(utest::v1::case_teardown_handler_t) testcase_teardown)
#endif
};

View File

@ -192,13 +192,15 @@ bool sleep_manager_can_deep_sleep(void)
bool sleep_manager_can_deep_sleep_test_check()
{
const uint32_t check_time_us = 2000;
const ticker_data_t *const ticker = get_us_ticker_data();
uint32_t start = ticker_read(ticker);
while ((ticker_read(ticker) - start) < check_time_us) {
uint32_t check_time_ns = 2000000;
while (check_time_ns) {
if (sleep_manager_can_deep_sleep()) {
return true;
}
wait_ns(100000); // 100 us
check_time_ns -= 100000;
}
return false;
}