1 /* mbed Microcontroller Library
2 * Copyright (c) 2015 ARM Limited
3 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 #include "ticker_api.h"
18 #include "ll.h"
19
20 #define core_util_critical_section_enter() critical_section_start()
21 #define core_util_critical_section_exit() critical_section_end()
22 #define MBED_ASSERT(...) do {} while (0)
23
24 static void schedule_interrupt(const ticker_data_t *const ticker);
25 static void update_present_time(const ticker_data_t *const ticker);
26
27 /*
28 * Initialize a ticker instance.
29 */
initialize(const ticker_data_t * ticker)30 static void initialize(const ticker_data_t *ticker)
31 {
32 // return if the queue has already been initialized, in that case the
33 // interface used by the queue is already initialized.
34 if (ticker->queue->initialized) {
35 return;
36 }
37 if (ticker->queue->suspended) {
38 return;
39 }
40
41 ticker->interface->init();
42
43 const ticker_info_t *info = ticker->interface->get_info();
44 uint32_t frequency = info->frequency;
45 if (info->frequency == 0) {
46 MBED_ASSERT(0);
47 frequency = 1000000;
48 }
49
50 uint8_t frequency_shifts = 0;
51 for (uint8_t i = 31; i > 0; --i) {
52 if ((1U << i) == frequency) {
53 frequency_shifts = i;
54 break;
55 }
56 }
57
58 uint32_t bits = info->bits;
59 if ((info->bits > 32) || (info->bits < 4)) {
60 MBED_ASSERT(0);
61 bits = 32;
62 }
63 uint32_t max_delta = 0x7 << (bits - 4); // 7/16th
64 uint64_t max_delta_us =
65 ((uint64_t)max_delta * 1000000 + frequency - 1) / frequency;
66
67 ticker->queue->event_handler = NULL;
68 ticker->queue->head = NULL;
69 ticker->queue->tick_last_read = ticker->interface->read();
70 ticker->queue->tick_remainder = 0;
71 ticker->queue->frequency = frequency;
72 ticker->queue->frequency_shifts = frequency_shifts;
73 ticker->queue->bitmask = ((uint64_t)1 << bits) - 1;
74 ticker->queue->max_delta = max_delta;
75 ticker->queue->max_delta_us = max_delta_us;
76 ticker->queue->present_time = 0;
77 ticker->queue->dispatching = false;
78 ticker->queue->suspended = false;
79 ticker->queue->initialized = true;
80
81 update_present_time(ticker);
82 schedule_interrupt(ticker);
83 }
84
85 /**
86 * Set the event handler function of a ticker instance.
87 */
set_handler(const ticker_data_t * const ticker,ticker_event_handler handler)88 static void set_handler(const ticker_data_t *const ticker, ticker_event_handler handler)
89 {
90 ticker->queue->event_handler = handler;
91 }
92
93 /*
94 * Convert a 32 bit timestamp into a 64 bit timestamp.
95 *
96 * A 64 bit timestamp is used as the point of time of reference while the
97 * timestamp to convert is relative to this point of time.
98 *
99 * The lower 32 bits of the timestamp returned will be equal to the timestamp to
100 * convert.
101 *
102 * If the timestamp to convert is less than the lower 32 bits of the time
103 * reference then the timestamp to convert is seen as an overflowed value and
104 * the upper 32 bit of the timestamp returned will be equal to the upper 32 bit
105 * of the reference point + 1.
106 * Otherwise, the upper 32 bit returned will be equal to the upper 32 bit of the
107 * reference point.
108 *
109 * @param ref: The 64 bit timestamp of reference.
110 * @param timestamp: The timestamp to convert.
111 */
convert_timestamp(us_timestamp_t ref,timestamp_t timestamp)112 static us_timestamp_t convert_timestamp(us_timestamp_t ref, timestamp_t timestamp)
113 {
114 bool overflow = timestamp < ((timestamp_t) ref) ? true : false;
115
116 us_timestamp_t result = (ref & ~((us_timestamp_t)UINT32_MAX)) | timestamp;
117 if (overflow) {
118 result += (1ULL << 32);
119 }
120
121 return result;
122 }
123
124 /**
125 * Update the present timestamp value of a ticker.
126 */
update_present_time(const ticker_data_t * const ticker)127 static void update_present_time(const ticker_data_t *const ticker)
128 {
129 ticker_event_queue_t *queue = ticker->queue;
130 if (queue->suspended) {
131 return;
132 }
133 uint32_t ticker_time = ticker->interface->read();
134 if (ticker_time == ticker->queue->tick_last_read) {
135 // No work to do
136 return;
137 }
138
139 uint64_t elapsed_ticks = (ticker_time - queue->tick_last_read) & queue->bitmask;
140 queue->tick_last_read = ticker_time;
141
142 uint64_t elapsed_us;
143 if (1000000 == queue->frequency) {
144 // Optimized for 1MHz
145
146 elapsed_us = elapsed_ticks;
147 } else if (0 != queue->frequency_shifts) {
148 // Optimized for frequencies divisible by 2
149 uint64_t us_x_ticks = elapsed_ticks * 1000000;
150 elapsed_us = us_x_ticks >> queue->frequency_shifts;
151
152 // Update remainder
153 queue->tick_remainder += us_x_ticks - (elapsed_us << queue->frequency_shifts);
154 if (queue->tick_remainder >= queue->frequency) {
155 elapsed_us += 1;
156 queue->tick_remainder -= queue->frequency;
157 }
158 } else {
159 // General case
160
161 uint64_t us_x_ticks = elapsed_ticks * 1000000;
162 elapsed_us = us_x_ticks / queue->frequency;
163
164 // Update remainder
165 queue->tick_remainder += us_x_ticks - elapsed_us * queue->frequency;
166 if (queue->tick_remainder >= queue->frequency) {
167 elapsed_us += 1;
168 queue->tick_remainder -= queue->frequency;
169 }
170 }
171
172 // Update current time
173 queue->present_time += elapsed_us;
174 }
175
176 /**
177 * Given the absolute timestamp compute the hal tick timestamp rounded up.
178 */
compute_tick_round_up(const ticker_data_t * const ticker,us_timestamp_t timestamp)179 static timestamp_t compute_tick_round_up(const ticker_data_t *const ticker, us_timestamp_t timestamp)
180 {
181 ticker_event_queue_t *queue = ticker->queue;
182 us_timestamp_t delta_us = timestamp - queue->present_time;
183
184 timestamp_t delta = ticker->queue->max_delta;
185 if (delta_us <= ticker->queue->max_delta_us) {
186 // Checking max_delta_us ensures the operation will not overflow
187
188 if (1000000 == queue->frequency) {
189 // Optimized for 1MHz
190
191 delta = delta_us;
192 if (delta > ticker->queue->max_delta) {
193 delta = ticker->queue->max_delta;
194 }
195 } else if (0 != queue->frequency_shifts) {
196 // Optimized frequencies divisible by 2
197
198 delta = ((delta_us << ticker->queue->frequency_shifts) + 1000000 - 1) / 1000000;
199 if (delta > ticker->queue->max_delta) {
200 delta = ticker->queue->max_delta;
201 }
202 } else {
203 // General case
204
205 delta = (delta_us * queue->frequency + 1000000 - 1) / 1000000;
206 if (delta > ticker->queue->max_delta) {
207 delta = ticker->queue->max_delta;
208 }
209 }
210 }
211 return (queue->tick_last_read + delta) & queue->bitmask;
212 }
213
214 /**
215 * Return 1 if the tick has incremented to or past match_tick, otherwise 0.
216 */
_ticker_match_interval_passed(timestamp_t prev_tick,timestamp_t cur_tick,timestamp_t match_tick)217 int _ticker_match_interval_passed(timestamp_t prev_tick, timestamp_t cur_tick, timestamp_t match_tick)
218 {
219 if (match_tick > prev_tick) {
220 return (cur_tick >= match_tick) || (cur_tick < prev_tick);
221 } else {
222 return (cur_tick < prev_tick) && (cur_tick >= match_tick);
223 }
224 }
225
226 /**
227 * Compute the time when the interrupt has to be triggered and schedule it.
228 *
229 * If there is no event in the queue or the next event to execute is in more
230 * than ticker.queue.max_delta ticks from now then the ticker irq will be
231 * scheduled in ticker.queue.max_delta ticks. Otherwise the irq will be
232 * scheduled to happen when the running counter reach the timestamp of the
233 * first event in the queue.
234 *
235 * @note If there is no event in the queue then the interrupt is scheduled to
236 * in ticker.queue.max_delta. This is necessary to keep track
237 * of the timer overflow.
238 */
schedule_interrupt(const ticker_data_t * const ticker)239 static void schedule_interrupt(const ticker_data_t *const ticker)
240 {
241 ticker_event_queue_t *queue = ticker->queue;
242 if (queue->suspended || ticker->queue->dispatching) {
243 // Don't schedule the next interrupt until dispatching is
244 // finished. This prevents repeated calls to interface->set_interrupt
245 return;
246 }
247
248 update_present_time(ticker);
249
250 if (ticker->queue->head) {
251 us_timestamp_t present = ticker->queue->present_time;
252 us_timestamp_t match_time = ticker->queue->head->timestamp;
253
254 // if the event at the head of the queue is in the past then schedule
255 // it immediately.
256 if (match_time <= present) {
257 ticker->interface->fire_interrupt();
258 return;
259 }
260
261 timestamp_t match_tick = compute_tick_round_up(ticker, match_time);
262
263 // The same tick should never occur since match_tick is rounded up.
264 // If the same tick is returned scheduling will not work correctly.
265 MBED_ASSERT(match_tick != queue->tick_last_read);
266
267 ticker->interface->set_interrupt(match_tick);
268 timestamp_t cur_tick = ticker->interface->read();
269
270 if (_ticker_match_interval_passed(queue->tick_last_read, cur_tick, match_tick)) {
271 ticker->interface->fire_interrupt();
272 }
273 } else {
274 uint32_t match_tick =
275 (queue->tick_last_read + queue->max_delta) & queue->bitmask;
276 ticker->interface->set_interrupt(match_tick);
277 }
278 }
279
ticker_set_handler(const ticker_data_t * const ticker,ticker_event_handler handler)280 void ticker_set_handler(const ticker_data_t *const ticker, ticker_event_handler handler)
281 {
282 initialize(ticker);
283
284 core_util_critical_section_enter();
285 set_handler(ticker, handler);
286 core_util_critical_section_exit();
287 }
288
ticker_irq_handler(const ticker_data_t * const ticker)289 void ticker_irq_handler(const ticker_data_t *const ticker)
290 {
291 core_util_critical_section_enter();
292
293 ticker->interface->clear_interrupt();
294 if (ticker->queue->suspended) {
295 core_util_critical_section_exit();
296 return;
297 }
298
299 /* Go through all the pending TimerEvents */
300 ticker->queue->dispatching = true;
301 while (1) {
302 if (ticker->queue->head == NULL) {
303 break;
304 }
305
306 // update the current timestamp used by the queue
307 update_present_time(ticker);
308
309 if (ticker->queue->head->timestamp <= ticker->queue->present_time) {
310 // This event was in the past:
311 // point to the following one and execute its handler
312 ticker_event_t *p = ticker->queue->head;
313 ticker->queue->head = ticker->queue->head->next;
314 if (ticker->queue->event_handler != NULL) {
315 (*ticker->queue->event_handler)(p->id); // NOTE: the handler can set new events
316 }
317 /* Note: We continue back to examining the head because calling the
318 * event handler may have altered the chain of pending events. */
319 } else {
320 break;
321 }
322 }
323 ticker->queue->dispatching = false;
324
325 schedule_interrupt(ticker);
326
327 core_util_critical_section_exit();
328 }
329
ticker_insert_event(const ticker_data_t * const ticker,ticker_event_t * obj,timestamp_t timestamp,uint32_t id)330 void ticker_insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, timestamp_t timestamp, uint32_t id)
331 {
332 core_util_critical_section_enter();
333
334 // update the current timestamp
335 update_present_time(ticker);
336 us_timestamp_t absolute_timestamp = convert_timestamp(
337 ticker->queue->present_time,
338 timestamp
339 );
340
341 // defer to ticker_insert_event_us
342 ticker_insert_event_us(
343 ticker,
344 obj, absolute_timestamp, id
345 );
346
347 core_util_critical_section_exit();
348 }
349
ticker_insert_event_us(const ticker_data_t * const ticker,ticker_event_t * obj,us_timestamp_t timestamp,uint32_t id)350 void ticker_insert_event_us(const ticker_data_t *const ticker, ticker_event_t *obj, us_timestamp_t timestamp, uint32_t id)
351 {
352 core_util_critical_section_enter();
353
354 // update the current timestamp
355 update_present_time(ticker);
356
357 // initialise our data
358 obj->timestamp = timestamp;
359 obj->id = id;
360
361 /* Go through the list until we either reach the end, or find
362 an element this should come before (which is possibly the
363 head). */
364 ticker_event_t *prev = NULL, *p = ticker->queue->head;
365 while (p != NULL) {
366 /* check if we come before p */
367 if (timestamp < p->timestamp) {
368 break;
369 }
370 /* go to the next element */
371 prev = p;
372 p = p->next;
373 }
374
375 /* if we're at the end p will be NULL, which is correct */
376 obj->next = p;
377
378 /* if prev is NULL we're at the head */
379 if (prev == NULL) {
380 ticker->queue->head = obj;
381 } else {
382 prev->next = obj;
383 }
384
385 if (prev == NULL || timestamp <= ticker->queue->present_time) {
386 schedule_interrupt(ticker);
387 }
388
389 core_util_critical_section_exit();
390 }
391
ticker_remove_event(const ticker_data_t * const ticker,ticker_event_t * obj)392 void ticker_remove_event(const ticker_data_t *const ticker, ticker_event_t *obj)
393 {
394 core_util_critical_section_enter();
395
396 // remove this object from the list
397 if (ticker->queue->head == obj) {
398 // first in the list, so just drop me
399 ticker->queue->head = obj->next;
400 schedule_interrupt(ticker);
401 } else {
402 // find the object before me, then drop me
403 ticker_event_t *p = ticker->queue->head;
404 while (p != NULL) {
405 if (p->next == obj) {
406 p->next = obj->next;
407 break;
408 }
409 p = p->next;
410 }
411 }
412
413 core_util_critical_section_exit();
414 }
415
ticker_read(const ticker_data_t * const ticker)416 timestamp_t ticker_read(const ticker_data_t *const ticker)
417 {
418 return ticker_read_us(ticker);
419 }
420
ticker_read_us(const ticker_data_t * const ticker)421 us_timestamp_t ticker_read_us(const ticker_data_t *const ticker)
422 {
423 us_timestamp_t ret;
424
425 initialize(ticker);
426
427 core_util_critical_section_enter();
428 update_present_time(ticker);
429 ret = ticker->queue->present_time;
430 core_util_critical_section_exit();
431
432 return ret;
433 }
434
ticker_get_next_timestamp(const ticker_data_t * const data,timestamp_t * timestamp)435 int ticker_get_next_timestamp(const ticker_data_t *const data, timestamp_t *timestamp)
436 {
437 int ret = 0;
438
439 /* if head is NULL, there are no pending events */
440 core_util_critical_section_enter();
441 if (data->queue->head != NULL) {
442 *timestamp = data->queue->head->timestamp;
443 ret = 1;
444 }
445 core_util_critical_section_exit();
446
447 return ret;
448 }
449
ticker_suspend(const ticker_data_t * const ticker)450 void ticker_suspend(const ticker_data_t *const ticker)
451 {
452 core_util_critical_section_enter();
453
454 ticker->queue->suspended = true;
455
456 core_util_critical_section_exit();
457 }
458
ticker_resume(const ticker_data_t * const ticker)459 void ticker_resume(const ticker_data_t *const ticker)
460 {
461 core_util_critical_section_enter();
462
463 ticker->queue->suspended = false;
464 if (ticker->queue->initialized) {
465 ticker->queue->tick_last_read = ticker->interface->read();
466
467 update_present_time(ticker);
468 schedule_interrupt(ticker);
469 } else {
470 initialize(ticker);
471 }
472
473 core_util_critical_section_exit();
474 }
475