diff --git a/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp b/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp new file mode 100644 index 0000000000..51a85cea96 --- /dev/null +++ b/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp @@ -0,0 +1,274 @@ +/* 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. + */ +#if !MBED_TICKLESS +#error [NOT_SUPPORTED] Tickless mode not supported for this target. +#endif + +#if !DEVICE_LOWPOWERTIMER +#error [NOT_SUPPORTED] Current SysTimer implementation requires lp ticker support. +#endif + +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" + +extern "C" { +#include "rtx_lib.h" +} +#include "rtos/TARGET_CORTEX/SysTimer.h" + +#define TEST_TICKS 42UL +#define DELAY_DELTA_US 2500ULL + +using namespace utest::v1; + +const us_timestamp_t DELAY_US = 1000000ULL * TEST_TICKS / OS_TICK_FREQ; + +// Override the handler() -- the SysTick interrupt must not be set as pending by the test code. +class SysTimerTest: public rtos::internal::SysTimer { +private: + Semaphore _sem; + virtual void handler() + { + increment_tick(); + _sem.release(); + } + +public: + SysTimerTest() : + SysTimer(), _sem(0, 1) + { + } + + virtual ~SysTimerTest() + { + } + + int32_t sem_wait(uint32_t millisec) + { + return _sem.wait(millisec); + } +}; + +/** Test tick count is zero upon creation + * + * Given a SysTimer + * When the timer is created + * Then tick count is zero + */ +void test_created_with_zero_tick_count(void) +{ + SysTimerTest st; + TEST_ASSERT_EQUAL_UINT32(0, st.get_tick()); +} + +/** Test tick count is updated correctly + * + * Given a SysTimer + * When @a update_tick method is called immediately after creation + * Then the tick count is not updated + * When @a update_tick is called again after a delay + * Then the tick count is updated + * and the number of ticks incremented is equal TEST_TICKS - 1 + * When @a update_tick is called again without a delay + * Then the tick count is not updated + */ +void test_update_tick(void) +{ + SysTimerTest st; + TEST_ASSERT_EQUAL_UINT32(0, st.update_tick()); + TEST_ASSERT_EQUAL_UINT32(0, st.get_tick()); + us_timestamp_t test_ticks_elapsed_ts = st.get_time() + DELAY_US; + + while (st.get_time() <= test_ticks_elapsed_ts) {} + TEST_ASSERT_EQUAL_UINT32(TEST_TICKS - 1, st.update_tick()); + TEST_ASSERT_EQUAL_UINT32(TEST_TICKS - 1, st.get_tick()); + + TEST_ASSERT_EQUAL_UINT32(0, st.update_tick()); + TEST_ASSERT_EQUAL_UINT32(TEST_TICKS - 1, st.get_tick()); +} + +/** Test get_time returns correct time + * + * Given a SysTimer + * When @a get_time method is called before and after a delay + * Then time difference is equal the delay + */ +void test_get_time(void) +{ + SysTimerTest st; + us_timestamp_t t1 = st.get_time(); + + wait_us(DELAY_US); + us_timestamp_t t2 = st.get_time(); + TEST_ASSERT_UINT64_WITHIN(DELAY_DELTA_US, DELAY_US, t2 - t1); +} + +/** Test cancel_tick + * + * Given a SysTimer with a scheduled tick + * When @a cancel_tick is called before the given number of ticks elapse + * Then the handler is never called + * and the tick count is not incremented + */ +void test_cancel_tick(void) +{ + SysTimerTest st; + st.cancel_tick(); + st.schedule_tick(TEST_TICKS); + + st.cancel_tick(); + int32_t sem_slots = st.sem_wait((DELAY_US + DELAY_DELTA_US) / 1000ULL); + TEST_ASSERT_EQUAL_INT32(0, sem_slots); + TEST_ASSERT_EQUAL_UINT32(0, st.get_tick()); +} + +/** Test schedule zero + * + * Given a SysTimer + * When a tick is scheduled with delta = 0 ticks + * Then the handler is called instantly + */ +void test_schedule_zero(void) +{ + SysTimerTest st; + + st.schedule_tick(0UL); + int32_t sem_slots = st.sem_wait(0UL); + TEST_ASSERT_EQUAL_INT32(1, sem_slots); +} + +/** Test handler called once + * + * Given a SysTimer with a tick scheduled with delta = TEST_TICKS + * When the handler is called + * Then the tick count is incremented by 1 + * and elapsed time is equal 1000000ULL * TEST_TICKS / OS_TICK_FREQ; + * When more time elapses + * Then the handler is not called again + */ +void test_handler_called_once(void) +{ + SysTimerTest st; + st.schedule_tick(TEST_TICKS); + us_timestamp_t t1 = st.get_time(); + int32_t sem_slots = st.sem_wait(0); + TEST_ASSERT_EQUAL_INT32(0, sem_slots); + + sem_slots = st.sem_wait(osWaitForever); + us_timestamp_t t2 = st.get_time(); + TEST_ASSERT_EQUAL_INT32(1, sem_slots); + TEST_ASSERT_EQUAL_UINT32(1, st.get_tick()); + TEST_ASSERT_UINT64_WITHIN(DELAY_DELTA_US, DELAY_US, t2 - t1); + + sem_slots = st.sem_wait((DELAY_US + DELAY_DELTA_US) / 1000ULL); + TEST_ASSERT_EQUAL_INT32(0, sem_slots); + TEST_ASSERT_EQUAL_UINT32(1, st.get_tick()); +} + +/** Test wake up from sleep + * + * Given a SysTimer with a tick scheduled in the future + * and a core in sleep mode + * When given time elapses + * Then the uC is woken up from sleep + * and the tick handler is called + * and measured time matches requested delay + */ +void test_sleep(void) +{ + Timer timer; + SysTimerTest st; + + sleep_manager_lock_deep_sleep(); + timer.start(); + st.schedule_tick(TEST_TICKS); + + TEST_ASSERT_FALSE_MESSAGE(sleep_manager_can_deep_sleep(), "Deep sleep should be disallowed"); + while (st.sem_wait(0) != 1) { + sleep(); + } + timer.stop(); + sleep_manager_unlock_deep_sleep(); + + TEST_ASSERT_UINT64_WITHIN(DELAY_DELTA_US, DELAY_US, timer.read_high_resolution_us()); +} + +#if DEVICE_LOWPOWERTIMER +/** Test wake up from deepsleep + * + * Given a SysTimer with a tick scheduled in the future + * and a core in deepsleep mode + * When given time elapses + * Then the uC is woken up from deepsleep + * and the tick handler is called + * and measured time matches requested delay + */ +void test_deepsleep(void) +{ + /* + * Since deepsleep() may shut down the UART peripheral, we wait for 10ms + * to allow for hardware serial buffers to completely flush. + + * 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. + */ + wait_ms(10); + + // Regular Timer might be disabled during deepsleep. + LowPowerTimer lptimer; + SysTimerTest st; + + lptimer.start(); + st.schedule_tick(TEST_TICKS); + TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep(), "Deep sleep should be allowed"); + while (st.sem_wait(0) != 1) { + sleep(); + } + lptimer.stop(); + + TEST_ASSERT_UINT64_WITHIN(DELAY_DELTA_US, DELAY_US, lptimer.read_high_resolution_us()); +} +#endif + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(5, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Tick count is zero upon creation", test_created_with_zero_tick_count), + Case("Tick count is updated correctly", test_update_tick), + Case("Time is updated correctly", test_get_time), + Case("Tick can be cancelled", test_cancel_tick), + Case("Schedule zero ticks", test_schedule_zero), + Case("Handler called once", test_handler_called_once), + Case("Wake up from sleep", test_sleep), +#if DEVICE_LOWPOWERTIMER + Case("Wake up from deep sleep", test_deepsleep), +#endif + +}; + +Specification specification(test_setup, cases); + +int main() +{ + return !Harness::run(specification); +}