forked from ARMmbed/mbed-os
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mbed_os_timer.cpp
259 lines (221 loc) · 8.64 KB
/
mbed_os_timer.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/*
* Copyright (c) 2006-2019, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "platform/mbed_power_mgmt.h"
#include "platform/CriticalSectionLock.h"
#include "platform/internal/SysTimer.h"
#include "platform/internal/mbed_os_timer.h"
#include "us_ticker_api.h"
#include "lp_ticker_api.h"
#include "mbed_critical.h"
#include "mbed_error.h"
#include <new>
/* This provides the marshalling point for a system global SysTimer, which
* is used to provide:
* - timed sleeps (for default idle hook in RTOS tickless mode, or non-RTOS sleeps)
* - regular ticks for RTOS
* - absolute system timing (directly for non-RTOS, or indirectly via RTOS tick count)
*/
namespace mbed {
namespace internal {
OsTimer *os_timer;
namespace {
uint64_t os_timer_data[(sizeof(OsTimer) + 7) / 8];
}
OsTimer *init_os_timer()
{
// Do not use SingletonPtr since this relies on the RTOS.
// Locking not required as it will be first called during
// OS init, or else we're a non-RTOS single-threaded setup.
if (!os_timer) {
#if DEVICE_LPTICKER && !MBED_CONF_TARGET_TICKLESS_FROM_US_TICKER
os_timer = new (os_timer_data) OsTimer(get_lp_ticker_data());
#elif DEVICE_USTICKER
os_timer = new (os_timer_data) OsTimer(get_us_ticker_data());
#else
MBED_ERROR(
MBED_MAKE_ERROR(
MBED_MODULE_PLATFORM,
MBED_ERROR_CODE_CONFIG_UNSUPPORTED),
"OS timer not available");
#endif
}
return os_timer;
}
/* These traits classes are designed to permit chunks of code to be
* omitted - in particular eliminating timers. However, we don't want
* to cause template bloat, so don't have too many traits variants.
*/
/* Optionally timed operation, with optional predicate */
struct timed_predicate_op {
timed_predicate_op(OsClock::time_point t) : wake_time(t), orig_predicate(NULL), orig_handle(NULL)
{
init_os_timer();
}
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();
}
~timed_predicate_op()
{
// Make sure wake timer is cancelled. (It may or may not be, depending on
// why we woke).
os_timer->cancel_wake();
}
bool wake_condition() const
{
return (orig_predicate && orig_predicate(orig_handle)) || os_timer->wake_time_passed();
}
void sleep_prepare()
{
if (wake_time != wake_time.max()) {
OsClock::set_wake_time(wake_time);
}
}
bool sleep_prepared()
{
return wake_time == wake_time.max() || os_timer->wake_time_set();
}
private:
OsClock::time_point wake_time;
bool (*orig_predicate)(void *);
void *orig_handle;
};
/* Untimed operation with predicate */
struct untimed_op {
untimed_op(bool (*wake_predicate)(void *), void *wake_predicate_handle) : orig_predicate(wake_predicate), orig_handle(wake_predicate_handle)
{
}
bool wake_condition() const
{
return orig_predicate(orig_handle);
}
void sleep_prepare()
{
}
bool sleep_prepared()
{
return true;
}
private:
bool (*orig_predicate)(void *);
void *orig_handle;
};
/* We require that this is called from thread context, outside a critical section,
* and the kernel already suspended if an RTOS, meaning we don't have to worry
* about any potential threading issues.
*
* The wake predicate will be called from both outside and inside a critical
* section, so appropriate atomic care must be taken.
*/
template <class OpT>
void do_sleep_operation(OpT &op)
{
// We assume the ticker is not already in use - without RTOS, it
// is never used, with RTOS, it will have been disabled with OS_Tick_Disable
while (!op.wake_condition()) {
// Set (or re-set) the wake time - outside a critical section, as
// it could take long enough to cause UART data loss on some platforms.
op.sleep_prepare();
// If no target sleep function, nothing else to do - just keep
// rechecking the wake condition.
#if DEVICE_SLEEP
// Now we need to enter the critical section for the race-free sleep
{
CriticalSectionLock lock;
// Recheck wake conditions before starting sleep, avoiding race
if (op.wake_condition()) {
break;
}
// It's possible that an intermediate wake interrupt occurred
// between "set_wake_time" and the critical lock - only sleep
// if we see that the timer is armed or we don't need it. Otherwise,
// we go round to set the timer again.
if (op.sleep_prepared()) {
// Enter HAL sleep (normal or deep)
sleep();
}
}
// Ensure interrupts get a chance to fire, which allows new result from
// wake_predicate() and wake_time_passed()
__ISB();
#endif
}
}
/* We require that this is called from thread context, outside a critical section,
* and the kernel already suspended if an RTOS, meaning we don't have to worry
* about any potential threading issues.
*
* The wake predicate will be called from both outside and inside a critical
* section, so appropriate atomic care must be taken.
*/
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 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 */
/* Note that unlike do_timed_sleep_relative_or_forever it does not do a tick update on entry; */
/* it assumes the caller has been using the ticker, and is passing a delay relative to the */
/* time point of the ticks it has acknowledged. */
OsClock::duration_u32 do_timed_sleep_relative_to_acknowledged_ticks(OsClock::duration_u32 wake_delay, bool (*wake_predicate)(void *), void *wake_predicate_handle)
{
// 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 basing the start time
// on its current idea of time - OsClock::acknowledged_ticks().
// Example: OS acknowledged tick count = 100, our reported tick count = 101, requested delay = 50
// We need to schedule wake for tick 150, report 50 ticks back to our caller, and
// clear the unacknowledged tick count.
OsClock::time_point sleep_start = OsClock::acknowledged_ticks();
OsClock::time_point sleep_finish = do_timed_sleep_absolute(sleep_start + wake_delay, wake_predicate, wake_predicate_handle);
return sleep_finish - sleep_start;
}
#else
void do_untimed_sleep(bool (*wake_predicate)(void *), void *wake_predicate_handle)
{
untimed_op op(wake_predicate, wake_predicate_handle);
do_sleep_operation(op);
}
/* max() delay is treated as "wait forever" */
/* This version is tuned for non-RTOS use, where we must update the tick count, */
/* don't need to return sleep time, and waiting forever is possible */
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 == wake_delay.zero()) {
if (wake_predicate) {
wake_predicate(wake_predicate_handle);
}
return;
}
OsClock::time_point wake_time;
if (wake_delay == OsClock::duration_u32::max()) {
wake_time = OsClock::time_point::max();
} else {
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);
do_sleep_operation(op);
}
#endif
} // namespace internal
} // namespace mbed