diff --git a/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp b/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp index 44492f1..13b60ac 100644 --- a/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp +++ b/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp @@ -23,7 +23,7 @@ extern "C" { #include "rtx_lib.h" } -#include "platform/SysTimer.h" +#include "platform/source/SysTimer.h" #define TEST_TICKS 42 #define TEST_TICK_US (TEST_TICKS * 1000) diff --git a/events/source/equeue_mbed.cpp b/events/source/equeue_mbed.cpp index 394e12e..f875f56 100644 --- a/events/source/equeue_mbed.cpp +++ b/events/source/equeue_mbed.cpp @@ -38,7 +38,7 @@ #if MBED_CONF_RTOS_API_PRESENT #include "rtos/Kernel.h" -#include "platform/mbed_os_timer.h" +#include "platform/source/mbed_os_timer.h" void equeue_tick_init() { diff --git a/platform/SysTimer.cpp b/platform/SysTimer.cpp deleted file mode 100644 index a79a4b8..0000000 --- a/platform/SysTimer.cpp +++ /dev/null @@ -1,353 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * 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/us_ticker_api.h" -#include "hal/lp_ticker_api.h" -#include "mbed_atomic.h" -#include "mbed_critical.h" -#include "mbed_assert.h" -#include "platform/mbed_power_mgmt.h" -#include "platform/CriticalSectionLock.h" -#include "platform/SysTimer.h" -extern "C" { -#if MBED_CONF_RTOS_PRESENT -#include "rtx_lib.h" -#endif -} - -#if (defined(NO_SYSTICK)) -/** - * Return an IRQ number that can be used in the absence of SysTick - * - * @return Free IRQ number that can be used - */ -extern "C" IRQn_Type mbed_get_m0_tick_irqn(void); -#endif - -#if defined(TARGET_CORTEX_A) -extern "C" IRQn_ID_t mbed_get_a9_tick_irqn(void); -#endif - -namespace mbed { -namespace internal { - -template -SysTimer::SysTimer() : -#if DEVICE_LPTICKER - TimerEvent(get_lp_ticker_data()), -#else - TimerEvent(get_us_ticker_data()), -#endif - _time_us(ticker_read_us(_ticker_data)), - _tick(0), - _unacknowledged_ticks(0), - _wake_time_set(false), - _wake_time_passed(false), - _ticking(false), - _deep_sleep_locked(false) -{ -} - -template -SysTimer::SysTimer(const ticker_data_t *data) : - TimerEvent(data), - _time_us(ticker_read_us(_ticker_data)), - _tick(0), - _unacknowledged_ticks(0), - _wake_time_set(false), - _wake_time_passed(false), - _ticking(false), - _deep_sleep_locked(false) -{ -} - -template -SysTimer::~SysTimer() -{ - cancel_tick(); - cancel_wake(); -} - -template -void SysTimer::set_wake_time(uint64_t at) -{ - // SysTimer must not be active - we must be in suspend state - MBED_ASSERT(!_ticking); - - // There is a potential race here, when called from outside - // a critical section. See function documentation for notes on - // handling it. - if (core_util_atomic_load_bool(&_wake_time_set)) { - return; - } - - // Analyse the timers - if (update_and_get_tick() >= at) { - _wake_time_passed = true; - return; - } - - uint64_t ticks_to_sleep = at - _tick; - uint64_t wake_time = at * US_IN_TICK; - - /* Set this first, before attaching the interrupt that can unset it */ - _wake_time_set = true; - _wake_time_passed = false; - - if (!_deep_sleep_locked && !_ticker_data->interface->runs_in_deep_sleep) { - _deep_sleep_locked = true; - sleep_manager_lock_deep_sleep(); - } - - /* If deep sleep is unlocked, and we have enough time, let's go for it */ - if (MBED_CONF_TARGET_DEEP_SLEEP_LATENCY > 0 && - ticks_to_sleep > MBED_CONF_TARGET_DEEP_SLEEP_LATENCY && - sleep_manager_can_deep_sleep()) { - /* Schedule the wake up interrupt early, allowing for the deep sleep latency */ - _wake_early = true; - insert_absolute(wake_time - MBED_CONF_TARGET_DEEP_SLEEP_LATENCY * US_IN_TICK); - } else { - /* Otherwise, we'll set up for shallow sleep at the precise time. - * To make absolutely sure it's shallow so we don't incur the latency, - * take our own lock, to avoid a race on a thread unlocking it. - */ - _wake_early = false; - if (MBED_CONF_TARGET_DEEP_SLEEP_LATENCY > 0 && !_deep_sleep_locked) { - _deep_sleep_locked = true; - sleep_manager_lock_deep_sleep(); - } - insert_absolute(wake_time); - } -} - -template -void SysTimer::cancel_wake() -{ - MBED_ASSERT(!_ticking); - // Remove ensures serialized access to SysTimer by stopping timer interrupt - remove(); - - _wake_time_set = false; - _wake_time_passed = false; - - if (_deep_sleep_locked) { - _deep_sleep_locked = false; - sleep_manager_unlock_deep_sleep(); - } -} - -template -uint64_t SysTimer::_elapsed_ticks() const -{ - uint64_t elapsed_us = ticker_read_us(_ticker_data) - _time_us; - if (elapsed_us < US_IN_TICK) { - return 0; - } else if (elapsed_us < 2 * US_IN_TICK) { - return 1; - } else if (elapsed_us <= 0xFFFFFFFF) { - // Fast common case avoiding 64-bit division - return (uint32_t) elapsed_us / US_IN_TICK; - } else { - return elapsed_us / US_IN_TICK; - } -} - -template -void SysTimer::start_tick() -{ - _ticking = true; - if (_unacknowledged_ticks > 0) { - _set_irq_pending(); - } - _schedule_tick(); -} - -template -void SysTimer::_schedule_tick() -{ - insert_absolute(_time_us + US_IN_TICK); -} - -template -void SysTimer::acknowledge_tick() -{ - // Try to avoid missed ticks if OS's IRQ level is not keeping - // up with our handler. - // 8-bit counter to save space, and also make sure it we don't - // try TOO hard to resync if something goes really awry - - // resync will reset if the count hits 256. - if (core_util_atomic_decr_u8(&_unacknowledged_ticks, 1) > 0) { - _set_irq_pending(); - } -} - -template -void SysTimer::cancel_tick() -{ - // Underlying call is interrupt safe - - remove(); - _ticking = false; - - _clear_irq_pending(); -} - -template -uint64_t SysTimer::get_tick() const -{ - // Atomic is necessary as this can be called from any foreground context, - // while IRQ can update it. - return core_util_atomic_load_u64(&_tick); -} - -template -uint64_t SysTimer::update_and_get_tick() -{ - MBED_ASSERT(!_ticking && !_wake_time_set); - // Can only be used when no interrupts are scheduled - // Update counters to reflect elapsed time - uint64_t elapsed_ticks = _elapsed_ticks(); - _unacknowledged_ticks = 0; - _time_us += elapsed_ticks * US_IN_TICK; - _tick += elapsed_ticks; - - return _tick; -} - -template -us_timestamp_t SysTimer::get_time() const -{ - // Underlying call is interrupt safe - - return ticker_read_us(_ticker_data); -} - -template -us_timestamp_t SysTimer::get_time_since_tick() const -{ - // Underlying call is interrupt safe, and _time_us is not updated by IRQ - - return get_time() - _time_us; -} - -#if (defined(NO_SYSTICK)) -template -IRQn_Type SysTimer::get_irq_number() -{ - return mbed_get_m0_tick_irqn(); -} -#elif (TARGET_CORTEX_M) -template -IRQn_Type SysTimer::get_irq_number() -{ - return SysTick_IRQn; -} -#elif (TARGET_CORTEX_A) -template -IRQn_ID_t SysTimer::get_irq_number() -{ - return mbed_get_a9_tick_irqn(); -} -#endif - -template -void SysTimer::_set_irq_pending() -{ - // Protected function synchronized externally - if (!IRQ) { - return; - } -#if (defined(NO_SYSTICK)) - NVIC_SetPendingIRQ(mbed_get_m0_tick_irqn()); -#elif (TARGET_CORTEX_M) - SCB->ICSR = SCB_ICSR_PENDSTSET_Msk; -#else - IRQ_SetPending(mbed_get_a9_tick_irqn()); -#endif -} - -template -void SysTimer::_clear_irq_pending() -{ - // Protected function synchronized externally - if (!IRQ) { - return; - } -#if (defined(NO_SYSTICK)) - NVIC_ClearPendingIRQ(mbed_get_m0_tick_irqn()); -#elif (TARGET_CORTEX_M) - SCB->ICSR = SCB_ICSR_PENDSTCLR_Msk; -#else - IRQ_ClearPending(mbed_get_a9_tick_irqn()); -#endif -} - -template -void SysTimer::_increment_tick() -{ - // Protected function synchronized externally - - _tick++; - _time_us += US_IN_TICK; -} - -template -void SysTimer::handler() -{ - /* To reduce IRQ latency problems, we do not re-arm in the interrupt handler */ - if (_wake_time_set) { - _wake_time_set = false; - if (!_wake_early) { - _wake_time_passed = true; - } - /* If this was an early interrupt, user has the responsibility to check and - * note the combination of (!set, !passed), and re-arm the wake timer if - * necessary. - */ - } else if (_ticking) { - _unacknowledged_ticks++; - _set_irq_pending(); - _increment_tick(); - // We do this now, rather than in acknowledgement, as we get it "for free" - // here - because we're in the ticker handler, the programming gets deferred - // until end of dispatch, and the ticker would likely be rescheduling - // anyway after dispatch. - - _schedule_tick(); - } -} - -#if MBED_CONF_RTOS_PRESENT -/* Whatever the OS wants (in case it isn't 1ms) */ -MBED_STATIC_ASSERT(1000000 % OS_TICK_FREQ == 0, "OS_TICK_FREQ must be a divisor of 1000000 for correct tick calculations"); -#define OS_TICK_US (1000000 / OS_TICK_FREQ) -#if OS_TICK_US != 1000 -template class SysTimer; -#endif -#endif - -/* Standard 1ms SysTimer */ -template class SysTimer<1000>; - -/* Standard 1ms SysTimer that doesn't set interrupts, used for Greentea tests */ -template class SysTimer<1000, false>; - -/* Slowed-down SysTimer that doesn't set interrupts, used for Greentea tests */ -template class SysTimer<42000, false>; - -} // namespace internal -} // namespace mbed diff --git a/platform/SysTimer.h b/platform/SysTimer.h deleted file mode 100644 index f6e3b2a..0000000 --- a/platform/SysTimer.h +++ /dev/null @@ -1,251 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * 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_SYS_TIMER_H -#define MBED_SYS_TIMER_H - -#include "platform/NonCopyable.h" -#include "platform/mbed_atomic.h" -#include "drivers/TimerEvent.h" -#include "cmsis.h" - -extern "C" { -#if defined(TARGET_CORTEX_A) -#include "irq_ctrl.h" -#endif -} - -namespace mbed { -namespace internal { - -/** - * @cond MBED_INTERNAL - * - * @addtogroup mbed - * @{ - * - * @defgroup mbed_SysTimer SysTimer class - * @{ - */ - -/** - * The SysTimer class is used to provide timing for system suspension, and - * the idle loop in TICKLESS mode. - * - * Template for speed for testing - only one instance will be used normally. - * - * @note SysTimer is not the part of Mbed API. - */ -template -class SysTimer: private mbed::TimerEvent, private mbed::NonCopyable > { -public: - - /** - * Default constructor uses LPTICKER if available (so the timer will - * continue to run in deep sleep), else USTICKER. - */ - SysTimer(); - - SysTimer(const ticker_data_t *data); - - ~SysTimer(); - - /** - * Get the interrupt number for the tick - * - * @return interrupt number - */ -#if TARGET_CORTEX_A - static IRQn_ID_t get_irq_number(); -#elif TARGET_CORTEX_M - static IRQn_Type get_irq_number(); -#endif - - /** - * Set the wake time - * - * Schedules an interrupt to cause wake-up in time for the event. Interrupt - * may be arranged early to account for latency. If the time has already - * passed, no interrupt will be scheduled. - * - * This is called from outside a critical section, as it is known to be - * a slow operation. - * - * If the wake time is already set, this is a no-op. But that check is racy, - * which means wake_time_set() should be rechecked after taking a critical - * section. - * - * As a side-effect, this clears the unacknowledged tick count - the caller - * is expected to use update_and_get_tick() after the suspend operation. - * - * @param at Wake up tick - * @warning If the ticker tick is already scheduled it needs to be cancelled first! - */ - void set_wake_time(uint64_t at); - - /** - * Check whether the wake time has passed - * - * This is a fast operation, based on checking whether the wake interrupt - * has run. - * - * @return true if the specified wake tick has passed - */ - bool wake_time_passed() const - { - return core_util_atomic_load_bool(&_wake_time_passed); - } - - /** - * Check whether wake timer is active - * - * @return true if the wake timer is active. - */ - bool wake_time_set() const - { - return core_util_atomic_load_bool(&_wake_time_set); - } - - /** - * Cancel any pending wake - */ - void cancel_wake(); - - /** - * Schedule an os tick to fire - * - * Ticks will be rescheduled automatically every tick until cancel_tick is called. - * - * A tick will be fired immediately if there are any unacknowledged ticks. - * - * @warning If a tick is already scheduled it needs to be cancelled first! - */ - void start_tick(); - - /** - * Acknowledge an os tick - * - * This will queue another os tick immediately if the os is running slow - */ - void acknowledge_tick(); - - /** - * Prevent any more scheduled ticks from triggering - * - * If called from OS tick context, there may be remaining unacknowledged ticks. - */ - void cancel_tick(); - - /** - * Check whether ticker is active - * - * Each time the tick interrupt fires, it is automatically rescheduled, - * so this will remain true once the tick is started, except during - * processing. - * - * @return true if the ticker is active. - */ - bool ticking() const - { - return core_util_atomic_load_bool(&_ticking); - } - - /** - * Check unacknowledged ticks - * - * Returns the count of how many times the OS timer has been queued minus - * the number of times is has been acknowledged. - * - * get_tick() - unacknowledged_ticks() should equal the OS's tick count, - * although such a calculation is not atomic if the ticker is currently running. - * - * @return number of unacknowledged ticks - */ - int unacknowledged_ticks() const - { - return core_util_atomic_load_u8(&_unacknowledged_ticks); - } - - /** Get the current tick count - * - * This count is updated by the ticker interrupt, if the ticker interrupt - * is running. It the ticker interrupt is not running, update_and_get_tick() - * should be used instead. - * - * This indicates how many ticks have been generated by the tick interrupt. - * The os_timer should equal this number minus the number of unacknowledged ticks. - * - * @return The number of ticks since timer creation. - */ - uint64_t get_tick() const; - - /** Update and get the current tick count - * - * This is a slow operation that reads the timer and adjusts for elapsed time. - * Can only be used when the ticker is not running, as there is no IRQ - * synchronization. - * - * This clears the unacknowledged tick counter - the caller is assumed to update - * their timer based on this return. - * - * @return The number of ticks since timer creation. - */ - uint64_t update_and_get_tick(); - - /** - * Returns time since last tick - * - * @return Relative time in microseconds - */ - us_timestamp_t get_time_since_tick() const; - - /** - * Get the time - * - * Returns the instantaneous precision time from underlying timer. - * This is a slow operation so should not be called from critical sections. - * - * @return Current time in microseconds - */ - us_timestamp_t get_time() const; - -protected: - virtual void handler(); - void _increment_tick(); - void _schedule_tick(); - uint64_t _elapsed_ticks() const; - static void _set_irq_pending(); - static void _clear_irq_pending(); - us_timestamp_t _time_us; - uint64_t _tick; - uint8_t _unacknowledged_ticks; - bool _wake_time_set; - bool _wake_time_passed; - bool _wake_early; - bool _ticking; - bool _deep_sleep_locked; -}; - -/** - * @} - * @} - * @endcond - */ - -} -} - -#endif diff --git a/platform/mbed_os_timer.cpp b/platform/mbed_os_timer.cpp deleted file mode 100644 index 3032249..0000000 --- a/platform/mbed_os_timer.cpp +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (c) 2006-2019, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * 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 "platform/mbed_power_mgmt.h" -#include "platform/mbed_os_timer.h" -#include "platform/CriticalSectionLock.h" -#include "platform/SysTimer.h" -#include "us_ticker_api.h" -#include "lp_ticker_api.h" -#include "mbed_critical.h" -#include "mbed_assert.h" -#include - -/* This provides the marshalling point for a system global SysTimer, which - * is used to provide: - * - timed sleeps (for default idle hook in RTOS tickless mode, or non-RTOS sleeps) - * - regular ticks for RTOS - * - absolute system timing (directly for non-RTOS, or indirectly via RTOS tick count) - */ - -namespace mbed { -namespace internal { - -OsTimer *os_timer; - -namespace { -uint64_t os_timer_data[(sizeof(OsTimer) + 7) / 8]; -} - -OsTimer *init_os_timer() -{ - // Do not use SingletonPtr since this relies on the RTOS. - // Locking not required as it will be first called during - // OS init, or else we're a non-RTOS single-threaded setup. - if (!os_timer) { -#if MBED_CONF_TARGET_TICKLESS_FROM_US_TICKER && DEVICE_USTICKER - os_timer = new (os_timer_data) OsTimer(get_us_ticker_data()); -#elif !MBED_CONF_TARGET_TICKLESS_FROM_US_TICKER && DEVICE_LPTICKER - os_timer = new (os_timer_data) OsTimer(get_lp_ticker_data()); -#else - MBED_ASSERT("OS timer not available - check MBED_CONF_TARGET_TICKLESS_FROM_US_TICKER" && false); - return NULL; -#endif - //os_timer->setup_irq(); - } - - return os_timer; -} - -/* These traits classes are designed to permit chunks of code to be - * omitted - in particular eliminating timers. However, we don't want - * to cause template bloat, so don't have too many traits variants. - */ - -/* Optionally timed operation, with optional predicate */ -struct timed_predicate_op { - timed_predicate_op(uint64_t t) : wake_time(t), orig_predicate(NULL), orig_handle(NULL) - { - init_os_timer(); - } - - timed_predicate_op(uint64_t t, bool (*wake_predicate)(void *), void *wake_predicate_handle) : wake_time(t), orig_predicate(wake_predicate), orig_handle(wake_predicate_handle) - { - init_os_timer(); - } - - ~timed_predicate_op() - { - // Make sure wake timer is cancelled. (It may or may not be, depending on - // why we woke). - os_timer->cancel_wake(); - } - - bool wake_condition() const - { - return (orig_predicate && orig_predicate(orig_handle)) || os_timer->wake_time_passed(); - } - - void sleep_prepare() - { - if (wake_time != (uint64_t) -1) { - os_timer->set_wake_time(wake_time); - } - } - - bool sleep_prepared() - { - return wake_time == (uint64_t) -1 || os_timer->wake_time_set(); - } - -private: - uint64_t wake_time; - bool (*orig_predicate)(void *); - void *orig_handle; -}; - -/* Untimed operation with predicate */ -struct untimed_op { - untimed_op(bool (*wake_predicate)(void *), void *wake_predicate_handle) : orig_predicate(wake_predicate), orig_handle(wake_predicate_handle) - { - } - - bool wake_condition() const - { - return orig_predicate(orig_handle); - } - - void sleep_prepare() - { - } - - bool sleep_prepared() - { - return true; - } - -private: - bool (*orig_predicate)(void *); - void *orig_handle; -}; - -/* We require that this is called from thread context, outside a critical section, - * and the kernel already suspended if an RTOS, meaning we don't have to worry - * about any potential threading issues. - * - * The wake predicate will be called from both outside and inside a critical - * section, so appropriate atomic care must be taken. - */ -template -void do_sleep_operation(OpT &op) -{ - // We assume the ticker is not already in use - without RTOS, it - // is never used, with RTOS, it will have been disabled with OS_Tick_Disable - while (!op.wake_condition()) { - // Set (or re-set) the wake time - outside a critical section, as - // it could take long enough to cause UART data loss on some platforms. - op.sleep_prepare(); - - // If no target sleep function, nothing else to do - just keep - // rechecking the wake condition. -#if DEVICE_SLEEP - // Now we need to enter the critical section for the race-free sleep - { - CriticalSectionLock lock; - - // Recheck wake conditions before starting sleep, avoiding race - if (op.wake_condition()) { - break; - } - - // It's possible that an intermediate wake interrupt occurred - // between "set_wake_time" and the critical lock - only sleep - // if we see that the timer is armed or we don't need it. Otherwise, - // we go round to set the timer again. - if (op.sleep_prepared()) { - // Enter HAL sleep (normal or deep) - sleep(); - } - } - - // Ensure interrupts get a chance to fire, which allows new result from - // wake_predicate() and wake_time_passed() - __ISB(); -#endif - } -} - -/* We require that this is called from thread context, outside a critical section, - * and the kernel already suspended if an RTOS, meaning we don't have to worry - * about any potential threading issues. - * - * The wake predicate will be called from both outside and inside a critical - * section, so appropriate atomic care must be taken. - */ -uint64_t do_timed_sleep_absolute(uint64_t wake_time, bool (*wake_predicate)(void *), void *wake_predicate_handle) -{ - { - timed_predicate_op op(wake_time, wake_predicate, wake_predicate_handle); - do_sleep_operation(op); - } - - return os_timer->update_and_get_tick(); -} - - -#if MBED_CONF_RTOS_PRESENT -/* The 32-bit limit is part of the API - we will always wake within 2^32 ticks */ -/* This version is tuned for RTOS use, where the RTOS needs to know the time spent sleeping */ -uint32_t do_timed_sleep_relative(uint32_t wake_delay, bool (*wake_predicate)(void *), void *wake_predicate_handle) -{ - uint64_t sleep_start = init_os_timer()->get_tick(); - // When running with RTOS, the requested delay will be based on the kernel's tick count. - // If it missed a tick as entering idle, we should reflect that by moving the - // start time back to reflect its current idea of time. - // Example: OS tick count = 100, our tick count = 101, requested delay = 50 - // We need to schedule wake for tick 150, report 50 ticks back to our caller, and - // clear the unacknowledged tick count. - sleep_start -= os_timer->unacknowledged_ticks(); - - uint64_t sleep_finish = do_timed_sleep_absolute(sleep_start + wake_delay, wake_predicate, wake_predicate_handle); - - return static_cast(sleep_finish - sleep_start); -} - -#else - -void do_untimed_sleep(bool (*wake_predicate)(void *), void *wake_predicate_handle) -{ - untimed_op op(wake_predicate, wake_predicate_handle); - - do_sleep_operation(op); -} - -/* (uint32_t)-1 delay is treated as "wait forever" */ -/* This version is tuned for non-RTOS use, where we don't need to return sleep time, and waiting forever is possible */ -void do_timed_sleep_relative_or_forever(uint32_t wake_delay, bool (*wake_predicate)(void *), void *wake_predicate_handle) -{ - // Special-case 0 delay, to save multiple callers having to do it. Just call the predicate once. - if (wake_delay == 0) { - wake_predicate(wake_predicate_handle); - return; - } - - uint64_t wake_time; - if (wake_delay == (uint32_t) -1) { - wake_time = (uint64_t) -1; - } else { - wake_time = init_os_timer()->update_and_get_tick() + wake_delay; - } - /* Always use timed_predicate_op here to save pulling in two templates */ - timed_predicate_op op(wake_time, wake_predicate, wake_predicate_handle); - do_sleep_operation(op); -} - -#endif - -} // namespace internal -} // namespace mbed diff --git a/platform/mbed_os_timer.h b/platform/mbed_os_timer.h deleted file mode 100644 index 639dc3f..0000000 --- a/platform/mbed_os_timer.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2006-2019, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * 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_MBED_SLEEP_TIMER_H -#define MBED_MBED_SLEEP_TIMER_H - -#include "platform/SysTimer.h" - -#if MBED_CONF_RTOS_PRESENT -extern "C" { -#include "rtx_lib.h" -} -#endif - -namespace mbed { -namespace internal { - -#if MBED_CONF_RTOS_PRESENT -#define OS_TICK_US (1000000 / OS_TICK_FREQ) -#else -#define OS_TICK_US 1000 -#endif -typedef SysTimer OsTimer; - -/* A SysTimer is used to provide the timed sleep - this provides access to share it for - * other use, such as ticks. If accessed this way, it must not be in use when a sleep function below is called. - */ -extern OsTimer *os_timer; -OsTimer *init_os_timer(); - -/* -1 is effectively "sleep forever" */ -uint64_t do_timed_sleep_absolute(uint64_t wake_time, bool (*wake_predicate)(void *) = NULL, void *wake_predicate_handle = NULL); - -#if MBED_CONF_RTOS_PRESENT -/* Maximum sleep time is 2^32-1 ticks; timer is always set to achieve this */ -/* Assumes that ticker has been in use prior to call, so restricted to RTOS use */ -uint32_t do_timed_sleep_relative(uint32_t wake_delay, bool (*wake_predicate)(void *) = NULL, void *wake_predicate_handle = NULL); -#else - -void do_untimed_sleep(bool (*wake_predicate)(void *), void *wake_predicate_handle = NULL); - -/* (uint32_t)-1 delay is sleep forever */ - -void do_timed_sleep_relative_or_forever(uint32_t wake_delay, bool (*wake_predicate)(void *) = NULL, void *wake_predicate_handle = NULL); - -#endif - -} -} - -#endif diff --git a/platform/mbed_thread.cpp b/platform/mbed_thread.cpp deleted file mode 100644 index 0cdcae1..0000000 --- a/platform/mbed_thread.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2019, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * 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 "platform/mbed_thread.h" -#include "platform/mbed_critical.h" -#include "platform/mbed_os_timer.h" - -/* If the RTOS is present, we call the RTOS API to do the work */ -/* If the RTOS is not present, the RTOS API calls us to do the work */ -#if MBED_CONF_RTOS_PRESENT -#include "rtos/Kernel.h" -#include "rtos/ThisThread.h" -#endif - -extern "C" { - - uint64_t get_ms_count(void) - { -#if MBED_CONF_RTOS_PRESENT - return rtos::Kernel::get_ms_count(); -#else - return mbed::internal::init_os_timer()->update_and_get_tick(); -#endif - } - - void thread_sleep_for(uint32_t millisec) - { -#if MBED_CONF_RTOS_PRESENT - rtos::ThisThread::sleep_for(millisec); -#else - // Undocumented, but osDelay(UINT32_MAX) does actually sleep forever - mbed::internal::do_timed_sleep_relative_or_forever(millisec); -#endif - } - - void thread_sleep_until(uint64_t millisec) - { -#if MBED_CONF_RTOS_PRESENT - rtos::ThisThread::sleep_until(millisec); -#else - mbed::internal::do_timed_sleep_absolute(millisec); -#endif - } - -} diff --git a/platform/mbed_thread.h b/platform/mbed_thread.h index 6968ebd..8b9fff1 100644 --- a/platform/mbed_thread.h +++ b/platform/mbed_thread.h @@ -22,6 +22,12 @@ extern "C" { #endif +/** + * \defgroup mbed_thread Mbed Thread + * \ingroup platform-public-api + * @{ + */ + /** Generic thread functions. * * These are C versions of functions provided in C++ via rtos::Thread and rtos::ThisThread @@ -59,6 +65,8 @@ */ void thread_sleep_until(uint64_t millisec); +/** @}*/ + #ifdef __cplusplus } #endif diff --git a/platform/source/SysTimer.cpp b/platform/source/SysTimer.cpp new file mode 100644 index 0000000..8186c11 --- /dev/null +++ b/platform/source/SysTimer.cpp @@ -0,0 +1,353 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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/us_ticker_api.h" +#include "hal/lp_ticker_api.h" +#include "mbed_atomic.h" +#include "mbed_critical.h" +#include "mbed_assert.h" +#include "platform/mbed_power_mgmt.h" +#include "platform/CriticalSectionLock.h" +#include "platform/source/SysTimer.h" +extern "C" { +#if MBED_CONF_RTOS_PRESENT +#include "rtx_lib.h" +#endif +} + +#if (defined(NO_SYSTICK)) +/** + * Return an IRQ number that can be used in the absence of SysTick + * + * @return Free IRQ number that can be used + */ +extern "C" IRQn_Type mbed_get_m0_tick_irqn(void); +#endif + +#if defined(TARGET_CORTEX_A) +extern "C" IRQn_ID_t mbed_get_a9_tick_irqn(void); +#endif + +namespace mbed { +namespace internal { + +template +SysTimer::SysTimer() : +#if DEVICE_LPTICKER + TimerEvent(get_lp_ticker_data()), +#else + TimerEvent(get_us_ticker_data()), +#endif + _time_us(ticker_read_us(_ticker_data)), + _tick(0), + _unacknowledged_ticks(0), + _wake_time_set(false), + _wake_time_passed(false), + _ticking(false), + _deep_sleep_locked(false) +{ +} + +template +SysTimer::SysTimer(const ticker_data_t *data) : + TimerEvent(data), + _time_us(ticker_read_us(_ticker_data)), + _tick(0), + _unacknowledged_ticks(0), + _wake_time_set(false), + _wake_time_passed(false), + _ticking(false), + _deep_sleep_locked(false) +{ +} + +template +SysTimer::~SysTimer() +{ + cancel_tick(); + cancel_wake(); +} + +template +void SysTimer::set_wake_time(uint64_t at) +{ + // SysTimer must not be active - we must be in suspend state + MBED_ASSERT(!_ticking); + + // There is a potential race here, when called from outside + // a critical section. See function documentation for notes on + // handling it. + if (core_util_atomic_load_bool(&_wake_time_set)) { + return; + } + + // Analyse the timers + if (update_and_get_tick() >= at) { + _wake_time_passed = true; + return; + } + + uint64_t ticks_to_sleep = at - _tick; + uint64_t wake_time = at * US_IN_TICK; + + /* Set this first, before attaching the interrupt that can unset it */ + _wake_time_set = true; + _wake_time_passed = false; + + if (!_deep_sleep_locked && !_ticker_data->interface->runs_in_deep_sleep) { + _deep_sleep_locked = true; + sleep_manager_lock_deep_sleep(); + } + + /* If deep sleep is unlocked, and we have enough time, let's go for it */ + if (MBED_CONF_TARGET_DEEP_SLEEP_LATENCY > 0 && + ticks_to_sleep > MBED_CONF_TARGET_DEEP_SLEEP_LATENCY && + sleep_manager_can_deep_sleep()) { + /* Schedule the wake up interrupt early, allowing for the deep sleep latency */ + _wake_early = true; + insert_absolute(wake_time - MBED_CONF_TARGET_DEEP_SLEEP_LATENCY * US_IN_TICK); + } else { + /* Otherwise, we'll set up for shallow sleep at the precise time. + * To make absolutely sure it's shallow so we don't incur the latency, + * take our own lock, to avoid a race on a thread unlocking it. + */ + _wake_early = false; + if (MBED_CONF_TARGET_DEEP_SLEEP_LATENCY > 0 && !_deep_sleep_locked) { + _deep_sleep_locked = true; + sleep_manager_lock_deep_sleep(); + } + insert_absolute(wake_time); + } +} + +template +void SysTimer::cancel_wake() +{ + MBED_ASSERT(!_ticking); + // Remove ensures serialized access to SysTimer by stopping timer interrupt + remove(); + + _wake_time_set = false; + _wake_time_passed = false; + + if (_deep_sleep_locked) { + _deep_sleep_locked = false; + sleep_manager_unlock_deep_sleep(); + } +} + +template +uint64_t SysTimer::_elapsed_ticks() const +{ + uint64_t elapsed_us = ticker_read_us(_ticker_data) - _time_us; + if (elapsed_us < US_IN_TICK) { + return 0; + } else if (elapsed_us < 2 * US_IN_TICK) { + return 1; + } else if (elapsed_us <= 0xFFFFFFFF) { + // Fast common case avoiding 64-bit division + return (uint32_t) elapsed_us / US_IN_TICK; + } else { + return elapsed_us / US_IN_TICK; + } +} + +template +void SysTimer::start_tick() +{ + _ticking = true; + if (_unacknowledged_ticks > 0) { + _set_irq_pending(); + } + _schedule_tick(); +} + +template +void SysTimer::_schedule_tick() +{ + insert_absolute(_time_us + US_IN_TICK); +} + +template +void SysTimer::acknowledge_tick() +{ + // Try to avoid missed ticks if OS's IRQ level is not keeping + // up with our handler. + // 8-bit counter to save space, and also make sure it we don't + // try TOO hard to resync if something goes really awry - + // resync will reset if the count hits 256. + if (core_util_atomic_decr_u8(&_unacknowledged_ticks, 1) > 0) { + _set_irq_pending(); + } +} + +template +void SysTimer::cancel_tick() +{ + // Underlying call is interrupt safe + + remove(); + _ticking = false; + + _clear_irq_pending(); +} + +template +uint64_t SysTimer::get_tick() const +{ + // Atomic is necessary as this can be called from any foreground context, + // while IRQ can update it. + return core_util_atomic_load_u64(&_tick); +} + +template +uint64_t SysTimer::update_and_get_tick() +{ + MBED_ASSERT(!_ticking && !_wake_time_set); + // Can only be used when no interrupts are scheduled + // Update counters to reflect elapsed time + uint64_t elapsed_ticks = _elapsed_ticks(); + _unacknowledged_ticks = 0; + _time_us += elapsed_ticks * US_IN_TICK; + _tick += elapsed_ticks; + + return _tick; +} + +template +us_timestamp_t SysTimer::get_time() const +{ + // Underlying call is interrupt safe + + return ticker_read_us(_ticker_data); +} + +template +us_timestamp_t SysTimer::get_time_since_tick() const +{ + // Underlying call is interrupt safe, and _time_us is not updated by IRQ + + return get_time() - _time_us; +} + +#if (defined(NO_SYSTICK)) +template +IRQn_Type SysTimer::get_irq_number() +{ + return mbed_get_m0_tick_irqn(); +} +#elif (TARGET_CORTEX_M) +template +IRQn_Type SysTimer::get_irq_number() +{ + return SysTick_IRQn; +} +#elif (TARGET_CORTEX_A) +template +IRQn_ID_t SysTimer::get_irq_number() +{ + return mbed_get_a9_tick_irqn(); +} +#endif + +template +void SysTimer::_set_irq_pending() +{ + // Protected function synchronized externally + if (!IRQ) { + return; + } +#if (defined(NO_SYSTICK)) + NVIC_SetPendingIRQ(mbed_get_m0_tick_irqn()); +#elif (TARGET_CORTEX_M) + SCB->ICSR = SCB_ICSR_PENDSTSET_Msk; +#else + IRQ_SetPending(mbed_get_a9_tick_irqn()); +#endif +} + +template +void SysTimer::_clear_irq_pending() +{ + // Protected function synchronized externally + if (!IRQ) { + return; + } +#if (defined(NO_SYSTICK)) + NVIC_ClearPendingIRQ(mbed_get_m0_tick_irqn()); +#elif (TARGET_CORTEX_M) + SCB->ICSR = SCB_ICSR_PENDSTCLR_Msk; +#else + IRQ_ClearPending(mbed_get_a9_tick_irqn()); +#endif +} + +template +void SysTimer::_increment_tick() +{ + // Protected function synchronized externally + + _tick++; + _time_us += US_IN_TICK; +} + +template +void SysTimer::handler() +{ + /* To reduce IRQ latency problems, we do not re-arm in the interrupt handler */ + if (_wake_time_set) { + _wake_time_set = false; + if (!_wake_early) { + _wake_time_passed = true; + } + /* If this was an early interrupt, user has the responsibility to check and + * note the combination of (!set, !passed), and re-arm the wake timer if + * necessary. + */ + } else if (_ticking) { + _unacknowledged_ticks++; + _set_irq_pending(); + _increment_tick(); + // We do this now, rather than in acknowledgement, as we get it "for free" + // here - because we're in the ticker handler, the programming gets deferred + // until end of dispatch, and the ticker would likely be rescheduling + // anyway after dispatch. + + _schedule_tick(); + } +} + +#if MBED_CONF_RTOS_PRESENT +/* Whatever the OS wants (in case it isn't 1ms) */ +MBED_STATIC_ASSERT(1000000 % OS_TICK_FREQ == 0, "OS_TICK_FREQ must be a divisor of 1000000 for correct tick calculations"); +#define OS_TICK_US (1000000 / OS_TICK_FREQ) +#if OS_TICK_US != 1000 +template class SysTimer; +#endif +#endif + +/* Standard 1ms SysTimer */ +template class SysTimer<1000>; + +/* Standard 1ms SysTimer that doesn't set interrupts, used for Greentea tests */ +template class SysTimer<1000, false>; + +/* Slowed-down SysTimer that doesn't set interrupts, used for Greentea tests */ +template class SysTimer<42000, false>; + +} // namespace internal +} // namespace mbed diff --git a/platform/source/SysTimer.h b/platform/source/SysTimer.h new file mode 100644 index 0000000..3098a8e --- /dev/null +++ b/platform/source/SysTimer.h @@ -0,0 +1,243 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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_SYS_TIMER_H +#define MBED_SYS_TIMER_H + +#include "platform/NonCopyable.h" +#include "platform/mbed_atomic.h" +#include "drivers/TimerEvent.h" +#include "cmsis.h" + +extern "C" { +#if defined(TARGET_CORTEX_A) +#include "irq_ctrl.h" +#endif +} + +namespace mbed { +namespace internal { + +/** + * \defgroup mbed_SysTimer SysTimer class + * \ingroup platform-internal-api + * @{ + */ + +/** + * The SysTimer class is used to provide timing for system suspension, and + * the idle loop in TICKLESS mode. + * + * Template for speed for testing - only one instance will be used normally. + * + * @note SysTimer is not the part of Mbed API. + */ +template +class SysTimer: private mbed::TimerEvent, private mbed::NonCopyable > { +public: + + /** + * Default constructor uses LPTICKER if available (so the timer will + * continue to run in deep sleep), else USTICKER. + */ + SysTimer(); + + SysTimer(const ticker_data_t *data); + + ~SysTimer(); + + /** + * Get the interrupt number for the tick + * + * @return interrupt number + */ +#if TARGET_CORTEX_A + static IRQn_ID_t get_irq_number(); +#elif TARGET_CORTEX_M + static IRQn_Type get_irq_number(); +#endif + + /** + * Set the wake time + * + * Schedules an interrupt to cause wake-up in time for the event. Interrupt + * may be arranged early to account for latency. If the time has already + * passed, no interrupt will be scheduled. + * + * This is called from outside a critical section, as it is known to be + * a slow operation. + * + * If the wake time is already set, this is a no-op. But that check is racy, + * which means wake_time_set() should be rechecked after taking a critical + * section. + * + * As a side-effect, this clears the unacknowledged tick count - the caller + * is expected to use update_and_get_tick() after the suspend operation. + * + * @param at Wake up tick + * @warning If the ticker tick is already scheduled it needs to be cancelled first! + */ + void set_wake_time(uint64_t at); + + /** + * Check whether the wake time has passed + * + * This is a fast operation, based on checking whether the wake interrupt + * has run. + * + * @return true if the specified wake tick has passed + */ + bool wake_time_passed() const + { + return core_util_atomic_load_bool(&_wake_time_passed); + } + + /** + * Check whether wake timer is active + * + * @return true if the wake timer is active. + */ + bool wake_time_set() const + { + return core_util_atomic_load_bool(&_wake_time_set); + } + + /** + * Cancel any pending wake + */ + void cancel_wake(); + + /** + * Schedule an os tick to fire + * + * Ticks will be rescheduled automatically every tick until cancel_tick is called. + * + * A tick will be fired immediately if there are any unacknowledged ticks. + * + * @warning If a tick is already scheduled it needs to be cancelled first! + */ + void start_tick(); + + /** + * Acknowledge an os tick + * + * This will queue another os tick immediately if the os is running slow + */ + void acknowledge_tick(); + + /** + * Prevent any more scheduled ticks from triggering + * + * If called from OS tick context, there may be remaining unacknowledged ticks. + */ + void cancel_tick(); + + /** + * Check whether ticker is active + * + * Each time the tick interrupt fires, it is automatically rescheduled, + * so this will remain true once the tick is started, except during + * processing. + * + * @return true if the ticker is active. + */ + bool ticking() const + { + return core_util_atomic_load_bool(&_ticking); + } + + /** + * Check unacknowledged ticks + * + * Returns the count of how many times the OS timer has been queued minus + * the number of times is has been acknowledged. + * + * get_tick() - unacknowledged_ticks() should equal the OS's tick count, + * although such a calculation is not atomic if the ticker is currently running. + * + * @return number of unacknowledged ticks + */ + int unacknowledged_ticks() const + { + return core_util_atomic_load_u8(&_unacknowledged_ticks); + } + + /** Get the current tick count + * + * This count is updated by the ticker interrupt, if the ticker interrupt + * is running. It the ticker interrupt is not running, update_and_get_tick() + * should be used instead. + * + * This indicates how many ticks have been generated by the tick interrupt. + * The os_timer should equal this number minus the number of unacknowledged ticks. + * + * @return The number of ticks since timer creation. + */ + uint64_t get_tick() const; + + /** Update and get the current tick count + * + * This is a slow operation that reads the timer and adjusts for elapsed time. + * Can only be used when the ticker is not running, as there is no IRQ + * synchronization. + * + * This clears the unacknowledged tick counter - the caller is assumed to update + * their timer based on this return. + * + * @return The number of ticks since timer creation. + */ + uint64_t update_and_get_tick(); + + /** + * Returns time since last tick + * + * @return Relative time in microseconds + */ + us_timestamp_t get_time_since_tick() const; + + /** + * Get the time + * + * Returns the instantaneous precision time from underlying timer. + * This is a slow operation so should not be called from critical sections. + * + * @return Current time in microseconds + */ + us_timestamp_t get_time() const; + +protected: + virtual void handler(); + void _increment_tick(); + void _schedule_tick(); + uint64_t _elapsed_ticks() const; + static void _set_irq_pending(); + static void _clear_irq_pending(); + us_timestamp_t _time_us; + uint64_t _tick; + uint8_t _unacknowledged_ticks; + bool _wake_time_set; + bool _wake_time_passed; + bool _wake_early; + bool _ticking; + bool _deep_sleep_locked; +}; + +/** @} */ + +} +} + +#endif diff --git a/platform/source/mbed_os_timer.cpp b/platform/source/mbed_os_timer.cpp new file mode 100644 index 0000000..8f75254 --- /dev/null +++ b/platform/source/mbed_os_timer.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2006-2019, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * 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 "platform/mbed_power_mgmt.h" +#include "platform/source/mbed_os_timer.h" +#include "platform/CriticalSectionLock.h" +#include "platform/source/SysTimer.h" +#include "us_ticker_api.h" +#include "lp_ticker_api.h" +#include "mbed_critical.h" +#include "mbed_assert.h" +#include + +/* This provides the marshalling point for a system global SysTimer, which + * is used to provide: + * - timed sleeps (for default idle hook in RTOS tickless mode, or non-RTOS sleeps) + * - regular ticks for RTOS + * - absolute system timing (directly for non-RTOS, or indirectly via RTOS tick count) + */ + +namespace mbed { +namespace internal { + +OsTimer *os_timer; + +namespace { +uint64_t os_timer_data[(sizeof(OsTimer) + 7) / 8]; +} + +OsTimer *init_os_timer() +{ + // Do not use SingletonPtr since this relies on the RTOS. + // Locking not required as it will be first called during + // OS init, or else we're a non-RTOS single-threaded setup. + if (!os_timer) { +#if MBED_CONF_TARGET_TICKLESS_FROM_US_TICKER && DEVICE_USTICKER + os_timer = new (os_timer_data) OsTimer(get_us_ticker_data()); +#elif !MBED_CONF_TARGET_TICKLESS_FROM_US_TICKER && DEVICE_LPTICKER + os_timer = new (os_timer_data) OsTimer(get_lp_ticker_data()); +#else + MBED_ASSERT("OS timer not available - check MBED_CONF_TARGET_TICKLESS_FROM_US_TICKER" && false); + return NULL; +#endif + //os_timer->setup_irq(); + } + + return os_timer; +} + +/* These traits classes are designed to permit chunks of code to be + * omitted - in particular eliminating timers. However, we don't want + * to cause template bloat, so don't have too many traits variants. + */ + +/* Optionally timed operation, with optional predicate */ +struct timed_predicate_op { + timed_predicate_op(uint64_t t) : wake_time(t), orig_predicate(NULL), orig_handle(NULL) + { + init_os_timer(); + } + + timed_predicate_op(uint64_t t, bool (*wake_predicate)(void *), void *wake_predicate_handle) : wake_time(t), orig_predicate(wake_predicate), orig_handle(wake_predicate_handle) + { + init_os_timer(); + } + + ~timed_predicate_op() + { + // Make sure wake timer is cancelled. (It may or may not be, depending on + // why we woke). + os_timer->cancel_wake(); + } + + bool wake_condition() const + { + return (orig_predicate && orig_predicate(orig_handle)) || os_timer->wake_time_passed(); + } + + void sleep_prepare() + { + if (wake_time != (uint64_t) -1) { + os_timer->set_wake_time(wake_time); + } + } + + bool sleep_prepared() + { + return wake_time == (uint64_t) -1 || os_timer->wake_time_set(); + } + +private: + uint64_t wake_time; + bool (*orig_predicate)(void *); + void *orig_handle; +}; + +/* Untimed operation with predicate */ +struct untimed_op { + untimed_op(bool (*wake_predicate)(void *), void *wake_predicate_handle) : orig_predicate(wake_predicate), orig_handle(wake_predicate_handle) + { + } + + bool wake_condition() const + { + return orig_predicate(orig_handle); + } + + void sleep_prepare() + { + } + + bool sleep_prepared() + { + return true; + } + +private: + bool (*orig_predicate)(void *); + void *orig_handle; +}; + +/* We require that this is called from thread context, outside a critical section, + * and the kernel already suspended if an RTOS, meaning we don't have to worry + * about any potential threading issues. + * + * The wake predicate will be called from both outside and inside a critical + * section, so appropriate atomic care must be taken. + */ +template +void do_sleep_operation(OpT &op) +{ + // We assume the ticker is not already in use - without RTOS, it + // is never used, with RTOS, it will have been disabled with OS_Tick_Disable + while (!op.wake_condition()) { + // Set (or re-set) the wake time - outside a critical section, as + // it could take long enough to cause UART data loss on some platforms. + op.sleep_prepare(); + + // If no target sleep function, nothing else to do - just keep + // rechecking the wake condition. +#if DEVICE_SLEEP + // Now we need to enter the critical section for the race-free sleep + { + CriticalSectionLock lock; + + // Recheck wake conditions before starting sleep, avoiding race + if (op.wake_condition()) { + break; + } + + // It's possible that an intermediate wake interrupt occurred + // between "set_wake_time" and the critical lock - only sleep + // if we see that the timer is armed or we don't need it. Otherwise, + // we go round to set the timer again. + if (op.sleep_prepared()) { + // Enter HAL sleep (normal or deep) + sleep(); + } + } + + // Ensure interrupts get a chance to fire, which allows new result from + // wake_predicate() and wake_time_passed() + __ISB(); +#endif + } +} + +/* We require that this is called from thread context, outside a critical section, + * and the kernel already suspended if an RTOS, meaning we don't have to worry + * about any potential threading issues. + * + * The wake predicate will be called from both outside and inside a critical + * section, so appropriate atomic care must be taken. + */ +uint64_t do_timed_sleep_absolute(uint64_t wake_time, bool (*wake_predicate)(void *), void *wake_predicate_handle) +{ + { + timed_predicate_op op(wake_time, wake_predicate, wake_predicate_handle); + do_sleep_operation(op); + } + + return os_timer->update_and_get_tick(); +} + + +#if MBED_CONF_RTOS_PRESENT +/* The 32-bit limit is part of the API - we will always wake within 2^32 ticks */ +/* This version is tuned for RTOS use, where the RTOS needs to know the time spent sleeping */ +uint32_t do_timed_sleep_relative(uint32_t wake_delay, bool (*wake_predicate)(void *), void *wake_predicate_handle) +{ + uint64_t sleep_start = init_os_timer()->get_tick(); + // When running with RTOS, the requested delay will be based on the kernel's tick count. + // If it missed a tick as entering idle, we should reflect that by moving the + // start time back to reflect its current idea of time. + // Example: OS tick count = 100, our tick count = 101, requested delay = 50 + // We need to schedule wake for tick 150, report 50 ticks back to our caller, and + // clear the unacknowledged tick count. + sleep_start -= os_timer->unacknowledged_ticks(); + + uint64_t sleep_finish = do_timed_sleep_absolute(sleep_start + wake_delay, wake_predicate, wake_predicate_handle); + + return static_cast(sleep_finish - sleep_start); +} + +#else + +void do_untimed_sleep(bool (*wake_predicate)(void *), void *wake_predicate_handle) +{ + untimed_op op(wake_predicate, wake_predicate_handle); + + do_sleep_operation(op); +} + +/* (uint32_t)-1 delay is treated as "wait forever" */ +/* This version is tuned for non-RTOS use, where we don't need to return sleep time, and waiting forever is possible */ +void do_timed_sleep_relative_or_forever(uint32_t wake_delay, bool (*wake_predicate)(void *), void *wake_predicate_handle) +{ + // Special-case 0 delay, to save multiple callers having to do it. Just call the predicate once. + if (wake_delay == 0) { + wake_predicate(wake_predicate_handle); + return; + } + + uint64_t wake_time; + if (wake_delay == (uint32_t) -1) { + wake_time = (uint64_t) -1; + } else { + wake_time = init_os_timer()->update_and_get_tick() + wake_delay; + } + /* Always use timed_predicate_op here to save pulling in two templates */ + timed_predicate_op op(wake_time, wake_predicate, wake_predicate_handle); + do_sleep_operation(op); +} + +#endif + +} // namespace internal +} // namespace mbed diff --git a/platform/source/mbed_os_timer.h b/platform/source/mbed_os_timer.h new file mode 100644 index 0000000..a765e91 --- /dev/null +++ b/platform/source/mbed_os_timer.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2006-2019, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * 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_MBED_SLEEP_TIMER_H +#define MBED_MBED_SLEEP_TIMER_H + +#include "platform/source/SysTimer.h" + +#if MBED_CONF_RTOS_PRESENT +extern "C" { +#include "rtx_lib.h" +} +#endif + +namespace mbed { +namespace internal { + +#if MBED_CONF_RTOS_PRESENT +#define OS_TICK_US (1000000 / OS_TICK_FREQ) +#else +#define OS_TICK_US 1000 +#endif +typedef SysTimer OsTimer; + +/* A SysTimer is used to provide the timed sleep - this provides access to share it for + * other use, such as ticks. If accessed this way, it must not be in use when a sleep function below is called. + */ +extern OsTimer *os_timer; +OsTimer *init_os_timer(); + +/* -1 is effectively "sleep forever" */ +uint64_t do_timed_sleep_absolute(uint64_t wake_time, bool (*wake_predicate)(void *) = NULL, void *wake_predicate_handle = NULL); + +#if MBED_CONF_RTOS_PRESENT +/* Maximum sleep time is 2^32-1 ticks; timer is always set to achieve this */ +/* Assumes that ticker has been in use prior to call, so restricted to RTOS use */ +uint32_t do_timed_sleep_relative(uint32_t wake_delay, bool (*wake_predicate)(void *) = NULL, void *wake_predicate_handle = NULL); +#else + +void do_untimed_sleep(bool (*wake_predicate)(void *), void *wake_predicate_handle = NULL); + +/* (uint32_t)-1 delay is sleep forever */ + +void do_timed_sleep_relative_or_forever(uint32_t wake_delay, bool (*wake_predicate)(void *) = NULL, void *wake_predicate_handle = NULL); + +#endif + +} +} + +#endif diff --git a/platform/source/mbed_thread.cpp b/platform/source/mbed_thread.cpp new file mode 100644 index 0000000..f24f8e2 --- /dev/null +++ b/platform/source/mbed_thread.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * 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 "platform/mbed_thread.h" +#include "platform/mbed_critical.h" +#include "platform/source/mbed_os_timer.h" + +/* If the RTOS is present, we call the RTOS API to do the work */ +/* If the RTOS is not present, the RTOS API calls us to do the work */ +#if MBED_CONF_RTOS_PRESENT +#include "rtos/Kernel.h" +#include "rtos/ThisThread.h" +#endif + +extern "C" { + + uint64_t get_ms_count(void) + { +#if MBED_CONF_RTOS_PRESENT + return rtos::Kernel::get_ms_count(); +#else + return mbed::internal::init_os_timer()->update_and_get_tick(); +#endif + } + + void thread_sleep_for(uint32_t millisec) + { +#if MBED_CONF_RTOS_PRESENT + rtos::ThisThread::sleep_for(millisec); +#else + // Undocumented, but osDelay(UINT32_MAX) does actually sleep forever + mbed::internal::do_timed_sleep_relative_or_forever(millisec); +#endif + } + + void thread_sleep_until(uint64_t millisec) + { +#if MBED_CONF_RTOS_PRESENT + rtos::ThisThread::sleep_until(millisec); +#else + mbed::internal::do_timed_sleep_absolute(millisec); +#endif + } + +} diff --git a/rtos/source/Kernel.cpp b/rtos/source/Kernel.cpp index e5e3443..c62a4b9 100644 --- a/rtos/source/Kernel.cpp +++ b/rtos/source/Kernel.cpp @@ -24,7 +24,7 @@ #include "rtos_idle.h" #include "rtos_handlers.h" #include "platform/mbed_critical.h" -#include "platform/mbed_os_timer.h" +#include "platform/source/mbed_os_timer.h" #if !MBED_CONF_RTOS_PRESENT /* If the RTOS is not present, we call mbed_thread.cpp to do the work */ diff --git a/rtos/source/Semaphore.cpp b/rtos/source/Semaphore.cpp index 8ae83c9..86d5db6 100644 --- a/rtos/source/Semaphore.cpp +++ b/rtos/source/Semaphore.cpp @@ -24,7 +24,7 @@ #include "platform/mbed_assert.h" #include "platform/mbed_critical.h" #include "platform/mbed_error.h" -#include "platform/mbed_os_timer.h" +#include "platform/source/mbed_os_timer.h" #include diff --git a/rtos/source/TARGET_CORTEX/mbed_rtx_idle.cpp b/rtos/source/TARGET_CORTEX/mbed_rtx_idle.cpp index 27b6d5e..36e5d7c 100644 --- a/rtos/source/TARGET_CORTEX/mbed_rtx_idle.cpp +++ b/rtos/source/TARGET_CORTEX/mbed_rtx_idle.cpp @@ -22,7 +22,7 @@ #include "rtos/source/rtos_idle.h" #include "platform/mbed_power_mgmt.h" -#include "platform/mbed_os_timer.h" +#include "platform/source/mbed_os_timer.h" #include "TimerEvent.h" #include "mbed_critical.h" #include "mbed_assert.h" diff --git a/rtos/source/ThisThread.cpp b/rtos/source/ThisThread.cpp index 0ee2d73..e02b4bb 100644 --- a/rtos/source/ThisThread.cpp +++ b/rtos/source/ThisThread.cpp @@ -28,7 +28,7 @@ #include "platform/CriticalSectionLock.h" #include "platform/mbed_assert.h" #include "platform/mbed_critical.h" -#include "platform/mbed_os_timer.h" +#include "platform/source/mbed_os_timer.h" #if !MBED_CONF_RTOS_PRESENT /* If the RTOS is not present, we call mbed_thread.cpp to do the work */