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