Add Chrono support to Kernel and SysTimer

pull/12425/head
Kevin Bracey 2020-02-12 15:26:59 +02:00
parent 4ee7d24adc
commit f4e0ea2c75
9 changed files with 277 additions and 135 deletions

View File

@ -29,6 +29,10 @@ extern "C" {
#endif #endif
} }
using namespace std::chrono;
constexpr milliseconds deep_sleep_latency{MBED_CONF_TARGET_DEEP_SLEEP_LATENCY};
#if (defined(NO_SYSTICK)) #if (defined(NO_SYSTICK))
/** /**
* Return an IRQ number that can be used in the absence of SysTick * Return an IRQ number that can be used in the absence of SysTick
@ -45,30 +49,21 @@ extern "C" IRQn_ID_t mbed_get_a9_tick_irqn(void);
namespace mbed { namespace mbed {
namespace internal { namespace internal {
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
SysTimer<US_IN_TICK, IRQ>::SysTimer() : SysTimer<Period, IRQ>::SysTimer() :
#if DEVICE_LPTICKER #if DEVICE_LPTICKER
TimerEvent(get_lp_ticker_data()), SysTimer(get_lp_ticker_data())
#else #else
TimerEvent(get_us_ticker_data()), SysTimer(get_us_ticker_data())
#endif #endif
_epoch(ticker_read_us(_ticker_data)),
_time_us(_epoch),
_tick(0),
_unacknowledged_ticks(0),
_wake_time_set(false),
_wake_time_passed(false),
_wake_early(false),
_ticking(false),
_deep_sleep_locked(false)
{ {
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
SysTimer<US_IN_TICK, IRQ>::SysTimer(const ticker_data_t *data) : SysTimer<Period, IRQ>::SysTimer(const ticker_data_t *data) :
TimerEvent(data), TimerEvent(data),
_epoch(ticker_read_us(_ticker_data)), _epoch(_ticker_data.now()),
_time_us(_epoch), _time(_epoch),
_tick(0), _tick(0),
_unacknowledged_ticks(0), _unacknowledged_ticks(0),
_wake_time_set(false), _wake_time_set(false),
@ -79,15 +74,15 @@ SysTimer<US_IN_TICK, IRQ>::SysTimer(const ticker_data_t *data) :
{ {
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
SysTimer<US_IN_TICK, IRQ>::~SysTimer() SysTimer<Period, IRQ>::~SysTimer()
{ {
cancel_tick(); cancel_tick();
cancel_wake(); cancel_wake();
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::set_wake_time(uint64_t at) void SysTimer<Period, IRQ>::set_wake_time(time_point at)
{ {
// SysTimer must not be active - we must be in suspend state // SysTimer must not be active - we must be in suspend state
MBED_ASSERT(!_ticking); MBED_ASSERT(!_ticking);
@ -105,8 +100,8 @@ void SysTimer<US_IN_TICK, IRQ>::set_wake_time(uint64_t at)
return; return;
} }
uint64_t ticks_to_sleep = at - _tick; duration ticks_to_sleep = at - get_tick();
uint64_t wake_time = _epoch + at * US_IN_TICK; highres_time_point wake_time = _epoch + at.time_since_epoch();
/* Set this first, before attaching the interrupt that can unset it */ /* Set this first, before attaching the interrupt that can unset it */
_wake_time_set = true; _wake_time_set = true;
@ -117,8 +112,8 @@ void SysTimer<US_IN_TICK, IRQ>::set_wake_time(uint64_t at)
sleep_manager_lock_deep_sleep(); sleep_manager_lock_deep_sleep();
} }
/* Consider whether we will need early or precise wake-up */ /* Consider whether we will need early or precise wake-up */
if (MBED_CONF_TARGET_DEEP_SLEEP_LATENCY > 0 && if (deep_sleep_latency > deep_sleep_latency.zero() &&
ticks_to_sleep > MBED_CONF_TARGET_DEEP_SLEEP_LATENCY && ticks_to_sleep > deep_sleep_latency &&
!_deep_sleep_locked) { !_deep_sleep_locked) {
/* If there is deep sleep latency, but we still have enough time, /* If there is deep sleep latency, but we still have enough time,
* and we haven't blocked deep sleep ourselves, * and we haven't blocked deep sleep ourselves,
@ -126,14 +121,14 @@ void SysTimer<US_IN_TICK, IRQ>::set_wake_time(uint64_t at)
* Actual sleep may or may not be deep, depending on other actors. * Actual sleep may or may not be deep, depending on other actors.
*/ */
_wake_early = true; _wake_early = true;
insert_absolute(wake_time - MBED_CONF_TARGET_DEEP_SLEEP_LATENCY * US_IN_TICK); insert_absolute(wake_time - deep_sleep_latency);
} else { } else {
/* Otherwise, set up to wake at the precise time. /* Otherwise, set up to wake at the precise time.
* If there is a deep sleep latency, ensure that we're holding the lock so the sleep * If there is a deep sleep latency, ensure that we're holding the lock so the sleep
* is shallow. (If there is no deep sleep latency, we're fine with it being deep). * is shallow. (If there is no deep sleep latency, we're fine with it being deep).
*/ */
_wake_early = false; _wake_early = false;
if (MBED_CONF_TARGET_DEEP_SLEEP_LATENCY > 0 && !_deep_sleep_locked) { if (deep_sleep_latency > deep_sleep_latency.zero() && !_deep_sleep_locked) {
_deep_sleep_locked = true; _deep_sleep_locked = true;
sleep_manager_lock_deep_sleep(); sleep_manager_lock_deep_sleep();
} }
@ -141,8 +136,8 @@ void SysTimer<US_IN_TICK, IRQ>::set_wake_time(uint64_t at)
} }
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::cancel_wake() void SysTimer<Period, IRQ>::cancel_wake()
{ {
MBED_ASSERT(!_ticking); MBED_ASSERT(!_ticking);
// Remove ensures serialized access to SysTimer by stopping timer interrupt // Remove ensures serialized access to SysTimer by stopping timer interrupt
@ -157,24 +152,26 @@ void SysTimer<US_IN_TICK, IRQ>::cancel_wake()
} }
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
uint64_t SysTimer<US_IN_TICK, IRQ>::_elapsed_ticks() const auto SysTimer<Period, IRQ>::_elapsed_ticks() const -> duration
{ {
uint64_t elapsed_us = ticker_read_us(_ticker_data) - _time_us; highres_duration elapsed_us = _ticker_data.now() - _time;
if (elapsed_us < US_IN_TICK) { // Fastest common cases avoiding any division for 0 or 1 ticks
return 0; if (elapsed_us < duration(1)) {
} else if (elapsed_us < 2 * US_IN_TICK) { return duration(0);
return 1; } else if (elapsed_us < duration(2)) {
} else if (elapsed_us <= 0xFFFFFFFF) { return duration(1);
} else if (elapsed_us.count() <= 0xFFFFFFFF) {
// Fast common case avoiding 64-bit division // Fast common case avoiding 64-bit division
return (uint32_t) elapsed_us / US_IN_TICK; return duration_cast<duration>(highres_duration_u32(elapsed_us));
} else { } else {
return elapsed_us / US_IN_TICK; // Worst case will require 64-bit division to convert highres to ticks
return duration_cast<duration>(elapsed_us);
} }
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::start_tick() void SysTimer<Period, IRQ>::start_tick()
{ {
_ticking = true; _ticking = true;
if (_unacknowledged_ticks > 0) { if (_unacknowledged_ticks > 0) {
@ -183,14 +180,14 @@ void SysTimer<US_IN_TICK, IRQ>::start_tick()
_schedule_tick(); _schedule_tick();
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::_schedule_tick() void SysTimer<Period, IRQ>::_schedule_tick()
{ {
insert_absolute(_time_us + US_IN_TICK); insert_absolute(_time + duration(1));
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::acknowledge_tick() void SysTimer<Period, IRQ>::acknowledge_tick()
{ {
// Try to avoid missed ticks if OS's IRQ level is not keeping // Try to avoid missed ticks if OS's IRQ level is not keeping
// up with our handler. // up with our handler.
@ -202,8 +199,8 @@ void SysTimer<US_IN_TICK, IRQ>::acknowledge_tick()
} }
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::cancel_tick() void SysTimer<Period, IRQ>::cancel_tick()
{ {
// Underlying call is interrupt safe // Underlying call is interrupt safe
@ -213,66 +210,66 @@ void SysTimer<US_IN_TICK, IRQ>::cancel_tick()
_clear_irq_pending(); _clear_irq_pending();
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
uint64_t SysTimer<US_IN_TICK, IRQ>::get_tick() const auto SysTimer<Period, IRQ>::get_tick() const -> time_point
{ {
// Atomic is necessary as this can be called from any foreground context, // Atomic is necessary as this can be called from any foreground context,
// while IRQ can update it. // while IRQ can update it.
return core_util_atomic_load_u64(&_tick); return time_point(duration(core_util_atomic_load_u64(&_tick)));
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
uint64_t SysTimer<US_IN_TICK, IRQ>::update_and_get_tick() auto SysTimer<Period, IRQ>::update_and_get_tick() -> time_point
{ {
MBED_ASSERT(!_ticking && !_wake_time_set); MBED_ASSERT(!_ticking && !_wake_time_set);
// Can only be used when no interrupts are scheduled // Can only be used when no interrupts are scheduled
// Update counters to reflect elapsed time // Update counters to reflect elapsed time
uint64_t elapsed_ticks = _elapsed_ticks(); duration elapsed_ticks = _elapsed_ticks();
_unacknowledged_ticks = 0; _unacknowledged_ticks = 0;
_time_us += elapsed_ticks * US_IN_TICK; _time += elapsed_ticks;
_tick += elapsed_ticks; _tick += elapsed_ticks.count();
return _tick; return time_point(duration(_tick));
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
us_timestamp_t SysTimer<US_IN_TICK, IRQ>::get_time() const auto SysTimer<Period, IRQ>::get_time() const -> highres_time_point
{ {
// Underlying call is interrupt safe // Underlying call is interrupt safe
return ticker_read_us(_ticker_data); return _ticker_data.now();
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
us_timestamp_t SysTimer<US_IN_TICK, IRQ>::get_time_since_tick() const auto SysTimer<Period, IRQ>::get_time_since_tick() const -> highres_duration
{ {
// Underlying call is interrupt safe, and _time_us is not updated by IRQ // Underlying call is interrupt safe, and _time is not updated by IRQ
return get_time() - _time_us; return get_time() - _time;
} }
#if (defined(NO_SYSTICK)) #if (defined(NO_SYSTICK))
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
IRQn_Type SysTimer<US_IN_TICK, IRQ>::get_irq_number() IRQn_Type SysTimer<Period, IRQ>::get_irq_number()
{ {
return mbed_get_m0_tick_irqn(); return mbed_get_m0_tick_irqn();
} }
#elif (TARGET_CORTEX_M) #elif (TARGET_CORTEX_M)
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
IRQn_Type SysTimer<US_IN_TICK, IRQ>::get_irq_number() IRQn_Type SysTimer<Period, IRQ>::get_irq_number()
{ {
return SysTick_IRQn; return SysTick_IRQn;
} }
#elif (TARGET_CORTEX_A) #elif (TARGET_CORTEX_A)
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
IRQn_ID_t SysTimer<US_IN_TICK, IRQ>::get_irq_number() IRQn_ID_t SysTimer<Period, IRQ>::get_irq_number()
{ {
return mbed_get_a9_tick_irqn(); return mbed_get_a9_tick_irqn();
} }
#endif #endif
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::_set_irq_pending() void SysTimer<Period, IRQ>::_set_irq_pending()
{ {
// Protected function synchronized externally // Protected function synchronized externally
if (!IRQ) { if (!IRQ) {
@ -287,8 +284,8 @@ void SysTimer<US_IN_TICK, IRQ>::_set_irq_pending()
#endif #endif
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::_clear_irq_pending() void SysTimer<Period, IRQ>::_clear_irq_pending()
{ {
// Protected function synchronized externally // Protected function synchronized externally
if (!IRQ) { if (!IRQ) {
@ -303,17 +300,17 @@ void SysTimer<US_IN_TICK, IRQ>::_clear_irq_pending()
#endif #endif
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::_increment_tick() void SysTimer<Period, IRQ>::_increment_tick()
{ {
// Protected function synchronized externally // Protected function synchronized externally
_tick++; _tick++;
_time_us += US_IN_TICK; _time += duration(1);
} }
template<uint32_t US_IN_TICK, bool IRQ> template<class Period, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::handler() void SysTimer<Period, IRQ>::handler()
{ {
/* To reduce IRQ latency problems, we do not re-arm in the interrupt handler */ /* To reduce IRQ latency problems, we do not re-arm in the interrupt handler */
if (_wake_time_set) { if (_wake_time_set) {
@ -343,18 +340,18 @@ void SysTimer<US_IN_TICK, IRQ>::handler()
MBED_STATIC_ASSERT(1000000 % OS_TICK_FREQ == 0, "OS_TICK_FREQ must be a divisor of 1000000 for correct tick calculations"); 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) #define OS_TICK_US (1000000 / OS_TICK_FREQ)
#if OS_TICK_US != 1000 #if OS_TICK_US != 1000
template class SysTimer<OS_TICK_US>; template class SysTimer<std::ratio_multiply<std::ratio<OS_TICK_US>, std::micro>>;
#endif #endif
#endif #endif
/* Standard 1ms SysTimer */ /* Standard 1ms SysTimer */
template class SysTimer<1000>; template class SysTimer<std::milli>;
/* Standard 1ms SysTimer that doesn't set interrupts, used for Greentea tests */ /* Standard 1ms SysTimer that doesn't set interrupts, used for Greentea tests */
template class SysTimer<1000, false>; template class SysTimer<std::milli, false>;
/* Slowed-down SysTimer that doesn't set interrupts, used for Greentea tests */ /* Slowed-down SysTimer that doesn't set interrupts, used for Greentea tests */
template class SysTimer<42000, false>; template class SysTimer<std::ratio_multiply<std::ratio<42>, std::milli>, false>;
} // namespace internal } // namespace internal
} // namespace mbed } // namespace mbed

View File

@ -20,6 +20,7 @@
#include "platform/NonCopyable.h" #include "platform/NonCopyable.h"
#include "platform/mbed_atomic.h" #include "platform/mbed_atomic.h"
#include "drivers/TimerEvent.h" #include "drivers/TimerEvent.h"
#include <chrono>
#include "cmsis.h" #include "cmsis.h"
extern "C" { extern "C" {
@ -45,10 +46,26 @@ namespace internal {
* *
* @note SysTimer is not the part of Mbed API. * @note SysTimer is not the part of Mbed API.
*/ */
template <uint32_t US_IN_TICK, bool IRQ = true> template <class Period, bool IRQ = true>
class SysTimer: private mbed::TimerEvent, private mbed::NonCopyable<SysTimer<US_IN_TICK, IRQ> > { class SysTimer: private mbed::TimerEvent, private mbed::NonCopyable<SysTimer<Period, IRQ> > {
public: public:
/* pseudo-Clock for our ticks - see TickerDataClock for more discussion */
using rep = uint64_t;
using period = Period;
using duration = std::chrono::duration<uint64_t, period>;
using time_point = std::chrono::time_point<SysTimer>;
static const bool is_steady = false;
/** duration type used for underlying high-res timer */
using highres_duration = TickerDataClock::duration;
/** time_point type used for underlying high-res timer */
using highres_time_point = TickerDataClock::time_point;
/** period of underlying high-res timer */
using highres_period = TickerDataClock::period;
static_assert(std::ratio_divide<period, highres_period>::den == 1, "Tick period must be an exact multiple of highres time period");
/** /**
* Default constructor uses LPTICKER if available (so the timer will * Default constructor uses LPTICKER if available (so the timer will
* continue to run in deep sleep), else USTICKER. * continue to run in deep sleep), else USTICKER.
@ -90,7 +107,7 @@ public:
* @param at Wake up tick * @param at Wake up tick
* @warning If the ticker tick is already scheduled it needs to be cancelled first! * @warning If the ticker tick is already scheduled it needs to be cancelled first!
*/ */
void set_wake_time(uint64_t at); void set_wake_time(time_point at);
/** /**
* Check whether the wake time has passed * Check whether the wake time has passed
@ -170,9 +187,9 @@ public:
* *
* @return number of unacknowledged ticks * @return number of unacknowledged ticks
*/ */
int unacknowledged_ticks() const std::chrono::duration<int, period> unacknowledged_ticks() const
{ {
return core_util_atomic_load_u8(&_unacknowledged_ticks); return std::chrono::duration<int, period>(core_util_atomic_load_u8(&_unacknowledged_ticks));
} }
/** Get the current tick count /** Get the current tick count
@ -186,7 +203,7 @@ public:
* *
* @return The number of ticks since timer creation. * @return The number of ticks since timer creation.
*/ */
uint64_t get_tick() const; time_point get_tick() const;
/** Update and get the current tick count /** Update and get the current tick count
* *
@ -199,14 +216,14 @@ public:
* *
* @return The number of ticks since timer creation. * @return The number of ticks since timer creation.
*/ */
uint64_t update_and_get_tick(); time_point update_and_get_tick();
/** /**
* Returns time since last tick * Returns time since last tick
* *
* @return Relative time in microseconds * @return Relative time in microseconds
*/ */
us_timestamp_t get_time_since_tick() const; highres_duration get_time_since_tick() const;
/** /**
* Get the time * Get the time
@ -216,17 +233,18 @@ public:
* *
* @return Current time in microseconds * @return Current time in microseconds
*/ */
us_timestamp_t get_time() const; highres_time_point get_time() const;
protected: protected:
using highres_duration_u32 = std::chrono::duration<uint32_t, highres_period>;
virtual void handler(); virtual void handler();
void _increment_tick(); void _increment_tick();
void _schedule_tick(); void _schedule_tick();
uint64_t _elapsed_ticks() const; duration _elapsed_ticks() const;
static void _set_irq_pending(); static void _set_irq_pending();
static void _clear_irq_pending(); static void _clear_irq_pending();
const us_timestamp_t _epoch; const highres_time_point _epoch;
us_timestamp_t _time_us; highres_time_point _time;
uint64_t _tick; uint64_t _tick;
uint8_t _unacknowledged_ticks; uint8_t _unacknowledged_ticks;
bool _wake_time_set; bool _wake_time_set;

View File

@ -68,12 +68,12 @@ OsTimer *init_os_timer()
/* Optionally timed operation, with optional predicate */ /* Optionally timed operation, with optional predicate */
struct timed_predicate_op { struct timed_predicate_op {
timed_predicate_op(uint64_t t) : wake_time(t), orig_predicate(NULL), orig_handle(NULL) timed_predicate_op(OsClock::time_point t) : wake_time(t), orig_predicate(NULL), orig_handle(NULL)
{ {
init_os_timer(); 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) timed_predicate_op(OsClock::time_point t, bool (*wake_predicate)(void *), void *wake_predicate_handle) : wake_time(t), orig_predicate(wake_predicate), orig_handle(wake_predicate_handle)
{ {
init_os_timer(); init_os_timer();
} }
@ -92,18 +92,18 @@ struct timed_predicate_op {
void sleep_prepare() void sleep_prepare()
{ {
if (wake_time != (uint64_t) -1) { if (wake_time != wake_time.max()) {
os_timer->set_wake_time(wake_time); OsClock::set_wake_time(wake_time);
} }
} }
bool sleep_prepared() bool sleep_prepared()
{ {
return wake_time == (uint64_t) -1 || os_timer->wake_time_set(); return wake_time == wake_time.max() || os_timer->wake_time_set();
} }
private: private:
uint64_t wake_time; OsClock::time_point wake_time;
bool (*orig_predicate)(void *); bool (*orig_predicate)(void *);
void *orig_handle; void *orig_handle;
}; };
@ -186,23 +186,23 @@ void do_sleep_operation(OpT &op)
* The wake predicate will be called from both outside and inside a critical * The wake predicate will be called from both outside and inside a critical
* section, so appropriate atomic care must be taken. * 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) OsClock::time_point do_timed_sleep_absolute(OsClock::time_point wake_time, bool (*wake_predicate)(void *), void *wake_predicate_handle)
{ {
{ {
timed_predicate_op op(wake_time, wake_predicate, wake_predicate_handle); timed_predicate_op op(wake_time, wake_predicate, wake_predicate_handle);
do_sleep_operation(op); do_sleep_operation(op);
} }
return os_timer->update_and_get_tick(); return OsClock::now_with_init_done();
} }
#if MBED_CONF_RTOS_PRESENT #if MBED_CONF_RTOS_PRESENT
/* The 32-bit limit is part of the API - we will always wake within 2^32 ticks */ /* 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 */ /* 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) OsClock::duration_u32 do_timed_sleep_relative(OsClock::duration_u32 wake_delay, bool (*wake_predicate)(void *), void *wake_predicate_handle)
{ {
uint64_t sleep_start = init_os_timer()->get_tick(); OsClock::time_point sleep_start = OsClock::now();
// When running with RTOS, the requested delay will be based on the kernel's tick count. // 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 // 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. // start time back to reflect its current idea of time.
@ -211,9 +211,9 @@ uint32_t do_timed_sleep_relative(uint32_t wake_delay, bool (*wake_predicate)(voi
// clear the unacknowledged tick count. // clear the unacknowledged tick count.
sleep_start -= os_timer->unacknowledged_ticks(); sleep_start -= os_timer->unacknowledged_ticks();
uint64_t sleep_finish = do_timed_sleep_absolute(sleep_start + wake_delay, wake_predicate, wake_predicate_handle); OsClock::time_point sleep_finish = do_timed_sleep_absolute(sleep_start + wake_delay, wake_predicate, wake_predicate_handle);
return static_cast<uint32_t>(sleep_finish - sleep_start); return sleep_finish - sleep_start;
} }
#else #else
@ -225,23 +225,23 @@ void do_untimed_sleep(bool (*wake_predicate)(void *), void *wake_predicate_handl
do_sleep_operation(op); do_sleep_operation(op);
} }
/* (uint32_t)-1 delay is treated as "wait forever" */ /* max() 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 */ /* 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) void do_timed_sleep_relative_or_forever(OsClock::duration_u32 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. // Special-case 0 delay, to save multiple callers having to do it. Just call the predicate once.
if (wake_delay == 0) { if (wake_delay == wake_delay.zero()) {
if (wake_predicate) { if (wake_predicate) {
wake_predicate(wake_predicate_handle); wake_predicate(wake_predicate_handle);
} }
return; return;
} }
uint64_t wake_time; OsClock::time_point wake_time;
if (wake_delay == (uint32_t) -1) { if (wake_delay == OsClock::duration_u32::max()) {
wake_time = (uint64_t) -1; wake_time = OsClock::time_point::max();
} else { } else {
wake_time = init_os_timer()->update_and_get_tick() + wake_delay; wake_time = OsClock::now() + wake_delay;
} }
/* Always use timed_predicate_op here to save pulling in two templates */ /* Always use timed_predicate_op here to save pulling in two templates */
timed_predicate_op op(wake_time, wake_predicate, wake_predicate_handle); timed_predicate_op op(wake_time, wake_predicate, wake_predicate_handle);

View File

@ -17,6 +17,7 @@
#ifndef MBED_MBED_SLEEP_TIMER_H #ifndef MBED_MBED_SLEEP_TIMER_H
#define MBED_MBED_SLEEP_TIMER_H #define MBED_MBED_SLEEP_TIMER_H
#include <chrono>
#include "platform/source/SysTimer.h" #include "platform/source/SysTimer.h"
#if MBED_CONF_RTOS_PRESENT #if MBED_CONF_RTOS_PRESENT
@ -29,11 +30,10 @@ namespace mbed {
namespace internal { namespace internal {
#if MBED_CONF_RTOS_PRESENT #if MBED_CONF_RTOS_PRESENT
#define OS_TICK_US (1000000 / OS_TICK_FREQ) using OsTimer = SysTimer<std::ratio<1, OS_TICK_FREQ>>;
#else #else
#define OS_TICK_US 1000 using OsTimer = SysTimer<std::milli>;
#endif #endif
typedef SysTimer<OS_TICK_US> OsTimer;
/* A SysTimer is used to provide the timed sleep - this provides access to share it for /* 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. * other use, such as ticks. If accessed this way, it must not be in use when a sleep function below is called.
@ -41,20 +41,54 @@ typedef SysTimer<OS_TICK_US> OsTimer;
extern OsTimer *os_timer; extern OsTimer *os_timer;
OsTimer *init_os_timer(); OsTimer *init_os_timer();
/* -1 is effectively "sleep forever" */ /** A C++11 chrono TrivialClock for os_timer
uint64_t do_timed_sleep_absolute(uint64_t wake_time, bool (*wake_predicate)(void *) = NULL, void *wake_predicate_handle = NULL); *
* @note To fit better into the chrono framework, OsClock uses
* chrono::milliseconds as its representation, which makes it signed
* and at least 45 bits (so it will be int64_t or equivalent).
*/
struct OsClock {
/* Standard TrivialClock fields */
using duration = std::chrono::milliseconds;
using rep = duration::rep;
using period = duration::period;
using time_point = std::chrono::time_point<OsClock, duration>;
static constexpr bool is_steady = true;
static time_point now()
{
// We are a real Clock with a well-defined epoch. As such we distinguish ourselves
// from the less-well-defined SysTimer pseudo-Clock. This means our time_points
// are not convertible, so need to fiddle here.
return time_point(init_os_timer()->update_and_get_tick().time_since_epoch());
}
// Slightly-optimised variant of OsClock::now() that assumes os_timer is initialised.
static time_point now_with_init_done()
{
return time_point(os_timer->update_and_get_tick().time_since_epoch());
}
static void set_wake_time(time_point wake_time)
{
return os_timer->set_wake_time(OsTimer::time_point(wake_time.time_since_epoch()));
}
/* Extension to
* make it easy to use 32-bit durations for some APIs, as we historically do */
using duration_u32 = std::chrono::duration<uint32_t, period>;
};
/* time_point::max() is effectively "sleep forever" */
OsClock::time_point do_timed_sleep_absolute(OsClock::time_point wake_time, bool (*wake_predicate)(void *) = NULL, void *wake_predicate_handle = NULL);
#if MBED_CONF_RTOS_PRESENT #if MBED_CONF_RTOS_PRESENT
/* Maximum sleep time is 2^32-1 ticks; timer is always set to achieve this */ /* 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 */ /* 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); OsClock::duration_u32 do_timed_sleep_relative(OsClock::duration_u32 wake_delay, bool (*wake_predicate)(void *) = NULL, void *wake_predicate_handle = NULL);
#else #else
void do_untimed_sleep(bool (*wake_predicate)(void *), void *wake_predicate_handle = NULL); void do_untimed_sleep(bool (*wake_predicate)(void *), void *wake_predicate_handle = NULL);
/* (uint32_t)-1 delay is sleep forever */ /* duration_u32::max() 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); void do_timed_sleep_relative_or_forever(OsClock::duration_u32 wake_delay, bool (*wake_predicate)(void *) = NULL, void *wake_predicate_handle = NULL);
#endif #endif

View File

@ -26,33 +26,38 @@
#include "rtos/ThisThread.h" #include "rtos/ThisThread.h"
#endif #endif
using namespace std::chrono;
extern "C" { extern "C" {
uint64_t get_ms_count(void) uint64_t get_ms_count(void)
{ {
#if MBED_CONF_RTOS_PRESENT #if MBED_CONF_RTOS_PRESENT
return rtos::Kernel::get_ms_count(); rtos::Kernel::Clock::time_point tp = rtos::Kernel::Clock::now();
#else #else
return mbed::internal::init_os_timer()->update_and_get_tick(); mbed::internal::OsClock::time_point tp = mbed::internal::OsClock::now();
#endif #endif
return duration<uint64_t, std::milli>(tp.time_since_epoch()).count();
} }
void thread_sleep_for(uint32_t millisec) void thread_sleep_for(uint32_t millisec)
{ {
auto d = duration<uint32_t, std::milli>(millisec);
#if MBED_CONF_RTOS_PRESENT #if MBED_CONF_RTOS_PRESENT
rtos::ThisThread::sleep_for(millisec); rtos::ThisThread::sleep_for(d);
#else #else
// Undocumented, but osDelay(UINT32_MAX) does actually sleep forever // Undocumented, but osDelay(UINT32_MAX) does actually sleep forever
mbed::internal::do_timed_sleep_relative_or_forever(millisec); mbed::internal::do_timed_sleep_relative_or_forever(d);
#endif #endif
} }
void thread_sleep_until(uint64_t millisec) void thread_sleep_until(uint64_t millisec)
{ {
auto d = duration<uint64_t, std::milli>(millisec);
#if MBED_CONF_RTOS_PRESENT #if MBED_CONF_RTOS_PRESENT
rtos::ThisThread::sleep_until(millisec); rtos::ThisThread::sleep_until(rtos::Kernel::Clock::time_point(d));
#else #else
mbed::internal::do_timed_sleep_absolute(millisec); mbed::internal::do_timed_sleep_absolute(mbed::internal::OsClock::time_point(d));
#endif #endif
} }

View File

@ -24,15 +24,32 @@
#define KERNEL_H #define KERNEL_H
#include <stdint.h> #include <stdint.h>
#include <chrono>
#include "rtos/mbed_rtos_types.h" #include "rtos/mbed_rtos_types.h"
#include "platform/mbed_toolchain.h"
#if !MBED_CONF_RTOS_PRESENT
#include "platform/source/mbed_os_timer.h"
#endif
namespace rtos { namespace rtos {
/** \addtogroup rtos-public-api */ /** \addtogroup rtos-public-api */
/** @{*/ /** @{*/
/** Functions in the Kernel namespace control RTOS kernel information. */ /** Functions in the Kernel namespace control RTOS kernel information. */
namespace Kernel { namespace Kernel {
namespace impl {
/* Internal integer-returning function.
*
* ARM EABI means that `time_point`s do not get returned in registers, so
* it's worth having the actual exteernal definition return an integer, and only
* convert to `time_point` via the inline function `now()`.
*/
uint64_t get_tick_count();
}
/** Read the current RTOS kernel millisecond tick count. /** Read the current RTOS kernel millisecond tick count.
The tick count corresponds to the tick count the RTOS uses for timing The tick count corresponds to the tick count the RTOS uses for timing
purposes. It increments monotonically from 0 at boot, so it effectively purposes. It increments monotonically from 0 at boot, so it effectively
@ -42,9 +59,66 @@ namespace Kernel {
@note Mbed OS always uses millisecond RTOS ticks, and this could only wrap @note Mbed OS always uses millisecond RTOS ticks, and this could only wrap
after half a billion years. after half a billion years.
@note You cannot call this function from ISR context. @note You cannot call this function from ISR context.
@deprecated Use `Kernel::Clock::now()` to get a chrono time_point instead of an integer millisecond count.
*/ */
MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Use `Kernel::Clock::now()` to get a chrono time_point instead of an integer millisecond count.")
uint64_t get_ms_count(); uint64_t get_ms_count();
/** A C++11 chrono TrivialClock for the kernel millisecond tick count
*
* @note To fit better into the chrono framework, Kernel::Clock uses
* std::chrono::milliseconds as its representation, which makes it signed
* and at least 45 bits (so it will be int64_t or equivalent).
*/
struct Clock {
Clock() = delete;
/* Standard TrivialClock fields */
using duration = std::chrono::milliseconds;
using rep = duration::rep;
using period = duration::period;
#if MBED_CONF_RTOS_PRESENT
using time_point = std::chrono::time_point<Clock>;
#else
/* In non-RTOS builds, the clock maps directly to the underlying clock, and must
* indicate that here, so we can do implicit conversion internally.
*/
using time_point = std::chrono::time_point<mbed::internal::OsClock, duration>;
#endif
static constexpr bool is_steady = true;
static time_point now()
{
return time_point(duration(impl::get_tick_count()));
}
/* Extension to make it easy to use 32-bit durations for some APIs, as we historically have,
* for efficiency.
*/
using duration_u32 = std::chrono::duration<uint32_t, period>;
/** Lock the clock to ensure it stays running; dummy for API compatibility with HighResClock */
static void lock()
{
}
/** Unlock the clock, allowing it to stop during power saving; dummy for API compatibility with HighResClock */
static void unlock()
{
}
};
/** Maximum duration for Kernel::Clock::duration_u32-based APIs
*
* @note As duration_u32-based APIs pass through straight to CMSIS-RTOS, they will
* interpret duration_u32(0xFFFFFFFF) as "wait forever". Indicate maximum
* wait time of 0xFFFFFFFE for these calls (which is ~49 days).
*/
constexpr Clock::duration_u32 wait_for_u32_max{osWaitForever - 1};
/** Magic "wait forever" constant for Kernel::Clock::duration_u32-based APIs
*
* Many duration_u32-based APIs treat duration_u32(0xFFFFFFFF) as "wait forever".
*/
constexpr Clock::duration_u32 wait_for_u32_forever{osWaitForever};
/** Attach a function to be called by the RTOS idle task. /** Attach a function to be called by the RTOS idle task.
@param fptr pointer to the function to be called @param fptr pointer to the function to be called

View File

@ -35,7 +35,14 @@
namespace rtos { namespace rtos {
constexpr bool Kernel::Clock::is_steady;
uint64_t Kernel::get_ms_count() uint64_t Kernel::get_ms_count()
{
return impl::get_tick_count();
}
uint64_t Kernel::impl::get_tick_count()
{ {
#if MBED_CONF_RTOS_PRESENT #if MBED_CONF_RTOS_PRESENT
// CMSIS-RTOS 2.1.0 and 2.1.1 differ in the time type. We assume // CMSIS-RTOS 2.1.0 and 2.1.1 differ in the time type. We assume

View File

@ -22,6 +22,7 @@
*/ */
#include "rtos/source/rtos_idle.h" #include "rtos/source/rtos_idle.h"
#include "rtos/Kernel.h"
#include "platform/mbed_power_mgmt.h" #include "platform/mbed_power_mgmt.h"
#include "platform/source/mbed_os_timer.h" #include "platform/source/mbed_os_timer.h"
#include "TimerEvent.h" #include "TimerEvent.h"
@ -95,7 +96,7 @@ extern "C" {
// Get System Timer count. // Get System Timer count.
uint32_t OS_Tick_GetCount(void) uint32_t OS_Tick_GetCount(void)
{ {
return (uint32_t) os_timer->get_time_since_tick(); return (uint32_t) os_timer->get_time_since_tick().count();
} }
// Get OS Tick IRQ number. // Get OS Tick IRQ number.
@ -115,13 +116,17 @@ extern "C" {
// Get OS Tick timer clock frequency // Get OS Tick timer clock frequency
uint32_t OS_Tick_GetClock(void) uint32_t OS_Tick_GetClock(void)
{ {
return 1000000; static_assert(OsTimer::highres_duration::period::num == 1, "Non-integral timer frequency");
static_assert(OsTimer::highres_duration::period::den <= 0xFFFFFFFF, "Too fast timer frequency");
return OsTimer::highres_duration::period::den;
} }
// Get OS Tick interval. // Get OS Tick interval.
uint32_t OS_Tick_GetInterval(void) uint32_t OS_Tick_GetInterval(void)
{ {
return 1000; static_assert(OsTimer::period::num == 1, "Non-integral tick frequency");
static_assert(OsTimer::period::den <= 0xFFFFFFFF, "Too fast tick frequency");
return OsTimer::period::den;
} }
static bool rtos_event_pending(void *) static bool rtos_event_pending(void *)
@ -131,12 +136,12 @@ extern "C" {
static void default_idle_hook(void) static void default_idle_hook(void)
{ {
uint32_t ticks_to_sleep = osKernelSuspend(); rtos::Kernel::Clock::duration_u32 ticks_to_sleep{osKernelSuspend()};
// osKernelSuspend will call OS_Tick_Disable, cancelling the tick, which frees // osKernelSuspend will call OS_Tick_Disable, cancelling the tick, which frees
// up the os timer for the timed sleep // up the os timer for the timed sleep
uint64_t ticks_slept = mbed::internal::do_timed_sleep_relative(ticks_to_sleep, rtos_event_pending); rtos::Kernel::Clock::duration_u32 ticks_slept = mbed::internal::do_timed_sleep_relative(ticks_to_sleep, rtos_event_pending);
MBED_ASSERT(ticks_slept < osWaitForever); MBED_ASSERT(ticks_slept < rtos::Kernel::wait_for_u32_max);
osKernelResume((uint32_t) ticks_slept); osKernelResume(ticks_slept.count());
} }

View File

@ -112,4 +112,6 @@ pppdebug
ppp ppp
api api
uart uart
chrono
Hinnant
_doxy_ _doxy_