diff --git a/TESTS/mbed_drivers/sleep_lock/main.cpp b/TESTS/mbed_drivers/sleep_lock/main.cpp index 234c70dc2d2..7986845f578 100644 --- a/TESTS/mbed_drivers/sleep_lock/main.cpp +++ b/TESTS/mbed_drivers/sleep_lock/main.cpp @@ -29,90 +29,90 @@ using namespace utest::v1; void deep_sleep_lock_lock_test() { - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); { // Check basic usage works DeepSleepLock lock; - TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep_test_check()); } - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); { // Check that unlock and lock change can deep sleep as expected DeepSleepLock lock; - TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep_test_check()); lock.unlock(); - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); lock.lock(); - TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep_test_check()); } - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); { // Check that unlock releases sleep based on count DeepSleepLock lock; lock.lock(); lock.lock(); lock.unlock(); - TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep_test_check()); } - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); { // Check that unbalanced locks do not leave deep sleep locked DeepSleepLock lock; lock.lock(); - TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep_test_check()); } - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); } void timer_lock_test() { - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); { // Just creating a timer object does not lock sleep Timer timer; - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); } - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); { // Starting a timer does lock sleep Timer timer; timer.start(); - TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep_test_check()); } - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); { // Stopping a timer after starting it allows sleep Timer timer; timer.start(); timer.stop(); - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); } - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); { // Starting a timer multiple times still lets you sleep Timer timer; timer.start(); timer.start(); } - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); { // Stopping a timer multiple times still lets you sleep Timer timer; timer.start(); timer.stop(); timer.stop(); - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); } - TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep()); + TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check()); } Case cases[] = { diff --git a/TESTS/mbed_drivers/timeout/timeout_tests.h b/TESTS/mbed_drivers/timeout/timeout_tests.h index 8f5d9abdf13..4c1311d85da 100644 --- a/TESTS/mbed_drivers/timeout/timeout_tests.h +++ b/TESTS/mbed_drivers/timeout/timeout_tests.h @@ -263,7 +263,7 @@ void test_sleep(void) timer.start(); timeout.attach_callback(mbed::callback(sem_callback, &sem), delay_us); - bool deep_sleep_allowed = sleep_manager_can_deep_sleep(); + bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check(); TEST_ASSERT_FALSE_MESSAGE(deep_sleep_allowed, "Deep sleep should be disallowed"); while (sem.wait(0) != 1) { sleep(); @@ -322,7 +322,7 @@ void test_deepsleep(void) timer.start(); timeout.attach_callback(mbed::callback(sem_callback, &sem), delay_us); - bool deep_sleep_allowed = sleep_manager_can_deep_sleep(); + bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check(); TEST_ASSERT_TRUE_MESSAGE(deep_sleep_allowed, "Deep sleep should be allowed"); while (sem.wait(0) != 1) { sleep(); diff --git a/TESTS/mbed_hal/common_tickers/main.cpp b/TESTS/mbed_hal/common_tickers/main.cpp index 16b48d0c77e..ca539bd115c 100644 --- a/TESTS/mbed_hal/common_tickers/main.cpp +++ b/TESTS/mbed_hal/common_tickers/main.cpp @@ -20,6 +20,7 @@ #include "ticker_api_tests.h" #include "hal/us_ticker_api.h" #include "hal/lp_ticker_api.h" +#include "hal/mbed_lp_ticker_wrapper.h" #ifdef __cplusplus extern "C" { @@ -496,7 +497,15 @@ utest::v1::status_t us_ticker_setup(const Case *const source, const size_t index { intf = get_us_ticker_data()->interface; - OS_Tick_Disable(); + /* OS, common ticker and low power ticker wrapper + * may make use of us ticker so suspend them for this test */ + osKernelSuspend(); +#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0) + /* Suspend the lp ticker wrapper since it makes use of the us ticker */ + ticker_suspend(get_lp_ticker_data()); + lp_ticker_wrapper_suspend(); +#endif + ticker_suspend(get_us_ticker_data()); intf->init(); @@ -515,7 +524,12 @@ utest::v1::status_t us_ticker_teardown(const Case *const source, const size_t pa prev_irq_handler = NULL; - OS_Tick_Enable(); + ticker_resume(get_us_ticker_data()); +#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0) + lp_ticker_wrapper_resume(); + ticker_resume(get_lp_ticker_data()); +#endif + osKernelResume(0); return greentea_case_teardown_handler(source, passed, failed, reason); } @@ -525,7 +539,9 @@ utest::v1::status_t lp_ticker_setup(const Case *const source, const size_t index { intf = get_lp_ticker_data()->interface; - OS_Tick_Disable(); + /* OS and common ticker may make use of lp ticker so suspend them for this test */ + osKernelSuspend(); + ticker_suspend(get_lp_ticker_data()); intf->init(); @@ -544,7 +560,8 @@ utest::v1::status_t lp_ticker_teardown(const Case *const source, const size_t pa prev_irq_handler = NULL; - OS_Tick_Enable(); + ticker_resume(get_lp_ticker_data()); + osKernelResume(0); return greentea_case_teardown_handler(source, passed, failed, reason); } diff --git a/TESTS/mbed_hal/common_tickers_freq/main.cpp b/TESTS/mbed_hal/common_tickers_freq/main.cpp index c34527b2184..bf5c9a0301c 100644 --- a/TESTS/mbed_hal/common_tickers_freq/main.cpp +++ b/TESTS/mbed_hal/common_tickers_freq/main.cpp @@ -28,6 +28,7 @@ #include "ticker_api_test_freq.h" #include "hal/us_ticker_api.h" #include "hal/lp_ticker_api.h" +#include "hal/mbed_lp_ticker_wrapper.h" #if !DEVICE_USTICKER #error [NOT_SUPPORTED] test not supported @@ -38,25 +39,51 @@ using namespace utest::v1; const ticker_interface_t *intf; - -static volatile unsigned int overflowCounter; +uint32_t intf_mask; +uint32_t intf_last_tick; +uint32_t intf_elapsed_ticks; +ticker_irq_handler_type prev_handler; uint32_t ticks_to_us(uint32_t ticks, uint32_t freq) { return (uint32_t)((uint64_t)ticks * US_PER_S / freq); } -void ticker_event_handler_stub(const ticker_data_t *const ticker) +void elapsed_ticks_reset() { - if (ticker == get_us_ticker_data()) { - us_ticker_clear_interrupt(); - } else { -#if DEVICE_LPTICKER - lp_ticker_clear_interrupt(); -#endif - } + core_util_critical_section_enter(); + + const uint32_t ticker_bits = intf->get_info()->bits; + + intf_mask = (1 << ticker_bits) - 1; + intf_last_tick = intf->read(); + intf_elapsed_ticks = 0; + + core_util_critical_section_exit(); +} - overflowCounter++; +uint32_t elapsed_ticks_update() +{ + core_util_critical_section_enter(); + + const uint32_t current_tick = intf->read(); + intf_elapsed_ticks += (current_tick - intf_last_tick) & intf_mask; + intf_last_tick = current_tick; + + /* Schedule next interrupt half way to overflow */ + uint32_t next = (current_tick + intf_mask / 2) & intf_mask; + intf->set_interrupt(next); + + uint32_t elapsed = intf_elapsed_ticks; + + core_util_critical_section_exit(); + return elapsed; +} + +void ticker_event_handler_stub(const ticker_data_t *const ticker) +{ + intf->clear_interrupt(); + elapsed_ticks_update(); } /* Test that the ticker is operating at the frequency it specifies. */ @@ -66,13 +93,13 @@ void ticker_frequency_test() char _value[128] = { }; int expected_key = 1; const uint32_t ticker_freq = intf->get_info()->frequency; - const uint32_t ticker_bits = intf->get_info()->bits; - const uint32_t ticker_max = (1 << ticker_bits) - 1; intf->init(); + elapsed_ticks_reset(); + /* Detect overflow for tickers with lower counters width. */ - intf->set_interrupt(0); + elapsed_ticks_update(); greentea_send_kv("timing_drift_check_start", 0); @@ -82,9 +109,7 @@ void ticker_frequency_test() expected_key = strcmp(_key, "base_time"); } while (expected_key); - overflowCounter = 0; - - const uint32_t begin_ticks = intf->read(); + const uint32_t begin_ticks = elapsed_ticks_update(); /* Assume that there was no overflow at this point - we are just after init. */ greentea_send_kv(_key, ticks_to_us(begin_ticks, ticker_freq)); @@ -92,9 +117,9 @@ void ticker_frequency_test() /* Wait for 2nd signal from host. */ greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); - const uint32_t end_ticks = intf->read(); + const uint32_t end_ticks = elapsed_ticks_update(); - greentea_send_kv(_key, ticks_to_us(end_ticks + overflowCounter * ticker_max, ticker_freq)); + greentea_send_kv(_key, ticks_to_us(end_ticks, ticker_freq)); /* Get the results from host. */ greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); @@ -106,43 +131,74 @@ void ticker_frequency_test() utest::v1::status_t us_ticker_case_setup_handler_t(const Case *const source, const size_t index_of_case) { +#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0) + /* Suspend the lp ticker wrapper since it makes use of the us ticker */ + ticker_suspend(get_lp_ticker_data()); + lp_ticker_wrapper_suspend(); +#endif + ticker_suspend(get_us_ticker_data()); intf = get_us_ticker_data()->interface; - set_us_ticker_irq_handler(ticker_event_handler_stub); + prev_handler = set_us_ticker_irq_handler(ticker_event_handler_stub); return greentea_case_setup_handler(source, index_of_case); } +utest::v1::status_t us_ticker_case_teardown_handler_t(const Case *const source, const size_t passed, const size_t failed, + const failure_t reason) +{ + set_us_ticker_irq_handler(prev_handler); + ticker_resume(get_us_ticker_data()); +#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0) + lp_ticker_wrapper_resume(); + ticker_resume(get_lp_ticker_data()); +#endif + return greentea_case_teardown_handler(source, passed, failed, reason); +} + #if DEVICE_LPTICKER utest::v1::status_t lp_ticker_case_setup_handler_t(const Case *const source, const size_t index_of_case) { + ticker_suspend(get_lp_ticker_data()); intf = get_lp_ticker_data()->interface; - set_lp_ticker_irq_handler(ticker_event_handler_stub); + prev_handler = set_lp_ticker_irq_handler(ticker_event_handler_stub); return greentea_case_setup_handler(source, index_of_case); } -#endif -utest::v1::status_t ticker_case_teardown_handler_t(const Case *const source, const size_t passed, const size_t failed, - const failure_t reason) +utest::v1::status_t lp_ticker_case_teardown_handler_t(const Case *const source, const size_t passed, const size_t failed, + const failure_t reason) { + set_lp_ticker_irq_handler(prev_handler); + ticker_resume(get_lp_ticker_data()); return greentea_case_teardown_handler(source, passed, failed, reason); } +#endif // Test cases Case cases[] = { Case("Microsecond ticker frequency test", us_ticker_case_setup_handler_t, ticker_frequency_test, - ticker_case_teardown_handler_t), + us_ticker_case_teardown_handler_t), #if DEVICE_LPTICKER Case("Low power ticker frequency test", lp_ticker_case_setup_handler_t, ticker_frequency_test, - ticker_case_teardown_handler_t), + lp_ticker_case_teardown_handler_t), #endif }; utest::v1::status_t greentea_test_setup(const size_t number_of_cases) { + /* Suspend RTOS Kernel so the timers are not in use. */ + osKernelSuspend(); + GREENTEA_SETUP(120, "timing_drift_auto"); return greentea_test_setup_handler(number_of_cases); } -Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); +void greentea_test_teardown(const size_t passed, const size_t failed, const failure_t failure) +{ + osKernelResume(0); + + greentea_test_teardown_handler(passed, failed, failure); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown); int main() { diff --git a/TESTS/mbed_hal/lp_ticker/main.cpp b/TESTS/mbed_hal/lp_ticker/main.cpp index f8cd8f1abf9..58ff4acf7f1 100644 --- a/TESTS/mbed_hal/lp_ticker/main.cpp +++ b/TESTS/mbed_hal/lp_ticker/main.cpp @@ -20,6 +20,7 @@ #include "rtos.h" #include "lp_ticker_api_tests.h" #include "hal/lp_ticker_api.h" +#include "hal/mbed_lp_ticker_wrapper.h" #if !DEVICE_LPTICKER #error [NOT_SUPPORTED] Low power timer not supported for this target @@ -29,6 +30,8 @@ using namespace utest::v1; volatile int intFlag = 0; +ticker_irq_handler_type prev_handler; + #define US_PER_MS 1000 #define TICKER_GLITCH_TEST_TICKS 1000 @@ -113,8 +116,6 @@ void lp_ticker_deepsleep_test() { intFlag = 0; - set_lp_ticker_irq_handler(ticker_event_handler_stub); - lp_ticker_init(); /* Give some time Green Tea to finish UART transmission before entering @@ -130,7 +131,7 @@ void lp_ticker_deepsleep_test() * tick_count + TICKER_INT_VAL. */ lp_ticker_set_interrupt(tick_count + TICKER_INT_VAL); - TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep()); + TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check()); while (!intFlag) { sleep(); @@ -157,6 +158,32 @@ void lp_ticker_glitch_test() } } +#if DEVICE_LPTICKER +utest::v1::status_t lp_ticker_deepsleep_test_setup_handler(const Case *const source, const size_t index_of_case) +{ + /* disable everything using the lp ticker for this test */ + osKernelSuspend(); + ticker_suspend(get_lp_ticker_data()); +#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0) + lp_ticker_wrapper_suspend(); +#endif + prev_handler = set_lp_ticker_irq_handler(ticker_event_handler_stub); + return greentea_case_setup_handler(source, index_of_case); +} + +utest::v1::status_t lp_ticker_deepsleep_test_teardown_handler(const Case *const source, const size_t passed, const size_t failed, + const failure_t reason) +{ + set_lp_ticker_irq_handler(prev_handler); +#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0) + lp_ticker_wrapper_resume(); +#endif + ticker_resume(get_lp_ticker_data()); + osKernelResume(0); + return greentea_case_teardown_handler(source, passed, failed, reason); +} +#endif + utest::v1::status_t test_setup(const size_t number_of_cases) { GREENTEA_SETUP(20, "default_auto"); @@ -166,7 +193,7 @@ utest::v1::status_t test_setup(const size_t number_of_cases) Case cases[] = { Case("lp ticker info test", lp_ticker_info_test), #if DEVICE_SLEEP - Case("lp ticker sleep test", lp_ticker_deepsleep_test), + Case("lp ticker sleep test", lp_ticker_deepsleep_test_setup_handler, lp_ticker_deepsleep_test, lp_ticker_deepsleep_test_teardown_handler), #endif Case("lp ticker glitch test", lp_ticker_glitch_test) }; diff --git a/TESTS/mbed_hal/rtc/main.cpp b/TESTS/mbed_hal/rtc/main.cpp index 87cc3acf3d1..bd65e38cd4f 100644 --- a/TESTS/mbed_hal/rtc/main.cpp +++ b/TESTS/mbed_hal/rtc/main.cpp @@ -74,7 +74,7 @@ void rtc_sleep_test_support(bool deepsleep_mode) timeout.attach(callback, DELAY_4S); - TEST_ASSERT(sleep_manager_can_deep_sleep() == deepsleep_mode); + TEST_ASSERT(sleep_manager_can_deep_sleep_test_check() == deepsleep_mode); while (!expired) { sleep(); diff --git a/TESTS/mbed_hal/sleep/main.cpp b/TESTS/mbed_hal/sleep/main.cpp index 5d64b8517be..46beb039b24 100644 --- a/TESTS/mbed_hal/sleep/main.cpp +++ b/TESTS/mbed_hal/sleep/main.cpp @@ -23,6 +23,7 @@ #include "utest/utest.h" #include "unity/unity.h" #include "greentea-client/test_env.h" +#include "mbed_lp_ticker_wrapper.h" #include "sleep_api_tests.h" @@ -40,7 +41,7 @@ * * 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 wait_ms() function for now. + * so we'll use the busy_wait_ms() function for now. */ #define SERIAL_FLUSH_TIME_MS 20 @@ -103,6 +104,20 @@ bool compare_timestamps(unsigned int delta_ticks, unsigned int ticker_width, uns } } +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(); @@ -125,17 +140,6 @@ void sleep_usticker_test() const ticker_irq_handler_type us_ticker_irq_handler_org = set_us_ticker_irq_handler(us_ticker_isr); - // call ticker_read_us to initialize ticker upper layer - // prevents subsequent scheduling of max_delta interrupt during ticker initialization while test execution - // (e.g when ticker_read_us is called) - ticker_read_us(ticker); -#ifdef DEVICE_LPTICKER - // call ticker_read_us to initialize lp_ticker - // prevents scheduling interrupt during ticker initialization (in lp_ticker_init) while test execution - // (e.g when ticker_read_us is called for lp_ticker, see MBED_CPU_STATS_ENABLED) - ticker_read_us(get_lp_ticker_data()); -#endif - /* Test only sleep functionality. */ sleep_manager_lock_deep_sleep(); TEST_ASSERT_FALSE_MESSAGE(sleep_manager_can_deep_sleep(), "deep sleep should be locked"); @@ -173,17 +177,12 @@ void deepsleep_lpticker_test() const unsigned int ticker_freq = ticker->interface->get_info()->frequency; const unsigned int ticker_width = ticker->interface->get_info()->bits; - // call ticker_read_us to initialize ticker upper layer - // prevents subsequent scheduling of max_delta interrupt during ticker initialization while test execution - // (e.g when ticker_read_us is called) - ticker_read_us(ticker); - const ticker_irq_handler_type lp_ticker_irq_handler_org = set_lp_ticker_irq_handler(lp_ticker_isr); /* Give some time Green Tea to finish UART transmission before entering * deep-sleep mode. */ - wait_ms(SERIAL_FLUSH_TIME_MS); + busy_wait_ms(SERIAL_FLUSH_TIME_MS); TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep(), "deep sleep should not be locked"); @@ -218,7 +217,7 @@ void deepsleep_high_speed_clocks_turned_off_test() /* Give some time Green Tea to finish UART transmission before entering * deep-sleep mode. */ - wait_ms(SERIAL_FLUSH_TIME_MS); + busy_wait_ms(SERIAL_FLUSH_TIME_MS); TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep(), "deep sleep should not be locked"); @@ -256,15 +255,38 @@ utest::v1::status_t greentea_failure_handler(const Case *const source, const fai utest::v1::status_t greentea_test_setup(const size_t number_of_cases) { GREENTEA_SETUP(60, "default_auto"); + /* Suspend RTOS Kernel to enable sleep modes. */ + osKernelSuspend(); +#if DEVICE_LPTICKER + ticker_suspend(get_lp_ticker_data()); +#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0) + lp_ticker_wrapper_suspend(); +#endif +#endif + ticker_suspend(get_us_ticker_data()); + us_ticker_init(); #if DEVICE_LPTICKER lp_ticker_init(); #endif - /* Suspend RTOS Kernel to enable sleep modes. */ - osKernelSuspend(); + return greentea_test_setup_handler(number_of_cases); } +void greentea_test_teardown(const size_t passed, const size_t failed, const failure_t failure) +{ + ticker_resume(get_us_ticker_data()); +#if DEVICE_LPTICKER +#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0) + lp_ticker_wrapper_resume(); +#endif + ticker_resume(get_lp_ticker_data()); +#endif + osKernelResume(0); + + greentea_test_teardown_handler(passed, failed, failure); +} + Case cases[] = { Case("sleep - source of wake-up - us ticker", sleep_usticker_test, greentea_failure_handler), #if DEVICE_LPTICKER @@ -273,7 +295,7 @@ Case cases[] = { #endif }; -Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); +Specification specification(greentea_test_setup, cases, greentea_test_teardown); int main() { diff --git a/TESTS/mbed_hal/sleep_manager/main.cpp b/TESTS/mbed_hal/sleep_manager/main.cpp index c04daca78c1..3d3e392b50c 100644 --- a/TESTS/mbed_hal/sleep_manager/main.cpp +++ b/TESTS/mbed_hal/sleep_manager/main.cpp @@ -25,15 +25,15 @@ using namespace utest::v1; void sleep_manager_deepsleep_counter_test() { - bool deep_sleep_allowed = sleep_manager_can_deep_sleep(); + bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check(); TEST_ASSERT_TRUE(deep_sleep_allowed); sleep_manager_lock_deep_sleep(); - deep_sleep_allowed = sleep_manager_can_deep_sleep(); + deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check(); TEST_ASSERT_FALSE(deep_sleep_allowed); sleep_manager_unlock_deep_sleep(); - deep_sleep_allowed = sleep_manager_can_deep_sleep(); + deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check(); TEST_ASSERT_TRUE(deep_sleep_allowed); } diff --git a/TESTS/mbed_hal/sleep_manager_racecondition/main.cpp b/TESTS/mbed_hal/sleep_manager_racecondition/main.cpp index 710f3caf16a..d43492c0525 100644 --- a/TESTS/mbed_hal/sleep_manager_racecondition/main.cpp +++ b/TESTS/mbed_hal/sleep_manager_racecondition/main.cpp @@ -55,7 +55,7 @@ void sleep_manager_multithread_test() t2.join(); } - bool deep_sleep_allowed = sleep_manager_can_deep_sleep(); + bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check(); TEST_ASSERT_TRUE_MESSAGE(deep_sleep_allowed, "Deep sleep should be allowed"); } @@ -83,7 +83,7 @@ void sleep_manager_irq_test() timer.stop(); } - bool deep_sleep_allowed = sleep_manager_can_deep_sleep(); + bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check(); TEST_ASSERT_TRUE_MESSAGE(deep_sleep_allowed, "Deep sleep should be allowed"); } diff --git a/TESTS/mbed_hal/ticker/main.cpp b/TESTS/mbed_hal/ticker/main.cpp index 6d5708af53e..754728cc27f 100644 --- a/TESTS/mbed_hal/ticker/main.cpp +++ b/TESTS/mbed_hal/ticker/main.cpp @@ -2268,6 +2268,82 @@ static void test_match_interval_passed_table() } } +/** + * Check that suspend and resume work as expected + * + * Check the following + * -time does not change while suspended + * -restricted interface functions are not called + * -scheduling resumes correctly + */ +static void test_suspend_resume() +{ + ticker_set_handler(&ticker_stub, NULL); + + interface_stub.timestamp = 1000; + us_timestamp_t start = ticker_read_us(&ticker_stub); + TEST_ASSERT_EQUAL(1000, start); + + /* Reset call count */ + interface_stub.init_call = 0; + interface_stub.read_call = 0; + interface_stub.set_interrupt_call = 0; + + /* Suspend the ticker */ + ticker_suspend(&ticker_stub); + const timestamp_t suspend_time = queue_stub.present_time; + + + /* Simulate time passing */ + interface_stub.timestamp = 1500; + us_timestamp_t next = ticker_read_us(&ticker_stub); + + /* Time should not have passed and no calls to interface should have been made */ + TEST_ASSERT_EQUAL(start, next); + TEST_ASSERT_EQUAL(0, interface_stub.init_call); + TEST_ASSERT_EQUAL(0, interface_stub.read_call); + TEST_ASSERT_EQUAL(0, interface_stub.disable_interrupt_call); + TEST_ASSERT_EQUAL(0, interface_stub.clear_interrupt_call); + TEST_ASSERT_EQUAL(0, interface_stub.set_interrupt_call); + TEST_ASSERT_EQUAL(0, interface_stub.fire_interrupt_call); + + + /* Simulate a reinit (time reset to 0) */ + interface_stub.timestamp = 0; + next = ticker_read_us(&ticker_stub); + + /* Time should not have passed and no calls to interface should have been made */ + TEST_ASSERT_EQUAL(start, next); + TEST_ASSERT_EQUAL(0, interface_stub.init_call); + TEST_ASSERT_EQUAL(0, interface_stub.read_call); + TEST_ASSERT_EQUAL(0, interface_stub.disable_interrupt_call); + TEST_ASSERT_EQUAL(0, interface_stub.clear_interrupt_call); + TEST_ASSERT_EQUAL(0, interface_stub.set_interrupt_call); + TEST_ASSERT_EQUAL(0, interface_stub.fire_interrupt_call); + + + /* Insert an event in the past and future */ + ticker_event_t event_past = { 0 }; + const timestamp_t event_past_timestamp = suspend_time - 10; + ticker_insert_event_us(&ticker_stub, &event_past, event_past_timestamp, 0); + + ticker_event_t event_future = { 0 }; + const timestamp_t event_future_timestamp = suspend_time + 10; + ticker_insert_event_us(&ticker_stub, &event_future, event_future_timestamp, 0); + + TEST_ASSERT_EQUAL(0, interface_stub.init_call); + TEST_ASSERT_EQUAL(0, interface_stub.read_call); + TEST_ASSERT_EQUAL(0, interface_stub.disable_interrupt_call); + TEST_ASSERT_EQUAL(0, interface_stub.clear_interrupt_call); + TEST_ASSERT_EQUAL(0, interface_stub.set_interrupt_call); + TEST_ASSERT_EQUAL(0, interface_stub.fire_interrupt_call); + + /* Resume and verify everything starts again */ + ticker_resume(&ticker_stub); + TEST_ASSERT_EQUAL(suspend_time, queue_stub.present_time); + TEST_ASSERT_EQUAL(1, interface_stub.fire_interrupt_call); +} + static const case_t cases[] = { MAKE_TEST_CASE("ticker initialization", test_ticker_initialization), MAKE_TEST_CASE( @@ -2376,6 +2452,10 @@ static const case_t cases[] = { MAKE_TEST_CASE( "test_match_interval_passed_table", test_match_interval_passed_table + ), + MAKE_TEST_CASE( + "test_suspend_resume", + test_suspend_resume ) }; diff --git a/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp b/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp index 27cdae531a7..8a25ad28719 100644 --- a/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp +++ b/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp @@ -313,7 +313,7 @@ void test_deepsleep(void) lptimer.start(); st.schedule_tick(TEST_TICKS); - TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep(), "Deep sleep should be allowed"); + TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep_test_check(), "Deep sleep should be allowed"); while (st.sem_wait(0) != 1) { sleep(); } diff --git a/hal/LowPowerTickerWrapper.cpp b/hal/LowPowerTickerWrapper.cpp new file mode 100644 index 00000000000..70b5190d4bd --- /dev/null +++ b/hal/LowPowerTickerWrapper.cpp @@ -0,0 +1,295 @@ +/* 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. + */ +#include "hal/LowPowerTickerWrapper.h" +#include "platform/Callback.h" + +LowPowerTickerWrapper::LowPowerTickerWrapper(const ticker_data_t *data, const ticker_interface_t *interface, uint32_t min_cycles_between_writes, uint32_t min_cycles_until_match) + : _intf(data->interface), _min_count_between_writes(min_cycles_between_writes + 1), _min_count_until_match(min_cycles_until_match + 1), _suspended(false) +{ + core_util_critical_section_enter(); + + this->data.interface = interface; + this->data.queue = data->queue; + _reset(); + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::irq_handler(ticker_irq_handler_type handler) +{ + core_util_critical_section_enter(); + + if (_suspended) { + if (handler) { + handler(&data); + } + core_util_critical_section_exit(); + return; + } + + if (_pending_fire_now || _match_check(_intf->read())) { + _timeout.detach(); + _pending_timeout = false; + _pending_match = false; + _pending_fire_now = false; + if (handler) { + handler(&data); + } + } else { + // Spurious interrupt + _intf->clear_interrupt(); + } + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::suspend() +{ + core_util_critical_section_enter(); + + // Wait until rescheduling is allowed + while (!_set_interrupt_allowed) { + timestamp_t current = _intf->read(); + if (((current - _last_actual_set_interrupt) & _mask) >= _min_count_between_writes) { + _set_interrupt_allowed = true; + } + } + + _reset(); + _suspended = true; + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::resume() +{ + core_util_critical_section_enter(); + + _suspended = false; + + core_util_critical_section_exit(); +} + +bool LowPowerTickerWrapper::timeout_pending() +{ + core_util_critical_section_enter(); + + bool pending = _pending_timeout; + + core_util_critical_section_exit(); + return pending; +} + +void LowPowerTickerWrapper::init() +{ + core_util_critical_section_enter(); + + _reset(); + _intf->init(); + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::free() +{ + core_util_critical_section_enter(); + + _reset(); + _intf->free(); + + core_util_critical_section_exit(); +} + +uint32_t LowPowerTickerWrapper::read() +{ + core_util_critical_section_enter(); + + timestamp_t current = _intf->read(); + if (_match_check(current)) { + _intf->fire_interrupt(); + } + + core_util_critical_section_exit(); + return current; +} + +void LowPowerTickerWrapper::set_interrupt(timestamp_t timestamp) +{ + core_util_critical_section_enter(); + + _last_set_interrupt = _intf->read(); + _cur_match_time = timestamp; + _pending_match = true; + _schedule_match(_last_set_interrupt); + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::disable_interrupt() +{ + core_util_critical_section_enter(); + + _intf->disable_interrupt(); + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::clear_interrupt() +{ + core_util_critical_section_enter(); + + _intf->clear_interrupt(); + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::fire_interrupt() +{ + core_util_critical_section_enter(); + + _pending_fire_now = 1; + _intf->fire_interrupt(); + + core_util_critical_section_exit(); +} + +const ticker_info_t *LowPowerTickerWrapper::get_info() +{ + + core_util_critical_section_enter(); + + const ticker_info_t *info = _intf->get_info(); + + core_util_critical_section_exit(); + return info; +} + +void LowPowerTickerWrapper::_reset() +{ + MBED_ASSERT(core_util_in_critical_section()); + + _timeout.detach(); + _pending_timeout = false; + _pending_match = false; + _pending_fire_now = false; + _set_interrupt_allowed = true; + _cur_match_time = 0; + _last_set_interrupt = 0; + _last_actual_set_interrupt = 0; + + const ticker_info_t *info = _intf->get_info(); + if (info->bits >= 32) { + _mask = 0xffffffff; + } else { + _mask = ((uint64_t)1 << info->bits) - 1; + } + + // Round us_per_tick up + _us_per_tick = (1000000 + info->frequency - 1) / info->frequency; +} + +void LowPowerTickerWrapper::_timeout_handler() +{ + core_util_critical_section_enter(); + _pending_timeout = false; + + timestamp_t current = _intf->read(); + if (_ticker_match_interval_passed(_last_set_interrupt, current, _cur_match_time)) { + _intf->fire_interrupt(); + } else { + _schedule_match(current); + } + + core_util_critical_section_exit(); +} + +bool LowPowerTickerWrapper::_match_check(timestamp_t current) +{ + MBED_ASSERT(core_util_in_critical_section()); + + if (!_pending_match) { + return false; + } + return _ticker_match_interval_passed(_last_set_interrupt, current, _cur_match_time); +} + +uint32_t LowPowerTickerWrapper::_lp_ticks_to_us(uint32_t ticks) +{ + MBED_ASSERT(core_util_in_critical_section()); + + // Add 4 microseconds to round up the micro second ticker time (which has a frequency of at least 250KHz - 4us period) + return _us_per_tick * ticks + 4; +} + +void LowPowerTickerWrapper::_schedule_match(timestamp_t current) +{ + MBED_ASSERT(core_util_in_critical_section()); + + // Check if _intf->set_interrupt is allowed + if (!_set_interrupt_allowed) { + if (((current - _last_actual_set_interrupt) & _mask) >= _min_count_between_writes) { + _set_interrupt_allowed = true; + } + } + + uint32_t cycles_until_match = (_cur_match_time - _last_set_interrupt) & _mask; + bool too_close = cycles_until_match < _min_count_until_match; + + if (!_set_interrupt_allowed) { + + // Can't use _intf->set_interrupt so use microsecond Timeout instead + + // Speed optimization - if a timer has already been scheduled + // then don't schedule it again. + if (!_pending_timeout) { + uint32_t ticks = cycles_until_match < _min_count_until_match ? cycles_until_match : _min_count_until_match; + _timeout.attach_us(mbed::callback(this, &LowPowerTickerWrapper::_timeout_handler), _lp_ticks_to_us(ticks)); + _pending_timeout = true; + } + return; + } + + if (!too_close) { + + // Schedule LP ticker + _intf->set_interrupt(_cur_match_time); + current = _intf->read(); + _last_actual_set_interrupt = current; + _set_interrupt_allowed = false; + + // Check for overflow + uint32_t new_cycles_until_match = (_cur_match_time - current) & _mask; + if (new_cycles_until_match > cycles_until_match) { + // Overflow so fire now + _intf->fire_interrupt(); + return; + } + + // Update variables with new time + cycles_until_match = new_cycles_until_match; + too_close = cycles_until_match < _min_count_until_match; + } + + if (too_close) { + + // Low power ticker incremented to less than _min_count_until_match + // so low power ticker may not fire. Use Timeout to ensure it does fire. + uint32_t ticks = cycles_until_match < _min_count_until_match ? cycles_until_match : _min_count_until_match; + _timeout.attach_us(mbed::callback(this, &LowPowerTickerWrapper::_timeout_handler), _lp_ticks_to_us(ticks)); + _pending_timeout = true; + return; + } +} diff --git a/hal/LowPowerTickerWrapper.h b/hal/LowPowerTickerWrapper.h new file mode 100644 index 00000000000..aac8d70d171 --- /dev/null +++ b/hal/LowPowerTickerWrapper.h @@ -0,0 +1,239 @@ + +/** \addtogroup hal */ +/** @{*/ +/* 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. + */ +#ifndef MBED_LOW_POWER_TICKER_WRAPPER_H +#define MBED_LOW_POWER_TICKER_WRAPPER_H + +#include "device.h" + +#include "hal/ticker_api.h" +#include "hal/us_ticker_api.h" +#include "drivers/Timeout.h" + + +class LowPowerTickerWrapper { +public: + + + /** + * Create a new wrapped low power ticker object + * + * @param data Low power ticker data to wrap + * @param interface new ticker interface functions + * @param min_cycles_between_writes The number of whole low power clock periods + * which must complete before subsequent calls to set_interrupt + * @param min_cycles_until_match The minimum number of whole low power clock periods + * from the current time for which the match timestamp passed to set_interrupt is + * guaranteed to fire. + * + * N = min_cycles_between_writes + * + * 0 1 N - 1 N N + 1 N + 2 N + 3 + * |-------|------...------|-------|-------|-------|-------| + * ^ ^ + * | | + * set_interrupt Next set_interrupt allowed + * + * N = min_cycles_until_match + * + * 0 1 N - 1 N N + 1 N + 2 N + 3 + * |-------|------...------|-------|-------|-------|-------| + * ^ ^ + * | | + * set_interrupt Earliest match timestamp allowed + * + * + */ + + LowPowerTickerWrapper(const ticker_data_t *data, const ticker_interface_t *interface, uint32_t min_cycles_between_writes, uint32_t min_cycles_until_match); + + /** + * Interrupt handler called by the underlying driver/hardware + * + * @param handler The callback which would normally be called by the underlying driver/hardware + */ + void irq_handler(ticker_irq_handler_type handler); + + /** + * Suspend wrapper operation and pass through interrupts. + * + * This stops to wrapper layer from using the microsecond ticker. + * This should be called before using the low power ticker APIs directly. + */ + void suspend(); + + /** + * Resume wrapper operation and filter interrupts normally + */ + void resume(); + + /** + * Check if a Timeout object is being used + * + * @return true if Timeout is used for scheduling false otherwise + */ + bool timeout_pending(); + + /* + * Implementation of ticker_init + */ + void init(); + + /* + * Implementation of free + */ + void free(); + + /* + * Implementation of read + */ + uint32_t read(); + + /* + * Implementation of set_interrupt + */ + void set_interrupt(timestamp_t timestamp); + + /* + * Implementation of disable_interrupt + */ + void disable_interrupt(); + + /* + * Implementation of clear_interrupt + */ + void clear_interrupt(); + + /* + * Implementation of fire_interrupt + */ + void fire_interrupt(); + + /* + * Implementation of get_info + */ + const ticker_info_t *get_info(); + + ticker_data_t data; + +private: + mbed::Timeout _timeout; + const ticker_interface_t *const _intf; + + /* + * The number of low power clock cycles which must pass between subsequent + * calls to intf->set_interrupt + */ + const uint32_t _min_count_between_writes; + + /* + * The minimum number of low power clock cycles in the future that + * a match value can be set to and still fire + */ + const uint32_t _min_count_until_match; + + /* + * Flag to indicate if the timer is suspended + */ + bool _suspended; + + /* + * _cur_match_time is valid and Timeout is scheduled to fire + */ + bool _pending_timeout; + + /* + * set_interrupt has been called and _cur_match_time is valid + */ + bool _pending_match; + + /* + * The function LowPowerTickerWrapper::fire_interrupt has been called + * and an interrupt is expected. + */ + bool _pending_fire_now; + + /* + * It is safe to call intf->set_interrupt + */ + bool _set_interrupt_allowed; + + /* + * Last value written by LowPowerTickerWrapper::set_interrupt + */ + timestamp_t _cur_match_time; + + /* + * Time of last call to LowPowerTickerWrapper::set_interrupt + */ + uint32_t _last_set_interrupt; + + /* + * Time of last call to intf->set_interrupt + */ + uint32_t _last_actual_set_interrupt; + + /* + * Mask of valid bits from intf->read() + */ + uint32_t _mask; + + /* + * Microsecond per low power tick (rounded up) + */ + uint32_t _us_per_tick; + + + void _reset(); + + /** + * Set the low power ticker match time when hardware is ready + * + * This event is scheduled to set the lp timer after the previous write + * has taken effect and it is safe to write a new value without blocking. + * If the time has already passed then this function fires and interrupt + * immediately. + */ + void _timeout_handler(); + + /* + * Check match time has passed + */ + bool _match_check(timestamp_t current); + + /* + * Convert low power ticks to approximate microseconds + * + * This value is always larger or equal to exact value. + */ + uint32_t _lp_ticks_to_us(uint32_t); + + /* + * Schedule a match interrupt to fire at the correct time + * + * @param current The current low power ticker time + */ + void _schedule_match(timestamp_t current); + +}; + +#endif + +/** @}*/ + + diff --git a/hal/mbed_lp_ticker_api.c b/hal/mbed_lp_ticker_api.c index fbf72c33411..477c1b13875 100644 --- a/hal/mbed_lp_ticker_api.c +++ b/hal/mbed_lp_ticker_api.c @@ -14,11 +14,10 @@ * limitations under the License. */ #include "hal/lp_ticker_api.h" +#include "hal/mbed_lp_ticker_wrapper.h" #if DEVICE_LPTICKER -void lp_ticker_set_interrupt_wrapper(timestamp_t timestamp); - static ticker_event_queue_t events = { 0 }; static ticker_irq_handler_type irq_handler = ticker_irq_handler; @@ -28,11 +27,7 @@ static const ticker_interface_t lp_interface = { .read = lp_ticker_read, .disable_interrupt = lp_ticker_disable_interrupt, .clear_interrupt = lp_ticker_clear_interrupt, -#if LPTICKER_DELAY_TICKS > 0 - .set_interrupt = lp_ticker_set_interrupt_wrapper, -#else .set_interrupt = lp_ticker_set_interrupt, -#endif .fire_interrupt = lp_ticker_fire_interrupt, .get_info = lp_ticker_get_info, .free = lp_ticker_free, @@ -45,7 +40,11 @@ static const ticker_data_t lp_data = { const ticker_data_t *get_lp_ticker_data(void) { +#if LPTICKER_DELAY_TICKS > 0 + return get_lp_ticker_wrapper_data(&lp_data); +#else return &lp_data; +#endif } ticker_irq_handler_type set_lp_ticker_irq_handler(ticker_irq_handler_type ticker_irq_handler) @@ -59,9 +58,13 @@ ticker_irq_handler_type set_lp_ticker_irq_handler(ticker_irq_handler_type ticker void lp_ticker_irq_handler(void) { +#if LPTICKER_DELAY_TICKS > 0 + lp_ticker_wrapper_irq_handler(irq_handler); +#else if (irq_handler) { irq_handler(&lp_data); } +#endif } #endif diff --git a/hal/mbed_lp_ticker_wrapper.cpp b/hal/mbed_lp_ticker_wrapper.cpp index 9c1a2b47289..403141fceaa 100644 --- a/hal/mbed_lp_ticker_wrapper.cpp +++ b/hal/mbed_lp_ticker_wrapper.cpp @@ -13,144 +13,115 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "hal/lp_ticker_api.h" +#include "hal/mbed_lp_ticker_wrapper.h" #if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0) -#include "Timeout.h" -#include "mbed_critical.h" - -static const timestamp_t min_delta = LPTICKER_DELAY_TICKS; +#include "hal/LowPowerTickerWrapper.h" +#include "platform/mbed_critical.h" +// Do not use SingletonPtr since this must be initialized in a critical section +static LowPowerTickerWrapper *ticker_wrapper; +static uint64_t ticker_wrapper_data[(sizeof(LowPowerTickerWrapper) + 7) / 8]; static bool init = false; -static bool pending = false; -static bool timeout_pending = false; -static timestamp_t last_set_interrupt = 0; -static timestamp_t last_request = 0; -static timestamp_t next = 0; -static timestamp_t mask; -static timestamp_t reschedule_us; +static void lp_ticker_wrapper_init() +{ + ticker_wrapper->init(); +} -// Do not use SingletonPtr since this must be initialized in a critical section -static mbed::Timeout *timeout; -static uint64_t timeout_data[sizeof(mbed::Timeout) / 8]; +static uint32_t lp_ticker_wrapper_read() +{ + return ticker_wrapper->read(); +} -/** - * Initialize variables - */ -static void init_local() +static void lp_ticker_wrapper_set_interrupt(timestamp_t timestamp) { - MBED_ASSERT(core_util_in_critical_section()); + ticker_wrapper->set_interrupt(timestamp); +} - const ticker_info_t *info = lp_ticker_get_info(); - if (info->bits >= 32) { - mask = 0xffffffff; - } else { - mask = ((uint64_t)1 << info->bits) - 1; - } +static void lp_ticker_wrapper_disable_interrupt() +{ + ticker_wrapper->disable_interrupt(); +} - // Round us_per_tick up - timestamp_t us_per_tick = (1000000 + info->frequency - 1) / info->frequency; +static void lp_ticker_wrapper_clear_interrupt() +{ + ticker_wrapper->clear_interrupt(); +} - // Add 1 tick to the min delta for the case where the clock transitions after you read it - // Add 4 microseconds to round up the micro second ticker time (which has a frequency of at least 250KHz - 4us period) - reschedule_us = (min_delta + 1) * us_per_tick + 4; +static void lp_ticker_wrapper_fire_interrupt() +{ + ticker_wrapper->fire_interrupt(); +} - timeout = new (timeout_data) mbed::Timeout(); +static const ticker_info_t *lp_ticker_wrapper_get_info() +{ + return ticker_wrapper->get_info(); } -/** - * Call lp_ticker_set_interrupt with a value that is guaranteed to fire - * - * Assumptions - * -Only one low power clock tick can pass from the last read (last_read) - * -The closest an interrupt can fire is max_delta + 1 - * - * @param last_read The last value read from lp_ticker_read - * @param timestamp The timestamp to trigger the interrupt at - */ -static void set_interrupt_safe(timestamp_t last_read, timestamp_t timestamp) +static void lp_ticker_wrapper_free() { - MBED_ASSERT(core_util_in_critical_section()); - uint32_t delta = (timestamp - last_read) & mask; - if (delta < min_delta + 2) { - timestamp = (last_read + min_delta + 2) & mask; - } - lp_ticker_set_interrupt(timestamp); + ticker_wrapper->free(); } -/** - * Set the low power ticker match time when hardware is ready - * - * This event is scheduled to set the lp timer after the previous write - * has taken effect and it is safe to write a new value without blocking. - * If the time has already passed then this function fires and interrupt - * immediately. - */ -static void set_interrupt_later() +static const ticker_interface_t lp_interface = { + lp_ticker_wrapper_init, + lp_ticker_wrapper_read, + lp_ticker_wrapper_disable_interrupt, + lp_ticker_wrapper_clear_interrupt, + lp_ticker_wrapper_set_interrupt, + lp_ticker_wrapper_fire_interrupt, + lp_ticker_wrapper_free, + lp_ticker_wrapper_get_info +}; + +void lp_ticker_wrapper_irq_handler(ticker_irq_handler_type handler) { core_util_critical_section_enter(); - timestamp_t current = lp_ticker_read(); - if (_ticker_match_interval_passed(last_request, current, next)) { - lp_ticker_fire_interrupt(); - } else { - set_interrupt_safe(current, next); - last_set_interrupt = lp_ticker_read(); + if (!init) { + // Force ticker to initialize + get_lp_ticker_data(); } - timeout_pending = false; + + ticker_wrapper->irq_handler(handler); core_util_critical_section_exit(); } -/** - * Wrapper around lp_ticker_set_interrupt to prevent blocking - * - * Problems this function is solving: - * 1. Interrupt may not fire if set earlier than LPTICKER_DELAY_TICKS low power clock cycles - * 2. Setting the interrupt back-to-back will block - * - * This wrapper function prevents lp_ticker_set_interrupt from being called - * back-to-back and blocking while the first write is in progress. This function - * avoids that problem by scheduling a timeout event if the lp ticker is in the - * middle of a write operation. - * - * @param timestamp Time to call ticker irq - * @note this is a utility function and it's not required part of HAL implementation - */ -extern "C" void lp_ticker_set_interrupt_wrapper(timestamp_t timestamp) +const ticker_data_t *get_lp_ticker_wrapper_data(const ticker_data_t *data) { core_util_critical_section_enter(); if (!init) { - init_local(); + ticker_wrapper = new (ticker_wrapper_data) LowPowerTickerWrapper(data, &lp_interface, LPTICKER_DELAY_TICKS, LPTICKER_DELAY_TICKS); init = true; } - timestamp_t current = lp_ticker_read(); - if (pending) { - // Check if pending should be cleared - if (((current - last_set_interrupt) & mask) >= min_delta) { - pending = false; - } + core_util_critical_section_exit(); + + return &ticker_wrapper->data; +} + +void lp_ticker_wrapper_suspend() +{ + if (!init) { + // Force ticker to initialize + get_lp_ticker_data(); } - if (pending || timeout_pending) { - next = timestamp; - last_request = current; - if (!timeout_pending) { - timeout->attach_us(set_interrupt_later, reschedule_us); - timeout_pending = true; - } - } else { - // Schedule immediately if nothing is pending - set_interrupt_safe(current, timestamp); - last_set_interrupt = lp_ticker_read(); - pending = true; + ticker_wrapper->suspend(); +} + +void lp_ticker_wrapper_resume() +{ + if (!init) { + // Force ticker to initialize + get_lp_ticker_data(); } - core_util_critical_section_exit(); + ticker_wrapper->resume(); } #endif diff --git a/hal/mbed_lp_ticker_wrapper.h b/hal/mbed_lp_ticker_wrapper.h new file mode 100644 index 00000000000..4b317e36c27 --- /dev/null +++ b/hal/mbed_lp_ticker_wrapper.h @@ -0,0 +1,78 @@ + +/** \addtogroup hal */ +/** @{*/ +/* 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. + */ +#ifndef MBED_LP_TICKER_WRAPPER_H +#define MBED_LP_TICKER_WRAPPER_H + +#include "device.h" + +#if DEVICE_LPTICKER + +#include "hal/ticker_api.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*ticker_irq_handler_type)(const ticker_data_t *const); + +/** + * Interrupt handler for the wrapped lp ticker + * + * @param handler the function which would normally be called by the + * lp ticker handler when it is not wrapped + */ +void lp_ticker_wrapper_irq_handler(ticker_irq_handler_type handler); + +/** + * Get wrapped lp ticker data + * + * @param data hardware low power ticker object + * @return wrapped low power ticker object + */ +const ticker_data_t *get_lp_ticker_wrapper_data(const ticker_data_t *data); + +/** + * Suspend the wrapper layer code + * + * Pass through all interrupts to the low power ticker and stop using + * the microsecond ticker. + */ +void lp_ticker_wrapper_suspend(void); + +/** + * Resume the wrapper layer code + * + * Resume operation of the wrapper layer. Interrupts will be filtered + * as normal and the microsecond timer will be used for interrupts scheduled + * too quickly back-to-back. + */ +void lp_ticker_wrapper_resume(void); + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif + +#endif + +/** @}*/ diff --git a/hal/mbed_sleep_manager.c b/hal/mbed_sleep_manager.c index d485965da67..30e2d74837c 100644 --- a/hal/mbed_sleep_manager.c +++ b/hal/mbed_sleep_manager.c @@ -21,6 +21,7 @@ #include "mbed_error.h" #include "mbed_debug.h" #include "mbed_stats.h" +#include "us_ticker_api.h" #include "lp_ticker_api.h" #include #include @@ -183,6 +184,19 @@ bool sleep_manager_can_deep_sleep(void) return deep_sleep_lock == 0 ? true : false; } +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) { + if (sleep_manager_can_deep_sleep()) { + return true; + } + } + return false; +} + void sleep_manager_sleep_auto(void) { #ifdef MBED_SLEEP_TRACING_ENABLED diff --git a/hal/mbed_ticker_api.c b/hal/mbed_ticker_api.c index f1dcf3bf107..cdddc507fd6 100644 --- a/hal/mbed_ticker_api.c +++ b/hal/mbed_ticker_api.c @@ -32,6 +32,9 @@ static void initialize(const ticker_data_t *ticker) if (ticker->queue->initialized) { return; } + if (ticker->queue->suspended) { + return; + } ticker->interface->init(); @@ -70,6 +73,7 @@ static void initialize(const ticker_data_t *ticker) ticker->queue->max_delta_us = max_delta_us; ticker->queue->present_time = 0; ticker->queue->dispatching = false; + ticker->queue->suspended = false; ticker->queue->initialized = true; update_present_time(ticker); @@ -121,6 +125,9 @@ static us_timestamp_t convert_timestamp(us_timestamp_t ref, timestamp_t timestam static void update_present_time(const ticker_data_t *const ticker) { ticker_event_queue_t *queue = ticker->queue; + if (queue->suspended) { + return; + } uint32_t ticker_time = ticker->interface->read(); if (ticker_time == ticker->queue->tick_last_read) { // No work to do @@ -230,7 +237,7 @@ int _ticker_match_interval_passed(timestamp_t prev_tick, timestamp_t cur_tick, t static void schedule_interrupt(const ticker_data_t *const ticker) { ticker_event_queue_t *queue = ticker->queue; - if (ticker->queue->dispatching) { + if (queue->suspended || ticker->queue->dispatching) { // Don't schedule the next interrupt until dispatching is // finished. This prevents repeated calls to interface->set_interrupt return; @@ -285,6 +292,10 @@ void ticker_irq_handler(const ticker_data_t *const ticker) core_util_critical_section_enter(); ticker->interface->clear_interrupt(); + if (ticker->queue->suspended) { + core_util_critical_section_exit(); + return; + } /* Go through all the pending TimerEvents */ ticker->queue->dispatching = true; @@ -430,3 +441,29 @@ int ticker_get_next_timestamp(const ticker_data_t *const data, timestamp_t *time return ret; } + +void ticker_suspend(const ticker_data_t *const ticker) +{ + core_util_critical_section_enter(); + + ticker->queue->suspended = true; + + core_util_critical_section_exit(); +} + +void ticker_resume(const ticker_data_t *const ticker) +{ + core_util_critical_section_enter(); + + ticker->queue->suspended = false; + if (ticker->queue->initialized) { + ticker->queue->tick_last_read = ticker->interface->read(); + + update_present_time(ticker); + schedule_interrupt(ticker); + } else { + initialize(ticker); + } + + core_util_critical_section_exit(); +} diff --git a/hal/ticker_api.h b/hal/ticker_api.h index 1ab8b0e32be..98b2786ee35 100644 --- a/hal/ticker_api.h +++ b/hal/ticker_api.h @@ -82,6 +82,7 @@ typedef struct { us_timestamp_t present_time; /**< Store the timestamp used for present time */ bool initialized; /**< Indicate if the instance is initialized */ bool dispatching; /**< The function ticker_irq_handler is dispatching */ + bool suspended; /**< Indicate if the instance is suspended */ uint8_t frequency_shifts; /**< If frequency is a value of 2^n, this is n, otherwise 0 */ } ticker_event_queue_t; @@ -184,6 +185,27 @@ us_timestamp_t ticker_read_us(const ticker_data_t *const ticker); */ int ticker_get_next_timestamp(const ticker_data_t *const ticker, timestamp_t *timestamp); +/** Suspend this ticker + * + * When suspended reads will always return the same time and no + * events will be dispatched. When suspended the common layer + * will only ever call the interface function clear_interrupt() + * and that is only if ticker_irq_handler is called. + * + * + * @param ticker The ticker object. + */ +void ticker_suspend(const ticker_data_t *const ticker); + +/** Resume this ticker + * + * When resumed the ticker will ignore any time that has passed + * and continue counting up where it left off. + * + * @param ticker The ticker object. + */ +void ticker_resume(const ticker_data_t *const ticker); + /* Private functions * * @cond PRIVATE diff --git a/platform/mbed_power_mgmt.h b/platform/mbed_power_mgmt.h index 5d30348dcea..026cbf214d1 100644 --- a/platform/mbed_power_mgmt.h +++ b/platform/mbed_power_mgmt.h @@ -122,6 +122,17 @@ void sleep_manager_unlock_deep_sleep_internal(void); */ bool sleep_manager_can_deep_sleep(void); +/** Check if the target can deep sleep within a period of time + * + * This function in intended for use in testing. The amount + * of time this functions waits for deeps sleep to be available + * is currently 2ms. This may change in the future depending + * on testing requirements. + * + * @return true if a target can go to deepsleep, false otherwise + */ +bool sleep_manager_can_deep_sleep_test_check(void); + /** Enter auto selected sleep mode. It chooses the sleep or deeepsleep modes based * on the deepsleep locking counter *