From a868cb9fc2869be61eff03c40b8748f612a929e7 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Mon, 11 Apr 2016 12:37:43 +0200 Subject: [PATCH 1/4] sys: div: add div_u64_by_125() --- sys/include/div.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/sys/include/div.h b/sys/include/div.h index d55a81ac223b..e36e06f9429c 100644 --- a/sys/include/div.h +++ b/sys/include/div.h @@ -44,6 +44,33 @@ static inline uint64_t div_u64_by_15625(uint64_t val) return (val * 0x431bde83UL) >> (12 + 32); } +/** + * @brief Integer divide val by 125 + * + * This function can be used to convert uint64_t microsecond times (or + * intervals) to miliseconds and store them in uint32_t variables, with up to + * ~50 days worth of miliseconds ((2**32*1000) -1). + * Use e.g., ms = div_u64_by_125(microseconds >> 3) + * + * @pre val <= 536870911999 ((2**32 * 125) -1) + * + * @param[in] val dividend + * @return (val / 125) + */ +static inline uint32_t div_u64_by_125(uint64_t val) +{ + /* a higher value would overflow the result type */ + assert(val <= 536870911999LLU); + + uint32_t hi = val >> 32; + uint32_t lo = val; + uint32_t r = (lo >> 16) + (hi << 16); + uint32_t res = r / 125; + r = ((r % 125) << 16) + (lo & 0xFFFF); + res = (res << 16) + r / 125; + return res; +} + /** * @brief Integer divide val by 1000000 * From d20ceb8809daf9bdc372276f834e9d5783dae233 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Mon, 11 Apr 2016 21:05:08 +0200 Subject: [PATCH 2/4] WIP: evtimer initial --- Makefile.dep | 4 + sys/evtimer/Makefile | 1 + sys/evtimer/evtimer.c | 185 ++++++++++++++++++++++++++++++++++++++++++ sys/include/evtimer.h | 80 ++++++++++++++++++ 4 files changed, 270 insertions(+) create mode 100644 sys/evtimer/Makefile create mode 100644 sys/evtimer/evtimer.c create mode 100644 sys/include/evtimer.h diff --git a/Makefile.dep b/Makefile.dep index e6f70295b468..af39fe1142a5 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -531,6 +531,10 @@ ifneq (,$(filter phydat,$(USEMODULE))) USEMODULE += fmt endif +ifneq (,$(filter evtimer,$(USEMODULE))) + USEMODULE += xtimer +endif + ifneq (,$(filter random,$(USEMODULE))) # select default prng ifeq (,$(filter prng_%,$(USEMODULE))) diff --git a/sys/evtimer/Makefile b/sys/evtimer/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/sys/evtimer/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/evtimer/evtimer.c b/sys/evtimer/evtimer.c new file mode 100644 index 000000000000..a052fd7404ea --- /dev/null +++ b/sys/evtimer/evtimer.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2016 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_evtimer + * @{ + * + * @file + * @brief event timer implementation + * + * @author Kaspar Schleiser + * + * @} + */ +#include "evtimer.h" +#include "div.h" +#include "irq.h" +#include "msg.h" +#include "xtimer.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static void _add_event_to_list(evtimer_event_t *list, evtimer_event_t *event) +{ + uint32_t delta_sum = 0; + while (list->next) { + evtimer_event_t *list_entry = list->next; + if ((list_entry->offset + delta_sum) > event->offset) { + break; + } + delta_sum += list_entry->offset; + list = list->next; + } + + event->next = list->next; + if (list->next) { + evtimer_event_t *next_entry = list->next; + next_entry->offset += delta_sum; + next_entry->offset -= event->offset; + } + event->offset -= delta_sum; + + list->next = event; +} + +static void _del_event_from_list(evtimer_event_t *list, evtimer_event_t *event) +{ + while (list->next) { + evtimer_event_t *list_entry = list->next; + if (list_entry == event) { + list->next = event->next; + if (list->next) { + list_entry = list->next; + list_entry->offset += event->offset; + } + break; + } + list = list->next; + } +} + +static void _set_timer(xtimer_t *timer, uint32_t offset) +{ + uint64_t offset_in_us = (uint64_t)offset * 1000; + DEBUG("evtimer: now=%"PRIu32" setting xtimer to %"PRIu32":%"PRIu32"\n", xtimer_now(), + (uint32_t)(offset_in_us>>32), (uint32_t)(offset_in_us)); + _xtimer_set64(timer, offset_in_us, offset_in_us>>32); +} + +static void _update_timer(evtimer_t *evtimer) +{ + if (evtimer->events) { + evtimer_event_t *event = evtimer->events; + _set_timer(&evtimer->timer, event->offset); + } + else { + xtimer_remove(&evtimer->timer); + } +} + +static uint32_t _get_offset(xtimer_t *timer) +{ + uint64_t now = xtimer_now64(); + uint64_t target = ((uint64_t)timer->long_target)<<32 | timer->target; + + if (target <= now) { + return 0; + } + else { + target -= now; + /* add half of 125 so integer division rounds to nearest */ + return div_u64_by_125((target >> 3) + 62); + } +} + +static void _update_head_offset(evtimer_t *evtimer) +{ + if (evtimer->events) { + evtimer_event_t *event = evtimer->events; + event->offset = _get_offset(&evtimer->timer); + DEBUG("evtimer: _update_head_offset(): new head offset %"PRIu32"\n", event->offset); + } +} + +void evtimer_add(evtimer_t *evtimer, evtimer_event_t *event) +{ + unsigned state = irq_disable(); + DEBUG("evtimer_add(): adding event with offset %"PRIu32"\n", event->offset); + + _update_head_offset(evtimer); + _add_event_to_list((evtimer_event_t*)&evtimer->events, event); + + if (evtimer->events == event) { + _set_timer(&evtimer->timer, event->offset); + } + irq_restore(state); +} + +void evtimer_del(evtimer_t *evtimer, evtimer_event_t *event) +{ + unsigned state = irq_disable(); + DEBUG("evtimer_del(): removing event with offset %"PRIu32"\n", event->offset); + _update_head_offset(evtimer); + _del_event_from_list((evtimer_event_t*)&evtimer->events, event); + _update_timer(evtimer); + irq_restore(state); +} + +static evtimer_event_t *_get_next(evtimer_t *evtimer) +{ + evtimer_event_t *event = evtimer->events; + if (event && (event->offset == 0)) { + evtimer->events = event->next; + return event; + } + else { + return NULL; + } +} + +void evtimer_msg_handler(void *arg) +{ + DEBUG("evtimer_msg_handler()\n"); + + evtimer_t *evtimer = (evtimer_t*) arg; + + evtimer_event_t *event = evtimer->events; + event->offset = 0; + + evtimer_msg_event_t *mevent; + while ((mevent = (evtimer_msg_event_t *)_get_next(evtimer))) { + msg_send_int(&mevent->msg, mevent->msg.sender_pid); + } + + _update_timer(evtimer); + + if (!irq_is_in()) { + thread_yield_higher(); + } +} + +void evtimer_init(evtimer_t *evtimer, void(*handler)(void*)) +{ + evtimer->timer.callback = handler; + evtimer->timer.arg = (void*) evtimer; + evtimer->events = NULL; +} + +#if ENABLE_DEBUG == 1 +void evtimer_print(evtimer_t *evtimer) +{ + evtimer_event_t *list = evtimer->events; + while (list->next) { + evtimer_event_t *list_entry = list->next; + DEBUG("ev offset=%u\n", (unsigned)list_entry->offset); + list = list->next; + } +} +#endif diff --git a/sys/include/evtimer.h b/sys/include/evtimer.h new file mode 100644 index 000000000000..dfa1cbd26c1e --- /dev/null +++ b/sys/include/evtimer.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_evtimer Milisecond interval timers + * @ingroup sys + * @brief Provides timers for events up to 2**32 miliseconds in the future + * + * RIOT's main timer subsystem is xtimer, but for many applications xtimer's + * 64bit absolute time values are wasteful or clumsy to use. + * + * Compared to xtimer, evtimer offers: + * + * - only relative 32bit milisecond timer values + * Events can be scheduled with a relative offset of up to ~49.7 days in the + * future. + * For time-critical stuff, use xtimer! + * + * - more flexible, "intrusive" timer type + * evtimer_event_t only contains the necessary fields, which can be extended + * as needed, and handlers define actions taken on timer triggers. + * Check out evtimer_msg_event as example. + * + * - uses xtimer as backend + * + * @{ + * + * @file + * @brief evtimer API + * + * @author Kaspar Schleiser + */ + +#ifndef EVTIMER_H +#define EVTIMER_H + +#include + +#include "msg.h" +#include "xtimer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** forward declaration */ +typedef struct evtimer_event evtimer_event_t; + +typedef struct { + xtimer_t timer; + evtimer_event_t *events; +} evtimer_t; + +struct evtimer_event { + evtimer_event_t *next; + uint32_t offset; +}; + +typedef struct { + evtimer_event_t event; + msg_t msg; +} evtimer_msg_event_t; + +void evtimer_init(evtimer_t *evtimer, void(*handler)(void*)); +void evtimer_add(evtimer_t *evtimer, evtimer_event_t *event); +void evtimer_del(evtimer_t *evtimer, evtimer_event_t *event); +void evtimer_msg_handler(void *arg); +void evtimer_print(evtimer_t *evtimer); + +#ifdef __cplusplus +} +#endif + +#endif /* EVTIMER_H */ +/** @} */ From 6acf0d6dde4ee6633ac57b231647ddb6c4e4c3ea Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Thu, 27 Oct 2016 15:03:14 +0200 Subject: [PATCH 3/4] squash: evtimer: misc changes imported from miri64 --- sys/evtimer/evtimer.c | 37 +++++++++++++++++++++---------------- sys/include/evtimer.h | 8 ++++---- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/sys/evtimer/evtimer.c b/sys/evtimer/evtimer.c index a052fd7404ea..c8daddb264be 100644 --- a/sys/evtimer/evtimer.c +++ b/sys/evtimer/evtimer.c @@ -29,6 +29,7 @@ static void _add_event_to_list(evtimer_event_t *list, evtimer_event_t *event) { uint32_t delta_sum = 0; + while (list->next) { evtimer_event_t *list_entry = list->next; if ((list_entry->offset + delta_sum) > event->offset) { @@ -68,9 +69,10 @@ static void _del_event_from_list(evtimer_event_t *list, evtimer_event_t *event) static void _set_timer(xtimer_t *timer, uint32_t offset) { uint64_t offset_in_us = (uint64_t)offset * 1000; - DEBUG("evtimer: now=%"PRIu32" setting xtimer to %"PRIu32":%"PRIu32"\n", xtimer_now(), - (uint32_t)(offset_in_us>>32), (uint32_t)(offset_in_us)); - _xtimer_set64(timer, offset_in_us, offset_in_us>>32); + + DEBUG("evtimer: now=%" PRIu32 " setting xtimer to %" PRIu32 ":%" PRIu32 "\n", xtimer_now(), + (uint32_t)(offset_in_us >> 32), (uint32_t)(offset_in_us)); + _xtimer_set64(timer, offset_in_us, offset_in_us >> 32); } static void _update_timer(evtimer_t *evtimer) @@ -87,7 +89,7 @@ static void _update_timer(evtimer_t *evtimer) static uint32_t _get_offset(xtimer_t *timer) { uint64_t now = xtimer_now64(); - uint64_t target = ((uint64_t)timer->long_target)<<32 | timer->target; + uint64_t target = ((uint64_t)timer->long_target) << 32 | timer->target; if (target <= now) { return 0; @@ -104,17 +106,18 @@ static void _update_head_offset(evtimer_t *evtimer) if (evtimer->events) { evtimer_event_t *event = evtimer->events; event->offset = _get_offset(&evtimer->timer); - DEBUG("evtimer: _update_head_offset(): new head offset %"PRIu32"\n", event->offset); + DEBUG("evtimer: _update_head_offset(): new head offset %" PRIu32 "\n", event->offset); } } void evtimer_add(evtimer_t *evtimer, evtimer_event_t *event) { unsigned state = irq_disable(); - DEBUG("evtimer_add(): adding event with offset %"PRIu32"\n", event->offset); + + DEBUG("evtimer_add(): adding event with offset %" PRIu32 "\n", event->offset); _update_head_offset(evtimer); - _add_event_to_list((evtimer_event_t*)&evtimer->events, event); + _add_event_to_list(evtimer->events, event); if (evtimer->events == event) { _set_timer(&evtimer->timer, event->offset); @@ -125,9 +128,11 @@ void evtimer_add(evtimer_t *evtimer, evtimer_event_t *event) void evtimer_del(evtimer_t *evtimer, evtimer_event_t *event) { unsigned state = irq_disable(); - DEBUG("evtimer_del(): removing event with offset %"PRIu32"\n", event->offset); + + DEBUG("evtimer_del(): removing event with offset %" PRIu32 "\n", event->offset); + _update_head_offset(evtimer); - _del_event_from_list((evtimer_event_t*)&evtimer->events, event); + _del_event_from_list(evtimer->events, event); _update_timer(evtimer); irq_restore(state); } @@ -135,6 +140,7 @@ void evtimer_del(evtimer_t *evtimer, evtimer_event_t *event) static evtimer_event_t *_get_next(evtimer_t *evtimer) { evtimer_event_t *event = evtimer->events; + if (event && (event->offset == 0)) { evtimer->events = event->next; return event; @@ -148,7 +154,7 @@ void evtimer_msg_handler(void *arg) { DEBUG("evtimer_msg_handler()\n"); - evtimer_t *evtimer = (evtimer_t*) arg; + evtimer_t *evtimer = (evtimer_t *) arg; evtimer_event_t *event = evtimer->events; event->offset = 0; @@ -165,21 +171,20 @@ void evtimer_msg_handler(void *arg) } } -void evtimer_init(evtimer_t *evtimer, void(*handler)(void*)) +void evtimer_init(evtimer_t *evtimer, void (*handler)(void *)) { evtimer->timer.callback = handler; - evtimer->timer.arg = (void*) evtimer; + evtimer->timer.arg = (void *) evtimer; evtimer->events = NULL; } -#if ENABLE_DEBUG == 1 -void evtimer_print(evtimer_t *evtimer) +void evtimer_print(const evtimer_t *evtimer) { evtimer_event_t *list = evtimer->events; + while (list->next) { evtimer_event_t *list_entry = list->next; - DEBUG("ev offset=%u\n", (unsigned)list_entry->offset); + printf("ev offset=%u\n", (unsigned)list_entry->offset); list = list->next; } } -#endif diff --git a/sys/include/evtimer.h b/sys/include/evtimer.h index dfa1cbd26c1e..b06a9305e133 100644 --- a/sys/include/evtimer.h +++ b/sys/include/evtimer.h @@ -7,16 +7,16 @@ */ /** - * @defgroup sys_evtimer Milisecond interval timers + * @defgroup sys_evtimer Millisecond interval timers * @ingroup sys - * @brief Provides timers for events up to 2**32 miliseconds in the future + * @brief Provides timers for events up to 2**32 milliseconds in the future * * RIOT's main timer subsystem is xtimer, but for many applications xtimer's * 64bit absolute time values are wasteful or clumsy to use. * * Compared to xtimer, evtimer offers: * - * - only relative 32bit milisecond timer values + * - only relative 32bit millisecond timer values * Events can be scheduled with a relative offset of up to ~49.7 days in the * future. * For time-critical stuff, use xtimer! @@ -70,7 +70,7 @@ void evtimer_init(evtimer_t *evtimer, void(*handler)(void*)); void evtimer_add(evtimer_t *evtimer, evtimer_event_t *event); void evtimer_del(evtimer_t *evtimer, evtimer_event_t *event); void evtimer_msg_handler(void *arg); -void evtimer_print(evtimer_t *evtimer); +void evtimer_print(const evtimer_t *evtimer); #ifdef __cplusplus } From 0b46bcbab419b8ec3b76fb178b7794370bde3590 Mon Sep 17 00:00:00 2001 From: zhuoshuguo Date: Sun, 6 Nov 2016 12:13:51 +0100 Subject: [PATCH 4/4] evtimer: add check event scheduled functionality. --- sys/evtimer/evtimer.c | 12 ++++++++++++ sys/include/evtimer.h | 1 + 2 files changed, 13 insertions(+) diff --git a/sys/evtimer/evtimer.c b/sys/evtimer/evtimer.c index c8daddb264be..5a255e7f1fc9 100644 --- a/sys/evtimer/evtimer.c +++ b/sys/evtimer/evtimer.c @@ -137,6 +137,18 @@ void evtimer_del(evtimer_t *evtimer, evtimer_event_t *event) irq_restore(state); } +bool evtimer_is_event_sched(evtimer_t *evtimer, evtimer_event_t *event) +{ + evtimer_event_t *list_entry = evtimer->events; + while (list_entry) { + if (list_entry == event) { + return true; + } + list_entry = list_entry->next; + } + return false; +} + static evtimer_event_t *_get_next(evtimer_t *evtimer) { evtimer_event_t *event = evtimer->events; diff --git a/sys/include/evtimer.h b/sys/include/evtimer.h index b06a9305e133..15b8151d6667 100644 --- a/sys/include/evtimer.h +++ b/sys/include/evtimer.h @@ -69,6 +69,7 @@ typedef struct { void evtimer_init(evtimer_t *evtimer, void(*handler)(void*)); void evtimer_add(evtimer_t *evtimer, evtimer_event_t *event); void evtimer_del(evtimer_t *evtimer, evtimer_event_t *event); +bool evtimer_is_event_sched(evtimer_t *evtimer, evtimer_event_t *event); void evtimer_msg_handler(void *arg); void evtimer_print(const evtimer_t *evtimer);