Skip to content

Commit 7698b34

Browse files
authored
Merge pull request #12938 from kjbracey-arm/chrono-sleep-correct
Correct core RTOS sleep routine timing
2 parents f13ae08 + 3f67eed commit 7698b34

File tree

3 files changed

+52
-13
lines changed

3 files changed

+52
-13
lines changed

platform/source/mbed_os_timer.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -200,16 +200,18 @@ OsClock::time_point do_timed_sleep_absolute(OsClock::time_point wake_time, bool
200200
#if MBED_CONF_RTOS_PRESENT
201201
/* The 32-bit limit is part of the API - we will always wake within 2^32 ticks */
202202
/* This version is tuned for RTOS use, where the RTOS needs to know the time spent sleeping */
203-
OsClock::duration_u32 do_timed_sleep_relative(OsClock::duration_u32 wake_delay, bool (*wake_predicate)(void *), void *wake_predicate_handle)
203+
/* Note that unlike do_timed_sleep_relative_or_forever it does not do a tick update on entry; */
204+
/* it assumes the caller has been using the ticker, and is passing a delay relative to the */
205+
/* time point of the ticks it has acknowledged. */
206+
OsClock::duration_u32 do_timed_sleep_relative_to_acknowledged_ticks(OsClock::duration_u32 wake_delay, bool (*wake_predicate)(void *), void *wake_predicate_handle)
204207
{
205-
OsClock::time_point sleep_start = OsClock::now();
206208
// When running with RTOS, the requested delay will be based on the kernel's tick count.
207-
// If it missed a tick as entering idle, we should reflect that by moving the
208-
// start time back to reflect its current idea of time.
209-
// Example: OS tick count = 100, our tick count = 101, requested delay = 50
209+
// If it missed a tick as entering idle, we should reflect that by basing the start time
210+
// on its current idea of time - OsClock::acknowledged_ticks().
211+
// Example: OS acknowledged tick count = 100, our reported tick count = 101, requested delay = 50
210212
// We need to schedule wake for tick 150, report 50 ticks back to our caller, and
211213
// clear the unacknowledged tick count.
212-
sleep_start -= os_timer->unacknowledged_ticks();
214+
OsClock::time_point sleep_start = OsClock::acknowledged_ticks();
213215

214216
OsClock::time_point sleep_finish = do_timed_sleep_absolute(sleep_start + wake_delay, wake_predicate, wake_predicate_handle);
215217

@@ -226,7 +228,8 @@ void do_untimed_sleep(bool (*wake_predicate)(void *), void *wake_predicate_handl
226228
}
227229

228230
/* max() delay is treated as "wait forever" */
229-
/* This version is tuned for non-RTOS use, where we don't need to return sleep time, and waiting forever is possible */
231+
/* This version is tuned for non-RTOS use, where we must update the tick count, */
232+
/* don't need to return sleep time, and waiting forever is possible */
230233
void do_timed_sleep_relative_or_forever(OsClock::duration_u32 wake_delay, bool (*wake_predicate)(void *), void *wake_predicate_handle)
231234
{
232235
// Special-case 0 delay, to save multiple callers having to do it. Just call the predicate once.

platform/source/mbed_os_timer.h

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,61 @@ extern OsTimer *os_timer;
4242
OsTimer *init_os_timer();
4343

4444
/** A C++11 chrono TrivialClock for os_timer
45+
*
46+
* Due to the nature of OsTimer/SysTimer, this does not have a single `now` method, but has
47+
* multiple ways to report the current state:
48+
*
49+
* High-res timeline -------------------------------------------------------------
50+
* Ticks | a | b | b | b | c | c | c | c | c | d ^
51+
* ^ ^ ^ os_timer->get_time()
52+
* acknowledged_ticks() reported_ticks() now()
53+
*
54+
* (a) is time read from hardware by OsTimer, reported to the user of OsTimer, and acknowledged by that user.
55+
* (b) is time read from hardware by OsTimer, reported to the user of OsTimer, but not yet acknowledged.
56+
* (c) is time already elapsed in the hardware but yet to be read and processed as ticks by OsTimer.
57+
* (d) is time already elapsed in the hardware that doesn't yet form a tick.
58+
*
59+
* Time is "reported" either by:
60+
* * calls to the OsTimer's handler following start_tick - these must be acknowledged
61+
* * the result of OsTimer::update_and_get_tick() / OsClock::now() - calling this implies acknowledgment.
62+
*
63+
* As such `now()` is used when the ticker is not in use - it processes ticks that would have been
64+
* processed by the tick handler. If the ticker is in uses `reported_ticks` or `acknowleged_ticks` must be used.
4565
*
4666
* @note To fit better into the chrono framework, OsClock uses
4767
* chrono::milliseconds as its representation, which makes it signed
48-
* and at least 45 bits (so it will be int64_t or equivalent).
68+
* and at least 45 bits, so it will be int64_t or equivalent, unlike
69+
* OsTimer which uses uint64_t rep.
4970
*/
5071
struct OsClock {
5172
/* Standard TrivialClock fields */
52-
using duration = std::chrono::milliseconds;
53-
using rep = duration::rep;
54-
using period = duration::period;
73+
using period = OsTimer::period;
74+
using rep = std::chrono::milliseconds::rep;
75+
using duration = std::chrono::duration<rep, period>; // == std::chrono::milliseconds, if period is std::milli
5576
using time_point = std::chrono::time_point<OsClock, duration>;
5677
static constexpr bool is_steady = true;
78+
// Read the hardware, and return the updated time_point.
79+
// Initialize the timing system if necessary - this could be the first call.
80+
// See SysTimer::update_and_get_tick for more details.
5781
static time_point now()
5882
{
5983
// We are a real Clock with a well-defined epoch. As such we distinguish ourselves
6084
// from the less-well-defined SysTimer pseudo-Clock. This means our time_points
6185
// are not convertible, so need to fiddle here.
6286
return time_point(init_os_timer()->update_and_get_tick().time_since_epoch());
6387
}
88+
// Return the current reported tick count, without update.
89+
// Assumes timer has already been initialized, as ticker should have been in use to
90+
// keep that tick count up-to-date. See SysTimer::get_tick for more details.
91+
static time_point reported_ticks()
92+
{
93+
return time_point(os_timer->get_tick().time_since_epoch());
94+
}
95+
// Return the acknowledged tick count.
96+
static time_point acknowledged_ticks()
97+
{
98+
return reported_ticks() - os_timer->unacknowledged_ticks();
99+
}
64100
// Slightly-optimised variant of OsClock::now() that assumes os_timer is initialised.
65101
static time_point now_with_init_done()
66102
{
@@ -81,7 +117,7 @@ OsClock::time_point do_timed_sleep_absolute(OsClock::time_point wake_time, bool
81117
#if MBED_CONF_RTOS_PRESENT
82118
/* Maximum sleep time is 2^32-1 ticks; timer is always set to achieve this */
83119
/* Assumes that ticker has been in use prior to call, so restricted to RTOS use */
84-
OsClock::duration_u32 do_timed_sleep_relative(OsClock::duration_u32 wake_delay, bool (*wake_predicate)(void *) = NULL, void *wake_predicate_handle = NULL);
120+
OsClock::duration_u32 do_timed_sleep_relative_to_acknowledged_ticks(OsClock::duration_u32 wake_delay, bool (*wake_predicate)(void *) = NULL, void *wake_predicate_handle = NULL);
85121
#else
86122

87123
void do_untimed_sleep(bool (*wake_predicate)(void *), void *wake_predicate_handle = NULL);

rtos/source/TARGET_CORTEX/mbed_rtx_idle.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ extern "C" {
139139
rtos::Kernel::Clock::duration_u32 ticks_to_sleep{osKernelSuspend()};
140140
// osKernelSuspend will call OS_Tick_Disable, cancelling the tick, which frees
141141
// up the os timer for the timed sleep
142-
rtos::Kernel::Clock::duration_u32 ticks_slept = mbed::internal::do_timed_sleep_relative(ticks_to_sleep, rtos_event_pending);
142+
rtos::Kernel::Clock::duration_u32 ticks_slept = mbed::internal::do_timed_sleep_relative_to_acknowledged_ticks(ticks_to_sleep, rtos_event_pending);
143143
MBED_ASSERT(ticks_slept < rtos::Kernel::wait_for_u32_max);
144144
osKernelResume(ticks_slept.count());
145145
}

0 commit comments

Comments
 (0)