Fork for workshops

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ns_timer.c Source File

ns_timer.c

00001 /*
00002  * Copyright (c) 2014-2015 ARM Limited. All rights reserved.
00003  * SPDX-License-Identifier: Apache-2.0
00004  * Licensed under the Apache License, Version 2.0 (the License); you may
00005  * not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  * http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
00012  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include "ns_types.h"
00018 #include "ns_list.h"
00019 #include "ns_timer.h"
00020 #include "eventOS_callback_timer.h"
00021 #include "platform/arm_hal_interrupt.h"
00022 #include "platform/arm_hal_timer.h"
00023 #include "nsdynmemLIB.h"
00024 
00025 #ifndef NS_EXCLUDE_HIGHRES_TIMER
00026 typedef enum ns_timer_state_e {
00027     NS_TIMER_ACTIVE = 0,        // Will run on the next HAL interrupt
00028     NS_TIMER_HOLD,              // Will run on a later HAL interrupt
00029     NS_TIMER_RUN_INTERRUPT,     // Running on the interrupt we're currently handling
00030     NS_TIMER_STOP               // Timer not scheduled ("start" not called since last callback)
00031 } ns_timer_state_e;
00032 
00033 typedef struct ns_timer_struct {
00034     int8_t ns_timer_id;
00035     ns_timer_state_e timer_state;
00036     uint16_t slots;
00037     uint16_t remaining_slots;
00038     void (*interrupt_handler)(int8_t, uint16_t);
00039     ns_list_link_t link;
00040 } ns_timer_struct;
00041 
00042 static NS_LIST_DEFINE(ns_timer_list, ns_timer_struct, link);
00043 
00044 #define NS_TIMER_RUNNING    1
00045 static uint8_t ns_timer_state = 0;
00046 
00047 #ifdef ATMEGA256RFR2
00048 #define COMPENSATION 3
00049 #define COMPENSATION_TUNE 1
00050 #else
00051 #define COMPENSATION 0
00052 #define COMPENSATION_TUNE 0
00053 #endif
00054 
00055 static void ns_timer_interrupt_handler(void);
00056 static ns_timer_struct *ns_timer_get_pointer_to_timer_struct(int8_t timer_id);
00057 static bool ns_timer_initialized = 0;
00058 
00059 int8_t eventOS_callback_timer_register(void (*timer_interrupt_handler)(int8_t, uint16_t))
00060 {
00061     int8_t retval = -1;
00062 
00063     if (!ns_timer_initialized) {
00064         /*Set interrupt handler in HAL driver*/
00065         platform_timer_set_cb(ns_timer_interrupt_handler);
00066         ns_timer_initialized = 1;
00067     }
00068 
00069     /*Find first free timer ID in timer list*/
00070     /*(Note use of uint8_t to avoid overflow if we reach 0x7F)*/
00071     for (uint8_t i = 0; i <= INT8_MAX; i++) {
00072         if (!ns_timer_get_pointer_to_timer_struct(i)) {
00073             retval = i;
00074             break;
00075         }
00076     }
00077 
00078     if (retval == -1) {
00079         return -1;
00080     }
00081 
00082     ns_timer_struct *new_timer = ns_dyn_mem_alloc(sizeof(ns_timer_struct));
00083     if (!new_timer) {
00084         return -1;
00085     }
00086 
00087     /*Initialise new timer*/
00088     new_timer->ns_timer_id = retval;
00089     new_timer->timer_state = NS_TIMER_STOP;
00090     new_timer->remaining_slots = 0;
00091     new_timer->interrupt_handler = timer_interrupt_handler;
00092 
00093     // Critical section sufficient as long as list can't be reordered from
00094     // interrupt, otherwise will need to cover whole routine
00095     platform_enter_critical();
00096     ns_list_add_to_end(&ns_timer_list, new_timer);
00097     platform_exit_critical();
00098 
00099     /*Return timer ID*/
00100     return retval;
00101 }
00102 
00103 int8_t eventOS_callback_timer_unregister(int8_t ns_timer_id)
00104 {
00105     ns_timer_struct *current_timer;
00106 
00107     current_timer = ns_timer_get_pointer_to_timer_struct(ns_timer_id);
00108     if (!current_timer) {
00109         return -1;
00110     }
00111 
00112     // Critical section sufficient as long as list can't be reordered from
00113     // interrupt, otherwise will need to cover whole routine
00114     platform_enter_critical();
00115     ns_list_remove(&ns_timer_list, current_timer);
00116     platform_exit_critical();
00117 
00118     ns_dyn_mem_free(current_timer);
00119     return 0;
00120 }
00121 
00122 
00123 static int8_t ns_timer_start_pl_timer(uint16_t pl_timer_start_slots)
00124 {
00125     /*Don't start timer with 0 slots*/
00126     if (!pl_timer_start_slots) {
00127         pl_timer_start_slots = 1;
00128     }
00129 
00130     /*Start HAL timer*/
00131     platform_timer_start(pl_timer_start_slots);
00132     /*Set HAL timer state to running*/
00133     ns_timer_state |= NS_TIMER_RUNNING;
00134     return 0;
00135 }
00136 
00137 int8_t ns_timer_sleep(void)
00138 {
00139     int8_t ret_val = -1;
00140     if (ns_timer_state & NS_TIMER_RUNNING) {
00141         /*Start HAL timer*/
00142         platform_timer_disable();
00143         /*Set HAL timer state to running*/
00144         ns_timer_state &= ~NS_TIMER_RUNNING;
00145         ret_val = 0;
00146     }
00147     return ret_val;
00148 }
00149 
00150 static int8_t ns_timer_get_next_running_to(void)
00151 {
00152     uint8_t hold_count = 0;
00153     ns_timer_struct *first_timer = NULL;
00154 
00155     /*Find hold-labelled timer with the least remaining slots*/
00156     ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) {
00157         if (current_timer->timer_state == NS_TIMER_HOLD) {
00158             if (!first_timer || current_timer->remaining_slots < first_timer->remaining_slots) {
00159                 first_timer = current_timer;
00160             }
00161             /*For optimisation, count the found timers*/
00162             hold_count++;
00163         }
00164     }
00165 
00166     if (!first_timer) {
00167         return 0;
00168     }
00169 
00170     /*If hold-labelled timer found, set it active and start the HAL driver*/
00171     hold_count--;
00172     first_timer->timer_state = NS_TIMER_ACTIVE;
00173     /*Compensate time spent in timer function*/
00174     if (first_timer->remaining_slots > COMPENSATION) {
00175         first_timer->remaining_slots -= COMPENSATION;
00176     }
00177     /*Start HAL timer*/
00178     ns_timer_start_pl_timer(first_timer->remaining_slots);
00179 
00180     /*Update other hold-labelled timers*/
00181     ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) {
00182         if (hold_count == 0) { // early termination optimisation
00183             break;
00184         }
00185         if (current_timer->timer_state == NS_TIMER_HOLD) {
00186             if (current_timer->remaining_slots == first_timer->remaining_slots) {
00187                 current_timer->timer_state = NS_TIMER_ACTIVE;
00188             } else {
00189                 current_timer->remaining_slots -= first_timer->remaining_slots;
00190                 /*Compensate time spent in timer function*/
00191                 if (current_timer->remaining_slots > COMPENSATION) {
00192                     current_timer->remaining_slots -= COMPENSATION;
00193                 }
00194             }
00195             hold_count--;
00196         }
00197     }
00198 
00199     return 0;
00200 }
00201 
00202 
00203 static ns_timer_struct *ns_timer_get_pointer_to_timer_struct(int8_t timer_id)
00204 {
00205     /*Find timer with the given ID*/
00206     ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) {
00207         if (current_timer->ns_timer_id == timer_id) {
00208             return current_timer;
00209         }
00210     }
00211     return NULL;
00212 }
00213 
00214 int8_t eventOS_callback_timer_start(int8_t ns_timer_id, uint16_t slots)
00215 {
00216     int8_t ret_val = 0;
00217     uint16_t pl_timer_remaining_slots;
00218     ns_timer_struct *timer;
00219     platform_enter_critical();
00220 
00221     /*Find timer to be activated*/
00222     timer = ns_timer_get_pointer_to_timer_struct(ns_timer_id);
00223     if (!timer) {
00224         ret_val = -1;
00225         goto exit;
00226     }
00227 
00228     // XXX this assumes the timer currently isn't running?
00229     // Is event.c relying on this restarting HAL timer after ns_timer_sleep()?
00230 
00231     /*If any timers are active*/
00232     if (ns_timer_state & NS_TIMER_RUNNING) {
00233         /*Get remaining slots of the currently activated timeout*/
00234         pl_timer_remaining_slots = platform_timer_get_remaining_slots();
00235 
00236         /*New timeout is shorter than currently enabled timeout*/
00237         if (pl_timer_remaining_slots > slots) {
00238             /*Start HAL timer*/
00239             ns_timer_start_pl_timer(slots - 0);
00240 
00241             ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) {
00242                 /*Switch active timers to hold*/
00243                 if (current_timer->timer_state == NS_TIMER_ACTIVE) {
00244                     current_timer->timer_state = NS_TIMER_HOLD;
00245                     current_timer->remaining_slots = 0;
00246                 }
00247                 /*Update hold-labelled timers*/
00248                 if (current_timer->timer_state == NS_TIMER_HOLD) {
00249                     current_timer->remaining_slots += (pl_timer_remaining_slots - slots);
00250                     /*Compensate time spent in timer function*/
00251                     if (current_timer->remaining_slots > (COMPENSATION - COMPENSATION_TUNE)) {
00252                         current_timer->remaining_slots -= (COMPENSATION - COMPENSATION_TUNE);
00253                     }
00254                 }
00255             }
00256             /*Mark active and start the timer*/
00257             timer->timer_state = NS_TIMER_ACTIVE;
00258             timer->slots = slots;
00259             timer->remaining_slots = slots;
00260         }
00261 
00262         /*New timeout is longer than currently enabled timeout*/
00263         else if (pl_timer_remaining_slots < slots) {
00264             /*Mark hold and update remaining slots*/
00265             timer->timer_state = NS_TIMER_HOLD;
00266             timer->slots = slots;
00267             timer->remaining_slots = (slots - pl_timer_remaining_slots);
00268         }
00269         /*New timeout is equal to currently enabled timeout*/
00270         else {
00271             /*Mark it active and it will be handled in next interrupt*/
00272             timer->timer_state = NS_TIMER_ACTIVE;
00273             timer->slots = slots;
00274             timer->remaining_slots = slots;
00275         }
00276     } else {
00277         /*No timers running*/
00278         timer->timer_state = NS_TIMER_HOLD;
00279         timer->slots = slots;
00280         timer->remaining_slots = slots;
00281         /*Start next timeout*/
00282         ns_timer_get_next_running_to();
00283     }
00284 exit:
00285     platform_exit_critical();
00286     return ret_val;
00287 }
00288 
00289 static void ns_timer_interrupt_handler(void)
00290 {
00291     uint8_t i = 0;
00292 
00293     platform_enter_critical();
00294     /*Clear timer running state*/
00295     ns_timer_state &= ~NS_TIMER_RUNNING;
00296     /*Mark active timers as NS_TIMER_RUN_INTERRUPT, interrupt functions are called at the end of this function*/
00297     ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) {
00298         if (current_timer->timer_state == NS_TIMER_ACTIVE) {
00299             current_timer->timer_state = NS_TIMER_RUN_INTERRUPT;
00300             /*For optimisation, count the found timers*/
00301             i++;
00302         }
00303     }
00304 
00305     /*Start next timeout*/
00306     ns_timer_get_next_running_to();
00307 
00308     /*Call interrupt functions*/
00309     ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) {
00310         if (i == 0) {
00311             break;
00312         }
00313         if (current_timer->timer_state == NS_TIMER_RUN_INTERRUPT) {
00314             current_timer->timer_state = NS_TIMER_STOP;
00315             current_timer->interrupt_handler(current_timer->ns_timer_id, current_timer->slots);
00316             i--;
00317         }
00318     }
00319 
00320     platform_exit_critical();
00321 }
00322 
00323 int8_t eventOS_callback_timer_stop(int8_t ns_timer_id)
00324 {
00325     uint16_t pl_timer_remaining_slots;
00326     bool active_timer_found = false;
00327     ns_timer_struct *current_timer;
00328     ns_timer_struct *first_timer = NULL;
00329     int8_t retval = -1;
00330 
00331     platform_enter_critical();
00332     /*Find timer with given timer ID*/
00333     current_timer = ns_timer_get_pointer_to_timer_struct(ns_timer_id);
00334     if (!current_timer) {
00335         goto exit;
00336     }
00337 
00338     retval = 0;
00339 
00340     /*Check if already stopped*/
00341     if (current_timer->timer_state == NS_TIMER_STOP) {
00342         goto exit;
00343     }
00344 
00345     current_timer->timer_state = NS_TIMER_STOP;
00346     current_timer->remaining_slots = 0;
00347 
00348     /*Check if some timer is already active*/
00349     ns_list_foreach(ns_timer_struct, curr_timer, &ns_timer_list) {
00350         if (curr_timer->timer_state == NS_TIMER_ACTIVE) {
00351             active_timer_found = true;
00352             break;
00353         }
00354     }
00355     /*If no active timers found, start one*/
00356     if (!active_timer_found) {
00357         pl_timer_remaining_slots = platform_timer_get_remaining_slots();
00358         /*Find hold-labelled timer with the least remaining slots*/
00359         ns_list_foreach(ns_timer_struct, cur_timer, &ns_timer_list) {
00360             if (cur_timer->timer_state == NS_TIMER_HOLD) {
00361                 cur_timer->remaining_slots += pl_timer_remaining_slots;
00362 
00363                 if (!first_timer || cur_timer->remaining_slots < first_timer->remaining_slots) {
00364                     first_timer = cur_timer;
00365                 }
00366             }
00367         }
00368         /*If hold-labelled timer found, set it active and start the HAL driver*/
00369         if (first_timer) {
00370             first_timer->timer_state = NS_TIMER_ACTIVE;
00371             /*Start HAL timer*/
00372             ns_timer_start_pl_timer(first_timer->remaining_slots);
00373             /*If some of the other hold-labelled timers have the same remaining slots as the timer_tmp, mark them active*/
00374             ns_list_foreach(ns_timer_struct, cur_timer, &ns_timer_list) {
00375                 if (cur_timer->timer_state == NS_TIMER_HOLD) {
00376                     if (cur_timer->remaining_slots == first_timer->remaining_slots) {
00377                         cur_timer->timer_state = NS_TIMER_ACTIVE;
00378                     } else {
00379                         cur_timer->remaining_slots -= first_timer->remaining_slots;
00380                     }
00381                 }
00382             }
00383         }
00384     }
00385 
00386 exit:
00387     platform_exit_critical();
00388 
00389     return retval;
00390 }
00391 #endif // NS_EXCLUDE_HIGHRES_TIMER