• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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