BeRTOS
timer.c
Go to the documentation of this file.
00001 
00039 #include "timer.h"
00040 #include "hw/hw_timer.h"
00041 
00042 #include "cfg/cfg_timer.h"
00043 #include "cfg/cfg_wdt.h"
00044 #include "cfg/cfg_proc.h"
00045 #include "cfg/cfg_signal.h"
00046 #include <cfg/os.h>
00047 #include <cfg/debug.h>
00048 #include <cfg/module.h>
00049 
00050 #include <cpu/attr.h>
00051 #include <cpu/types.h>
00052 #include <cpu/irq.h>
00053 #include <cpu/power.h> // cpu_relax()
00054 
00055 #include <kern/proc_p.h> // proc_decQuantun()
00056 
00057 /*
00058  * Include platform-specific binding code if we're hosted.
00059  * Try the CPU specific one for bare-metal environments.
00060  */
00061 #if OS_HOSTED
00062     //#include OS_CSOURCE(timer)
00063     #include <emul/timer_posix.c>
00064 #else
00065     #ifndef WIZ_AUTOGEN
00066         #warning Deprecated: now you should include timer_<cpu> directly in the makefile. Remove this line and the following once done.
00067         #include CPU_CSOURCE(timer)
00068     #endif
00069 #endif
00070 
00071 /*
00072  * Sanity check for config parameters required by this module.
00073  */
00074 #if !defined(CONFIG_KERN) || ((CONFIG_KERN != 0) && CONFIG_KERN != 1)
00075     #error CONFIG_KERN must be set to either 0 or 1 in config.h
00076 #endif
00077 #if !defined(CONFIG_WATCHDOG) || ((CONFIG_WATCHDOG != 0) && CONFIG_WATCHDOG != 1)
00078     #error CONFIG_WATCHDOG must be set to either 0 or 1 in config.h
00079 #endif
00080 
00081 #if CONFIG_WATCHDOG
00082     #include <drv/wdt.h>
00083 #endif
00084 
00085 #if defined (CONFIG_KERN_SIGNALS) && CONFIG_KERN_SIGNALS
00086     #include <kern/signal.h> /* sig_wait(), sig_check() */
00087     #include <kern/proc.h>   /* proc_current() */
00088     #include <cfg/macros.h>  /* BV() */
00089 #endif
00090 
00091 
00102 #if !defined(CONFIG_TIMER_STROBE) || !CONFIG_TIMER_STROBE
00103     #define TIMER_STROBE_ON    do {/*nop*/} while(0)
00104     #define TIMER_STROBE_OFF   do {/*nop*/} while(0)
00105     #define TIMER_STROBE_INIT  do {/*nop*/} while(0)
00106 #endif
00107 
00108 
00110 volatile ticks_t _clock;
00111 
00112 
00113 #if CONFIG_TIMER_EVENTS
00114 
00118 REGISTER static List timers_queue;
00119 
00124 INLINE void timer_addToList(Timer *timer, List *queue)
00125 {
00126     /* Inserting timers twice causes mayhem. */
00127     ASSERT(timer->magic != TIMER_MAGIC_ACTIVE);
00128     DB(timer->magic = TIMER_MAGIC_ACTIVE;)
00129 
00130 
00131     /* Calculate expiration time for this timer */
00132     timer->tick = _clock + timer->_delay;
00133 
00134     /*
00135      * Search for the first node whose expiration time is
00136      * greater than the timer we want to add.
00137      */
00138     Timer *node = (Timer *)LIST_HEAD(queue);
00139     while (node->link.succ)
00140     {
00141         /*
00142          * Stop just after the insertion point.
00143          * (this fancy compare takes care of wrap-arounds).
00144          */
00145         if (node->tick - timer->tick > 0)
00146             break;
00147 
00148         /* Go to next node */
00149         node = (Timer *)node->link.succ;
00150     }
00151 
00152     /* Enqueue timer request into the list */
00153     INSERT_BEFORE(&timer->link, &node->link);
00154 }
00155 
00165 void timer_add(Timer *timer)
00166 {
00167     ATOMIC(timer_addToList(timer, &timers_queue));
00168 }
00169 
00176 Timer *timer_abort(Timer *timer)
00177 {
00178     ATOMIC(REMOVE(&timer->link));
00179     DB(timer->magic = TIMER_MAGIC_INACTIVE;)
00180 
00181     return timer;
00182 }
00183 
00184 
00185 INLINE void timer_poll(List *queue)
00186 {
00187     Timer *timer;
00188 
00189     /*
00190      * Check the first timer request in the list and process
00191      * it when it has expired. Repeat this check until the
00192      * first node has not yet expired. Since the list is sorted
00193      * by expiry time, all the following requests are guaranteed
00194      * to expire later.
00195      */
00196     while ((timer = (Timer *)LIST_HEAD(queue))->link.succ)
00197     {
00198         /* This request in list has not yet expired? */
00199         if (timer_clock() - timer->tick < 0)
00200             break;
00201 
00202         /* Retreat the expired timer */
00203         REMOVE(&timer->link);
00204         DB(timer->magic = TIMER_MAGIC_INACTIVE;)
00205 
00206         /* Execute the associated event */
00207         event_do(&timer->expire);
00208     }
00209 }
00210 
00215 void synctimer_add(Timer *timer, List *queue)
00216 {
00217     timer_addToList(timer, queue);
00218 }
00219 
00237 void synctimer_poll(List *queue)
00238 {
00239     timer_poll(queue);
00240 }
00241 
00242 #endif /* CONFIG_TIMER_EVENTS */
00243 
00244 
00250 void timer_delayTicks(ticks_t delay)
00251 {
00252     /* We shouldn't sleep with interrupts disabled */
00253     IRQ_ASSERT_ENABLED();
00254 
00255 #if CONFIG_KERN_SIGNALS
00256     Timer t;
00257     DB(t.magic = TIMER_MAGIC_INACTIVE;)
00258     if (proc_preemptAllowed())
00259     {
00260         timer_setEvent(&t);
00261         timer_setDelay(&t, delay);
00262         timer_add(&t);
00263         timer_waitEvent(&t);
00264     }
00265     else
00266 #endif /* !CONFIG_KERN_SIGNALS */
00267     {
00268         ticks_t start = timer_clock();
00269 
00270         /* Busy wait */
00271         while (timer_clock() - start < delay)
00272             cpu_relax();
00273     }
00274 }
00275 
00276 
00277 #if CONFIG_TIMER_UDELAY
00278 
00285 void timer_busyWait(hptime_t delay)
00286 {
00287     hptime_t now, prev = timer_hw_hpread();
00288     hptime_t delta;
00289 
00290     for (;;)
00291     {
00292         now = timer_hw_hpread();
00293         /*
00294          * The timer counter may wrap here and "prev" can become
00295          * greater than "now". So, be sure to always evaluate a
00296          * coherent timer difference:
00297          *
00298          * 0     prev            now   TIMER_HW_CNT
00299          * |_____|_______________|_____|
00300          *        ^^^^^^^^^^^^^^^
00301          * delta = now - prev
00302          *
00303          * 0     now             prev  TIMER_HW_CNT
00304          * |_____|_______________|_____|
00305          *  ^^^^^                 ^^^^^
00306          * delta = (TIMER_HW_CNT - prev) + now
00307          *
00308          * NOTE: TIMER_HW_CNT can be any value, not necessarily a power
00309          * of 2. For this reason the "%" operator is not suitable for
00310          * the generic case.
00311          */
00312         delta = (now < prev) ? ((hptime_t)TIMER_HW_CNT - prev + now) :
00313                         (now - prev);
00314         if (delta >= delay)
00315             break;
00316         delay -= delta;
00317         prev = now;
00318     }
00319 }
00320 
00328 void timer_delayHp(hptime_t delay)
00329 {
00330     if (UNLIKELY(delay > us_to_hptime(1000)))
00331     {
00332         timer_delayTicks(delay / (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC));
00333         delay %= (TIMER_HW_HPTICKS_PER_SEC / TIMER_TICKS_PER_SEC);
00334     }
00335 
00336     timer_busyWait(delay);
00337 }
00338 #endif /* CONFIG_TIMER_UDELAY */
00339 
00344 DEFINE_TIMER_ISR
00345 {
00346     /*
00347      * With the Metrowerks compiler, the only way to force the compiler generate
00348      * an interrupt service routine is to put a pragma directive within the function
00349      * body.
00350      */
00351     #ifdef __MWERKS__
00352     #pragma interrupt saveall
00353     #endif
00354 
00355     /*
00356      * On systems sharing IRQ line and vector, this check is needed
00357      * to ensure that IRQ is generated by timer source.
00358      */
00359     if (!timer_hw_triggered())
00360         return;
00361 
00362     TIMER_STROBE_ON;
00363 
00364     /* Update the master ms counter */
00365     ++_clock;
00366 
00367     /* Update the current task's quantum (if enabled). */
00368     proc_decQuantum();
00369 
00370     #if CONFIG_TIMER_EVENTS
00371         timer_poll(&timers_queue);
00372     #endif
00373 
00374     /* Perform hw IRQ handling */
00375     timer_hw_irq();
00376 
00377     TIMER_STROBE_OFF;
00378 }
00379 
00380 MOD_DEFINE(timer)
00381 
00382 
00385 void timer_init(void)
00386 {
00387     #if CONFIG_KERN_IRQ
00388         MOD_CHECK(irq);
00389     #endif
00390 
00391     #if CONFIG_TIMER_EVENTS
00392         LIST_INIT(&timers_queue);
00393     #endif
00394 
00395     TIMER_STROBE_INIT;
00396 
00397     _clock = 0;
00398 
00399     timer_hw_init();
00400 
00401     MOD_INIT(timer);
00402 }
00403 
00404 
00405 #if (ARCH & ARCH_EMUL)
00406 
00409 void timer_cleanup(void)
00410 {
00411     MOD_CLEANUP(timer);
00412 
00413     timer_hw_cleanup();
00414 
00415     // Hmmm... apparently, the demo app does not cleanup properly
00416     //ASSERT(LIST_EMPTY(&timers_queue));
00417 }
00418 #endif /* ARCH_EMUL */