mirror of https://github.com/ARMmbed/mbed-os.git
Add Chrono support to Kernel and SysTimer
parent
4ee7d24adc
commit
f4e0ea2c75
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -112,4 +112,6 @@ pppdebug
|
|||
ppp
|
||||
api
|
||||
uart
|
||||
chrono
|
||||
Hinnant
|
||||
_doxy_
|
||||
|
|
Loading…
Reference in New Issue