From: Olivier Matz Date: Sat, 24 May 2014 16:46:46 +0000 (+0200) Subject: callout: add a new module which is an alternative to the scheduler X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=9d7a205a0868595dc57f365eda4721b700e447b9;p=aversive.git callout: add a new module which is an alternative to the scheduler The 'callout' module is another timer manager that aims to replace the scheduler module. The management of timers can be done either from an asynchronous interruption or from the main program. This module is able to handle priorities between timers and allow to reschedule a timer later if its priority is too low. Signed-off-by: Olivier Matz --- diff --git a/config/Configure.help b/config/Configure.help index 63b9bca..6b98361 100644 --- a/config/Configure.help +++ b/config/Configure.help @@ -79,6 +79,14 @@ CONFIG_MODULE_SCHEDULER_USE_TIMERS macro has to be defined in configuration file. +CONFIG_MODULE_CALLOUT + The 'callout' module is another timer manager that aims to replace the + scheduler module. The management of timers can be done either from an + asynchronous interruption or from the main program. This module is + able to handle priorities between timers and allow to reschedule + a timer later if its priority is too low. + + CONFIG_TIME This module can be used to get a human readable time. It uses the scheduler module. Its goal is not to be very precise, but just diff --git a/config/config.in b/config/config.in index 8c479c0..500f69d 100644 --- a/config/config.in +++ b/config/config.in @@ -162,6 +162,8 @@ choice 'Scheduler config' "use_timer_module CONFIG_MODULE_SCHEDULER_USE_TIMERS\ fi +bool 'Callout' CONFIG_MODULE_CALLOUT + #### TIME dep_bool 'Time' CONFIG_MODULE_TIME \ $CONFIG_MODULE_SCHEDULER diff --git a/config/generate_aversive_config b/config/generate_aversive_config index f034165..564ba37 100755 --- a/config/generate_aversive_config +++ b/config/generate_aversive_config @@ -33,6 +33,7 @@ MODULES_LIST="CONFIG_MODULE_BRUSHLESS_3PHASE_DIGITAL_HALL,/devices/brushless_mot CONFIG_MODULE_CONTROL_SYSTEM_MANAGER,devices/control_system/control_system_manager CONFIG_MODULE_TIME,base/time CONFIG_MODULE_SCHEDULER,base/scheduler + CONFIG_MODULE_CALLOUT,base/callout CONFIG_MODULE_SPI,comm/spi CONFIG_MODULE_CC2420,devices/radio/cc2420 CONFIG_MODULE_XBEE,devices/radio/xbee diff --git a/modules/base/callout/Makefile b/modules/base/callout/Makefile new file mode 100644 index 0000000..6074e5b --- /dev/null +++ b/modules/base/callout/Makefile @@ -0,0 +1,5 @@ +TARGET = callout + +SRC := callout.c + +include $(AVERSIVE_DIR)/mk/aversive_module.mk diff --git a/modules/base/callout/callout.c b/modules/base/callout/callout.c new file mode 100644 index 0000000..f6c73be --- /dev/null +++ b/modules/base/callout/callout.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) <2014>, Olivier Matz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Inspired from Intel DPDK rte_timer library */ +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "callout.h" + +/* allow to browse a list while modifying the current element */ +#define LIST_FOREACH_SAFE(cur, next, head, field) \ + for ((cur) = LIST_FIRST((head)), \ + (next) = ((cur) ? LIST_NEXT((cur), field) : NULL); \ + (cur); \ + (cur) = (next), \ + (next) = ((cur) ? LIST_NEXT((cur), field) : NULL)) + +#ifdef CALLOUT_STATS +/* called with irq locked */ +#define CALLOUT_STAT_ADD(cm, field, x) do { \ + cm->stats.field += x; \ + } while(0) +#else +#define CALLOUT_STAT_ADD(cm, field, x) do { } while(0) +#endif + +#ifdef CALLOUT_DEBUG +#define callout_dprintf_P(fmt, ...) printf_P(PSTR("%s(): " fmt), __FUNCTION__, \ + __VA_ARGS__) +#else +#define callout_dprintf_P(...) do { } while (0) +#endif + +/* Initialize a callout manager */ +void +callout_mgr_init(struct callout_mgr *cm, get_time_t *get_time) +{ + memset(cm, 0, sizeof(*cm)); + cm->get_time = get_time; + LIST_INIT(&cm->sched_list); +} + +/* Initialize the timer handle tim for use */ +void +callout_init(struct callout *tim, callout_cb_t f, void *arg, uint8_t priority) +{ + memset(tim, 0, sizeof(*tim)); + tim->f = f; + tim->arg = arg; + tim->priority = priority; +} + +/* + * Add a timer in the scheduled list (timer must not already be in a list). The + * timers are sorted in the list according the expire time (the closer timers + * first). + * + * called with irq locked + */ +static void +callout_add_in_sched_list(struct callout_mgr *cm, struct callout *tim) +{ + struct callout *t, *prev_t; + + callout_dprintf_P("cm=%p tim=%p\r\n", cm, tim); + tim->state = CALLOUT_STATE_SCHEDULED; + + /* list is empty */ + if (LIST_EMPTY(&cm->sched_list)) { + LIST_INSERT_HEAD(&cm->sched_list, tim, next); + return; + } + + /* 'tim' expires before first entry */ + t = LIST_FIRST(&cm->sched_list); + if ((int16_t)(tim->expire - t->expire) <= 0) { + LIST_INSERT_HEAD(&cm->sched_list, tim, next); + return; + } + + /* find an element that will expire after 'tim' */ + LIST_FOREACH(t, &cm->sched_list, next) { + if ((int16_t)(tim->expire - t->expire) <= 0) { + LIST_INSERT_BEFORE(t, tim, next); + return; + } + prev_t = t; + } + + /* not found, insert at the end of the list */ + LIST_INSERT_AFTER(prev_t, tim, next); +} + +/* + * Add a timer in the local expired list (timer must not already be in a + * list). The timers are sorted in the list according to the priority (high + * priority first). + * + * called with irq locked + */ +static void +callout_add_in_expired_list(struct callout_mgr *cm, + struct callout_list *expired_list, struct callout *tim) +{ + struct callout *t, *prev_t; + + (void)cm; /* avoid warning if debug is disabled */ + + callout_dprintf_P("cm=%p tim=%p\r\n", cm, tim); + tim->state = CALLOUT_STATE_EXPIRED; + + /* list is empty */ + if (LIST_EMPTY(expired_list)) { + LIST_INSERT_HEAD(expired_list, tim, next); + return; + } + + /* 'tim' has a higher prio */ + t = LIST_FIRST(expired_list); + if (tim->priority >= t->priority) { + LIST_INSERT_HEAD(expired_list, tim, next); + return; + } + + /* find an element that will expire after 'tim' */ + LIST_FOREACH(t, expired_list, next) { + if (tim->priority >= t->priority) { + LIST_INSERT_BEFORE(t, tim, next); + return; + } + prev_t = t; + } + + /* not found, insert at the end of the list */ + LIST_INSERT_AFTER(prev_t, tim, next); +} + +/* + * del from list (timer must be in a list) + */ +static void +callout_del(struct callout_mgr *cm, struct callout *tim) +{ + (void)cm; /* avoid warning if debug is disabled */ + callout_dprintf_P("cm=%p tim=%p\r\n", cm, tim); + LIST_REMOVE(tim, next); +} + +/* Reset and start the timer associated with the timer handle tim */ +static int +__callout_schedule(struct callout_mgr *cm, struct callout *tim, + uint16_t expire) +{ + uint8_t flags; + + callout_dprintf_P("cm=%p tim=%p expire=%d\r\n", + cm, tim, expire); + + IRQ_LOCK(flags); + CALLOUT_STAT_ADD(cm, schedule, 1); + + /* remove it from list */ + if (tim->state != CALLOUT_STATE_STOPPED) { + /* stats */ + if (tim->state == CALLOUT_STATE_SCHEDULED) + CALLOUT_STAT_ADD(cm, cur_scheduled, -1); + else if (tim->state == CALLOUT_STATE_EXPIRED) + CALLOUT_STAT_ADD(cm, cur_expired, -1); + if (tim->state == CALLOUT_STATE_RUNNING) + CALLOUT_STAT_ADD(cm, cur_running, -1); + + callout_del(cm, tim); + } + + tim->expire = expire; + CALLOUT_STAT_ADD(cm, cur_scheduled, 1); + callout_add_in_sched_list(cm, tim); + IRQ_UNLOCK(flags); + + return 0; +} + +/* Reset and start the timer associated with the timer handle tim */ +int +callout_schedule(struct callout_mgr *cm, struct callout *tim, + uint16_t ticks) +{ + return __callout_schedule(cm, tim, cm->get_time() + ticks); +} + +/* Reset and start the timer associated with the timer handle tim */ +int +callout_reschedule(struct callout_mgr *cm, struct callout *tim, + uint16_t ticks) +{ + return __callout_schedule(cm, tim, tim->expire + ticks); +} + +/* Stop the timer associated with the timer handle tim */ +void +callout_stop(struct callout_mgr *cm, struct callout *tim) +{ + uint8_t flags; + + callout_dprintf_P("cm=%p tim=%p\r\n", cm, tim); + + IRQ_LOCK(flags); + if (tim->state != CALLOUT_STATE_STOPPED) { + + /* stats */ + if (tim->state == CALLOUT_STATE_SCHEDULED) + CALLOUT_STAT_ADD(cm, cur_scheduled, -1); + else if (tim->state == CALLOUT_STATE_EXPIRED) + CALLOUT_STAT_ADD(cm, cur_expired, -1); + if (tim->state == CALLOUT_STATE_RUNNING) + CALLOUT_STAT_ADD(cm, cur_running, -1); + CALLOUT_STAT_ADD(cm, stop, 1); + + /* remove it from list */ + callout_del(cm, tim); + tim->state = CALLOUT_STATE_STOPPED; + } + IRQ_UNLOCK(flags); +} + +/* must be called periodically, run all timer that expired */ +void callout_manage(struct callout_mgr *cm) +{ + struct callout_list expired_list; + struct callout_list reschedule_list; + struct callout *tim, *tim_next; + uint16_t cur_time; + uint8_t old_prio; + int16_t diff; + + CALLOUT_STAT_ADD(cm, manage, 1); + callout_dprintf_P("cm=%p\r\n", cm); + + /* maximize the number of self-recursions */ + if (cm->nb_recursion >= CALLOUT_MAX_RECURSION) { + CALLOUT_STAT_ADD(cm, max_recursion, 1); + return; + } + + cli(); + cm->nb_recursion++; + LIST_INIT(&expired_list); + LIST_INIT(&reschedule_list); + cur_time = cm->get_time(); + old_prio = cm->cur_priority; + + /* move all expired timers in a local list */ + LIST_FOREACH_SAFE(tim, tim_next, &cm->sched_list, next) { + + diff = cur_time - tim->expire; + + /* check the expiration time (tasks are sorted) */ + if (diff < 0) + break; + + callout_dprintf_P("cm=%p diff=%d\r\n", cm, diff); + + /* check the priority, if it's too low, inc stats */ + if (tim->priority <= cm->cur_priority) { + if (diff < 16484) + CALLOUT_STAT_ADD(cm, delayed, 1); + else { + /* reschedule to avoid an overflow */ + CALLOUT_STAT_ADD(cm, hard_delayed, 1); + LIST_REMOVE(tim, next); + tim->expire = cur_time; + LIST_INSERT_HEAD(&reschedule_list, tim, next); + } + continue; + } + + LIST_REMOVE(tim, next); + callout_add_in_expired_list(cm, &expired_list, tim); + CALLOUT_STAT_ADD(cm, cur_scheduled, -1); + CALLOUT_STAT_ADD(cm, cur_expired, 1); + } + + /* reschedule hard_delayed timers, this does not happen usually */ + while (!LIST_EMPTY(&reschedule_list)) { + tim = LIST_FIRST(&reschedule_list); + LIST_REMOVE(tim, next); + callout_add_in_sched_list(cm, tim); + } + + /* for each timer of 'expired' list, execute callback */ + while (!LIST_EMPTY(&expired_list)) { + tim = LIST_FIRST(&expired_list); + LIST_REMOVE(tim, next); + + /* execute callback function */ + CALLOUT_STAT_ADD(cm, cur_expired, -1); + CALLOUT_STAT_ADD(cm, cur_running, 1); + tim->state = CALLOUT_STATE_RUNNING; + cm->cur_priority = tim->priority; + sei(); + tim->f(cm, tim, tim->arg); + cli(); + } + + cm->cur_priority = old_prio; + cm->nb_recursion--; + sei(); +} + +/* set the current priority level */ +uint8_t callout_mgr_set_prio(struct callout_mgr *cm, uint8_t new_prio) +{ + uint8_t old_prio; + + old_prio = cm->cur_priority; + if (new_prio <= old_prio) + return old_prio; + + cm->cur_priority = new_prio; + return old_prio; +} + +/* restore the current priority level */ +void callout_mgr_restore_prio(struct callout_mgr *cm, uint8_t old_prio) +{ + cm->cur_priority = old_prio; +} + +/* dump statistics about timers */ +void callout_dump_stats(struct callout_mgr *cm) +{ +#ifdef CALLOUT_STATS + printf_P(PSTR("Timer statistics:\r\n")); + printf_P(PSTR(" schedule = %"PRIu32"\r\n"), cm->stats.schedule); + printf_P(PSTR(" stop = %"PRIu32"\r\n"), cm->stats.stop); + printf_P(PSTR(" manage = %"PRIu32"\r\n"), cm->stats.manage); + printf_P(PSTR(" max_recursion = %"PRIu32"\r\n"), cm->stats.max_recursion); + printf_P(PSTR(" delayed = %"PRIu32"\r\n"), cm->stats.delayed); + printf_P(PSTR(" hard_delayed = %"PRIu32"\r\n"), cm->stats.hard_delayed); + + printf_P(PSTR(" cur_scheduled = %u\r\n"), cm->stats.cur_scheduled); + printf_P(PSTR(" cur_expired = %u\r\n"), cm->stats.cur_expired); + printf_P(PSTR(" cur_running = %u\r\n"), cm->stats.cur_running); +#else + printf_P(PSTR("No timer statistics, CALLOUT_STATS is disabled\r\n")); +#endif +} diff --git a/modules/base/callout/callout.h b/modules/base/callout/callout.h new file mode 100644 index 0000000..2563933 --- /dev/null +++ b/modules/base/callout/callout.h @@ -0,0 +1,357 @@ +/* + * Copyright (c) <2014>, Olivier Matz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Inspired from Intel DPDK rte_timer library */ +/*- + * Copyright (c) <2010>, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * - Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CALLOUT_H_ +#define _CALLOUT_H_ + +#include + +#define CALLOUT_STATS +/* #define CALLOUT_DEBUG */ + +/** + * This module provides a timer service to Aversive similar to the old + * "scheduler" module. The manager function callout_manage() can be called from + * an interrupt (like the scheduler does) or from a standard function (usually a + * main-loop). + * + * Each timer has a priority: the timers with higher priorities are scheduled + * before the others. This feature is mostly useful when the manager is called + * from an interrupt. Indeed, the callback function of a timer with a high + * priority cannot be preempted by a timer with a lower priority. + * + * The module locks interrupts when doing critical operations, ensuring that + * critical data are accessed atomically. + * + * State of timers: + * - stopped: initial state after callout_init() + * - scheduled: after a call to callout_schedule(), the timer is in the + * scheduled list of the callout manager + * - expired: after a call to callout_manage(), if the expire time of a + * timer is reached, it is moved in a local list and its state is + * changed to "expired". + * - before starting the callback, the timer goes in state "running". + * + * Once running, the associated timer is not touched anymore by + * callout_manage(). As a result, the timer MUST be either reloaded or stopped + * (and potentially freed). + */ + +#define CALLOUT_MAX_RECURSION 5 + +#ifdef CALLOUT_STATS +/** + * The structure that stores the timer statistics, mostly useful for debug + * purposes. + */ +struct callout_debug_stats { + uint32_t schedule; /**< nb of calls to callout_(re)schedule() */ + uint32_t stop; /**< nb of calls to callout_stop() */ + uint32_t manage; /**< nb of calls to callout_manage() */ + uint32_t max_recursion; /** manage() skipped due to max recursion */ + uint32_t delayed; /** task delayed a bit due to low prio */ + uint32_t hard_delayed; /** task recheduled later due to low priority */ + + uint8_t cur_scheduled; /**< current number of scheduled timers */ + uint8_t cur_expired; /**< current number of expired timers */ + uint8_t cur_running; /**< current number of running timers */ +}; +#endif + +struct callout; +struct callout_mgr; + +/** + * The tyoe of a callout callback function. + */ +typedef void (callout_cb_t)(struct callout_mgr *cm, struct callout *tim, + void *arg); + +/** + * A callout structure, storing all data associated to a timer. + */ +struct callout { + LIST_ENTRY(callout) next; /**< next/prev in list */ + +#define CALLOUT_STATE_STOPPED 0 /**< not scheduled */ +#define CALLOUT_STATE_SCHEDULED 1 /**< pres*/ +#define CALLOUT_STATE_EXPIRED 2 +#define CALLOUT_STATE_RUNNING 3 + uint8_t state; /**< stopped, scheduled, expired */ + uint8_t priority; /**< the priority of the timer */ + uint16_t expire; /**< time when timer should expire */ + + callout_cb_t *f; /**< callback function pointer */ + void *arg; /**< argument given to the cb function. */ +}; + +/* define the callout list */ +LIST_HEAD(callout_list, callout); + +/* static initializer for a timer structure */ +#define CALLOUT_INITIALIZER { } + +/** + * Type of the function used by a callout manager to get a time reference + */ +typedef uint16_t (get_time_t)(void); + +/** + * An instance of callout manager. It is possible to have several managers. A + * callout is attached to one manager at a time. + */ +struct callout_mgr { + get_time_t *get_time; /**< func used to get the time reference */ + uint16_t prev_time; /**< time of previous call */ + uint8_t cur_priority; /** priority of running event */ + uint8_t nb_recursion; /** number of recursion */ + struct callout_list sched_list; /**< list of scheduled timers */ + +#ifdef CALLOUT_STATS + struct callout_debug_stats stats; /**< stats */ +#endif +}; + +/** + * Initialize a callout manager + * + * The callout manager must be initialized before callout_add() or + * callout_manage() can be called. + * + * @param cm + * Pointer to the uninitialized callout manager structure. + * @param get_time + * Pointer to a function that returns a time reference (unsigned 16 bits). + */ +void callout_mgr_init(struct callout_mgr *cm, get_time_t *get_time); + +/** + * Initialize a callout structure and set callback function + * + * Before doing any operation on the callout structure, it has to be initialized + * with this function. It is possible to reinitialize a timer that has been + * previously scheduled, but it must be stopped. + * + * @param tim + * The timer to initialize. + * @param priority + * The priority of the callout (high value means higher priority) + * @param f + * The callback function of the timer. + * @param arg + * The user argument of the callback function. + */ +void callout_init(struct callout *tim, callout_cb_t f, void *arg, + uint8_t priority); + +/** + * Schedule a callout + * + * The callout_schedule() function adds the timer in the scheduled list. After + * the specified amount of ticks are elapsed, the callback function of the timer + * previously given to callout_init() will be invoked with its argument. + * + * The given "tick" value is relative to the current time, and is 16 bits + * wide. As it internally uses signed 16 bits comparison, the max value for + * ticks is 32767. + * + * @param cm + * The callout manager where the timer should be scheduled + * @param tim + * The timer handle + * @param ticks + * The number of ticks before the callback function is called, relative to now + * (the reference is given by the get_time() function of the callout manager). + * @return + * 0 on success, negative on error + */ +int callout_schedule(struct callout_mgr *cm, struct callout *tim, + uint16_t ticks); + +/** + * Reschedule a callout + * + * This function does exactly the same than callout_schedule() except that + * the given time "ticks" is not relative to the current time but to the + * "expire" field of the timer. + * + * Using this function is advised to avoid drift if you want to have periodic + * timers. + * + * This function should preferably be called from the callback function of + * the timer. Indeed, if the "expire" field should be a known value or it + * can result in an undefined behavior + * + * The given "tick" value is relative to the "expire" field of the timer, and is + * 16 bits wide. As it internally uses signed 16 bits comparison, the max value + * for ticks is 32767. + * + * @param cm + * The callout manager where the timer should be scheduled + * @param tim + * The timer handle + * @param ticks + * The number of ticks before the callback function is called, relative to + * the "expire" value of the timer + * @return + * 0 on success, negative on error + */ +int callout_reschedule(struct callout_mgr *cm, struct callout *tim, + uint16_t ticks); + + +/** + * Stop a timer. + * + * The callout_stop() function stops a timer associated with the + * timer handle tim. + * + * If the timer is scheduled or expired, it is removed from the list: the + * callback function won't be invoked. If the timer is stopped or running the + * function does nothing. + * + * If a timer structure is dynamically allocated, invoking callout_stop() is + * needed before freeing the structure, even if the freeing occurs in a + * callback. Indeed, this function can be called safely from a timer + * callback. If it succeeds, the timer is not referenced anymore by the callout + * manager. + * + * @param cm + * The callout manager where the timer is or was scheduled + * @param tim + * The timer + * @return + * 0 on success, negative on error + */ +void callout_stop(struct callout_mgr *cm, struct callout *tim); + + +/** + * Return the state of a timer + * + * @param tim + * The timer + * @return + * - CALLOUT_STATE_STOPPED: the timer is stopped + * - CALLOUT_STATE_SCHEDULED: the timer is scheduled + * - CALLOUT_STATE_EXPIRED: the timer was moved in a local list before + * execution + */ +static inline uint8_t callout_state(struct callout *tim) +{ + return tim->state; +} + +/** + * Manage the timer list and execute callback functions. + * + * This function must be called periodically, either from a loop of from an + * interrupt. It browses the list of scheduled timers and runs all timers that + * are expired. + * + * This function must be called at least every 16384 reference ticks of + * cm->get_time(), but calling it more often is recommanded to avoid delaying + * task abusively. + * + * The function must be called with IRQ allowed. + */ +void callout_manage(struct callout_mgr *cm); + +/** + * Dump statistics about timers. + */ +void callout_dump_stats(struct callout_mgr *cm); + +/** + * Set the current priority level + * + * Prevent callout with a priority lower than "new_prio" to be executed. + * If the current priority of the callout manager is already lower higher + * than "new_prio", the function won't change the running priority. + * + * The returned value should be stored by the caller and restored with + * callout_mgr_restore_prio(), preferably in the same function. + * + * @param cm + * The callout manager + * @param new_prio + * The new running priority + * + * @return + * The value of the running priority before the call og this function + */ +uint8_t callout_mgr_set_prio(struct callout_mgr *cm, uint8_t new_prio); + +/** + * Restore the current priority level + * + * Used after a call to callout_mgr_set_prio(). + * + * @param cm + * The callout manager + * @param old_prio + * The old running priority + */ +void callout_mgr_restore_prio(struct callout_mgr *cm, uint8_t old_prio); + +#endif /* _CALLOUT_H_ */ diff --git a/modules/base/callout/test/Makefile b/modules/base/callout/test/Makefile new file mode 100644 index 0000000..bc52cb9 --- /dev/null +++ b/modules/base/callout/test/Makefile @@ -0,0 +1,39 @@ +# +# Copyright (c) <2014>, Olivier Matz +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the University of California, Berkeley nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +TARGET = main + +AVERSIVE_DIR = ../../../.. + +SRC = $(TARGET).c + +ASRC = + +-include .aversive_conf +include $(AVERSIVE_DIR)/mk/aversive_project.mk + diff --git a/modules/base/callout/test/error_config.h b/modules/base/callout/test/error_config.h new file mode 100644 index 0000000..2e1288a --- /dev/null +++ b/modules/base/callout/test/error_config.h @@ -0,0 +1,31 @@ +/* + * Copyright Droids Corporation, Microb Technology, Eirbot (2005) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Revision : $Id: error_config.h,v 1.4.6.1 2006-11-26 21:06:03 zer0 Exp $ + * + */ + +#ifndef _ERROR_CONFIG_ +#define _ERROR_CONFIG_ + +/** enable the dump of the comment */ +#define ERROR_DUMP_TEXTLOG + +/** enable the dump of filename and line number */ +#define ERROR_DUMP_FILE_LINE + +#endif diff --git a/modules/base/callout/test/main.c b/modules/base/callout/test/main.c new file mode 100644 index 0000000..38c5716 --- /dev/null +++ b/modules/base/callout/test/main.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) <2014>, Olivier Matz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University of California, Berkeley nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include +#include +#include +#include + +static struct callout_mgr global_cm; +static volatile uint16_t time_ref; + +#define C1_TIME_MS 500 +#define C2_TIME_MS 500 +#define C3_TIME_MS 1000 +#define C4_TIME_MS 4000 + +/* return the current time reference (the variable is incremented in the + * interuption */ +static uint16_t get_time_ms(void) +{ + uint8_t flags; + uint16_t ret; + + IRQ_LOCK(flags); + ret = time_ref; + IRQ_UNLOCK(flags); + return ret; +} + +/* printf with a timestamp */ +static void ts_printf(const char *fmt, ...) +{ + va_list ap; + + printf("%d: ", get_time_ms()); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); +} + +static void f1(struct callout_mgr *cm, struct callout *tim, void *arg) +{ + (void)arg; + ts_printf("%s\n", __FUNCTION__); + + callout_reschedule(cm, tim, C1_TIME_MS); +} + +static void f2(struct callout_mgr *cm, struct callout *tim, void *arg) +{ + (void)arg; + ts_printf("%s\n", __FUNCTION__); + + callout_reschedule(cm, tim, C2_TIME_MS); +} + +static void f3(struct callout_mgr *cm, struct callout *tim, void *arg) +{ + (void)arg; + ts_printf("%s START\n", __FUNCTION__); + wait_ms(600); + ts_printf("%s END\n", __FUNCTION__); + + callout_reschedule(cm, tim, C3_TIME_MS); +} + +static void supp(struct callout_mgr *cm, struct callout *tim, void *arg) +{ + struct callout *c2 = arg; + + ts_printf("stopping c1\n"); + callout_stop(cm, c2); +} + +static void timer_interrupt(void) +{ +#ifndef HOST_VERSION + static uint16_t cycles; + + cycles += 256 * 8; /* 8 bits timer + 8 divisor */ + if (cycles > (CONFIG_QUARTZ/1000)) { + cycles -= (CONFIG_QUARTZ/1000); + time_ref++; + } +#else + time_ref++; +#endif + + sei(); /* callout_manage() must be called with irq allowed */ + callout_manage(&global_cm); +} + +static void dump_stats(char c) +{ + if (c != 's') + return; + + callout_dump_stats(&global_cm); +} + +int main(void) +{ + struct callout c1, c2, c3, c4; + uint8_t old_prio; + +#ifdef HOST_VERSION + hostsim_uart_init(); + hostsim_ittimer_add(timer_interrupt, 1 * 1000 * 1000); /* 1ms period */ + hostsim_ittimer_enable(100); /* 100 us */ +#else + uart_init(); + fdevopen(uart0_dev_send, uart0_dev_recv); + timer_init(); + timer0_register_OV_intr(timer_interrupt); +#endif + uart_register_rx_event(0, dump_stats); + + callout_mgr_init(&global_cm, get_time_ms); + sei(); + + printf("f1 every %d ms, high prio (200)\n", C1_TIME_MS); + printf("f2 every %d ms, low prio (50)\n", C2_TIME_MS); + printf("f3 every %d ms, med prio (100), the function lasts 600ms\n", + C3_TIME_MS); + printf("f4 only once after %d ms, very high prio (250), " + "stop task f2\n", C4_TIME_MS); + printf("type s to dump stats\n"); + + callout_init(&c1, f1, NULL, 200); + callout_init(&c2, f2, NULL, 50); + callout_init(&c3, f3, NULL, 100); + callout_init(&c4, supp, &c2, 250); + + callout_schedule(&global_cm, &c1, C1_TIME_MS); + callout_schedule(&global_cm, &c2, C2_TIME_MS); + callout_schedule(&global_cm, &c3, C3_TIME_MS); + callout_schedule(&global_cm, &c4, C4_TIME_MS); + + while (get_time_ms() < 2900) + ; + + old_prio = callout_mgr_set_prio(&global_cm, 150); + ts_printf("set prio 150\n"); + + while (get_time_ms() < 3100) + ; + + ts_printf("set prio 0\n"); + callout_mgr_restore_prio(&global_cm, old_prio); + + while (get_time_ms() < 5000) + ; + + callout_stop(&global_cm, &c1); + callout_stop(&global_cm, &c3); + callout_stop(&global_cm, &c4); + + callout_dump_stats(&global_cm); + wait_ms(10); + +#ifdef HOST_VERSION + hostsim_uart_exit(); +#endif + + return 0; +} diff --git a/modules/base/callout/test/timer_config.h b/modules/base/callout/test/timer_config.h new file mode 100644 index 0000000..47d9f18 --- /dev/null +++ b/modules/base/callout/test/timer_config.h @@ -0,0 +1,36 @@ +/* + * Copyright Droids Corporation, Microb Technology, Eirbot (2006) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Revision : $Id: timer_config.h,v 1.1 2009-02-20 21:10:01 zer0 Exp $ + * + */ + +#define TIMER0_ENABLED + +/* #define TIMER1_ENABLED */ +/* #define TIMER1A_ENABLED */ +/* #define TIMER1B_ENABLED */ +/* #define TIMER1C_ENABLED */ + +/* #define TIMER2_ENABLED */ + +/* #define TIMER3_ENABLED */ +/* #define TIMER3A_ENABLED */ +/* #define TIMER3B_ENABLED */ +/* #define TIMER3C_ENABLED */ + +#define TIMER0_PRESCALER_DIV 8 diff --git a/modules/base/callout/test/uart_config.h b/modules/base/callout/test/uart_config.h new file mode 100644 index 0000000..e55c352 --- /dev/null +++ b/modules/base/callout/test/uart_config.h @@ -0,0 +1,72 @@ +/* + * Copyright Droids Corporation, Microb Technology, Eirbot (2005) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Revision : $Id: uart_config.h,v 1.3.10.1 2006-11-26 21:06:02 zer0 Exp $ + * + */ + +/* Droids-corp 2004 - Zer0 + * config for uart module + */ + +#ifndef UART_CONFIG_H +#define UART_CONFIG_H + +/* + * UART0 definitions + */ + +/* compile uart0 fonctions, undefine it to pass compilation */ +#define UART0_COMPILE + +/* enable uart0 if == 1, disable if == 0 */ +#define UART0_ENABLED 1 + +/* enable uart0 interrupts if == 1, disable if == 0 */ +#define UART0_INTERRUPT_ENABLED 1 + +#define UART0_BAUDRATE 38400 + +/* + * if you enable this, the maximum baudrate you can reach is + * higher, but the precision is lower. + */ +#define UART0_USE_DOUBLE_SPEED 0 +//#define UART0_USE_DOUBLE_SPEED 1 + +#define UART0_RX_FIFO_SIZE 4 +#define UART0_TX_FIFO_SIZE 4 +//#define UART0_NBITS 5 +//#define UART0_NBITS 6 +//#define UART0_NBITS 7 +#define UART0_NBITS 8 +//#define UART0_NBITS 9 + +#define UART0_PARITY UART_PARTITY_NONE +//#define UART0_PARITY UART_PARTITY_ODD +//#define UART0_PARITY UART_PARTITY_EVEN + +#define UART0_STOP_BIT UART_STOP_BITS_1 +//#define UART0_STOP_BIT UART_STOP_BITS_2 + + + + +/* .... same for uart 1, 2, 3 ... */ + +#endif +