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
}
using namespace std::chrono;
constexpr milliseconds deep_sleep_latency{MBED_CONF_TARGET_DEEP_SLEEP_LATENCY};
#if (defined(NO_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 internal {
template<uint32_t US_IN_TICK, bool IRQ>
SysTimer<US_IN_TICK, IRQ>::SysTimer() :
template<class Period, bool IRQ>
SysTimer<Period, IRQ>::SysTimer() :
#if DEVICE_LPTICKER
TimerEvent(get_lp_ticker_data()),
SysTimer(get_lp_ticker_data())
#else
TimerEvent(get_us_ticker_data()),
SysTimer(get_us_ticker_data())
#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>
SysTimer<US_IN_TICK, IRQ>::SysTimer(const ticker_data_t *data) :
template<class Period, bool IRQ>
SysTimer<Period, IRQ>::SysTimer(const ticker_data_t *data) :
TimerEvent(data),
_epoch(ticker_read_us(_ticker_data)),
_time_us(_epoch),
_epoch(_ticker_data.now()),
_time(_epoch),
_tick(0),
_unacknowledged_ticks(0),
_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>
SysTimer<US_IN_TICK, IRQ>::~SysTimer()
template<class Period, bool IRQ>
SysTimer<Period, IRQ>::~SysTimer()
{
cancel_tick();
cancel_wake();
}
template<uint32_t US_IN_TICK, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::set_wake_time(uint64_t at)
template<class Period, bool IRQ>
void SysTimer<Period, IRQ>::set_wake_time(time_point at)
{
// SysTimer must not be active - we must be in suspend state
MBED_ASSERT(!_ticking);
@ -105,8 +100,8 @@ void SysTimer<US_IN_TICK, IRQ>::set_wake_time(uint64_t at)
return;
}
uint64_t ticks_to_sleep = at - _tick;
uint64_t wake_time = _epoch + at * US_IN_TICK;
duration ticks_to_sleep = at - get_tick();
highres_time_point wake_time = _epoch + at.time_since_epoch();
/* Set this first, before attaching the interrupt that can unset it */
_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();
}
/* Consider whether we will need early or precise wake-up */
if (MBED_CONF_TARGET_DEEP_SLEEP_LATENCY > 0 &&
ticks_to_sleep > MBED_CONF_TARGET_DEEP_SLEEP_LATENCY &&
if (deep_sleep_latency > deep_sleep_latency.zero() &&
ticks_to_sleep > deep_sleep_latency &&
!_deep_sleep_locked) {
/* If there is deep sleep latency, but we still have enough time,
* 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.
*/
_wake_early = true;
insert_absolute(wake_time - MBED_CONF_TARGET_DEEP_SLEEP_LATENCY * US_IN_TICK);
insert_absolute(wake_time - deep_sleep_latency);
} else {
/* 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
* is shallow. (If there is no deep sleep latency, we're fine with it being deep).
*/
_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;
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>
void SysTimer<US_IN_TICK, IRQ>::cancel_wake()
template<class Period, bool IRQ>
void SysTimer<Period, IRQ>::cancel_wake()
{
MBED_ASSERT(!_ticking);
// 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>
uint64_t SysTimer<US_IN_TICK, IRQ>::_elapsed_ticks() const
template<class Period, bool IRQ>
auto SysTimer<Period, IRQ>::_elapsed_ticks() const -> duration
{
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) {
highres_duration elapsed_us = _ticker_data.now() - _time;
// Fastest common cases avoiding any division for 0 or 1 ticks
if (elapsed_us < duration(1)) {
return duration(0);
} else if (elapsed_us < duration(2)) {
return duration(1);
} else if (elapsed_us.count() <= 0xFFFFFFFF) {
// 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 {
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>
void SysTimer<US_IN_TICK, IRQ>::start_tick()
template<class Period, bool IRQ>
void SysTimer<Period, IRQ>::start_tick()
{
_ticking = true;
if (_unacknowledged_ticks > 0) {
@ -183,14 +180,14 @@ void SysTimer<US_IN_TICK, IRQ>::start_tick()
_schedule_tick();
}
template<uint32_t US_IN_TICK, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::_schedule_tick()
template<class Period, bool IRQ>
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>
void SysTimer<US_IN_TICK, IRQ>::acknowledge_tick()
template<class Period, bool IRQ>
void SysTimer<Period, IRQ>::acknowledge_tick()
{
// Try to avoid missed ticks if OS's IRQ level is not keeping
// up with our handler.
@ -202,8 +199,8 @@ void SysTimer<US_IN_TICK, IRQ>::acknowledge_tick()
}
}
template<uint32_t US_IN_TICK, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::cancel_tick()
template<class Period, bool IRQ>
void SysTimer<Period, IRQ>::cancel_tick()
{
// Underlying call is interrupt safe
@ -213,66 +210,66 @@ void SysTimer<US_IN_TICK, IRQ>::cancel_tick()
_clear_irq_pending();
}
template<uint32_t US_IN_TICK, bool IRQ>
uint64_t SysTimer<US_IN_TICK, IRQ>::get_tick() const
template<class Period, bool IRQ>
auto SysTimer<Period, IRQ>::get_tick() const -> time_point
{
// Atomic is necessary as this can be called from any foreground context,
// 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>
uint64_t SysTimer<US_IN_TICK, IRQ>::update_and_get_tick()
template<class Period, bool IRQ>
auto SysTimer<Period, IRQ>::update_and_get_tick() -> time_point
{
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();
duration elapsed_ticks = _elapsed_ticks();
_unacknowledged_ticks = 0;
_time_us += elapsed_ticks * US_IN_TICK;
_tick += elapsed_ticks;
_time += elapsed_ticks;
_tick += elapsed_ticks.count();
return _tick;
return time_point(duration(_tick));
}
template<uint32_t US_IN_TICK, bool IRQ>
us_timestamp_t SysTimer<US_IN_TICK, IRQ>::get_time() const
template<class Period, bool IRQ>
auto SysTimer<Period, IRQ>::get_time() const -> highres_time_point
{
// Underlying call is interrupt safe
return ticker_read_us(_ticker_data);
return _ticker_data.now();
}
template<uint32_t US_IN_TICK, bool IRQ>
us_timestamp_t SysTimer<US_IN_TICK, IRQ>::get_time_since_tick() const
template<class Period, bool IRQ>
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))
template<uint32_t US_IN_TICK, bool IRQ>
IRQn_Type SysTimer<US_IN_TICK, IRQ>::get_irq_number()
template<class Period, bool IRQ>
IRQn_Type SysTimer<Period, IRQ>::get_irq_number()
{
return mbed_get_m0_tick_irqn();
}
#elif (TARGET_CORTEX_M)
template<uint32_t US_IN_TICK, bool IRQ>
IRQn_Type SysTimer<US_IN_TICK, IRQ>::get_irq_number()
template<class Period, bool IRQ>
IRQn_Type SysTimer<Period, IRQ>::get_irq_number()
{
return SysTick_IRQn;
}
#elif (TARGET_CORTEX_A)
template<uint32_t US_IN_TICK, bool IRQ>
IRQn_ID_t SysTimer<US_IN_TICK, IRQ>::get_irq_number()
template<class Period, bool IRQ>
IRQn_ID_t SysTimer<Period, IRQ>::get_irq_number()
{
return mbed_get_a9_tick_irqn();
}
#endif
template<uint32_t US_IN_TICK, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::_set_irq_pending()
template<class Period, bool IRQ>
void SysTimer<Period, IRQ>::_set_irq_pending()
{
// Protected function synchronized externally
if (!IRQ) {
@ -287,8 +284,8 @@ void SysTimer<US_IN_TICK, IRQ>::_set_irq_pending()
#endif
}
template<uint32_t US_IN_TICK, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::_clear_irq_pending()
template<class Period, bool IRQ>
void SysTimer<Period, IRQ>::_clear_irq_pending()
{
// Protected function synchronized externally
if (!IRQ) {
@ -303,17 +300,17 @@ void SysTimer<US_IN_TICK, IRQ>::_clear_irq_pending()
#endif
}
template<uint32_t US_IN_TICK, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::_increment_tick()
template<class Period, bool IRQ>
void SysTimer<Period, IRQ>::_increment_tick()
{
// Protected function synchronized externally
_tick++;
_time_us += US_IN_TICK;
_time += duration(1);
}
template<uint32_t US_IN_TICK, bool IRQ>
void SysTimer<US_IN_TICK, IRQ>::handler()
template<class Period, bool IRQ>
void SysTimer<Period, IRQ>::handler()
{
/* To reduce IRQ latency problems, we do not re-arm in the interrupt handler */
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");
#define OS_TICK_US (1000000 / OS_TICK_FREQ)
#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
/* Standard 1ms SysTimer */
template class SysTimer<1000>;
template class SysTimer<std::milli>;
/* 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 */
template class SysTimer<42000, false>;
template class SysTimer<std::ratio_multiply<std::ratio<42>, std::milli>, false>;
} // namespace internal
} // namespace mbed

View File

@ -20,6 +20,7 @@
#include "platform/NonCopyable.h"
#include "platform/mbed_atomic.h"
#include "drivers/TimerEvent.h"
#include <chrono>
#include "cmsis.h"
extern "C" {
@ -45,10 +46,26 @@ namespace internal {
*
* @note SysTimer is not the part of Mbed API.
*/
template <uint32_t US_IN_TICK, bool IRQ = true>
class SysTimer: private mbed::TimerEvent, private mbed::NonCopyable<SysTimer<US_IN_TICK, IRQ> > {
template <class Period, bool IRQ = true>
class SysTimer: private mbed::TimerEvent, private mbed::NonCopyable<SysTimer<Period, IRQ> > {
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
* continue to run in deep sleep), else USTICKER.
@ -90,7 +107,7 @@ public:
* @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);
void set_wake_time(time_point at);
/**
* Check whether the wake time has passed
@ -170,9 +187,9 @@ public:
*
* @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
@ -186,7 +203,7 @@ public:
*
* @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
*
@ -199,14 +216,14 @@ public:
*
* @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
*
* @return Relative time in microseconds
*/
us_timestamp_t get_time_since_tick() const;
highres_duration get_time_since_tick() const;
/**
* Get the time
@ -216,17 +233,18 @@ public:
*
* @return Current time in microseconds
*/
us_timestamp_t get_time() const;
highres_time_point get_time() const;
protected:
using highres_duration_u32 = std::chrono::duration<uint32_t, highres_period>;
virtual void handler();
void _increment_tick();
void _schedule_tick();
uint64_t _elapsed_ticks() const;
duration _elapsed_ticks() const;
static void _set_irq_pending();
static void _clear_irq_pending();
const us_timestamp_t _epoch;
us_timestamp_t _time_us;
const highres_time_point _epoch;
highres_time_point _time;
uint64_t _tick;
uint8_t _unacknowledged_ticks;
bool _wake_time_set;

View File

@ -68,12 +68,12 @@ OsTimer *init_os_timer()
/* 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)
timed_predicate_op(OsClock::time_point 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)
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();
}
@ -92,18 +92,18 @@ struct timed_predicate_op {
void sleep_prepare()
{
if (wake_time != (uint64_t) -1) {
os_timer->set_wake_time(wake_time);
if (wake_time != wake_time.max()) {
OsClock::set_wake_time(wake_time);
}
}
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:
uint64_t wake_time;
OsClock::time_point wake_time;
bool (*orig_predicate)(void *);
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
* 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);
do_sleep_operation(op);
}
return os_timer->update_and_get_tick();
return OsClock::now_with_init_done();
}
#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)
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.
// 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.
@ -211,9 +211,9 @@ uint32_t do_timed_sleep_relative(uint32_t wake_delay, bool (*wake_predicate)(voi
// 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);
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
@ -225,23 +225,23 @@ void do_untimed_sleep(bool (*wake_predicate)(void *), void *wake_predicate_handl
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 */
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.
if (wake_delay == 0) {
if (wake_delay == wake_delay.zero()) {
if (wake_predicate) {
wake_predicate(wake_predicate_handle);
}
return;
}
uint64_t wake_time;
if (wake_delay == (uint32_t) -1) {
wake_time = (uint64_t) -1;
OsClock::time_point wake_time;
if (wake_delay == OsClock::duration_u32::max()) {
wake_time = OsClock::time_point::max();
} 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 */
timed_predicate_op op(wake_time, wake_predicate, wake_predicate_handle);

View File

@ -17,6 +17,7 @@
#ifndef MBED_MBED_SLEEP_TIMER_H
#define MBED_MBED_SLEEP_TIMER_H
#include <chrono>
#include "platform/source/SysTimer.h"
#if MBED_CONF_RTOS_PRESENT
@ -29,11 +30,10 @@ namespace mbed {
namespace internal {
#if MBED_CONF_RTOS_PRESENT
#define OS_TICK_US (1000000 / OS_TICK_FREQ)
using OsTimer = SysTimer<std::ratio<1, OS_TICK_FREQ>>;
#else
#define OS_TICK_US 1000
using OsTimer = SysTimer<std::milli>;
#endif
typedef SysTimer<OS_TICK_US> 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.
@ -41,20 +41,54 @@ typedef SysTimer<OS_TICK_US> OsTimer;
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);
/** A C++11 chrono TrivialClock for os_timer
*
* @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
/* 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);
OsClock::duration_u32 do_timed_sleep_relative(OsClock::duration_u32 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 */
/* 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

View File

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

View File

@ -24,15 +24,32 @@
#define KERNEL_H
#include <stdint.h>
#include <chrono>
#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 {
/** \addtogroup rtos-public-api */
/** @{*/
/** Functions in the Kernel namespace control RTOS kernel information. */
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.
The tick count corresponds to the tick count the RTOS uses for timing
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
after half a billion years.
@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();
/** 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.
@param fptr pointer to the function to be called

View File

@ -35,7 +35,14 @@
namespace rtos {
constexpr bool Kernel::Clock::is_steady;
uint64_t Kernel::get_ms_count()
{
return impl::get_tick_count();
}
uint64_t Kernel::impl::get_tick_count()
{
#if MBED_CONF_RTOS_PRESENT
// 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/Kernel.h"
#include "platform/mbed_power_mgmt.h"
#include "platform/source/mbed_os_timer.h"
#include "TimerEvent.h"
@ -95,7 +96,7 @@ extern "C" {
// Get System Timer count.
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.
@ -115,13 +116,17 @@ extern "C" {
// Get OS Tick timer clock frequency
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.
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 *)
@ -131,12 +136,12 @@ extern "C" {
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
// up the os timer for the timed sleep
uint64_t ticks_slept = mbed::internal::do_timed_sleep_relative(ticks_to_sleep, rtos_event_pending);
MBED_ASSERT(ticks_slept < osWaitForever);
osKernelResume((uint32_t) ticks_slept);
rtos::Kernel::Clock::duration_u32 ticks_slept = mbed::internal::do_timed_sleep_relative(ticks_to_sleep, rtos_event_pending);
MBED_ASSERT(ticks_slept < rtos::Kernel::wait_for_u32_max);
osKernelResume(ticks_slept.count());
}

View File

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